From ba7a7756b25884770f148b41a94e18f202720ba1 Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Thu, 17 Oct 2024 18:30:30 +0530 Subject: [PATCH 1/5] adding dev-v0.24.4 tag to this commit to ensure building --- Dockerfile | 6 + .../async_to_sync_wrapper.html | 166 - html/supertokens_python/asyncio/index.html | 384 --- html/supertokens_python/constants.html | 90 - html/supertokens_python/exceptions.html | 233 -- .../framework/django/django_middleware.html | 258 -- .../framework/django/django_request.html | 429 --- .../framework/django/django_response.html | 395 --- .../framework/django/framework.html | 140 - .../framework/django/index.html | 101 - .../framework/fastapi/fastapi_middleware.html | 281 -- .../framework/fastapi/fastapi_request.html | 391 --- .../framework/fastapi/fastapi_response.html | 412 --- .../framework/fastapi/framework.html | 139 - .../framework/fastapi/index.html | 101 - .../framework/flask/flask_middleware.html | 400 --- .../framework/flask/flask_request.html | 412 --- .../framework/flask/flask_response.html | 416 --- .../framework/flask/framework.html | 139 - .../framework/flask/index.html | 101 - html/supertokens_python/framework/index.html | 113 - .../supertokens_python/framework/request.html | 417 --- .../framework/response.html | 320 -- html/supertokens_python/framework/types.html | 190 -- html/supertokens_python/index.html | 267 -- .../ingredients/emaildelivery/index.html | 166 - .../emaildelivery/services/index.html | 83 - .../emaildelivery/services/smtp.html | 275 -- .../ingredients/emaildelivery/types.html | 433 --- .../supertokens_python/ingredients/index.html | 88 - .../ingredients/smsdelivery/index.html | 166 - .../smsdelivery/services/index.html | 88 - .../smsdelivery/services/supertokens.html | 73 - .../smsdelivery/services/twilio.html | 117 - .../ingredients/smsdelivery/types.html | 419 --- html/supertokens_python/interfaces.html | 254 -- html/supertokens_python/logger.html | 257 -- .../normalised_url_domain.html | 253 -- .../normalised_url_path.html | 369 --- .../post_init_callbacks.html | 172 - html/supertokens_python/process_state.html | 243 -- html/supertokens_python/querier.html | 1661 ---------- .../recipe/dashboard/api/analytics.html | 253 -- .../recipe/dashboard/api/dashboard.html | 127 - .../recipe/dashboard/api/implementation.html | 271 -- .../recipe/dashboard/api/index.html | 1174 ------- .../recipe/dashboard/api/list_tenants.html | 150 - .../recipe/dashboard/api/search/getTags.html | 116 - .../recipe/dashboard/api/search/index.html | 65 - .../recipe/dashboard/api/signin.html | 169 - .../recipe/dashboard/api/signout.html | 144 - .../dashboard/api/userdetails/index.html | 115 - .../api/userdetails/user_delete.html | 112 - .../userdetails/user_email_verify_get.html | 128 - .../userdetails/user_email_verify_put.html | 181 - .../user_email_verify_token_post.html | 143 - .../dashboard/api/userdetails/user_get.html | 188 -- .../api/userdetails/user_metadata_get.html | 125 - .../api/userdetails/user_metadata_put.html | 182 -- .../api/userdetails/user_password_put.html | 256 -- .../dashboard/api/userdetails/user_put.html | 584 ---- .../api/userdetails/user_sessions_get.html | 164 - .../api/userdetails/user_sessions_post.html | 116 - .../recipe/dashboard/api/users_count_get.html | 126 - .../recipe/dashboard/api/users_get.html | 275 -- .../recipe/dashboard/api/validate_key.html | 134 - .../recipe/dashboard/constants.html | 73 - .../recipe/dashboard/exceptions.html | 125 - .../recipe/dashboard/index.html | 166 - .../recipe/dashboard/interfaces.html | 2084 ------------ .../recipe/dashboard/recipe.html | 1187 ------- .../dashboard/recipe_implementation.html | 342 -- .../recipe/dashboard/utils.html | 1060 ------ .../emailpassword/api/email_exists.html | 136 - .../api/generate_password_reset_token.html | 150 - .../emailpassword/api/implementation.html | 720 ---- .../recipe/emailpassword/api/index.html | 120 - .../emailpassword/api/password_reset.html | 164 - .../recipe/emailpassword/api/signin.html | 148 - .../recipe/emailpassword/api/signup.html | 180 - .../recipe/emailpassword/api/utils.html | 270 -- .../recipe/emailpassword/asyncio/index.html | 490 --- .../recipe/emailpassword/constants.html | 80 - .../emailpassword/emaildelivery/index.html | 83 - .../backward_compatibility/index.html | 287 -- .../emaildelivery/services/index.html | 92 - .../emaildelivery/services/smtp/index.html | 217 -- .../services/smtp/password_reset.html | 146 - .../services/smtp/password_reset_email.html | 969 ------ .../smtp/service_implementation/index.html | 170 - .../recipe/emailpassword/exceptions.html | 208 -- .../recipe/emailpassword/index.html | 198 -- .../recipe/emailpassword/interfaces.html | 1607 --------- .../recipe/emailpassword/recipe.html | 861 ----- .../emailpassword/recipe_implementation.html | 675 ---- .../recipe/emailpassword/syncio/index.html | 428 --- .../recipe/emailpassword/types.html | 422 --- .../recipe/emailpassword/utils.html | 839 ----- .../emailverification/api/email_verify.html | 193 -- .../api/generate_email_verify_token.html | 137 - .../recipe/emailverification/api/index.html | 92 - .../emailverification/asyncio/index.html | 577 ---- .../recipe/emailverification/constants.html | 73 - .../emaildelivery/index.html | 83 - .../backward_compatibility/index.html | 245 -- .../emaildelivery/services/index.html | 92 - .../services/smtp/email_verify.html | 146 - .../services/smtp/email_verify_email.html | 977 ------ .../emaildelivery/services/smtp/index.html | 215 -- .../services/smtp/service_implementation.html | 175 - .../ev_claim_validators.html | 72 - .../recipe/emailverification/exceptions.html | 140 - .../recipe/emailverification/index.html | 210 -- .../recipe/emailverification/interfaces.html | 1169 ------- .../recipe/emailverification/recipe.html | 1679 ---------- .../recipe_implementation.html | 372 --- .../emailverification/syncio/index.html | 357 -- .../recipe/emailverification/types.html | 236 -- .../recipe/emailverification/utils.html | 322 -- html/supertokens_python/recipe/index.html | 115 - .../recipe/jwt/api/implementation.html | 157 - .../recipe/jwt/api/index.html | 70 - .../recipe/jwt/api/jwks_get.html | 126 - .../recipe/jwt/asyncio/index.html | 150 - .../recipe/jwt/constants.html | 73 - .../recipe/jwt/exceptions.html | 108 - html/supertokens_python/recipe/jwt/index.html | 165 - .../recipe/jwt/interfaces.html | 484 --- .../supertokens_python/recipe/jwt/recipe.html | 543 --- .../recipe/jwt/recipe_implementation.html | 332 -- .../recipe/jwt/syncio/index.html | 141 - html/supertokens_python/recipe/jwt/utils.html | 195 -- .../multitenancy/api/implementation.html | 316 -- .../recipe/multitenancy/api/index.html | 89 - .../multitenancy/api/login_methods.html | 132 - .../recipe/multitenancy/asyncio/index.html | 404 --- .../recipe/multitenancy/constants.html | 73 - .../recipe/multitenancy/exceptions.html | 108 - .../recipe/multitenancy/index.html | 180 - .../recipe/multitenancy/interfaces.html | 1705 ---------- .../recipe/multitenancy/recipe.html | 743 ----- .../multitenancy/recipe_implementation.html | 960 ------ .../recipe/multitenancy/syncio/index.html | 367 --- .../recipe/multitenancy/utils.html | 360 -- .../recipe/openid/api/implementation.html | 157 - .../recipe/openid/api/index.html | 70 - .../open_id_discovery_configuration_get.html | 128 - .../recipe/openid/asyncio/index.html | 188 -- .../recipe/openid/constants.html | 73 - .../recipe/openid/exceptions.html | 108 - .../recipe/openid/index.html | 167 - .../recipe/openid/interfaces.html | 417 --- .../recipe/openid/recipe.html | 595 ---- .../recipe/openid/recipe_implementation.html | 295 -- .../recipe/openid/syncio/index.html | 167 - .../recipe/openid/utils.html | 273 -- .../recipe/passwordless/api/consume_code.html | 194 -- .../recipe/passwordless/api/create_code.html | 287 -- .../recipe/passwordless/api/email_exists.html | 131 - .../passwordless/api/implementation.html | 1056 ------ .../recipe/passwordless/api/index.html | 114 - .../passwordless/api/phone_number_exists.html | 130 - .../recipe/passwordless/api/resend_code.html | 148 - .../recipe/passwordless/asyncio/index.html | 848 ----- .../recipe/passwordless/constants.html | 79 - .../passwordless/emaildelivery/index.html | 83 - .../backward_compatibility/index.html | 267 -- .../emaildelivery/services/index.html | 92 - .../emaildelivery/services/smtp/index.html | 217 -- .../services/smtp/pless_login.html | 193 -- .../services/smtp/pless_login_email.html | 2879 ---------------- .../services/smtp/service_implementation.html | 183 -- .../recipe/passwordless/exceptions.html | 108 - .../recipe/passwordless/index.html | 237 -- .../recipe/passwordless/interfaces.html | 2105 ------------ .../recipe/passwordless/recipe.html | 1165 ------- .../passwordless/recipe_implementation.html | 1625 --------- .../passwordless/smsdelivery/index.html | 83 - .../backward_compatibility/index.html | 305 -- .../smsdelivery/services/index.html | 98 - .../services/supertokens/index.html | 237 -- .../smsdelivery/services/twilio/index.html | 244 -- .../services/twilio/passwordless_login.html | 212 -- .../twilio/service_implementation.html | 228 -- .../recipe/passwordless/syncio/index.html | 770 ----- .../recipe/passwordless/types.html | 499 --- .../recipe/passwordless/utils.html | 657 ---- .../recipe/session/access_token.html | 428 --- .../recipe/session/api/implementation.html | 344 -- .../recipe/session/api/index.html | 95 - .../recipe/session/api/refresh.html | 130 - .../recipe/session/api/signout.html | 155 - .../recipe/session/asyncio/index.html | 1330 -------- .../claim_base_classes/boolean_claim.html | 245 -- .../session/claim_base_classes/index.html | 93 - .../primitive_array_claim.html | 1158 ------- .../claim_base_classes/primitive_claim.html | 636 ---- .../recipe/session/claims.html | 79 - .../recipe/session/constants.html | 104 - .../recipe/session/cookie_and_header.html | 1061 ------ .../recipe/session/exceptions.html | 470 --- .../framework/django/asyncio/index.html | 223 -- .../session/framework/django/index.html | 88 - .../framework/django/syncio/index.html | 232 -- .../session/framework/fastapi/index.html | 240 -- .../recipe/session/framework/flask/index.html | 228 -- .../recipe/session/framework/index.html | 93 - .../recipe/session/index.html | 285 -- .../recipe/session/interfaces.html | 2903 ----------------- .../recipe/session/jwks.html | 442 --- .../recipe/session/jwt.html | 265 -- .../recipe/session/recipe.html | 1237 ------- .../recipe/session/recipe_implementation.html | 1687 ---------- .../recipe/session/session_class.html | 1208 ------- .../recipe/session/session_functions.html | 1262 ------- .../session/session_request_functions.html | 1084 ------ .../recipe/session/syncio/index.html | 1033 ------ .../recipe/session/utils.html | 1556 --------- .../recipe/thirdparty/api/apple_redirect.html | 130 - .../thirdparty/api/authorisation_url.html | 188 -- .../recipe/thirdparty/api/implementation.html | 546 ---- .../recipe/thirdparty/api/index.html | 275 -- .../recipe/thirdparty/api/signinup.html | 234 -- .../recipe/thirdparty/asyncio/index.html | 272 -- .../recipe/thirdparty/constants.html | 77 - .../recipe/thirdparty/exceptions.html | 139 - .../recipe/thirdparty/index.html | 194 -- .../recipe/thirdparty/interfaces.html | 953 ------ .../recipe/thirdparty/provider.html | 1193 ------- .../providers/active_directory.html | 245 -- .../recipe/thirdparty/providers/apple.html | 299 -- .../thirdparty/providers/bitbucket.html | 378 --- .../thirdparty/providers/boxy_saml.html | 248 -- .../thirdparty/providers/config_utils.html | 684 ---- .../recipe/thirdparty/providers/custom.html | 1253 ------- .../recipe/thirdparty/providers/discord.html | 240 -- .../recipe/thirdparty/providers/facebook.html | 282 -- .../recipe/thirdparty/providers/github.html | 357 -- .../recipe/thirdparty/providers/gitlab.html | 261 -- .../recipe/thirdparty/providers/google.html | 262 -- .../providers/google_workspaces.html | 241 -- .../recipe/thirdparty/providers/index.html | 158 - .../recipe/thirdparty/providers/linkedin.html | 340 -- .../recipe/thirdparty/providers/okta.html | 270 -- .../recipe/thirdparty/providers/twitter.html | 385 --- .../recipe/thirdparty/providers/utils.html | 277 -- .../recipe/thirdparty/recipe.html | 711 ---- .../thirdparty/recipe_implementation.html | 723 ---- .../recipe/thirdparty/syncio/index.html | 261 -- .../recipe/thirdparty/types.html | 408 --- .../recipe/thirdparty/utils.html | 362 -- .../recipe/usermetadata/asyncio/index.html | 166 - .../recipe/usermetadata/exceptions.html | 107 - .../recipe/usermetadata/index.html | 153 - .../recipe/usermetadata/interfaces.html | 266 -- .../recipe/usermetadata/recipe.html | 458 --- .../usermetadata/recipe_implementation.html | 263 -- .../recipe/usermetadata/syncio/index.html | 142 - .../recipe/usermetadata/utils.html | 192 -- .../recipe/userroles/asyncio/index.html | 397 --- .../recipe/userroles/exceptions.html | 107 - .../recipe/userroles/index.html | 169 - .../recipe/userroles/interfaces.html | 677 ---- .../recipe/userroles/recipe.html | 704 ---- .../userroles/recipe_implementation.html | 641 ---- .../recipe/userroles/syncio/index.html | 375 --- .../recipe/userroles/utils.html | 229 -- html/supertokens_python/recipe_module.html | 580 ---- html/supertokens_python/supertokens.html | 2276 ------------- html/supertokens_python/syncio/index.html | 409 --- html/supertokens_python/types.html | 425 --- html/supertokens_python/utils.html | 1046 ------ 272 files changed, 6 insertions(+), 108199 deletions(-) create mode 100644 Dockerfile delete mode 100644 html/supertokens_python/async_to_sync_wrapper.html delete mode 100644 html/supertokens_python/asyncio/index.html delete mode 100644 html/supertokens_python/constants.html delete mode 100644 html/supertokens_python/exceptions.html delete mode 100644 html/supertokens_python/framework/django/django_middleware.html delete mode 100644 html/supertokens_python/framework/django/django_request.html delete mode 100644 html/supertokens_python/framework/django/django_response.html delete mode 100644 html/supertokens_python/framework/django/framework.html delete mode 100644 html/supertokens_python/framework/django/index.html delete mode 100644 html/supertokens_python/framework/fastapi/fastapi_middleware.html delete mode 100644 html/supertokens_python/framework/fastapi/fastapi_request.html delete mode 100644 html/supertokens_python/framework/fastapi/fastapi_response.html delete mode 100644 html/supertokens_python/framework/fastapi/framework.html delete mode 100644 html/supertokens_python/framework/fastapi/index.html delete mode 100644 html/supertokens_python/framework/flask/flask_middleware.html delete mode 100644 html/supertokens_python/framework/flask/flask_request.html delete mode 100644 html/supertokens_python/framework/flask/flask_response.html delete mode 100644 html/supertokens_python/framework/flask/framework.html delete mode 100644 html/supertokens_python/framework/flask/index.html delete mode 100644 html/supertokens_python/framework/index.html delete mode 100644 html/supertokens_python/framework/request.html delete mode 100644 html/supertokens_python/framework/response.html delete mode 100644 html/supertokens_python/framework/types.html delete mode 100644 html/supertokens_python/index.html delete mode 100644 html/supertokens_python/ingredients/emaildelivery/index.html delete mode 100644 html/supertokens_python/ingredients/emaildelivery/services/index.html delete mode 100644 html/supertokens_python/ingredients/emaildelivery/services/smtp.html delete mode 100644 html/supertokens_python/ingredients/emaildelivery/types.html delete mode 100644 html/supertokens_python/ingredients/index.html delete mode 100644 html/supertokens_python/ingredients/smsdelivery/index.html delete mode 100644 html/supertokens_python/ingredients/smsdelivery/services/index.html delete mode 100644 html/supertokens_python/ingredients/smsdelivery/services/supertokens.html delete mode 100644 html/supertokens_python/ingredients/smsdelivery/services/twilio.html delete mode 100644 html/supertokens_python/ingredients/smsdelivery/types.html delete mode 100644 html/supertokens_python/interfaces.html delete mode 100644 html/supertokens_python/logger.html delete mode 100644 html/supertokens_python/normalised_url_domain.html delete mode 100644 html/supertokens_python/normalised_url_path.html delete mode 100644 html/supertokens_python/post_init_callbacks.html delete mode 100644 html/supertokens_python/process_state.html delete mode 100644 html/supertokens_python/querier.html delete mode 100644 html/supertokens_python/recipe/dashboard/api/analytics.html delete mode 100644 html/supertokens_python/recipe/dashboard/api/dashboard.html delete mode 100644 html/supertokens_python/recipe/dashboard/api/implementation.html delete mode 100644 html/supertokens_python/recipe/dashboard/api/index.html delete mode 100644 html/supertokens_python/recipe/dashboard/api/list_tenants.html delete mode 100644 html/supertokens_python/recipe/dashboard/api/search/getTags.html delete mode 100644 html/supertokens_python/recipe/dashboard/api/search/index.html delete mode 100644 html/supertokens_python/recipe/dashboard/api/signin.html delete mode 100644 html/supertokens_python/recipe/dashboard/api/signout.html delete mode 100644 html/supertokens_python/recipe/dashboard/api/userdetails/index.html delete mode 100644 html/supertokens_python/recipe/dashboard/api/userdetails/user_delete.html delete mode 100644 html/supertokens_python/recipe/dashboard/api/userdetails/user_email_verify_get.html delete mode 100644 html/supertokens_python/recipe/dashboard/api/userdetails/user_email_verify_put.html delete mode 100644 html/supertokens_python/recipe/dashboard/api/userdetails/user_email_verify_token_post.html delete mode 100644 html/supertokens_python/recipe/dashboard/api/userdetails/user_get.html delete mode 100644 html/supertokens_python/recipe/dashboard/api/userdetails/user_metadata_get.html delete mode 100644 html/supertokens_python/recipe/dashboard/api/userdetails/user_metadata_put.html delete mode 100644 html/supertokens_python/recipe/dashboard/api/userdetails/user_password_put.html delete mode 100644 html/supertokens_python/recipe/dashboard/api/userdetails/user_put.html delete mode 100644 html/supertokens_python/recipe/dashboard/api/userdetails/user_sessions_get.html delete mode 100644 html/supertokens_python/recipe/dashboard/api/userdetails/user_sessions_post.html delete mode 100644 html/supertokens_python/recipe/dashboard/api/users_count_get.html delete mode 100644 html/supertokens_python/recipe/dashboard/api/users_get.html delete mode 100644 html/supertokens_python/recipe/dashboard/api/validate_key.html delete mode 100644 html/supertokens_python/recipe/dashboard/constants.html delete mode 100644 html/supertokens_python/recipe/dashboard/exceptions.html delete mode 100644 html/supertokens_python/recipe/dashboard/index.html delete mode 100644 html/supertokens_python/recipe/dashboard/interfaces.html delete mode 100644 html/supertokens_python/recipe/dashboard/recipe.html delete mode 100644 html/supertokens_python/recipe/dashboard/recipe_implementation.html delete mode 100644 html/supertokens_python/recipe/dashboard/utils.html delete mode 100644 html/supertokens_python/recipe/emailpassword/api/email_exists.html delete mode 100644 html/supertokens_python/recipe/emailpassword/api/generate_password_reset_token.html delete mode 100644 html/supertokens_python/recipe/emailpassword/api/implementation.html delete mode 100644 html/supertokens_python/recipe/emailpassword/api/index.html delete mode 100644 html/supertokens_python/recipe/emailpassword/api/password_reset.html delete mode 100644 html/supertokens_python/recipe/emailpassword/api/signin.html delete mode 100644 html/supertokens_python/recipe/emailpassword/api/signup.html delete mode 100644 html/supertokens_python/recipe/emailpassword/api/utils.html delete mode 100644 html/supertokens_python/recipe/emailpassword/asyncio/index.html delete mode 100644 html/supertokens_python/recipe/emailpassword/constants.html delete mode 100644 html/supertokens_python/recipe/emailpassword/emaildelivery/index.html delete mode 100644 html/supertokens_python/recipe/emailpassword/emaildelivery/services/backward_compatibility/index.html delete mode 100644 html/supertokens_python/recipe/emailpassword/emaildelivery/services/index.html delete mode 100644 html/supertokens_python/recipe/emailpassword/emaildelivery/services/smtp/index.html delete mode 100644 html/supertokens_python/recipe/emailpassword/emaildelivery/services/smtp/password_reset.html delete mode 100644 html/supertokens_python/recipe/emailpassword/emaildelivery/services/smtp/password_reset_email.html delete mode 100644 html/supertokens_python/recipe/emailpassword/emaildelivery/services/smtp/service_implementation/index.html delete mode 100644 html/supertokens_python/recipe/emailpassword/exceptions.html delete mode 100644 html/supertokens_python/recipe/emailpassword/index.html delete mode 100644 html/supertokens_python/recipe/emailpassword/interfaces.html delete mode 100644 html/supertokens_python/recipe/emailpassword/recipe.html delete mode 100644 html/supertokens_python/recipe/emailpassword/recipe_implementation.html delete mode 100644 html/supertokens_python/recipe/emailpassword/syncio/index.html delete mode 100644 html/supertokens_python/recipe/emailpassword/types.html delete mode 100644 html/supertokens_python/recipe/emailpassword/utils.html delete mode 100644 html/supertokens_python/recipe/emailverification/api/email_verify.html delete mode 100644 html/supertokens_python/recipe/emailverification/api/generate_email_verify_token.html delete mode 100644 html/supertokens_python/recipe/emailverification/api/index.html delete mode 100644 html/supertokens_python/recipe/emailverification/asyncio/index.html delete mode 100644 html/supertokens_python/recipe/emailverification/constants.html delete mode 100644 html/supertokens_python/recipe/emailverification/emaildelivery/index.html delete mode 100644 html/supertokens_python/recipe/emailverification/emaildelivery/services/backward_compatibility/index.html delete mode 100644 html/supertokens_python/recipe/emailverification/emaildelivery/services/index.html delete mode 100644 html/supertokens_python/recipe/emailverification/emaildelivery/services/smtp/email_verify.html delete mode 100644 html/supertokens_python/recipe/emailverification/emaildelivery/services/smtp/email_verify_email.html delete mode 100644 html/supertokens_python/recipe/emailverification/emaildelivery/services/smtp/index.html delete mode 100644 html/supertokens_python/recipe/emailverification/emaildelivery/services/smtp/service_implementation.html delete mode 100644 html/supertokens_python/recipe/emailverification/ev_claim_validators.html delete mode 100644 html/supertokens_python/recipe/emailverification/exceptions.html delete mode 100644 html/supertokens_python/recipe/emailverification/index.html delete mode 100644 html/supertokens_python/recipe/emailverification/interfaces.html delete mode 100644 html/supertokens_python/recipe/emailverification/recipe.html delete mode 100644 html/supertokens_python/recipe/emailverification/recipe_implementation.html delete mode 100644 html/supertokens_python/recipe/emailverification/syncio/index.html delete mode 100644 html/supertokens_python/recipe/emailverification/types.html delete mode 100644 html/supertokens_python/recipe/emailverification/utils.html delete mode 100644 html/supertokens_python/recipe/index.html delete mode 100644 html/supertokens_python/recipe/jwt/api/implementation.html delete mode 100644 html/supertokens_python/recipe/jwt/api/index.html delete mode 100644 html/supertokens_python/recipe/jwt/api/jwks_get.html delete mode 100644 html/supertokens_python/recipe/jwt/asyncio/index.html delete mode 100644 html/supertokens_python/recipe/jwt/constants.html delete mode 100644 html/supertokens_python/recipe/jwt/exceptions.html delete mode 100644 html/supertokens_python/recipe/jwt/index.html delete mode 100644 html/supertokens_python/recipe/jwt/interfaces.html delete mode 100644 html/supertokens_python/recipe/jwt/recipe.html delete mode 100644 html/supertokens_python/recipe/jwt/recipe_implementation.html delete mode 100644 html/supertokens_python/recipe/jwt/syncio/index.html delete mode 100644 html/supertokens_python/recipe/jwt/utils.html delete mode 100644 html/supertokens_python/recipe/multitenancy/api/implementation.html delete mode 100644 html/supertokens_python/recipe/multitenancy/api/index.html delete mode 100644 html/supertokens_python/recipe/multitenancy/api/login_methods.html delete mode 100644 html/supertokens_python/recipe/multitenancy/asyncio/index.html delete mode 100644 html/supertokens_python/recipe/multitenancy/constants.html delete mode 100644 html/supertokens_python/recipe/multitenancy/exceptions.html delete mode 100644 html/supertokens_python/recipe/multitenancy/index.html delete mode 100644 html/supertokens_python/recipe/multitenancy/interfaces.html delete mode 100644 html/supertokens_python/recipe/multitenancy/recipe.html delete mode 100644 html/supertokens_python/recipe/multitenancy/recipe_implementation.html delete mode 100644 html/supertokens_python/recipe/multitenancy/syncio/index.html delete mode 100644 html/supertokens_python/recipe/multitenancy/utils.html delete mode 100644 html/supertokens_python/recipe/openid/api/implementation.html delete mode 100644 html/supertokens_python/recipe/openid/api/index.html delete mode 100644 html/supertokens_python/recipe/openid/api/open_id_discovery_configuration_get.html delete mode 100644 html/supertokens_python/recipe/openid/asyncio/index.html delete mode 100644 html/supertokens_python/recipe/openid/constants.html delete mode 100644 html/supertokens_python/recipe/openid/exceptions.html delete mode 100644 html/supertokens_python/recipe/openid/index.html delete mode 100644 html/supertokens_python/recipe/openid/interfaces.html delete mode 100644 html/supertokens_python/recipe/openid/recipe.html delete mode 100644 html/supertokens_python/recipe/openid/recipe_implementation.html delete mode 100644 html/supertokens_python/recipe/openid/syncio/index.html delete mode 100644 html/supertokens_python/recipe/openid/utils.html delete mode 100644 html/supertokens_python/recipe/passwordless/api/consume_code.html delete mode 100644 html/supertokens_python/recipe/passwordless/api/create_code.html delete mode 100644 html/supertokens_python/recipe/passwordless/api/email_exists.html delete mode 100644 html/supertokens_python/recipe/passwordless/api/implementation.html delete mode 100644 html/supertokens_python/recipe/passwordless/api/index.html delete mode 100644 html/supertokens_python/recipe/passwordless/api/phone_number_exists.html delete mode 100644 html/supertokens_python/recipe/passwordless/api/resend_code.html delete mode 100644 html/supertokens_python/recipe/passwordless/asyncio/index.html delete mode 100644 html/supertokens_python/recipe/passwordless/constants.html delete mode 100644 html/supertokens_python/recipe/passwordless/emaildelivery/index.html delete mode 100644 html/supertokens_python/recipe/passwordless/emaildelivery/services/backward_compatibility/index.html delete mode 100644 html/supertokens_python/recipe/passwordless/emaildelivery/services/index.html delete mode 100644 html/supertokens_python/recipe/passwordless/emaildelivery/services/smtp/index.html delete mode 100644 html/supertokens_python/recipe/passwordless/emaildelivery/services/smtp/pless_login.html delete mode 100644 html/supertokens_python/recipe/passwordless/emaildelivery/services/smtp/pless_login_email.html delete mode 100644 html/supertokens_python/recipe/passwordless/emaildelivery/services/smtp/service_implementation.html delete mode 100644 html/supertokens_python/recipe/passwordless/exceptions.html delete mode 100644 html/supertokens_python/recipe/passwordless/index.html delete mode 100644 html/supertokens_python/recipe/passwordless/interfaces.html delete mode 100644 html/supertokens_python/recipe/passwordless/recipe.html delete mode 100644 html/supertokens_python/recipe/passwordless/recipe_implementation.html delete mode 100644 html/supertokens_python/recipe/passwordless/smsdelivery/index.html delete mode 100644 html/supertokens_python/recipe/passwordless/smsdelivery/services/backward_compatibility/index.html delete mode 100644 html/supertokens_python/recipe/passwordless/smsdelivery/services/index.html delete mode 100644 html/supertokens_python/recipe/passwordless/smsdelivery/services/supertokens/index.html delete mode 100644 html/supertokens_python/recipe/passwordless/smsdelivery/services/twilio/index.html delete mode 100644 html/supertokens_python/recipe/passwordless/smsdelivery/services/twilio/passwordless_login.html delete mode 100644 html/supertokens_python/recipe/passwordless/smsdelivery/services/twilio/service_implementation.html delete mode 100644 html/supertokens_python/recipe/passwordless/syncio/index.html delete mode 100644 html/supertokens_python/recipe/passwordless/types.html delete mode 100644 html/supertokens_python/recipe/passwordless/utils.html delete mode 100644 html/supertokens_python/recipe/session/access_token.html delete mode 100644 html/supertokens_python/recipe/session/api/implementation.html delete mode 100644 html/supertokens_python/recipe/session/api/index.html delete mode 100644 html/supertokens_python/recipe/session/api/refresh.html delete mode 100644 html/supertokens_python/recipe/session/api/signout.html delete mode 100644 html/supertokens_python/recipe/session/asyncio/index.html delete mode 100644 html/supertokens_python/recipe/session/claim_base_classes/boolean_claim.html delete mode 100644 html/supertokens_python/recipe/session/claim_base_classes/index.html delete mode 100644 html/supertokens_python/recipe/session/claim_base_classes/primitive_array_claim.html delete mode 100644 html/supertokens_python/recipe/session/claim_base_classes/primitive_claim.html delete mode 100644 html/supertokens_python/recipe/session/claims.html delete mode 100644 html/supertokens_python/recipe/session/constants.html delete mode 100644 html/supertokens_python/recipe/session/cookie_and_header.html delete mode 100644 html/supertokens_python/recipe/session/exceptions.html delete mode 100644 html/supertokens_python/recipe/session/framework/django/asyncio/index.html delete mode 100644 html/supertokens_python/recipe/session/framework/django/index.html delete mode 100644 html/supertokens_python/recipe/session/framework/django/syncio/index.html delete mode 100644 html/supertokens_python/recipe/session/framework/fastapi/index.html delete mode 100644 html/supertokens_python/recipe/session/framework/flask/index.html delete mode 100644 html/supertokens_python/recipe/session/framework/index.html delete mode 100644 html/supertokens_python/recipe/session/index.html delete mode 100644 html/supertokens_python/recipe/session/interfaces.html delete mode 100644 html/supertokens_python/recipe/session/jwks.html delete mode 100644 html/supertokens_python/recipe/session/jwt.html delete mode 100644 html/supertokens_python/recipe/session/recipe.html delete mode 100644 html/supertokens_python/recipe/session/recipe_implementation.html delete mode 100644 html/supertokens_python/recipe/session/session_class.html delete mode 100644 html/supertokens_python/recipe/session/session_functions.html delete mode 100644 html/supertokens_python/recipe/session/session_request_functions.html delete mode 100644 html/supertokens_python/recipe/session/syncio/index.html delete mode 100644 html/supertokens_python/recipe/session/utils.html delete mode 100644 html/supertokens_python/recipe/thirdparty/api/apple_redirect.html delete mode 100644 html/supertokens_python/recipe/thirdparty/api/authorisation_url.html delete mode 100644 html/supertokens_python/recipe/thirdparty/api/implementation.html delete mode 100644 html/supertokens_python/recipe/thirdparty/api/index.html delete mode 100644 html/supertokens_python/recipe/thirdparty/api/signinup.html delete mode 100644 html/supertokens_python/recipe/thirdparty/asyncio/index.html delete mode 100644 html/supertokens_python/recipe/thirdparty/constants.html delete mode 100644 html/supertokens_python/recipe/thirdparty/exceptions.html delete mode 100644 html/supertokens_python/recipe/thirdparty/index.html delete mode 100644 html/supertokens_python/recipe/thirdparty/interfaces.html delete mode 100644 html/supertokens_python/recipe/thirdparty/provider.html delete mode 100644 html/supertokens_python/recipe/thirdparty/providers/active_directory.html delete mode 100644 html/supertokens_python/recipe/thirdparty/providers/apple.html delete mode 100644 html/supertokens_python/recipe/thirdparty/providers/bitbucket.html delete mode 100644 html/supertokens_python/recipe/thirdparty/providers/boxy_saml.html delete mode 100644 html/supertokens_python/recipe/thirdparty/providers/config_utils.html delete mode 100644 html/supertokens_python/recipe/thirdparty/providers/custom.html delete mode 100644 html/supertokens_python/recipe/thirdparty/providers/discord.html delete mode 100644 html/supertokens_python/recipe/thirdparty/providers/facebook.html delete mode 100644 html/supertokens_python/recipe/thirdparty/providers/github.html delete mode 100644 html/supertokens_python/recipe/thirdparty/providers/gitlab.html delete mode 100644 html/supertokens_python/recipe/thirdparty/providers/google.html delete mode 100644 html/supertokens_python/recipe/thirdparty/providers/google_workspaces.html delete mode 100644 html/supertokens_python/recipe/thirdparty/providers/index.html delete mode 100644 html/supertokens_python/recipe/thirdparty/providers/linkedin.html delete mode 100644 html/supertokens_python/recipe/thirdparty/providers/okta.html delete mode 100644 html/supertokens_python/recipe/thirdparty/providers/twitter.html delete mode 100644 html/supertokens_python/recipe/thirdparty/providers/utils.html delete mode 100644 html/supertokens_python/recipe/thirdparty/recipe.html delete mode 100644 html/supertokens_python/recipe/thirdparty/recipe_implementation.html delete mode 100644 html/supertokens_python/recipe/thirdparty/syncio/index.html delete mode 100644 html/supertokens_python/recipe/thirdparty/types.html delete mode 100644 html/supertokens_python/recipe/thirdparty/utils.html delete mode 100644 html/supertokens_python/recipe/usermetadata/asyncio/index.html delete mode 100644 html/supertokens_python/recipe/usermetadata/exceptions.html delete mode 100644 html/supertokens_python/recipe/usermetadata/index.html delete mode 100644 html/supertokens_python/recipe/usermetadata/interfaces.html delete mode 100644 html/supertokens_python/recipe/usermetadata/recipe.html delete mode 100644 html/supertokens_python/recipe/usermetadata/recipe_implementation.html delete mode 100644 html/supertokens_python/recipe/usermetadata/syncio/index.html delete mode 100644 html/supertokens_python/recipe/usermetadata/utils.html delete mode 100644 html/supertokens_python/recipe/userroles/asyncio/index.html delete mode 100644 html/supertokens_python/recipe/userroles/exceptions.html delete mode 100644 html/supertokens_python/recipe/userroles/index.html delete mode 100644 html/supertokens_python/recipe/userroles/interfaces.html delete mode 100644 html/supertokens_python/recipe/userroles/recipe.html delete mode 100644 html/supertokens_python/recipe/userroles/recipe_implementation.html delete mode 100644 html/supertokens_python/recipe/userroles/syncio/index.html delete mode 100644 html/supertokens_python/recipe/userroles/utils.html delete mode 100644 html/supertokens_python/recipe_module.html delete mode 100644 html/supertokens_python/supertokens.html delete mode 100644 html/supertokens_python/syncio/index.html delete mode 100644 html/supertokens_python/types.html delete mode 100644 html/supertokens_python/utils.html diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 000000000..a323aeb96 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,6 @@ +FROM ubuntu:latest +RUN apt-get update && apt-get install -y python3 python3-pip python3-venv +ADD . /app +WORKDIR /app +RUN bash ./test-pre-commit.sh +RUN bash ./hooks/pre-commit.sh diff --git a/html/supertokens_python/async_to_sync_wrapper.html b/html/supertokens_python/async_to_sync_wrapper.html deleted file mode 100644 index dd7eb001c..000000000 --- a/html/supertokens_python/async_to_sync_wrapper.html +++ /dev/null @@ -1,166 +0,0 @@ - - - - - - -supertokens_python.async_to_sync_wrapper API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.async_to_sync_wrapper

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-import asyncio
-from typing import Any, Coroutine, TypeVar
-from os import getenv
-
-_T = TypeVar("_T")
-
-
-def nest_asyncio_enabled():
-    return getenv("SUPERTOKENS_NEST_ASYNCIO", "") == "1"
-
-
-def create_or_get_event_loop() -> asyncio.AbstractEventLoop:
-    try:
-        return asyncio.get_event_loop()
-    except Exception as ex:
-        if "There is no current event loop in thread" in str(ex):
-            loop = asyncio.new_event_loop()
-
-            if nest_asyncio_enabled():
-                import nest_asyncio  # type: ignore
-
-                nest_asyncio.apply(loop)  # type: ignore
-
-            asyncio.set_event_loop(loop)
-            return loop
-        raise ex
-
-
-def sync(co: Coroutine[Any, Any, _T]) -> _T:
-    loop = create_or_get_event_loop()
-    return loop.run_until_complete(co)
-
-
-
-
-
-
-
-

Functions

-
-
-def create_or_get_event_loop() ‑> asyncio.events.AbstractEventLoop -
-
-
-
- -Expand source code - -
def create_or_get_event_loop() -> asyncio.AbstractEventLoop:
-    try:
-        return asyncio.get_event_loop()
-    except Exception as ex:
-        if "There is no current event loop in thread" in str(ex):
-            loop = asyncio.new_event_loop()
-
-            if nest_asyncio_enabled():
-                import nest_asyncio  # type: ignore
-
-                nest_asyncio.apply(loop)  # type: ignore
-
-            asyncio.set_event_loop(loop)
-            return loop
-        raise ex
-
-
-
-def nest_asyncio_enabled() -
-
-
-
- -Expand source code - -
def nest_asyncio_enabled():
-    return getenv("SUPERTOKENS_NEST_ASYNCIO", "") == "1"
-
-
-
-def sync(co: Coroutine[Any, Any, ~_T]) ‑> ~_T -
-
-
-
- -Expand source code - -
def sync(co: Coroutine[Any, Any, _T]) -> _T:
-    loop = create_or_get_event_loop()
-    return loop.run_until_complete(co)
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/asyncio/index.html b/html/supertokens_python/asyncio/index.html deleted file mode 100644 index 176740f64..000000000 --- a/html/supertokens_python/asyncio/index.html +++ /dev/null @@ -1,384 +0,0 @@ - - - - - - -supertokens_python.asyncio API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.asyncio

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from typing import Dict, List, Optional, Union, Any
-
-from supertokens_python import Supertokens
-from supertokens_python.interfaces import (
-    CreateUserIdMappingOkResult,
-    DeleteUserIdMappingOkResult,
-    GetUserIdMappingOkResult,
-    UnknownMappingError,
-    UnknownSupertokensUserIDError,
-    UpdateOrDeleteUserIdMappingInfoOkResult,
-    UserIdMappingAlreadyExistsError,
-    UserIDTypes,
-)
-from supertokens_python.types import UsersResponse
-
-
-async def get_users_oldest_first(
-    tenant_id: str,
-    limit: Union[int, None] = None,
-    pagination_token: Union[str, None] = None,
-    include_recipe_ids: Union[None, List[str]] = None,
-    query: Union[None, Dict[str, str]] = None,
-    user_context: Optional[Dict[str, Any]] = None,
-) -> UsersResponse:
-    return await Supertokens.get_instance().get_users(
-        tenant_id,
-        "ASC",
-        limit,
-        pagination_token,
-        include_recipe_ids,
-        query,
-        user_context,
-    )
-
-
-async def get_users_newest_first(
-    tenant_id: str,
-    limit: Union[int, None] = None,
-    pagination_token: Union[str, None] = None,
-    include_recipe_ids: Union[None, List[str]] = None,
-    query: Union[None, Dict[str, str]] = None,
-    user_context: Optional[Dict[str, Any]] = None,
-) -> UsersResponse:
-    return await Supertokens.get_instance().get_users(
-        tenant_id,
-        "DESC",
-        limit,
-        pagination_token,
-        include_recipe_ids,
-        query,
-        user_context,
-    )
-
-
-async def get_user_count(
-    include_recipe_ids: Union[None, List[str]] = None,
-    tenant_id: Optional[str] = None,
-    user_context: Optional[Dict[str, Any]] = None,
-) -> int:
-    return await Supertokens.get_instance().get_user_count(
-        include_recipe_ids, tenant_id, user_context
-    )
-
-
-async def delete_user(
-    user_id: str, user_context: Optional[Dict[str, Any]] = None
-) -> None:
-    return await Supertokens.get_instance().delete_user(user_id, user_context)
-
-
-async def create_user_id_mapping(
-    supertokens_user_id: str,
-    external_user_id: str,
-    external_user_id_info: Optional[str] = None,
-    force: Optional[bool] = None,
-    user_context: Optional[Dict[str, Any]] = None,
-) -> Union[
-    CreateUserIdMappingOkResult,
-    UnknownSupertokensUserIDError,
-    UserIdMappingAlreadyExistsError,
-]:
-    return await Supertokens.get_instance().create_user_id_mapping(
-        supertokens_user_id,
-        external_user_id,
-        external_user_id_info,
-        force,
-        user_context,
-    )
-
-
-async def get_user_id_mapping(
-    user_id: str,
-    user_id_type: Optional[UserIDTypes] = None,
-    user_context: Optional[Dict[str, Any]] = None,
-) -> Union[GetUserIdMappingOkResult, UnknownMappingError]:
-    return await Supertokens.get_instance().get_user_id_mapping(
-        user_id, user_id_type, user_context
-    )
-
-
-async def delete_user_id_mapping(
-    user_id: str,
-    user_id_type: Optional[UserIDTypes] = None,
-    force: Optional[bool] = None,
-    user_context: Optional[Dict[str, Any]] = None,
-) -> DeleteUserIdMappingOkResult:
-    return await Supertokens.get_instance().delete_user_id_mapping(
-        user_id, user_id_type, force, user_context
-    )
-
-
-async def update_or_delete_user_id_mapping_info(
-    user_id: str,
-    user_id_type: Optional[UserIDTypes] = None,
-    external_user_id_info: Optional[str] = None,
-    user_context: Optional[Dict[str, Any]] = None,
-) -> Union[UpdateOrDeleteUserIdMappingInfoOkResult, UnknownMappingError]:
-    return await Supertokens.get_instance().update_or_delete_user_id_mapping_info(
-        user_id, user_id_type, external_user_id_info, user_context
-    )
-
-
-
-
-
-
-
-

Functions

-
-
-async def create_user_id_mapping(supertokens_user_id: str, external_user_id: str, external_user_id_info: Optional[str] = None, force: Optional[bool] = None, user_context: Optional[Dict[str, Any]] = None) ‑> Union[CreateUserIdMappingOkResultUnknownSupertokensUserIDErrorUserIdMappingAlreadyExistsError] -
-
-
-
- -Expand source code - -
async def create_user_id_mapping(
-    supertokens_user_id: str,
-    external_user_id: str,
-    external_user_id_info: Optional[str] = None,
-    force: Optional[bool] = None,
-    user_context: Optional[Dict[str, Any]] = None,
-) -> Union[
-    CreateUserIdMappingOkResult,
-    UnknownSupertokensUserIDError,
-    UserIdMappingAlreadyExistsError,
-]:
-    return await Supertokens.get_instance().create_user_id_mapping(
-        supertokens_user_id,
-        external_user_id,
-        external_user_id_info,
-        force,
-        user_context,
-    )
-
-
-
-async def delete_user(user_id: str, user_context: Optional[Dict[str, Any]] = None) ‑> None -
-
-
-
- -Expand source code - -
async def delete_user(
-    user_id: str, user_context: Optional[Dict[str, Any]] = None
-) -> None:
-    return await Supertokens.get_instance().delete_user(user_id, user_context)
-
-
-
-async def delete_user_id_mapping(user_id: str, user_id_type: Optional[Literal['SUPERTOKENS', 'EXTERNAL', 'ANY']] = None, force: Optional[bool] = None, user_context: Optional[Dict[str, Any]] = None) ‑> DeleteUserIdMappingOkResult -
-
-
-
- -Expand source code - -
async def delete_user_id_mapping(
-    user_id: str,
-    user_id_type: Optional[UserIDTypes] = None,
-    force: Optional[bool] = None,
-    user_context: Optional[Dict[str, Any]] = None,
-) -> DeleteUserIdMappingOkResult:
-    return await Supertokens.get_instance().delete_user_id_mapping(
-        user_id, user_id_type, force, user_context
-    )
-
-
-
-async def get_user_count(include_recipe_ids: Optional[None] = None, tenant_id: Optional[str] = None, user_context: Optional[Dict[str, Any]] = None) ‑> int -
-
-
-
- -Expand source code - -
async def get_user_count(
-    include_recipe_ids: Union[None, List[str]] = None,
-    tenant_id: Optional[str] = None,
-    user_context: Optional[Dict[str, Any]] = None,
-) -> int:
-    return await Supertokens.get_instance().get_user_count(
-        include_recipe_ids, tenant_id, user_context
-    )
-
-
-
-async def get_user_id_mapping(user_id: str, user_id_type: Optional[Literal['SUPERTOKENS', 'EXTERNAL', 'ANY']] = None, user_context: Optional[Dict[str, Any]] = None) ‑> Union[GetUserIdMappingOkResultUnknownMappingError] -
-
-
-
- -Expand source code - -
async def get_user_id_mapping(
-    user_id: str,
-    user_id_type: Optional[UserIDTypes] = None,
-    user_context: Optional[Dict[str, Any]] = None,
-) -> Union[GetUserIdMappingOkResult, UnknownMappingError]:
-    return await Supertokens.get_instance().get_user_id_mapping(
-        user_id, user_id_type, user_context
-    )
-
-
-
-async def get_users_newest_first(tenant_id: str, limit: Optional[int] = None, pagination_token: Optional[str] = None, include_recipe_ids: Optional[None] = None, query: Optional[None] = None, user_context: Optional[Dict[str, Any]] = None) ‑> UsersResponse -
-
-
-
- -Expand source code - -
async def get_users_newest_first(
-    tenant_id: str,
-    limit: Union[int, None] = None,
-    pagination_token: Union[str, None] = None,
-    include_recipe_ids: Union[None, List[str]] = None,
-    query: Union[None, Dict[str, str]] = None,
-    user_context: Optional[Dict[str, Any]] = None,
-) -> UsersResponse:
-    return await Supertokens.get_instance().get_users(
-        tenant_id,
-        "DESC",
-        limit,
-        pagination_token,
-        include_recipe_ids,
-        query,
-        user_context,
-    )
-
-
-
-async def get_users_oldest_first(tenant_id: str, limit: Optional[int] = None, pagination_token: Optional[str] = None, include_recipe_ids: Optional[None] = None, query: Optional[None] = None, user_context: Optional[Dict[str, Any]] = None) ‑> UsersResponse -
-
-
-
- -Expand source code - -
async def get_users_oldest_first(
-    tenant_id: str,
-    limit: Union[int, None] = None,
-    pagination_token: Union[str, None] = None,
-    include_recipe_ids: Union[None, List[str]] = None,
-    query: Union[None, Dict[str, str]] = None,
-    user_context: Optional[Dict[str, Any]] = None,
-) -> UsersResponse:
-    return await Supertokens.get_instance().get_users(
-        tenant_id,
-        "ASC",
-        limit,
-        pagination_token,
-        include_recipe_ids,
-        query,
-        user_context,
-    )
-
-
-
-async def update_or_delete_user_id_mapping_info(user_id: str, user_id_type: Optional[Literal['SUPERTOKENS', 'EXTERNAL', 'ANY']] = None, external_user_id_info: Optional[str] = None, user_context: Optional[Dict[str, Any]] = None) ‑> Union[UpdateOrDeleteUserIdMappingInfoOkResultUnknownMappingError] -
-
-
-
- -Expand source code - -
async def update_or_delete_user_id_mapping_info(
-    user_id: str,
-    user_id_type: Optional[UserIDTypes] = None,
-    external_user_id_info: Optional[str] = None,
-    user_context: Optional[Dict[str, Any]] = None,
-) -> Union[UpdateOrDeleteUserIdMappingInfoOkResult, UnknownMappingError]:
-    return await Supertokens.get_instance().update_or_delete_user_id_mapping_info(
-        user_id, user_id_type, external_user_id_info, user_context
-    )
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/constants.html b/html/supertokens_python/constants.html deleted file mode 100644 index 102b464af..000000000 --- a/html/supertokens_python/constants.html +++ /dev/null @@ -1,90 +0,0 @@ - - - - - - -supertokens_python.constants API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.constants

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-SUPPORTED_CDI_VERSIONS = ["3.0"]
-VERSION = "0.24.3"
-TELEMETRY = "/telemetry"
-USER_COUNT = "/users/count"
-USER_DELETE = "/user/remove"
-USERS = "/users"
-TELEMETRY_SUPERTOKENS_API_URL = "https://api.supertokens.com/0/st/telemetry"
-TELEMETRY_SUPERTOKENS_API_VERSION = "3"
-ERROR_MESSAGE_KEY = "message"
-API_KEY_HEADER = "api-key"
-RID_KEY_HEADER = "rid"
-FDI_KEY_HEADER = "fdi-version"
-API_VERSION = "/apiversion"
-API_VERSION_HEADER = "cdi-version"
-DASHBOARD_VERSION = "0.7"
-ONE_YEAR_IN_MS = 31536000000
-RATE_LIMIT_STATUS_CODE = 429
-
-
-
-
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/exceptions.html b/html/supertokens_python/exceptions.html deleted file mode 100644 index aa8b22e20..000000000 --- a/html/supertokens_python/exceptions.html +++ /dev/null @@ -1,233 +0,0 @@ - - - - - - -supertokens_python.exceptions API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.exceptions

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from typing import Union, NoReturn
-
-
-def raise_general_exception(
-    msg: Union[str, Exception], previous: Union[None, Exception] = None
-) -> NoReturn:
-    if isinstance(msg, SuperTokensError):
-        raise msg
-    if isinstance(msg, Exception):
-        raise GeneralError(msg) from None
-    raise GeneralError(msg) from previous
-
-
-def raise_bad_input_exception(msg: str) -> NoReturn:
-    raise BadInputError(msg)
-
-
-class SuperTokensError(Exception):
-    pass
-
-
-class GeneralError(SuperTokensError):
-    pass
-
-
-class BadInputError(SuperTokensError):
-    pass
-
-
-
-
-
-
-
-

Functions

-
-
-def raise_bad_input_exception(msg: str) ‑> NoReturn -
-
-
-
- -Expand source code - -
def raise_bad_input_exception(msg: str) -> NoReturn:
-    raise BadInputError(msg)
-
-
-
-def raise_general_exception(msg: Union[str, Exception], previous: Union[None, Exception] = None) ‑> NoReturn -
-
-
-
- -Expand source code - -
def raise_general_exception(
-    msg: Union[str, Exception], previous: Union[None, Exception] = None
-) -> NoReturn:
-    if isinstance(msg, SuperTokensError):
-        raise msg
-    if isinstance(msg, Exception):
-        raise GeneralError(msg) from None
-    raise GeneralError(msg) from previous
-
-
-
-
-
-

Classes

-
-
-class BadInputError -(*args, **kwargs) -
-
-

Common base class for all non-exit exceptions.

-
- -Expand source code - -
class BadInputError(SuperTokensError):
-    pass
-
-

Ancestors

- -
-
-class GeneralError -(*args, **kwargs) -
-
-

Common base class for all non-exit exceptions.

-
- -Expand source code - -
class GeneralError(SuperTokensError):
-    pass
-
-

Ancestors

- -
-
-class SuperTokensError -(*args, **kwargs) -
-
-

Common base class for all non-exit exceptions.

-
- -Expand source code - -
class SuperTokensError(Exception):
-    pass
-
-

Ancestors

-
    -
  • builtins.Exception
  • -
  • builtins.BaseException
  • -
-

Subclasses

- -
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/framework/django/django_middleware.html b/html/supertokens_python/framework/django/django_middleware.html deleted file mode 100644 index f890290a4..000000000 --- a/html/supertokens_python/framework/django/django_middleware.html +++ /dev/null @@ -1,258 +0,0 @@ - - - - - - -supertokens_python.framework.django.django_middleware API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.framework.django.django_middleware

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-import asyncio
-from typing import Any, Union
-
-from asgiref.sync import async_to_sync
-
-
-def middleware(get_response: Any):
-    from supertokens_python import Supertokens
-    from supertokens_python.exceptions import SuperTokensError
-    from supertokens_python.framework.django.django_request import DjangoRequest
-    from supertokens_python.framework.django.django_response import DjangoResponse
-    from supertokens_python.recipe.session import SessionContainer
-    from supertokens_python.supertokens import manage_session_post_response
-
-    from django.http import HttpRequest
-    from supertokens_python.utils import default_user_context
-
-    if asyncio.iscoroutinefunction(get_response):
-
-        async def __asyncMiddleware(request: HttpRequest):
-            st = Supertokens.get_instance()
-            custom_request = DjangoRequest(request)
-            from django.http import HttpResponse
-
-            response = DjangoResponse(HttpResponse())
-            user_context = default_user_context(custom_request)
-
-            try:
-                result = await st.middleware(custom_request, response, user_context)
-                if result is None:
-                    result = await get_response(request)
-                    result = DjangoResponse(result)
-                if hasattr(request, "supertokens") and isinstance(
-                    request.supertokens, SessionContainer  # type: ignore
-                ):
-                    manage_session_post_response(
-                        request.supertokens, result, user_context  # type: ignore
-                    )
-                if isinstance(result, DjangoResponse):
-                    return result.response
-            except SuperTokensError as e:
-                response = DjangoResponse(HttpResponse())
-                result = await st.handle_supertokens_error(
-                    DjangoRequest(request), e, response, user_context
-                )
-                if isinstance(result, DjangoResponse):
-                    return result.response
-
-            raise Exception("Should never come here")
-
-        return __asyncMiddleware
-
-    def __syncMiddleware(request: HttpRequest):
-        st = Supertokens.get_instance()
-        custom_request = DjangoRequest(request)
-        from django.http import HttpResponse
-
-        response = DjangoResponse(HttpResponse())
-        user_context = default_user_context(custom_request)
-
-        try:
-            result: Union[DjangoResponse, None] = async_to_sync(st.middleware)(
-                custom_request, response, user_context
-            )
-
-            if result is None:
-                result = DjangoResponse(get_response(request))
-
-            if hasattr(request, "supertokens") and isinstance(
-                request.supertokens, SessionContainer  # type: ignore
-            ):
-                manage_session_post_response(
-                    request.supertokens, result, user_context  # type: ignore
-                )
-            return result.response
-
-        except SuperTokensError as e:
-            response = DjangoResponse(HttpResponse())
-            result: Union[DjangoResponse, None] = async_to_sync(
-                st.handle_supertokens_error
-            )(DjangoRequest(request), e, response, user_context)
-            if result is not None:
-                return result.response
-        raise Exception("Should never come here")
-
-    return __syncMiddleware
-
-
-
-
-
-
-
-

Functions

-
-
-def middleware(get_response: Any) -
-
-
-
- -Expand source code - -
def middleware(get_response: Any):
-    from supertokens_python import Supertokens
-    from supertokens_python.exceptions import SuperTokensError
-    from supertokens_python.framework.django.django_request import DjangoRequest
-    from supertokens_python.framework.django.django_response import DjangoResponse
-    from supertokens_python.recipe.session import SessionContainer
-    from supertokens_python.supertokens import manage_session_post_response
-
-    from django.http import HttpRequest
-    from supertokens_python.utils import default_user_context
-
-    if asyncio.iscoroutinefunction(get_response):
-
-        async def __asyncMiddleware(request: HttpRequest):
-            st = Supertokens.get_instance()
-            custom_request = DjangoRequest(request)
-            from django.http import HttpResponse
-
-            response = DjangoResponse(HttpResponse())
-            user_context = default_user_context(custom_request)
-
-            try:
-                result = await st.middleware(custom_request, response, user_context)
-                if result is None:
-                    result = await get_response(request)
-                    result = DjangoResponse(result)
-                if hasattr(request, "supertokens") and isinstance(
-                    request.supertokens, SessionContainer  # type: ignore
-                ):
-                    manage_session_post_response(
-                        request.supertokens, result, user_context  # type: ignore
-                    )
-                if isinstance(result, DjangoResponse):
-                    return result.response
-            except SuperTokensError as e:
-                response = DjangoResponse(HttpResponse())
-                result = await st.handle_supertokens_error(
-                    DjangoRequest(request), e, response, user_context
-                )
-                if isinstance(result, DjangoResponse):
-                    return result.response
-
-            raise Exception("Should never come here")
-
-        return __asyncMiddleware
-
-    def __syncMiddleware(request: HttpRequest):
-        st = Supertokens.get_instance()
-        custom_request = DjangoRequest(request)
-        from django.http import HttpResponse
-
-        response = DjangoResponse(HttpResponse())
-        user_context = default_user_context(custom_request)
-
-        try:
-            result: Union[DjangoResponse, None] = async_to_sync(st.middleware)(
-                custom_request, response, user_context
-            )
-
-            if result is None:
-                result = DjangoResponse(get_response(request))
-
-            if hasattr(request, "supertokens") and isinstance(
-                request.supertokens, SessionContainer  # type: ignore
-            ):
-                manage_session_post_response(
-                    request.supertokens, result, user_context  # type: ignore
-                )
-            return result.response
-
-        except SuperTokensError as e:
-            response = DjangoResponse(HttpResponse())
-            result: Union[DjangoResponse, None] = async_to_sync(
-                st.handle_supertokens_error
-            )(DjangoRequest(request), e, response, user_context)
-            if result is not None:
-                return result.response
-        raise Exception("Should never come here")
-
-    return __syncMiddleware
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/framework/django/django_request.html b/html/supertokens_python/framework/django/django_request.html deleted file mode 100644 index e224e053e..000000000 --- a/html/supertokens_python/framework/django/django_request.html +++ /dev/null @@ -1,429 +0,0 @@ - - - - - - -supertokens_python.framework.django.django_request API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.framework.django.django_request

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-import json
-from typing import TYPE_CHECKING, Any, Dict, Union
-from urllib.parse import parse_qsl
-
-from supertokens_python.framework.request import BaseRequest
-
-if TYPE_CHECKING:
-    from supertokens_python.recipe.session.interfaces import SessionContainer
-    from django.http import HttpRequest
-
-
-class DjangoRequest(BaseRequest):
-    def __init__(self, request: HttpRequest):
-        super().__init__()
-        self.request = request
-
-    def get_original_url(self) -> str:
-        return self.request.build_absolute_uri()
-
-    def get_query_param(
-        self, key: str, default: Union[str, None] = None
-    ) -> Union[str, None]:
-        return self.request.GET.get(key, default)
-
-    def get_query_params(self) -> Dict[str, Any]:
-        return self.request.GET.dict()
-
-    async def json(self) -> Union[Any, None]:
-        try:
-            body = json.loads(self.request.body)
-            return body
-        except Exception:
-            return {}
-
-    def method(self) -> str:
-        if self.request.method is None:
-            raise Exception("Should never come here")
-        return self.request.method
-
-    def get_cookie(self, key: str) -> Union[str, None]:
-        return self.request.COOKIES.get(key)
-
-    def get_header(self, key: str) -> Union[None, str]:
-        key = key.replace("-", "_")
-        key = "HTTP_" + key
-        return self.request.META.get(key.upper())
-
-    def get_session(self) -> Union[SessionContainer, None]:
-        return self.request.supertokens  # type: ignore
-
-    def set_session(self, session: SessionContainer):
-        self.request.supertokens = session  # type: ignore
-        if hasattr(self.request, "_request"):
-            # this is there because in Django reset framework, the request object
-            # in the API is the one from reset framework, however, the
-            # one from the middleware is the original one. So we need to set
-            # this so that the middleware also gets the token updates which can then be
-            # used to update the response.
-            # pylint: disable=protected-access
-            self.request._request.supertokens = session  # type: ignore
-
-    def set_session_as_none(self):
-        self.request.supertokens = None  # type: ignore
-        if hasattr(self.request, "_request"):
-            # this is there because in Django reset framework, the request object
-            # in the API is the one from reset framework, however, the
-            # one from the middleware is the original one. So we need to set
-            # this so that the middleware also gets the token updates which can then be
-            # used to update the response.
-            # pylint: disable=protected-access
-            self.request._request.supertokens = None  # type: ignore
-
-    def get_path(self) -> str:
-        return self.request.path
-
-    async def form_data(self):
-        return dict(parse_qsl(self.request.body.decode("utf-8")))
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class DjangoRequest -(request: HttpRequest) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class DjangoRequest(BaseRequest):
-    def __init__(self, request: HttpRequest):
-        super().__init__()
-        self.request = request
-
-    def get_original_url(self) -> str:
-        return self.request.build_absolute_uri()
-
-    def get_query_param(
-        self, key: str, default: Union[str, None] = None
-    ) -> Union[str, None]:
-        return self.request.GET.get(key, default)
-
-    def get_query_params(self) -> Dict[str, Any]:
-        return self.request.GET.dict()
-
-    async def json(self) -> Union[Any, None]:
-        try:
-            body = json.loads(self.request.body)
-            return body
-        except Exception:
-            return {}
-
-    def method(self) -> str:
-        if self.request.method is None:
-            raise Exception("Should never come here")
-        return self.request.method
-
-    def get_cookie(self, key: str) -> Union[str, None]:
-        return self.request.COOKIES.get(key)
-
-    def get_header(self, key: str) -> Union[None, str]:
-        key = key.replace("-", "_")
-        key = "HTTP_" + key
-        return self.request.META.get(key.upper())
-
-    def get_session(self) -> Union[SessionContainer, None]:
-        return self.request.supertokens  # type: ignore
-
-    def set_session(self, session: SessionContainer):
-        self.request.supertokens = session  # type: ignore
-        if hasattr(self.request, "_request"):
-            # this is there because in Django reset framework, the request object
-            # in the API is the one from reset framework, however, the
-            # one from the middleware is the original one. So we need to set
-            # this so that the middleware also gets the token updates which can then be
-            # used to update the response.
-            # pylint: disable=protected-access
-            self.request._request.supertokens = session  # type: ignore
-
-    def set_session_as_none(self):
-        self.request.supertokens = None  # type: ignore
-        if hasattr(self.request, "_request"):
-            # this is there because in Django reset framework, the request object
-            # in the API is the one from reset framework, however, the
-            # one from the middleware is the original one. So we need to set
-            # this so that the middleware also gets the token updates which can then be
-            # used to update the response.
-            # pylint: disable=protected-access
-            self.request._request.supertokens = None  # type: ignore
-
-    def get_path(self) -> str:
-        return self.request.path
-
-    async def form_data(self):
-        return dict(parse_qsl(self.request.body.decode("utf-8")))
-
-

Ancestors

- -

Methods

-
-
-async def form_data(self) -
-
-
-
- -Expand source code - -
async def form_data(self):
-    return dict(parse_qsl(self.request.body.decode("utf-8")))
-
-
- -
-
-
- -Expand source code - -
def get_cookie(self, key: str) -> Union[str, None]:
-    return self.request.COOKIES.get(key)
-
-
-
-def get_header(self, key: str) ‑> Optional[str] -
-
-
-
- -Expand source code - -
def get_header(self, key: str) -> Union[None, str]:
-    key = key.replace("-", "_")
-    key = "HTTP_" + key
-    return self.request.META.get(key.upper())
-
-
-
-def get_original_url(self) ‑> str -
-
-
-
- -Expand source code - -
def get_original_url(self) -> str:
-    return self.request.build_absolute_uri()
-
-
-
-def get_path(self) ‑> str -
-
-
-
- -Expand source code - -
def get_path(self) -> str:
-    return self.request.path
-
-
-
-def get_query_param(self, key: str, default: Union[str, None] = None) ‑> Optional[str] -
-
-
-
- -Expand source code - -
def get_query_param(
-    self, key: str, default: Union[str, None] = None
-) -> Union[str, None]:
-    return self.request.GET.get(key, default)
-
-
-
-def get_query_params(self) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
def get_query_params(self) -> Dict[str, Any]:
-    return self.request.GET.dict()
-
-
-
-def get_session(self) ‑> Union[SessionContainer, None] -
-
-
-
- -Expand source code - -
def get_session(self) -> Union[SessionContainer, None]:
-    return self.request.supertokens  # type: ignore
-
-
-
-async def json(self) ‑> Optional[Any] -
-
-
-
- -Expand source code - -
async def json(self) -> Union[Any, None]:
-    try:
-        body = json.loads(self.request.body)
-        return body
-    except Exception:
-        return {}
-
-
-
-def method(self) ‑> str -
-
-
-
- -Expand source code - -
def method(self) -> str:
-    if self.request.method is None:
-        raise Exception("Should never come here")
-    return self.request.method
-
-
-
-def set_session(self, session: SessionContainer) -
-
-
-
- -Expand source code - -
def set_session(self, session: SessionContainer):
-    self.request.supertokens = session  # type: ignore
-    if hasattr(self.request, "_request"):
-        # this is there because in Django reset framework, the request object
-        # in the API is the one from reset framework, however, the
-        # one from the middleware is the original one. So we need to set
-        # this so that the middleware also gets the token updates which can then be
-        # used to update the response.
-        # pylint: disable=protected-access
-        self.request._request.supertokens = session  # type: ignore
-
-
-
-

Inherited members

- -
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/framework/django/django_response.html b/html/supertokens_python/framework/django/django_response.html deleted file mode 100644 index c8b888d71..000000000 --- a/html/supertokens_python/framework/django/django_response.html +++ /dev/null @@ -1,395 +0,0 @@ - - - - - - -supertokens_python.framework.django.django_response API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.framework.django.django_response

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-import json
-from datetime import datetime
-from math import ceil
-from typing import Any, Dict, Optional
-
-from supertokens_python.framework.response import BaseResponse
-
-
-class DjangoResponse(BaseResponse):
-    from django.http import HttpResponse
-
-    def __init__(self, response: HttpResponse):
-        super().__init__({})
-        self.response = response
-        self.original = response
-        self.parser_checked = False
-        self.response_sent = False
-        self.status_set = False
-
-    def set_html_content(self, content: str):
-        if not self.response_sent:
-            self.response.content = content
-            self.set_header("Content-Type", "text/html")
-            self.response_sent = True
-
-    def set_cookie(
-        self,
-        key: str,
-        value: str,
-        expires: int,
-        path: str = "/",
-        domain: Optional[str] = None,
-        secure: bool = False,
-        httponly: bool = False,
-        samesite: str = "lax",
-    ):
-        self.response.set_cookie(
-            key=key,
-            value=value,
-            expires=datetime.fromtimestamp(ceil(expires / 1000)).strftime(
-                "%a, %d %b %Y %H:%M:%S GMT"
-            ),
-            path=path,
-            domain=domain,
-            secure=secure,
-            httponly=httponly,
-        )
-        self.response.cookies[key]["samesite"] = samesite
-
-    def set_status_code(self, status_code: int):
-        if not self.status_set:
-            self.response.status_code = status_code
-            self.status_code = status_code
-            self.status_set = True
-
-    def set_header(self, key: str, value: str):
-        self.response[key] = value
-
-    def get_header(self, key: str) -> Optional[str]:
-        if self.response.has_header(key):
-            return self.response[key]
-        return None
-
-    def remove_header(self, key: str):
-        del self.response[key]
-
-    def set_json_content(self, content: Dict[str, Any]):
-        if not self.response_sent:
-            self.set_header("Content-Type", "application/json; charset=utf-8")
-            self.response.content = json.dumps(
-                content,
-                ensure_ascii=False,
-                allow_nan=False,
-                indent=None,
-                separators=(",", ":"),
-            ).encode("utf-8")
-            self.response_sent = True
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class DjangoResponse -(response: django.http.response.HttpResponse) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class DjangoResponse(BaseResponse):
-    from django.http import HttpResponse
-
-    def __init__(self, response: HttpResponse):
-        super().__init__({})
-        self.response = response
-        self.original = response
-        self.parser_checked = False
-        self.response_sent = False
-        self.status_set = False
-
-    def set_html_content(self, content: str):
-        if not self.response_sent:
-            self.response.content = content
-            self.set_header("Content-Type", "text/html")
-            self.response_sent = True
-
-    def set_cookie(
-        self,
-        key: str,
-        value: str,
-        expires: int,
-        path: str = "/",
-        domain: Optional[str] = None,
-        secure: bool = False,
-        httponly: bool = False,
-        samesite: str = "lax",
-    ):
-        self.response.set_cookie(
-            key=key,
-            value=value,
-            expires=datetime.fromtimestamp(ceil(expires / 1000)).strftime(
-                "%a, %d %b %Y %H:%M:%S GMT"
-            ),
-            path=path,
-            domain=domain,
-            secure=secure,
-            httponly=httponly,
-        )
-        self.response.cookies[key]["samesite"] = samesite
-
-    def set_status_code(self, status_code: int):
-        if not self.status_set:
-            self.response.status_code = status_code
-            self.status_code = status_code
-            self.status_set = True
-
-    def set_header(self, key: str, value: str):
-        self.response[key] = value
-
-    def get_header(self, key: str) -> Optional[str]:
-        if self.response.has_header(key):
-            return self.response[key]
-        return None
-
-    def remove_header(self, key: str):
-        del self.response[key]
-
-    def set_json_content(self, content: Dict[str, Any]):
-        if not self.response_sent:
-            self.set_header("Content-Type", "application/json; charset=utf-8")
-            self.response.content = json.dumps(
-                content,
-                ensure_ascii=False,
-                allow_nan=False,
-                indent=None,
-                separators=(",", ":"),
-            ).encode("utf-8")
-            self.response_sent = True
-
-

Ancestors

- -

Class variables

-
-
var HttpResponse
-
-

An HTTP response class with a string as content.

-

This content can be read, appended to, or replaced.

-
-
-

Methods

-
-
-def get_header(self, key: str) ‑> Optional[str] -
-
-
-
- -Expand source code - -
def get_header(self, key: str) -> Optional[str]:
-    if self.response.has_header(key):
-        return self.response[key]
-    return None
-
-
-
-def remove_header(self, key: str) -
-
-
-
- -Expand source code - -
def remove_header(self, key: str):
-    del self.response[key]
-
-
- -
-
-
- -Expand source code - -
def set_cookie(
-    self,
-    key: str,
-    value: str,
-    expires: int,
-    path: str = "/",
-    domain: Optional[str] = None,
-    secure: bool = False,
-    httponly: bool = False,
-    samesite: str = "lax",
-):
-    self.response.set_cookie(
-        key=key,
-        value=value,
-        expires=datetime.fromtimestamp(ceil(expires / 1000)).strftime(
-            "%a, %d %b %Y %H:%M:%S GMT"
-        ),
-        path=path,
-        domain=domain,
-        secure=secure,
-        httponly=httponly,
-    )
-    self.response.cookies[key]["samesite"] = samesite
-
-
-
-def set_header(self, key: str, value: str) -
-
-
-
- -Expand source code - -
def set_header(self, key: str, value: str):
-    self.response[key] = value
-
-
-
-def set_html_content(self, content: str) -
-
-
-
- -Expand source code - -
def set_html_content(self, content: str):
-    if not self.response_sent:
-        self.response.content = content
-        self.set_header("Content-Type", "text/html")
-        self.response_sent = True
-
-
-
-def set_json_content(self, content: Dict[str, Any]) -
-
-
-
- -Expand source code - -
def set_json_content(self, content: Dict[str, Any]):
-    if not self.response_sent:
-        self.set_header("Content-Type", "application/json; charset=utf-8")
-        self.response.content = json.dumps(
-            content,
-            ensure_ascii=False,
-            allow_nan=False,
-            indent=None,
-            separators=(",", ":"),
-        ).encode("utf-8")
-        self.response_sent = True
-
-
-
-def set_status_code(self, status_code: int) -
-
-
-
- -Expand source code - -
def set_status_code(self, status_code: int):
-    if not self.status_set:
-        self.response.status_code = status_code
-        self.status_code = status_code
-        self.status_set = True
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/framework/django/framework.html b/html/supertokens_python/framework/django/framework.html deleted file mode 100644 index deedf85d1..000000000 --- a/html/supertokens_python/framework/django/framework.html +++ /dev/null @@ -1,140 +0,0 @@ - - - - - - -supertokens_python.framework.django.framework API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.framework.django.framework

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from __future__ import annotations
-
-from typing import TYPE_CHECKING
-
-from supertokens_python.framework.types import Framework
-
-if TYPE_CHECKING:
-    from django.http import HttpRequest
-
-
-class DjangoFramework(Framework):
-    def wrap_request(self, unwrapped: HttpRequest):
-        from supertokens_python.framework.django.django_request import DjangoRequest
-
-        return DjangoRequest(unwrapped)
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class DjangoFramework -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class DjangoFramework(Framework):
-    def wrap_request(self, unwrapped: HttpRequest):
-        from supertokens_python.framework.django.django_request import DjangoRequest
-
-        return DjangoRequest(unwrapped)
-
-

Ancestors

- -

Methods

-
-
-def wrap_request(self, unwrapped: HttpRequest) -
-
-
-
- -Expand source code - -
def wrap_request(self, unwrapped: HttpRequest):
-    from supertokens_python.framework.django.django_request import DjangoRequest
-
-    return DjangoRequest(unwrapped)
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/framework/django/index.html b/html/supertokens_python/framework/django/index.html deleted file mode 100644 index 9eddf5134..000000000 --- a/html/supertokens_python/framework/django/index.html +++ /dev/null @@ -1,101 +0,0 @@ - - - - - - -supertokens_python.framework.django API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.framework.django

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from supertokens_python.framework.django.django_middleware import (
-    middleware,  # type: ignore
-)
-
-
-
-

Sub-modules

-
-
supertokens_python.framework.django.django_middleware
-
-
-
-
supertokens_python.framework.django.django_request
-
-
-
-
supertokens_python.framework.django.django_response
-
-
-
-
supertokens_python.framework.django.framework
-
-
-
-
-
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/framework/fastapi/fastapi_middleware.html b/html/supertokens_python/framework/fastapi/fastapi_middleware.html deleted file mode 100644 index a61f9c5c6..000000000 --- a/html/supertokens_python/framework/fastapi/fastapi_middleware.html +++ /dev/null @@ -1,281 +0,0 @@ - - - - - - -supertokens_python.framework.fastapi.fastapi_middleware API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.framework.fastapi.fastapi_middleware

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from typing import Union
-
-
-def get_middleware():
-    from supertokens_python import Supertokens
-    from supertokens_python.utils import default_user_context
-    from supertokens_python.exceptions import SuperTokensError
-    from supertokens_python.framework import BaseResponse
-    from supertokens_python.recipe.session import SessionContainer
-    from supertokens_python.supertokens import manage_session_post_response
-
-    from starlette.requests import Request
-    from starlette.responses import Response
-    from starlette.types import ASGIApp, Message, Receive, Scope, Send
-
-    from supertokens_python.framework.fastapi.fastapi_request import (
-        FastApiRequest,
-    )
-    from supertokens_python.framework.fastapi.fastapi_response import (
-        FastApiResponse,
-    )
-
-    class ASGIMiddleware:
-        def __init__(self, app: ASGIApp) -> None:
-            self.app = app
-
-        async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
-            if scope["type"] != "http":  # we pass through the non-http requests, if any
-                await self.app(scope, receive, send)
-                return
-
-            st = Supertokens.get_instance()
-
-            request = Request(scope, receive=receive)
-            custom_request = FastApiRequest(request)
-            user_context = default_user_context(custom_request)
-
-            try:
-                response = FastApiResponse(Response())
-                result: Union[BaseResponse, None] = await st.middleware(
-                    custom_request, response, user_context
-                )
-                if result is None:
-                    # This means that the supertokens middleware did not handle the request,
-                    # however, we may need to handle the header changes in the response,
-                    # based on response mutators used by the session.
-                    async def send_wrapper(message: Message):
-                        if message["type"] == "http.response.start":
-                            # Start message has the headers, so we update the headers here
-                            # by using `manage_session_post_response` function, which will
-                            # apply all the Response Mutators. In the end, we just replace
-                            # the updated headers in the message.
-                            if hasattr(request.state, "supertokens") and isinstance(
-                                request.state.supertokens, SessionContainer
-                            ):
-                                fapi_response = Response()
-                                fapi_response.raw_headers = message["headers"]
-                                response = FastApiResponse(fapi_response)
-                                manage_session_post_response(
-                                    request.state.supertokens, response, user_context
-                                )
-                                message["headers"] = fapi_response.raw_headers
-
-                        # For `http.response.start` message, we might have the headers updated,
-                        # otherwise, we just send all the messages as is
-                        await send(message)
-
-                    await self.app(scope, receive, send_wrapper)
-                    return
-
-                # This means that the request was handled by the supertokens middleware
-                # and hence we respond using the response object returned by the middleware.
-                if hasattr(request.state, "supertokens") and isinstance(
-                    request.state.supertokens, SessionContainer
-                ):
-                    manage_session_post_response(
-                        request.state.supertokens, result, user_context
-                    )
-
-                if isinstance(result, FastApiResponse):
-                    await result.response(scope, receive, send)
-                    return
-
-                return
-
-            except SuperTokensError as e:
-                response = FastApiResponse(Response())
-                result: Union[BaseResponse, None] = await st.handle_supertokens_error(
-                    FastApiRequest(request), e, response, user_context
-                )
-                if isinstance(result, FastApiResponse):
-                    await result.response(scope, receive, send)
-                    return
-
-            raise Exception("Should never come here")
-
-    return ASGIMiddleware
-
-
-
-
-
-
-
-

Functions

-
-
-def get_middleware() -
-
-
-
- -Expand source code - -
def get_middleware():
-    from supertokens_python import Supertokens
-    from supertokens_python.utils import default_user_context
-    from supertokens_python.exceptions import SuperTokensError
-    from supertokens_python.framework import BaseResponse
-    from supertokens_python.recipe.session import SessionContainer
-    from supertokens_python.supertokens import manage_session_post_response
-
-    from starlette.requests import Request
-    from starlette.responses import Response
-    from starlette.types import ASGIApp, Message, Receive, Scope, Send
-
-    from supertokens_python.framework.fastapi.fastapi_request import (
-        FastApiRequest,
-    )
-    from supertokens_python.framework.fastapi.fastapi_response import (
-        FastApiResponse,
-    )
-
-    class ASGIMiddleware:
-        def __init__(self, app: ASGIApp) -> None:
-            self.app = app
-
-        async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
-            if scope["type"] != "http":  # we pass through the non-http requests, if any
-                await self.app(scope, receive, send)
-                return
-
-            st = Supertokens.get_instance()
-
-            request = Request(scope, receive=receive)
-            custom_request = FastApiRequest(request)
-            user_context = default_user_context(custom_request)
-
-            try:
-                response = FastApiResponse(Response())
-                result: Union[BaseResponse, None] = await st.middleware(
-                    custom_request, response, user_context
-                )
-                if result is None:
-                    # This means that the supertokens middleware did not handle the request,
-                    # however, we may need to handle the header changes in the response,
-                    # based on response mutators used by the session.
-                    async def send_wrapper(message: Message):
-                        if message["type"] == "http.response.start":
-                            # Start message has the headers, so we update the headers here
-                            # by using `manage_session_post_response` function, which will
-                            # apply all the Response Mutators. In the end, we just replace
-                            # the updated headers in the message.
-                            if hasattr(request.state, "supertokens") and isinstance(
-                                request.state.supertokens, SessionContainer
-                            ):
-                                fapi_response = Response()
-                                fapi_response.raw_headers = message["headers"]
-                                response = FastApiResponse(fapi_response)
-                                manage_session_post_response(
-                                    request.state.supertokens, response, user_context
-                                )
-                                message["headers"] = fapi_response.raw_headers
-
-                        # For `http.response.start` message, we might have the headers updated,
-                        # otherwise, we just send all the messages as is
-                        await send(message)
-
-                    await self.app(scope, receive, send_wrapper)
-                    return
-
-                # This means that the request was handled by the supertokens middleware
-                # and hence we respond using the response object returned by the middleware.
-                if hasattr(request.state, "supertokens") and isinstance(
-                    request.state.supertokens, SessionContainer
-                ):
-                    manage_session_post_response(
-                        request.state.supertokens, result, user_context
-                    )
-
-                if isinstance(result, FastApiResponse):
-                    await result.response(scope, receive, send)
-                    return
-
-                return
-
-            except SuperTokensError as e:
-                response = FastApiResponse(Response())
-                result: Union[BaseResponse, None] = await st.handle_supertokens_error(
-                    FastApiRequest(request), e, response, user_context
-                )
-                if isinstance(result, FastApiResponse):
-                    await result.response(scope, receive, send)
-                    return
-
-            raise Exception("Should never come here")
-
-    return ASGIMiddleware
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/framework/fastapi/fastapi_request.html b/html/supertokens_python/framework/fastapi/fastapi_request.html deleted file mode 100644 index efa01ad1a..000000000 --- a/html/supertokens_python/framework/fastapi/fastapi_request.html +++ /dev/null @@ -1,391 +0,0 @@ - - - - - - -supertokens_python.framework.fastapi.fastapi_request API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.framework.fastapi.fastapi_request

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from typing import TYPE_CHECKING, Any, Dict, Union
-from urllib.parse import parse_qsl
-
-from supertokens_python.framework.request import BaseRequest
-
-if TYPE_CHECKING:
-    from supertokens_python.recipe.session.interfaces import SessionContainer
-    from fastapi import Request
-
-
-class FastApiRequest(BaseRequest):
-    def __init__(self, request: Request):
-        super().__init__()
-        self.request = request
-
-    def get_original_url(self) -> str:
-        return self.request.url.components.geturl()
-
-    def get_query_param(
-        self, key: str, default: Union[str, None] = None
-    ) -> Union[str, None]:
-        return self.request.query_params.get(key, default)
-
-    def get_query_params(self) -> Dict[str, Any]:
-        return dict(self.request.query_params.items())  # type: ignore
-
-    async def json(self) -> Union[Any, None]:
-        try:
-            return await self.request.json()
-        except Exception:
-            return {}
-
-    def method(self) -> str:
-        return self.request.method
-
-    def get_cookie(self, key: str) -> Union[str, None]:
-        # Note: Unlike other frameworks, FastAPI wraps the value in quotes in Set-Cookie header
-        # It also takes care of escaping the quotes while fetching the value
-        return self.request.cookies.get(key)
-
-    def get_header(self, key: str) -> Union[str, None]:
-        return self.request.headers.get(key, None)
-
-    def get_session(self) -> Union[SessionContainer, None]:
-        return self.request.state.supertokens
-
-    def set_session(self, session: SessionContainer):
-        self.request.state.supertokens = session
-
-    def set_session_as_none(self):
-        self.request.state.supertokens = None
-
-    def get_path(self) -> str:
-        root_path = self.request.scope.get("root_path")
-        url = self.request.url.path
-        # FastAPI seems buggy and it adds an extra root_path (if it matches):
-        # So we trim the extra root_path (from the left) from the url
-        return url[url.startswith(root_path) and len(root_path) :]
-
-    async def form_data(self):
-        return dict(parse_qsl((await self.request.body()).decode("utf-8")))
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class FastApiRequest -(request: Request) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class FastApiRequest(BaseRequest):
-    def __init__(self, request: Request):
-        super().__init__()
-        self.request = request
-
-    def get_original_url(self) -> str:
-        return self.request.url.components.geturl()
-
-    def get_query_param(
-        self, key: str, default: Union[str, None] = None
-    ) -> Union[str, None]:
-        return self.request.query_params.get(key, default)
-
-    def get_query_params(self) -> Dict[str, Any]:
-        return dict(self.request.query_params.items())  # type: ignore
-
-    async def json(self) -> Union[Any, None]:
-        try:
-            return await self.request.json()
-        except Exception:
-            return {}
-
-    def method(self) -> str:
-        return self.request.method
-
-    def get_cookie(self, key: str) -> Union[str, None]:
-        # Note: Unlike other frameworks, FastAPI wraps the value in quotes in Set-Cookie header
-        # It also takes care of escaping the quotes while fetching the value
-        return self.request.cookies.get(key)
-
-    def get_header(self, key: str) -> Union[str, None]:
-        return self.request.headers.get(key, None)
-
-    def get_session(self) -> Union[SessionContainer, None]:
-        return self.request.state.supertokens
-
-    def set_session(self, session: SessionContainer):
-        self.request.state.supertokens = session
-
-    def set_session_as_none(self):
-        self.request.state.supertokens = None
-
-    def get_path(self) -> str:
-        root_path = self.request.scope.get("root_path")
-        url = self.request.url.path
-        # FastAPI seems buggy and it adds an extra root_path (if it matches):
-        # So we trim the extra root_path (from the left) from the url
-        return url[url.startswith(root_path) and len(root_path) :]
-
-    async def form_data(self):
-        return dict(parse_qsl((await self.request.body()).decode("utf-8")))
-
-

Ancestors

- -

Methods

-
-
-async def form_data(self) -
-
-
-
- -Expand source code - -
async def form_data(self):
-    return dict(parse_qsl((await self.request.body()).decode("utf-8")))
-
-
- -
-
-
- -Expand source code - -
def get_cookie(self, key: str) -> Union[str, None]:
-    # Note: Unlike other frameworks, FastAPI wraps the value in quotes in Set-Cookie header
-    # It also takes care of escaping the quotes while fetching the value
-    return self.request.cookies.get(key)
-
-
-
-def get_header(self, key: str) ‑> Optional[str] -
-
-
-
- -Expand source code - -
def get_header(self, key: str) -> Union[str, None]:
-    return self.request.headers.get(key, None)
-
-
-
-def get_original_url(self) ‑> str -
-
-
-
- -Expand source code - -
def get_original_url(self) -> str:
-    return self.request.url.components.geturl()
-
-
-
-def get_path(self) ‑> str -
-
-
-
- -Expand source code - -
def get_path(self) -> str:
-    root_path = self.request.scope.get("root_path")
-    url = self.request.url.path
-    # FastAPI seems buggy and it adds an extra root_path (if it matches):
-    # So we trim the extra root_path (from the left) from the url
-    return url[url.startswith(root_path) and len(root_path) :]
-
-
-
-def get_query_param(self, key: str, default: Union[str, None] = None) ‑> Optional[str] -
-
-
-
- -Expand source code - -
def get_query_param(
-    self, key: str, default: Union[str, None] = None
-) -> Union[str, None]:
-    return self.request.query_params.get(key, default)
-
-
-
-def get_query_params(self) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
def get_query_params(self) -> Dict[str, Any]:
-    return dict(self.request.query_params.items())  # type: ignore
-
-
-
-def get_session(self) ‑> Union[SessionContainer, None] -
-
-
-
- -Expand source code - -
def get_session(self) -> Union[SessionContainer, None]:
-    return self.request.state.supertokens
-
-
-
-async def json(self) ‑> Optional[Any] -
-
-
-
- -Expand source code - -
async def json(self) -> Union[Any, None]:
-    try:
-        return await self.request.json()
-    except Exception:
-        return {}
-
-
-
-def method(self) ‑> str -
-
-
-
- -Expand source code - -
def method(self) -> str:
-    return self.request.method
-
-
-
-def set_session(self, session: SessionContainer) -
-
-
-
- -Expand source code - -
def set_session(self, session: SessionContainer):
-    self.request.state.supertokens = session
-
-
-
-

Inherited members

- -
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/framework/fastapi/fastapi_response.html b/html/supertokens_python/framework/fastapi/fastapi_response.html deleted file mode 100644 index 4a581ceb3..000000000 --- a/html/supertokens_python/framework/fastapi/fastapi_response.html +++ /dev/null @@ -1,412 +0,0 @@ - - - - - - -supertokens_python.framework.fastapi.fastapi_response API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.framework.fastapi.fastapi_response

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-import json
-from math import ceil
-from typing import Any, Dict, Optional
-
-from supertokens_python.framework.response import BaseResponse
-from supertokens_python.utils import get_timestamp_ms
-
-
-class FastApiResponse(BaseResponse):
-    from fastapi import Response
-
-    def __init__(self, response: Response):
-        super().__init__({})
-        self.response = response
-        self.original = response
-        self.parser_checked = False
-        self.response_sent = False
-        self.status_set = False
-
-    def set_html_content(self, content: str):
-        if not self.response_sent:
-            body = bytes(content, "utf-8")
-            self.set_header("Content-Length", str(len(body)))
-            self.set_header("Content-Type", "text/html")
-            self.response.body = body
-            self.response_sent = True
-
-    def set_cookie(
-        self,
-        key: str,
-        value: str,
-        expires: int,
-        path: str = "/",
-        domain: Optional[str] = None,
-        secure: bool = False,
-        httponly: bool = False,
-        samesite: str = "lax",
-    ):
-        # Note: For FastAPI response object, the expires value
-        # doesn't mean the absolute time in ms, but the duration in seconds
-        # So we need to convert our absolute expiry time (ms) to a duration (seconds)
-
-        # we do ceil because if we do floor, we tests may fail where the access
-        # token lifetime is set to 1 second
-        self.response.set_cookie(
-            key=key,
-            value=value,  # Note: Unlike other frameworks, FastAPI wraps the value in quotes in Set-Cookie header
-            expires=ceil((expires - get_timestamp_ms()) / 1000),
-            path=path,
-            domain=domain,  # type: ignore # starlette didn't set domain as optional type but their default value is None anyways
-            secure=secure,
-            httponly=httponly,
-            samesite=samesite,
-        )
-
-    def set_header(self, key: str, value: str):
-        self.response.headers[key] = value
-
-    def get_header(self, key: str) -> Optional[str]:
-        return self.response.headers.get(key, None)
-
-    def remove_header(self, key: str):
-        del self.response.headers[key]
-
-    def set_status_code(self, status_code: int):
-        if not self.status_set:
-            self.response.status_code = status_code
-            self.status_code = status_code
-            self.status_set = True
-
-    def set_json_content(self, content: Dict[str, Any]):
-        if not self.response_sent:
-            body = json.dumps(
-                content,
-                ensure_ascii=False,
-                allow_nan=False,
-                indent=None,
-                separators=(",", ":"),
-            ).encode("utf-8")
-            self.set_header("Content-Type", "application/json; charset=utf-8")
-            self.set_header("Content-Length", str(len(body)))
-            self.response.body = body
-            self.response_sent = True
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class FastApiResponse -(response: starlette.responses.Response) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class FastApiResponse(BaseResponse):
-    from fastapi import Response
-
-    def __init__(self, response: Response):
-        super().__init__({})
-        self.response = response
-        self.original = response
-        self.parser_checked = False
-        self.response_sent = False
-        self.status_set = False
-
-    def set_html_content(self, content: str):
-        if not self.response_sent:
-            body = bytes(content, "utf-8")
-            self.set_header("Content-Length", str(len(body)))
-            self.set_header("Content-Type", "text/html")
-            self.response.body = body
-            self.response_sent = True
-
-    def set_cookie(
-        self,
-        key: str,
-        value: str,
-        expires: int,
-        path: str = "/",
-        domain: Optional[str] = None,
-        secure: bool = False,
-        httponly: bool = False,
-        samesite: str = "lax",
-    ):
-        # Note: For FastAPI response object, the expires value
-        # doesn't mean the absolute time in ms, but the duration in seconds
-        # So we need to convert our absolute expiry time (ms) to a duration (seconds)
-
-        # we do ceil because if we do floor, we tests may fail where the access
-        # token lifetime is set to 1 second
-        self.response.set_cookie(
-            key=key,
-            value=value,  # Note: Unlike other frameworks, FastAPI wraps the value in quotes in Set-Cookie header
-            expires=ceil((expires - get_timestamp_ms()) / 1000),
-            path=path,
-            domain=domain,  # type: ignore # starlette didn't set domain as optional type but their default value is None anyways
-            secure=secure,
-            httponly=httponly,
-            samesite=samesite,
-        )
-
-    def set_header(self, key: str, value: str):
-        self.response.headers[key] = value
-
-    def get_header(self, key: str) -> Optional[str]:
-        return self.response.headers.get(key, None)
-
-    def remove_header(self, key: str):
-        del self.response.headers[key]
-
-    def set_status_code(self, status_code: int):
-        if not self.status_set:
-            self.response.status_code = status_code
-            self.status_code = status_code
-            self.status_set = True
-
-    def set_json_content(self, content: Dict[str, Any]):
-        if not self.response_sent:
-            body = json.dumps(
-                content,
-                ensure_ascii=False,
-                allow_nan=False,
-                indent=None,
-                separators=(",", ":"),
-            ).encode("utf-8")
-            self.set_header("Content-Type", "application/json; charset=utf-8")
-            self.set_header("Content-Length", str(len(body)))
-            self.response.body = body
-            self.response_sent = True
-
-

Ancestors

- -

Class variables

-
-
var Response
-
-
-
-
-

Methods

-
-
-def get_header(self, key: str) ‑> Optional[str] -
-
-
-
- -Expand source code - -
def get_header(self, key: str) -> Optional[str]:
-    return self.response.headers.get(key, None)
-
-
-
-def remove_header(self, key: str) -
-
-
-
- -Expand source code - -
def remove_header(self, key: str):
-    del self.response.headers[key]
-
-
- -
-
-
- -Expand source code - -
def set_cookie(
-    self,
-    key: str,
-    value: str,
-    expires: int,
-    path: str = "/",
-    domain: Optional[str] = None,
-    secure: bool = False,
-    httponly: bool = False,
-    samesite: str = "lax",
-):
-    # Note: For FastAPI response object, the expires value
-    # doesn't mean the absolute time in ms, but the duration in seconds
-    # So we need to convert our absolute expiry time (ms) to a duration (seconds)
-
-    # we do ceil because if we do floor, we tests may fail where the access
-    # token lifetime is set to 1 second
-    self.response.set_cookie(
-        key=key,
-        value=value,  # Note: Unlike other frameworks, FastAPI wraps the value in quotes in Set-Cookie header
-        expires=ceil((expires - get_timestamp_ms()) / 1000),
-        path=path,
-        domain=domain,  # type: ignore # starlette didn't set domain as optional type but their default value is None anyways
-        secure=secure,
-        httponly=httponly,
-        samesite=samesite,
-    )
-
-
-
-def set_header(self, key: str, value: str) -
-
-
-
- -Expand source code - -
def set_header(self, key: str, value: str):
-    self.response.headers[key] = value
-
-
-
-def set_html_content(self, content: str) -
-
-
-
- -Expand source code - -
def set_html_content(self, content: str):
-    if not self.response_sent:
-        body = bytes(content, "utf-8")
-        self.set_header("Content-Length", str(len(body)))
-        self.set_header("Content-Type", "text/html")
-        self.response.body = body
-        self.response_sent = True
-
-
-
-def set_json_content(self, content: Dict[str, Any]) -
-
-
-
- -Expand source code - -
def set_json_content(self, content: Dict[str, Any]):
-    if not self.response_sent:
-        body = json.dumps(
-            content,
-            ensure_ascii=False,
-            allow_nan=False,
-            indent=None,
-            separators=(",", ":"),
-        ).encode("utf-8")
-        self.set_header("Content-Type", "application/json; charset=utf-8")
-        self.set_header("Content-Length", str(len(body)))
-        self.response.body = body
-        self.response_sent = True
-
-
-
-def set_status_code(self, status_code: int) -
-
-
-
- -Expand source code - -
def set_status_code(self, status_code: int):
-    if not self.status_set:
-        self.response.status_code = status_code
-        self.status_code = status_code
-        self.status_set = True
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/framework/fastapi/framework.html b/html/supertokens_python/framework/fastapi/framework.html deleted file mode 100644 index 8de3ca8d0..000000000 --- a/html/supertokens_python/framework/fastapi/framework.html +++ /dev/null @@ -1,139 +0,0 @@ - - - - - - -supertokens_python.framework.fastapi.framework API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.framework.fastapi.framework

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from typing import TYPE_CHECKING
-
-from supertokens_python.framework.types import Framework
-
-if TYPE_CHECKING:
-    from fastapi import Request
-
-
-class FastapiFramework(Framework):
-    def wrap_request(self, unwrapped: Request):
-        from supertokens_python.framework.fastapi.fastapi_request import FastApiRequest
-
-        return FastApiRequest(unwrapped)
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class FastapiFramework -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class FastapiFramework(Framework):
-    def wrap_request(self, unwrapped: Request):
-        from supertokens_python.framework.fastapi.fastapi_request import FastApiRequest
-
-        return FastApiRequest(unwrapped)
-
-

Ancestors

- -

Methods

-
-
-def wrap_request(self, unwrapped: Request) -
-
-
-
- -Expand source code - -
def wrap_request(self, unwrapped: Request):
-    from supertokens_python.framework.fastapi.fastapi_request import FastApiRequest
-
-    return FastApiRequest(unwrapped)
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/framework/fastapi/index.html b/html/supertokens_python/framework/fastapi/index.html deleted file mode 100644 index e3fd29b30..000000000 --- a/html/supertokens_python/framework/fastapi/index.html +++ /dev/null @@ -1,101 +0,0 @@ - - - - - - -supertokens_python.framework.fastapi API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.framework.fastapi

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from supertokens_python.framework.fastapi import fastapi_middleware
-
-get_middleware = fastapi_middleware.get_middleware
-
-
-
-

Sub-modules

-
-
supertokens_python.framework.fastapi.fastapi_middleware
-
-
-
-
supertokens_python.framework.fastapi.fastapi_request
-
-
-
-
supertokens_python.framework.fastapi.fastapi_response
-
-
-
-
supertokens_python.framework.fastapi.framework
-
-
-
-
-
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/framework/flask/flask_middleware.html b/html/supertokens_python/framework/flask/flask_middleware.html deleted file mode 100644 index dc520919a..000000000 --- a/html/supertokens_python/framework/flask/flask_middleware.html +++ /dev/null @@ -1,400 +0,0 @@ - - - - - - -supertokens_python.framework.flask.flask_middleware API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.framework.flask.flask_middleware

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-import json
-from typing import TYPE_CHECKING, Union
-
-from supertokens_python.async_to_sync_wrapper import sync
-from supertokens_python.framework import BaseResponse
-
-if TYPE_CHECKING:
-    from flask import Flask
-
-
-class Middleware:
-    def __init__(self, app: Flask):
-        self.app = app
-        self.set_before_after_request()
-        self.set_error_handler()
-
-    def set_before_after_request(self):
-        app = self.app
-        from supertokens_python.framework.flask.flask_request import FlaskRequest
-        from supertokens_python.framework.flask.flask_response import FlaskResponse
-        from supertokens_python.supertokens import manage_session_post_response
-        from supertokens_python.utils import default_user_context
-
-        from flask.wrappers import Response
-
-        # There is an error in the typing provided by flask, so we ignore it
-        # for now.
-        @app.before_request  # type: ignore
-        def _():
-            from supertokens_python import Supertokens
-
-            from flask import request
-            from flask.wrappers import Response
-
-            st = Supertokens.get_instance()
-
-            request_ = FlaskRequest(request)
-            response_ = FlaskResponse(Response())
-            user_context = default_user_context(request_)
-
-            result: Union[BaseResponse, None] = sync(
-                st.middleware(request_, response_, user_context)
-            )
-
-            if result is not None:
-                if isinstance(result, FlaskResponse):
-                    return result.response
-                raise Exception("Should never come here")
-            return None
-
-        @app.after_request
-        def _(response: Response):
-            from flask import g
-
-            response_ = FlaskResponse(response)
-            if hasattr(g, "supertokens") and g.supertokens is not None:
-                manage_session_post_response(g.supertokens, response_, {})
-
-            return response_.response
-
-        @app.teardown_request
-        def _(_):
-            from flask import g
-
-            if hasattr(g, "supertokens"):
-                # this is to ensure there are no shared objects between requests.
-                # calling any other API with a shared request causes a security issue, resulting in unintentional
-                # sign-ins. More on this here - https://github.com/supertokens/supertokens-python/issues/463
-                g.pop("supertokens")
-
-    def set_error_handler(self):
-        app = self.app
-        from supertokens_python.exceptions import SuperTokensError
-
-        from flask import request
-
-        @app.errorhandler(SuperTokensError)
-        def _(error: Exception):
-            from supertokens_python import Supertokens
-            from supertokens_python.framework.flask.flask_request import FlaskRequest
-            from supertokens_python.framework.flask.flask_response import FlaskResponse
-            from supertokens_python.utils import default_user_context
-
-            from flask.wrappers import Response
-
-            st = Supertokens.get_instance()
-            response = Response(json.dumps({}), mimetype="application/json", status=200)
-            base_request = FlaskRequest(request)
-            user_context = default_user_context(base_request)
-
-            result: BaseResponse = sync(
-                st.handle_supertokens_error(
-                    base_request,
-                    error,
-                    FlaskResponse(response),
-                    user_context,
-                )
-            )
-            if isinstance(result, FlaskResponse):
-                return result.response
-            raise Exception("Should never come here")
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class Middleware -(app: Flask) -
-
-
-
- -Expand source code - -
class Middleware:
-    def __init__(self, app: Flask):
-        self.app = app
-        self.set_before_after_request()
-        self.set_error_handler()
-
-    def set_before_after_request(self):
-        app = self.app
-        from supertokens_python.framework.flask.flask_request import FlaskRequest
-        from supertokens_python.framework.flask.flask_response import FlaskResponse
-        from supertokens_python.supertokens import manage_session_post_response
-        from supertokens_python.utils import default_user_context
-
-        from flask.wrappers import Response
-
-        # There is an error in the typing provided by flask, so we ignore it
-        # for now.
-        @app.before_request  # type: ignore
-        def _():
-            from supertokens_python import Supertokens
-
-            from flask import request
-            from flask.wrappers import Response
-
-            st = Supertokens.get_instance()
-
-            request_ = FlaskRequest(request)
-            response_ = FlaskResponse(Response())
-            user_context = default_user_context(request_)
-
-            result: Union[BaseResponse, None] = sync(
-                st.middleware(request_, response_, user_context)
-            )
-
-            if result is not None:
-                if isinstance(result, FlaskResponse):
-                    return result.response
-                raise Exception("Should never come here")
-            return None
-
-        @app.after_request
-        def _(response: Response):
-            from flask import g
-
-            response_ = FlaskResponse(response)
-            if hasattr(g, "supertokens") and g.supertokens is not None:
-                manage_session_post_response(g.supertokens, response_, {})
-
-            return response_.response
-
-        @app.teardown_request
-        def _(_):
-            from flask import g
-
-            if hasattr(g, "supertokens"):
-                # this is to ensure there are no shared objects between requests.
-                # calling any other API with a shared request causes a security issue, resulting in unintentional
-                # sign-ins. More on this here - https://github.com/supertokens/supertokens-python/issues/463
-                g.pop("supertokens")
-
-    def set_error_handler(self):
-        app = self.app
-        from supertokens_python.exceptions import SuperTokensError
-
-        from flask import request
-
-        @app.errorhandler(SuperTokensError)
-        def _(error: Exception):
-            from supertokens_python import Supertokens
-            from supertokens_python.framework.flask.flask_request import FlaskRequest
-            from supertokens_python.framework.flask.flask_response import FlaskResponse
-            from supertokens_python.utils import default_user_context
-
-            from flask.wrappers import Response
-
-            st = Supertokens.get_instance()
-            response = Response(json.dumps({}), mimetype="application/json", status=200)
-            base_request = FlaskRequest(request)
-            user_context = default_user_context(base_request)
-
-            result: BaseResponse = sync(
-                st.handle_supertokens_error(
-                    base_request,
-                    error,
-                    FlaskResponse(response),
-                    user_context,
-                )
-            )
-            if isinstance(result, FlaskResponse):
-                return result.response
-            raise Exception("Should never come here")
-
-

Methods

-
-
-def set_before_after_request(self) -
-
-
-
- -Expand source code - -
def set_before_after_request(self):
-    app = self.app
-    from supertokens_python.framework.flask.flask_request import FlaskRequest
-    from supertokens_python.framework.flask.flask_response import FlaskResponse
-    from supertokens_python.supertokens import manage_session_post_response
-    from supertokens_python.utils import default_user_context
-
-    from flask.wrappers import Response
-
-    # There is an error in the typing provided by flask, so we ignore it
-    # for now.
-    @app.before_request  # type: ignore
-    def _():
-        from supertokens_python import Supertokens
-
-        from flask import request
-        from flask.wrappers import Response
-
-        st = Supertokens.get_instance()
-
-        request_ = FlaskRequest(request)
-        response_ = FlaskResponse(Response())
-        user_context = default_user_context(request_)
-
-        result: Union[BaseResponse, None] = sync(
-            st.middleware(request_, response_, user_context)
-        )
-
-        if result is not None:
-            if isinstance(result, FlaskResponse):
-                return result.response
-            raise Exception("Should never come here")
-        return None
-
-    @app.after_request
-    def _(response: Response):
-        from flask import g
-
-        response_ = FlaskResponse(response)
-        if hasattr(g, "supertokens") and g.supertokens is not None:
-            manage_session_post_response(g.supertokens, response_, {})
-
-        return response_.response
-
-    @app.teardown_request
-    def _(_):
-        from flask import g
-
-        if hasattr(g, "supertokens"):
-            # this is to ensure there are no shared objects between requests.
-            # calling any other API with a shared request causes a security issue, resulting in unintentional
-            # sign-ins. More on this here - https://github.com/supertokens/supertokens-python/issues/463
-            g.pop("supertokens")
-
-
-
-def set_error_handler(self) -
-
-
-
- -Expand source code - -
def set_error_handler(self):
-    app = self.app
-    from supertokens_python.exceptions import SuperTokensError
-
-    from flask import request
-
-    @app.errorhandler(SuperTokensError)
-    def _(error: Exception):
-        from supertokens_python import Supertokens
-        from supertokens_python.framework.flask.flask_request import FlaskRequest
-        from supertokens_python.framework.flask.flask_response import FlaskResponse
-        from supertokens_python.utils import default_user_context
-
-        from flask.wrappers import Response
-
-        st = Supertokens.get_instance()
-        response = Response(json.dumps({}), mimetype="application/json", status=200)
-        base_request = FlaskRequest(request)
-        user_context = default_user_context(base_request)
-
-        result: BaseResponse = sync(
-            st.handle_supertokens_error(
-                base_request,
-                error,
-                FlaskResponse(response),
-                user_context,
-            )
-        )
-        if isinstance(result, FlaskResponse):
-            return result.response
-        raise Exception("Should never come here")
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/framework/flask/flask_request.html b/html/supertokens_python/framework/flask/flask_request.html deleted file mode 100644 index 09d256795..000000000 --- a/html/supertokens_python/framework/flask/flask_request.html +++ /dev/null @@ -1,412 +0,0 @@ - - - - - - -supertokens_python.framework.flask.flask_request API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.framework.flask.flask_request

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from typing import TYPE_CHECKING, Any, Dict, Union
-
-from supertokens_python.framework.request import BaseRequest
-
-if TYPE_CHECKING:
-    from supertokens_python.recipe.session.interfaces import SessionContainer
-    from flask.wrappers import Request
-
-
-class FlaskRequest(BaseRequest):
-    def __init__(self, req: Request):
-        super().__init__()
-        self.request = req
-
-    def get_original_url(self) -> str:
-        return self.request.url
-
-    def get_query_param(self, key: str, default: Union[str, None] = None):
-        return self.request.args.get(key, default)
-
-    def get_query_params(self) -> Dict[str, Any]:
-        return self.request.args.to_dict()
-
-    async def json(self) -> Union[Any, None]:
-        try:
-            return self.request.get_json()
-        except Exception:
-            return {}
-
-    def method(self) -> str:
-        if isinstance(self.request, dict):
-            temp: str = self.request["REQUEST_METHOD"]
-            return temp
-        return self.request.method  # type: ignore
-
-    def get_cookie(self, key: str) -> Union[str, None]:
-        return self.request.cookies.get(key, None)
-
-    def get_header(self, key: str) -> Union[None, str]:
-        if isinstance(self.request, dict):
-            return self.request.get(key, None)  # type: ignore
-        return self.request.headers.get(key)  # type: ignore
-
-    def get_session(self) -> Union[SessionContainer, None]:
-        from flask import g
-
-        if hasattr(g, "supertokens"):
-            return g.supertokens
-        return None
-
-    def set_session(self, session: SessionContainer):
-        from flask import g
-
-        g.supertokens = session
-
-    def set_session_as_none(self):
-        from flask import g
-
-        g.supertokens = None
-
-    def get_path(self) -> str:
-        if isinstance(self.request, dict):
-            temp: str = self.request["PATH_INFO"]
-            return temp
-        return self.request.base_url
-
-    async def form_data(self) -> Dict[str, Any]:
-        return self.request.form.to_dict()
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class FlaskRequest -(req: Request) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class FlaskRequest(BaseRequest):
-    def __init__(self, req: Request):
-        super().__init__()
-        self.request = req
-
-    def get_original_url(self) -> str:
-        return self.request.url
-
-    def get_query_param(self, key: str, default: Union[str, None] = None):
-        return self.request.args.get(key, default)
-
-    def get_query_params(self) -> Dict[str, Any]:
-        return self.request.args.to_dict()
-
-    async def json(self) -> Union[Any, None]:
-        try:
-            return self.request.get_json()
-        except Exception:
-            return {}
-
-    def method(self) -> str:
-        if isinstance(self.request, dict):
-            temp: str = self.request["REQUEST_METHOD"]
-            return temp
-        return self.request.method  # type: ignore
-
-    def get_cookie(self, key: str) -> Union[str, None]:
-        return self.request.cookies.get(key, None)
-
-    def get_header(self, key: str) -> Union[None, str]:
-        if isinstance(self.request, dict):
-            return self.request.get(key, None)  # type: ignore
-        return self.request.headers.get(key)  # type: ignore
-
-    def get_session(self) -> Union[SessionContainer, None]:
-        from flask import g
-
-        if hasattr(g, "supertokens"):
-            return g.supertokens
-        return None
-
-    def set_session(self, session: SessionContainer):
-        from flask import g
-
-        g.supertokens = session
-
-    def set_session_as_none(self):
-        from flask import g
-
-        g.supertokens = None
-
-    def get_path(self) -> str:
-        if isinstance(self.request, dict):
-            temp: str = self.request["PATH_INFO"]
-            return temp
-        return self.request.base_url
-
-    async def form_data(self) -> Dict[str, Any]:
-        return self.request.form.to_dict()
-
-

Ancestors

- -

Methods

-
-
-async def form_data(self) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
async def form_data(self) -> Dict[str, Any]:
-    return self.request.form.to_dict()
-
-
- -
-
-
- -Expand source code - -
def get_cookie(self, key: str) -> Union[str, None]:
-    return self.request.cookies.get(key, None)
-
-
-
-def get_header(self, key: str) ‑> Optional[str] -
-
-
-
- -Expand source code - -
def get_header(self, key: str) -> Union[None, str]:
-    if isinstance(self.request, dict):
-        return self.request.get(key, None)  # type: ignore
-    return self.request.headers.get(key)  # type: ignore
-
-
-
-def get_original_url(self) ‑> str -
-
-
-
- -Expand source code - -
def get_original_url(self) -> str:
-    return self.request.url
-
-
-
-def get_path(self) ‑> str -
-
-
-
- -Expand source code - -
def get_path(self) -> str:
-    if isinstance(self.request, dict):
-        temp: str = self.request["PATH_INFO"]
-        return temp
-    return self.request.base_url
-
-
-
-def get_query_param(self, key: str, default: Union[str, None] = None) -
-
-
-
- -Expand source code - -
def get_query_param(self, key: str, default: Union[str, None] = None):
-    return self.request.args.get(key, default)
-
-
-
-def get_query_params(self) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
def get_query_params(self) -> Dict[str, Any]:
-    return self.request.args.to_dict()
-
-
-
-def get_session(self) ‑> Union[SessionContainer, None] -
-
-
-
- -Expand source code - -
def get_session(self) -> Union[SessionContainer, None]:
-    from flask import g
-
-    if hasattr(g, "supertokens"):
-        return g.supertokens
-    return None
-
-
-
-async def json(self) ‑> Optional[Any] -
-
-
-
- -Expand source code - -
async def json(self) -> Union[Any, None]:
-    try:
-        return self.request.get_json()
-    except Exception:
-        return {}
-
-
-
-def method(self) ‑> str -
-
-
-
- -Expand source code - -
def method(self) -> str:
-    if isinstance(self.request, dict):
-        temp: str = self.request["REQUEST_METHOD"]
-        return temp
-    return self.request.method  # type: ignore
-
-
-
-def set_session(self, session: SessionContainer) -
-
-
-
- -Expand source code - -
def set_session(self, session: SessionContainer):
-    from flask import g
-
-    g.supertokens = session
-
-
-
-

Inherited members

- -
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/framework/flask/flask_response.html b/html/supertokens_python/framework/flask/flask_response.html deleted file mode 100644 index ef62d579f..000000000 --- a/html/supertokens_python/framework/flask/flask_response.html +++ /dev/null @@ -1,416 +0,0 @@ - - - - - - -supertokens_python.framework.flask.flask_response API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.framework.flask.flask_response

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-import json
-from typing import Any, Dict, List, Optional
-
-from supertokens_python.framework.response import BaseResponse
-
-
-class FlaskResponse(BaseResponse):
-    from flask.wrappers import Response
-
-    def __init__(self, response: Response):
-        super().__init__({})
-        self.response = response
-        self.original = response
-        self.headers: List[Any] = []
-        self.response_sent = False
-        self.status_set = False
-
-    def set_html_content(self, content: str):
-        if not self.response_sent:
-            self.response.data = content
-            self.set_header("Content-Type", "text/html")
-            self.response_sent = True
-
-    def set_cookie(
-        self,
-        key: str,
-        value: str,
-        expires: int,
-        path: str = "/",
-        domain: Optional[str] = None,
-        secure: bool = False,
-        httponly: bool = False,
-        samesite: str = "lax",
-    ):
-        self.response.set_cookie(
-            key,
-            value=value,
-            expires=expires / 1000,
-            path=path,
-            domain=domain,
-            secure=secure,
-            httponly=httponly,
-            samesite=samesite,
-        )
-
-    def set_header(self, key: str, value: str):
-        self.response.headers.set(key, value)
-
-    def get_header(self, key: str) -> Optional[str]:
-        return self.response.headers.get(key)
-
-    def remove_header(self, key: str):
-        del self.response.headers[key]
-
-    def set_status_code(self, status_code: int):
-        if not self.status_set:
-            self.response.status_code = status_code
-            self.status_code = status_code
-            self.status_set = True
-
-    def get_headers(self):
-        return self.response.headers
-
-    def set_json_content(self, content: Dict[str, Any]):
-        if not self.response_sent:
-            self.set_header("Content-Type", "application/json; charset=utf-8")
-            self.response.data = json.dumps(
-                content,
-                ensure_ascii=False,
-                allow_nan=False,
-                indent=None,
-                separators=(",", ":"),
-            ).encode("utf-8")
-            self.response_sent = True
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class FlaskResponse -(response: flask.wrappers.Response) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class FlaskResponse(BaseResponse):
-    from flask.wrappers import Response
-
-    def __init__(self, response: Response):
-        super().__init__({})
-        self.response = response
-        self.original = response
-        self.headers: List[Any] = []
-        self.response_sent = False
-        self.status_set = False
-
-    def set_html_content(self, content: str):
-        if not self.response_sent:
-            self.response.data = content
-            self.set_header("Content-Type", "text/html")
-            self.response_sent = True
-
-    def set_cookie(
-        self,
-        key: str,
-        value: str,
-        expires: int,
-        path: str = "/",
-        domain: Optional[str] = None,
-        secure: bool = False,
-        httponly: bool = False,
-        samesite: str = "lax",
-    ):
-        self.response.set_cookie(
-            key,
-            value=value,
-            expires=expires / 1000,
-            path=path,
-            domain=domain,
-            secure=secure,
-            httponly=httponly,
-            samesite=samesite,
-        )
-
-    def set_header(self, key: str, value: str):
-        self.response.headers.set(key, value)
-
-    def get_header(self, key: str) -> Optional[str]:
-        return self.response.headers.get(key)
-
-    def remove_header(self, key: str):
-        del self.response.headers[key]
-
-    def set_status_code(self, status_code: int):
-        if not self.status_set:
-            self.response.status_code = status_code
-            self.status_code = status_code
-            self.status_set = True
-
-    def get_headers(self):
-        return self.response.headers
-
-    def set_json_content(self, content: Dict[str, Any]):
-        if not self.response_sent:
-            self.set_header("Content-Type", "application/json; charset=utf-8")
-            self.response.data = json.dumps(
-                content,
-                ensure_ascii=False,
-                allow_nan=False,
-                indent=None,
-                separators=(",", ":"),
-            ).encode("utf-8")
-            self.response_sent = True
-
-

Ancestors

- -

Class variables

-
-
var Response
-
-

The response object that is used by default in Flask. -Works like the -response object from Werkzeug but is set to have an HTML mimetype by -default. -Quite often you don't have to create this object yourself because -:meth:~flask.Flask.make_response will take care of that for you.

-

If you want to replace the response object used you can subclass this and -set :attr:~flask.Flask.response_class to your subclass.

-
-

Changed in version: 1.0

-

JSON support is added to the response, like the request. This is useful -when testing to get the test client response data as JSON.

-
-
-

Changed in version: 1.0

-

Added :attr:max_cookie_size.

-
-
-
-

Methods

-
-
-def get_header(self, key: str) ‑> Optional[str] -
-
-
-
- -Expand source code - -
def get_header(self, key: str) -> Optional[str]:
-    return self.response.headers.get(key)
-
-
-
-def get_headers(self) -
-
-
-
- -Expand source code - -
def get_headers(self):
-    return self.response.headers
-
-
-
-def remove_header(self, key: str) -
-
-
-
- -Expand source code - -
def remove_header(self, key: str):
-    del self.response.headers[key]
-
-
- -
-
-
- -Expand source code - -
def set_cookie(
-    self,
-    key: str,
-    value: str,
-    expires: int,
-    path: str = "/",
-    domain: Optional[str] = None,
-    secure: bool = False,
-    httponly: bool = False,
-    samesite: str = "lax",
-):
-    self.response.set_cookie(
-        key,
-        value=value,
-        expires=expires / 1000,
-        path=path,
-        domain=domain,
-        secure=secure,
-        httponly=httponly,
-        samesite=samesite,
-    )
-
-
-
-def set_header(self, key: str, value: str) -
-
-
-
- -Expand source code - -
def set_header(self, key: str, value: str):
-    self.response.headers.set(key, value)
-
-
-
-def set_html_content(self, content: str) -
-
-
-
- -Expand source code - -
def set_html_content(self, content: str):
-    if not self.response_sent:
-        self.response.data = content
-        self.set_header("Content-Type", "text/html")
-        self.response_sent = True
-
-
-
-def set_json_content(self, content: Dict[str, Any]) -
-
-
-
- -Expand source code - -
def set_json_content(self, content: Dict[str, Any]):
-    if not self.response_sent:
-        self.set_header("Content-Type", "application/json; charset=utf-8")
-        self.response.data = json.dumps(
-            content,
-            ensure_ascii=False,
-            allow_nan=False,
-            indent=None,
-            separators=(",", ":"),
-        ).encode("utf-8")
-        self.response_sent = True
-
-
-
-def set_status_code(self, status_code: int) -
-
-
-
- -Expand source code - -
def set_status_code(self, status_code: int):
-    if not self.status_set:
-        self.response.status_code = status_code
-        self.status_code = status_code
-        self.status_set = True
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/framework/flask/framework.html b/html/supertokens_python/framework/flask/framework.html deleted file mode 100644 index 1e7439b99..000000000 --- a/html/supertokens_python/framework/flask/framework.html +++ /dev/null @@ -1,139 +0,0 @@ - - - - - - -supertokens_python.framework.flask.framework API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.framework.flask.framework

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from typing import TYPE_CHECKING
-
-from supertokens_python.framework.types import Framework
-
-if TYPE_CHECKING:
-    from flask.wrappers import Request
-
-
-class FlaskFramework(Framework):
-    def wrap_request(self, unwrapped: Request):
-        from supertokens_python.framework.flask.flask_request import FlaskRequest
-
-        return FlaskRequest(unwrapped)
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class FlaskFramework -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class FlaskFramework(Framework):
-    def wrap_request(self, unwrapped: Request):
-        from supertokens_python.framework.flask.flask_request import FlaskRequest
-
-        return FlaskRequest(unwrapped)
-
-

Ancestors

- -

Methods

-
-
-def wrap_request(self, unwrapped: Request) -
-
-
-
- -Expand source code - -
def wrap_request(self, unwrapped: Request):
-    from supertokens_python.framework.flask.flask_request import FlaskRequest
-
-    return FlaskRequest(unwrapped)
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/framework/flask/index.html b/html/supertokens_python/framework/flask/index.html deleted file mode 100644 index 72dabf143..000000000 --- a/html/supertokens_python/framework/flask/index.html +++ /dev/null @@ -1,101 +0,0 @@ - - - - - - -supertokens_python.framework.flask API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.framework.flask

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from supertokens_python.framework.flask import flask_middleware
-
-Middleware = flask_middleware.Middleware
-
-
-
-

Sub-modules

-
-
supertokens_python.framework.flask.flask_middleware
-
-
-
-
supertokens_python.framework.flask.flask_request
-
-
-
-
supertokens_python.framework.flask.flask_response
-
-
-
-
supertokens_python.framework.flask.framework
-
-
-
-
-
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/framework/index.html b/html/supertokens_python/framework/index.html deleted file mode 100644 index 6cd358791..000000000 --- a/html/supertokens_python/framework/index.html +++ /dev/null @@ -1,113 +0,0 @@ - - - - - - -supertokens_python.framework API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.framework

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from . import request
-from . import response
-
-BaseRequest = request.BaseRequest
-BaseResponse = response.BaseResponse
-
-
-
-

Sub-modules

-
-
supertokens_python.framework.django
-
-
-
-
supertokens_python.framework.fastapi
-
-
-
-
supertokens_python.framework.flask
-
-
-
-
supertokens_python.framework.request
-
-
-
-
supertokens_python.framework.response
-
-
-
-
supertokens_python.framework.types
-
-
-
-
-
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/framework/request.html b/html/supertokens_python/framework/request.html deleted file mode 100644 index 594cf723a..000000000 --- a/html/supertokens_python/framework/request.html +++ /dev/null @@ -1,417 +0,0 @@ - - - - - - -supertokens_python.framework.request API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.framework.request

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from abc import ABC, abstractmethod
-from typing import TYPE_CHECKING, Any, Dict, Union
-
-if TYPE_CHECKING:
-    from supertokens_python.recipe.session.interfaces import SessionContainer
-
-
-class BaseRequest(ABC):
-    def __init__(self):
-        self.wrapper_used = True
-        self.request = None
-
-    @abstractmethod
-    def get_original_url(self) -> str:
-        pass
-
-    @abstractmethod
-    def get_query_param(
-        self, key: str, default: Union[str, None] = None
-    ) -> Union[str, None]:
-        pass
-
-    @abstractmethod
-    def get_query_params(self) -> Dict[str, Any]:
-        pass
-
-    @abstractmethod
-    async def json(self) -> Union[Any, None]:
-        pass
-
-    @abstractmethod
-    async def form_data(self) -> Dict[str, Any]:
-        pass
-
-    @abstractmethod
-    def method(self) -> str:
-        pass
-
-    @abstractmethod
-    def get_cookie(self, key: str) -> Union[str, None]:
-        pass
-
-    @abstractmethod
-    def get_header(self, key: str) -> Union[None, str]:
-        pass
-
-    @abstractmethod
-    def get_session(self) -> Union[SessionContainer, None]:
-        pass
-
-    @abstractmethod
-    def set_session(self, session: SessionContainer):
-        pass
-
-    @abstractmethod
-    def set_session_as_none(self):
-        """
-        This function is used to set the request's session variable to None.
-        See https://github.com/supertokens/supertokens-python/issues/90
-        """
-
-    @abstractmethod
-    def get_path(self) -> str:
-        pass
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class BaseRequest -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class BaseRequest(ABC):
-    def __init__(self):
-        self.wrapper_used = True
-        self.request = None
-
-    @abstractmethod
-    def get_original_url(self) -> str:
-        pass
-
-    @abstractmethod
-    def get_query_param(
-        self, key: str, default: Union[str, None] = None
-    ) -> Union[str, None]:
-        pass
-
-    @abstractmethod
-    def get_query_params(self) -> Dict[str, Any]:
-        pass
-
-    @abstractmethod
-    async def json(self) -> Union[Any, None]:
-        pass
-
-    @abstractmethod
-    async def form_data(self) -> Dict[str, Any]:
-        pass
-
-    @abstractmethod
-    def method(self) -> str:
-        pass
-
-    @abstractmethod
-    def get_cookie(self, key: str) -> Union[str, None]:
-        pass
-
-    @abstractmethod
-    def get_header(self, key: str) -> Union[None, str]:
-        pass
-
-    @abstractmethod
-    def get_session(self) -> Union[SessionContainer, None]:
-        pass
-
-    @abstractmethod
-    def set_session(self, session: SessionContainer):
-        pass
-
-    @abstractmethod
-    def set_session_as_none(self):
-        """
-        This function is used to set the request's session variable to None.
-        See https://github.com/supertokens/supertokens-python/issues/90
-        """
-
-    @abstractmethod
-    def get_path(self) -> str:
-        pass
-
-

Ancestors

-
    -
  • abc.ABC
  • -
-

Subclasses

- -

Methods

-
-
-async def form_data(self) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
@abstractmethod
-async def form_data(self) -> Dict[str, Any]:
-    pass
-
-
- -
-
-
- -Expand source code - -
@abstractmethod
-def get_cookie(self, key: str) -> Union[str, None]:
-    pass
-
-
-
-def get_header(self, key: str) ‑> Optional[str] -
-
-
-
- -Expand source code - -
@abstractmethod
-def get_header(self, key: str) -> Union[None, str]:
-    pass
-
-
-
-def get_original_url(self) ‑> str -
-
-
-
- -Expand source code - -
@abstractmethod
-def get_original_url(self) -> str:
-    pass
-
-
-
-def get_path(self) ‑> str -
-
-
-
- -Expand source code - -
@abstractmethod
-def get_path(self) -> str:
-    pass
-
-
-
-def get_query_param(self, key: str, default: Union[str, None] = None) ‑> Optional[str] -
-
-
-
- -Expand source code - -
@abstractmethod
-def get_query_param(
-    self, key: str, default: Union[str, None] = None
-) -> Union[str, None]:
-    pass
-
-
-
-def get_query_params(self) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
@abstractmethod
-def get_query_params(self) -> Dict[str, Any]:
-    pass
-
-
-
-def get_session(self) ‑> Union[SessionContainer, None] -
-
-
-
- -Expand source code - -
@abstractmethod
-def get_session(self) -> Union[SessionContainer, None]:
-    pass
-
-
-
-async def json(self) ‑> Optional[Any] -
-
-
-
- -Expand source code - -
@abstractmethod
-async def json(self) -> Union[Any, None]:
-    pass
-
-
-
-def method(self) ‑> str -
-
-
-
- -Expand source code - -
@abstractmethod
-def method(self) -> str:
-    pass
-
-
-
-def set_session(self, session: SessionContainer) -
-
-
-
- -Expand source code - -
@abstractmethod
-def set_session(self, session: SessionContainer):
-    pass
-
-
-
-def set_session_as_none(self) -
-
-

This function is used to set the request's session variable to None. -See https://github.com/supertokens/supertokens-python/issues/90

-
- -Expand source code - -
@abstractmethod
-def set_session_as_none(self):
-    """
-    This function is used to set the request's session variable to None.
-    See https://github.com/supertokens/supertokens-python/issues/90
-    """
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/framework/response.html b/html/supertokens_python/framework/response.html deleted file mode 100644 index 56134d1b5..000000000 --- a/html/supertokens_python/framework/response.html +++ /dev/null @@ -1,320 +0,0 @@ - - - - - - -supertokens_python.framework.response API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.framework.response

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from abc import ABC, abstractmethod
-from typing import Any, Dict, Optional
-
-
-class BaseResponse(ABC):
-    @abstractmethod
-    def __init__(self, content: Dict[str, Any], status_code: int = 200):
-        self.content = content
-        self.status_code = status_code
-        self.wrapper_used = True
-
-    @abstractmethod
-    def set_cookie(
-        self,
-        key: str,
-        value: str,
-        #    max_age: Union[int, None] = None,
-        expires: int,
-        path: str = "/",
-        domain: Optional[str] = None,
-        secure: bool = False,
-        httponly: bool = False,
-        samesite: str = "lax",
-    ):
-        pass
-
-    @abstractmethod
-    def set_header(self, key: str, value: str) -> None:
-        pass
-
-    @abstractmethod
-    def get_header(self, key: str) -> Optional[str]:
-        pass
-
-    @abstractmethod
-    def remove_header(self, key: str) -> None:
-        pass
-
-    @abstractmethod
-    def set_status_code(self, status_code: int):
-        pass
-
-    @abstractmethod
-    def set_json_content(self, content: Dict[str, Any]):
-        pass
-
-    @abstractmethod
-    def set_html_content(self, content: str):
-        pass
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class BaseResponse -(content: Dict[str, Any], status_code: int = 200) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class BaseResponse(ABC):
-    @abstractmethod
-    def __init__(self, content: Dict[str, Any], status_code: int = 200):
-        self.content = content
-        self.status_code = status_code
-        self.wrapper_used = True
-
-    @abstractmethod
-    def set_cookie(
-        self,
-        key: str,
-        value: str,
-        #    max_age: Union[int, None] = None,
-        expires: int,
-        path: str = "/",
-        domain: Optional[str] = None,
-        secure: bool = False,
-        httponly: bool = False,
-        samesite: str = "lax",
-    ):
-        pass
-
-    @abstractmethod
-    def set_header(self, key: str, value: str) -> None:
-        pass
-
-    @abstractmethod
-    def get_header(self, key: str) -> Optional[str]:
-        pass
-
-    @abstractmethod
-    def remove_header(self, key: str) -> None:
-        pass
-
-    @abstractmethod
-    def set_status_code(self, status_code: int):
-        pass
-
-    @abstractmethod
-    def set_json_content(self, content: Dict[str, Any]):
-        pass
-
-    @abstractmethod
-    def set_html_content(self, content: str):
-        pass
-
-

Ancestors

-
    -
  • abc.ABC
  • -
-

Subclasses

- -

Methods

-
-
-def get_header(self, key: str) ‑> Optional[str] -
-
-
-
- -Expand source code - -
@abstractmethod
-def get_header(self, key: str) -> Optional[str]:
-    pass
-
-
-
-def remove_header(self, key: str) ‑> None -
-
-
-
- -Expand source code - -
@abstractmethod
-def remove_header(self, key: str) -> None:
-    pass
-
-
- -
-
-
- -Expand source code - -
@abstractmethod
-def set_cookie(
-    self,
-    key: str,
-    value: str,
-    #    max_age: Union[int, None] = None,
-    expires: int,
-    path: str = "/",
-    domain: Optional[str] = None,
-    secure: bool = False,
-    httponly: bool = False,
-    samesite: str = "lax",
-):
-    pass
-
-
-
-def set_header(self, key: str, value: str) ‑> None -
-
-
-
- -Expand source code - -
@abstractmethod
-def set_header(self, key: str, value: str) -> None:
-    pass
-
-
-
-def set_html_content(self, content: str) -
-
-
-
- -Expand source code - -
@abstractmethod
-def set_html_content(self, content: str):
-    pass
-
-
-
-def set_json_content(self, content: Dict[str, Any]) -
-
-
-
- -Expand source code - -
@abstractmethod
-def set_json_content(self, content: Dict[str, Any]):
-    pass
-
-
-
-def set_status_code(self, status_code: int) -
-
-
-
- -Expand source code - -
@abstractmethod
-def set_status_code(self, status_code: int):
-    pass
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/framework/types.html b/html/supertokens_python/framework/types.html deleted file mode 100644 index d2a6aff05..000000000 --- a/html/supertokens_python/framework/types.html +++ /dev/null @@ -1,190 +0,0 @@ - - - - - - -supertokens_python.framework.types API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.framework.types

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from abc import ABC, abstractmethod
-from enum import Enum
-from typing import Any, Union
-
-from supertokens_python.framework.request import BaseRequest
-
-frameworks = ["fastapi", "flask", "django"]
-
-
-class FrameworkEnum(Enum):
-    FASTAPI = 1
-    FLASK = 2
-    DJANGO = 3
-
-
-class Framework(ABC):
-    @abstractmethod
-    def wrap_request(self, unwrapped: Any) -> Union[BaseRequest, None]:
-        pass
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class Framework -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class Framework(ABC):
-    @abstractmethod
-    def wrap_request(self, unwrapped: Any) -> Union[BaseRequest, None]:
-        pass
-
-

Ancestors

-
    -
  • abc.ABC
  • -
-

Subclasses

- -

Methods

-
-
-def wrap_request(self, unwrapped: Any) ‑> Optional[BaseRequest] -
-
-
-
- -Expand source code - -
@abstractmethod
-def wrap_request(self, unwrapped: Any) -> Union[BaseRequest, None]:
-    pass
-
-
-
-
-
-class FrameworkEnum -(value, names=None, *, module=None, qualname=None, type=None, start=1) -
-
-

An enumeration.

-
- -Expand source code - -
class FrameworkEnum(Enum):
-    FASTAPI = 1
-    FLASK = 2
-    DJANGO = 3
-
-

Ancestors

-
    -
  • enum.Enum
  • -
-

Class variables

-
-
var DJANGO
-
-
-
-
var FASTAPI
-
-
-
-
var FLASK
-
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/index.html b/html/supertokens_python/index.html deleted file mode 100644 index 5caaefc02..000000000 --- a/html/supertokens_python/index.html +++ /dev/null @@ -1,267 +0,0 @@ - - - - - - -supertokens_python API documentation - - - - - - - - - - - -
-
-
-

Package supertokens_python

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from typing import Any, Callable, Dict, List, Optional
-
-from typing_extensions import Literal
-
-from supertokens_python.framework.request import BaseRequest
-
-from . import supertokens
-from .recipe_module import RecipeModule
-
-InputAppInfo = supertokens.InputAppInfo
-Supertokens = supertokens.Supertokens
-SupertokensConfig = supertokens.SupertokensConfig
-AppInfo = supertokens.AppInfo
-
-
-def init(
-    app_info: InputAppInfo,
-    framework: Literal["fastapi", "flask", "django"],
-    supertokens_config: SupertokensConfig,
-    recipe_list: List[Callable[[supertokens.AppInfo], RecipeModule]],
-    mode: Optional[Literal["asgi", "wsgi"]] = None,
-    telemetry: Optional[bool] = None,
-    debug: Optional[bool] = None,
-):
-    return Supertokens.init(
-        app_info, framework, supertokens_config, recipe_list, mode, telemetry, debug
-    )
-
-
-def get_all_cors_headers() -> List[str]:
-    return supertokens.Supertokens.get_instance().get_all_cors_headers()
-
-
-def get_request_from_user_context(
-    user_context: Optional[Dict[str, Any]],
-) -> Optional[BaseRequest]:
-    return Supertokens.get_instance().get_request_from_user_context(user_context)
-
-
-
-

Sub-modules

-
-
supertokens_python.async_to_sync_wrapper
-
-
-
-
supertokens_python.asyncio
-
-
-
-
supertokens_python.constants
-
-
-
-
supertokens_python.exceptions
-
-
-
-
supertokens_python.framework
-
-
-
-
supertokens_python.ingredients
-
-
-
-
supertokens_python.interfaces
-
-
-
-
supertokens_python.logger
-
-
-
-
supertokens_python.normalised_url_domain
-
-
-
-
supertokens_python.normalised_url_path
-
-
-
-
supertokens_python.post_init_callbacks
-
-
-
-
supertokens_python.process_state
-
-
-
-
supertokens_python.querier
-
-
-
-
supertokens_python.recipe
-
-
-
-
supertokens_python.recipe_module
-
-
-
-
supertokens_python.supertokens
-
-
-
-
supertokens_python.syncio
-
-
-
-
supertokens_python.types
-
-
-
-
supertokens_python.utils
-
-
-
-
-
-
-
-
-

Functions

-
-
-def get_all_cors_headers() ‑> List[str] -
-
-
-
- -Expand source code - -
def get_all_cors_headers() -> List[str]:
-    return supertokens.Supertokens.get_instance().get_all_cors_headers()
-
-
-
-def get_request_from_user_context(user_context: Optional[Dict[str, Any]]) ‑> Optional[BaseRequest] -
-
-
-
- -Expand source code - -
def get_request_from_user_context(
-    user_context: Optional[Dict[str, Any]],
-) -> Optional[BaseRequest]:
-    return Supertokens.get_instance().get_request_from_user_context(user_context)
-
-
-
-def init(app_info: InputAppInfo, framework: Literal['fastapi', 'flask', 'django'], supertokens_config: SupertokensConfig, recipe_list: List[Callable[[AppInfo], RecipeModule]], mode: Optional[Literal['asgi', 'wsgi']] = None, telemetry: Optional[bool] = None, debug: Optional[bool] = None) -
-
-
-
- -Expand source code - -
def init(
-    app_info: InputAppInfo,
-    framework: Literal["fastapi", "flask", "django"],
-    supertokens_config: SupertokensConfig,
-    recipe_list: List[Callable[[supertokens.AppInfo], RecipeModule]],
-    mode: Optional[Literal["asgi", "wsgi"]] = None,
-    telemetry: Optional[bool] = None,
-    debug: Optional[bool] = None,
-):
-    return Supertokens.init(
-        app_info, framework, supertokens_config, recipe_list, mode, telemetry, debug
-    )
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/ingredients/emaildelivery/index.html b/html/supertokens_python/ingredients/emaildelivery/index.html deleted file mode 100644 index fe280bdd2..000000000 --- a/html/supertokens_python/ingredients/emaildelivery/index.html +++ /dev/null @@ -1,166 +0,0 @@ - - - - - - -supertokens_python.ingredients.emaildelivery API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.ingredients.emaildelivery

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from typing import Generic, TypeVar
-
-from supertokens_python.ingredients.emaildelivery.types import (
-    EmailDeliveryConfigWithService,
-    EmailDeliveryInterface,
-)
-
-_T = TypeVar("_T")
-
-
-class EmailDeliveryIngredient(Generic[_T]):
-    ingredient_interface_impl: EmailDeliveryInterface[_T]
-
-    def __init__(self, config: EmailDeliveryConfigWithService[_T]) -> None:
-        self.ingredient_interface_impl = (
-            config.service
-            if config.override is None
-            else config.override(config.service)
-        )
-
-
-
-

Sub-modules

-
-
supertokens_python.ingredients.emaildelivery.services
-
-
-
-
supertokens_python.ingredients.emaildelivery.types
-
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class EmailDeliveryIngredient -(config: EmailDeliveryConfigWithService[~_T]) -
-
-

Abstract base class for generic types.

-

A generic type is typically declared by inheriting from -this class parameterized with one or more type variables. -For example, a generic mapping type might be defined as::

-

class Mapping(Generic[KT, VT]): -def getitem(self, key: KT) -> VT: -… -# Etc.

-

This class can then be used as follows::

-

def lookup_name(mapping: Mapping[KT, VT], key: KT, default: VT) -> VT: -try: -return mapping[key] -except KeyError: -return default

-
- -Expand source code - -
class EmailDeliveryIngredient(Generic[_T]):
-    ingredient_interface_impl: EmailDeliveryInterface[_T]
-
-    def __init__(self, config: EmailDeliveryConfigWithService[_T]) -> None:
-        self.ingredient_interface_impl = (
-            config.service
-            if config.override is None
-            else config.override(config.service)
-        )
-
-

Ancestors

-
    -
  • typing.Generic
  • -
-

Class variables

-
-
var ingredient_interface_implEmailDeliveryInterface[~_T]
-
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/ingredients/emaildelivery/services/index.html b/html/supertokens_python/ingredients/emaildelivery/services/index.html deleted file mode 100644 index 634f38f2d..000000000 --- a/html/supertokens_python/ingredients/emaildelivery/services/index.html +++ /dev/null @@ -1,83 +0,0 @@ - - - - - - -supertokens_python.ingredients.emaildelivery.services API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.ingredients.emaildelivery.services

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-
-
-

Sub-modules

-
-
supertokens_python.ingredients.emaildelivery.services.smtp
-
-
-
-
-
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/ingredients/emaildelivery/services/smtp.html b/html/supertokens_python/ingredients/emaildelivery/services/smtp.html deleted file mode 100644 index 0541b36c6..000000000 --- a/html/supertokens_python/ingredients/emaildelivery/services/smtp.html +++ /dev/null @@ -1,275 +0,0 @@ - - - - - - -supertokens_python.ingredients.emaildelivery.services.smtp API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.ingredients.emaildelivery.services.smtp

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-
-import ssl
-from email.mime.text import MIMEText
-from typing import Any, Dict, TypeVar
-
-import aiosmtplib
-from supertokens_python.ingredients.emaildelivery.types import (
-    EmailContent,
-    SMTPSettings,
-)
-from supertokens_python.logger import log_debug_message
-
-_T = TypeVar("_T")
-
-
-class Transporter:
-    def __init__(self, smtp_settings: SMTPSettings) -> None:
-        self.smtp_settings = smtp_settings
-
-    async def _connect(self):
-        try:
-            tls_context = ssl.create_default_context()
-            if self.smtp_settings.secure:
-                # Use TLS from the beginning
-                mail = aiosmtplib.SMTP(
-                    hostname=self.smtp_settings.host,
-                    port=self.smtp_settings.port,
-                    use_tls=True,
-                    tls_context=tls_context,
-                )
-            else:
-                # Start without TLS (but later try upgrading)
-                mail = aiosmtplib.SMTP(
-                    hostname=self.smtp_settings.host,
-                    port=self.smtp_settings.port,
-                    use_tls=False,
-                )
-
-            await mail.connect()  # type: ignore
-
-            if not self.smtp_settings.secure:
-                # Try upgrading to TLS (even if the user opted for secure=False)
-                try:
-                    await mail.starttls(tls_context=tls_context)
-                except aiosmtplib.SMTPException:  # TLS wasn't supported by the server, so ignore.
-                    pass
-
-            if self.smtp_settings.password:
-                await mail.login(
-                    self.smtp_settings.username or self.smtp_settings.from_.email,
-                    self.smtp_settings.password,
-                )
-
-            return mail
-        except Exception as e:
-            log_debug_message("Couldn't connect to the SMTP server: %s", e)
-            raise e
-
-    async def send_email(self, input_: EmailContent, _: Dict[str, Any]) -> None:
-        connection = await self._connect()
-
-        from_ = self.smtp_settings.from_
-        try:
-            from_addr = f"{from_.name} <{from_.email}>"
-            if input_.is_html:
-                email_content = MIMEText(input_.body, "html")
-                email_content["From"] = from_addr
-                email_content["To"] = input_.to_email
-                email_content["Subject"] = input_.subject
-                await connection.sendmail(
-                    from_.email, input_.to_email, email_content.as_string()
-                )
-            else:
-                await connection.sendmail(from_addr, input_.to_email, input_.body)
-        except Exception as e:
-            log_debug_message("Error in sending email: %s", e)
-            raise e
-        finally:
-            await connection.quit()
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class Transporter -(smtp_settings: SMTPSettings) -
-
-
-
- -Expand source code - -
class Transporter:
-    def __init__(self, smtp_settings: SMTPSettings) -> None:
-        self.smtp_settings = smtp_settings
-
-    async def _connect(self):
-        try:
-            tls_context = ssl.create_default_context()
-            if self.smtp_settings.secure:
-                # Use TLS from the beginning
-                mail = aiosmtplib.SMTP(
-                    hostname=self.smtp_settings.host,
-                    port=self.smtp_settings.port,
-                    use_tls=True,
-                    tls_context=tls_context,
-                )
-            else:
-                # Start without TLS (but later try upgrading)
-                mail = aiosmtplib.SMTP(
-                    hostname=self.smtp_settings.host,
-                    port=self.smtp_settings.port,
-                    use_tls=False,
-                )
-
-            await mail.connect()  # type: ignore
-
-            if not self.smtp_settings.secure:
-                # Try upgrading to TLS (even if the user opted for secure=False)
-                try:
-                    await mail.starttls(tls_context=tls_context)
-                except aiosmtplib.SMTPException:  # TLS wasn't supported by the server, so ignore.
-                    pass
-
-            if self.smtp_settings.password:
-                await mail.login(
-                    self.smtp_settings.username or self.smtp_settings.from_.email,
-                    self.smtp_settings.password,
-                )
-
-            return mail
-        except Exception as e:
-            log_debug_message("Couldn't connect to the SMTP server: %s", e)
-            raise e
-
-    async def send_email(self, input_: EmailContent, _: Dict[str, Any]) -> None:
-        connection = await self._connect()
-
-        from_ = self.smtp_settings.from_
-        try:
-            from_addr = f"{from_.name} <{from_.email}>"
-            if input_.is_html:
-                email_content = MIMEText(input_.body, "html")
-                email_content["From"] = from_addr
-                email_content["To"] = input_.to_email
-                email_content["Subject"] = input_.subject
-                await connection.sendmail(
-                    from_.email, input_.to_email, email_content.as_string()
-                )
-            else:
-                await connection.sendmail(from_addr, input_.to_email, input_.body)
-        except Exception as e:
-            log_debug_message("Error in sending email: %s", e)
-            raise e
-        finally:
-            await connection.quit()
-
-

Methods

-
-
-async def send_email(self, input_: EmailContent, _: Dict[str, Any]) ‑> None -
-
-
-
- -Expand source code - -
async def send_email(self, input_: EmailContent, _: Dict[str, Any]) -> None:
-    connection = await self._connect()
-
-    from_ = self.smtp_settings.from_
-    try:
-        from_addr = f"{from_.name} <{from_.email}>"
-        if input_.is_html:
-            email_content = MIMEText(input_.body, "html")
-            email_content["From"] = from_addr
-            email_content["To"] = input_.to_email
-            email_content["Subject"] = input_.subject
-            await connection.sendmail(
-                from_.email, input_.to_email, email_content.as_string()
-            )
-        else:
-            await connection.sendmail(from_addr, input_.to_email, input_.body)
-    except Exception as e:
-        log_debug_message("Error in sending email: %s", e)
-        raise e
-    finally:
-        await connection.quit()
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/ingredients/emaildelivery/types.html b/html/supertokens_python/ingredients/emaildelivery/types.html deleted file mode 100644 index 083c72459..000000000 --- a/html/supertokens_python/ingredients/emaildelivery/types.html +++ /dev/null @@ -1,433 +0,0 @@ - - - - - - -supertokens_python.ingredients.emaildelivery.types API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.ingredients.emaildelivery.types

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from __future__ import annotations
-from abc import ABC, abstractmethod
-from typing import Any, Callable, Dict, Generic, TypeVar, Union, TYPE_CHECKING
-
-if TYPE_CHECKING:
-    from supertokens_python.ingredients.emaildelivery.services.smtp import Transporter
-
-_T = TypeVar("_T")
-
-
-class EmailDeliveryInterface(ABC, Generic[_T]):
-    @abstractmethod
-    async def send_email(self, template_vars: _T, user_context: Dict[str, Any]) -> None:
-        pass
-
-
-class EmailDeliveryConfig(ABC, Generic[_T]):
-    def __init__(
-        self,
-        service: Union[EmailDeliveryInterface[_T], None] = None,
-        override: Union[
-            Callable[[EmailDeliveryInterface[_T]], EmailDeliveryInterface[_T]], None
-        ] = None,
-    ) -> None:
-        self.service = service
-        self.override = override
-
-
-class EmailDeliveryConfigWithService(ABC, Generic[_T]):
-    def __init__(
-        self,
-        service: EmailDeliveryInterface[_T],
-        override: Union[
-            Callable[[EmailDeliveryInterface[_T]], EmailDeliveryInterface[_T]], None
-        ] = None,
-    ) -> None:
-        self.service = service
-        self.override = override
-
-
-class SMTPSettingsFrom:
-    def __init__(self, name: str, email: str) -> None:
-        self.name = name
-        self.email = email
-
-
-class SMTPSettings:
-    def __init__(
-        self,
-        host: str,
-        port: int,
-        from_: SMTPSettingsFrom,
-        password: Union[str, None] = None,
-        secure: Union[bool, None] = None,
-        username: Union[str, None] = None,
-    ) -> None:
-        self.host = host
-        self.from_ = from_
-        self.password = password
-        self.port = port
-        self.secure = secure
-        self.username = username
-
-
-class EmailContent:
-    def __init__(self, body: str, subject: str, to_email: str, is_html: bool) -> None:
-        self.body = body
-        self.subject = subject
-        self.to_email = to_email
-        self.is_html = is_html
-
-
-class SMTPServiceInterface(ABC, Generic[_T]):
-    def __init__(self, transporter: Transporter) -> None:
-        self.transporter = transporter
-
-    @abstractmethod
-    async def send_raw_email(
-        self, content: EmailContent, user_context: Dict[str, Any]
-    ) -> None:
-        pass
-
-    @abstractmethod
-    async def get_content(
-        self, template_vars: _T, user_context: Dict[str, Any]
-    ) -> EmailContent:
-        pass
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class EmailContent -(body: str, subject: str, to_email: str, is_html: bool) -
-
-
-
- -Expand source code - -
class EmailContent:
-    def __init__(self, body: str, subject: str, to_email: str, is_html: bool) -> None:
-        self.body = body
-        self.subject = subject
-        self.to_email = to_email
-        self.is_html = is_html
-
-
-
-class EmailDeliveryConfig -(service: Union[EmailDeliveryInterface[_T], None] = None, override: Union[Callable[[EmailDeliveryInterface[_T]], EmailDeliveryInterface[_T]], None] = None) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class EmailDeliveryConfig(ABC, Generic[_T]):
-    def __init__(
-        self,
-        service: Union[EmailDeliveryInterface[_T], None] = None,
-        override: Union[
-            Callable[[EmailDeliveryInterface[_T]], EmailDeliveryInterface[_T]], None
-        ] = None,
-    ) -> None:
-        self.service = service
-        self.override = override
-
-

Ancestors

-
    -
  • abc.ABC
  • -
  • typing.Generic
  • -
-
-
-class EmailDeliveryConfigWithService -(service: EmailDeliveryInterface[_T], override: Union[Callable[[EmailDeliveryInterface[_T]], EmailDeliveryInterface[_T]], None] = None) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class EmailDeliveryConfigWithService(ABC, Generic[_T]):
-    def __init__(
-        self,
-        service: EmailDeliveryInterface[_T],
-        override: Union[
-            Callable[[EmailDeliveryInterface[_T]], EmailDeliveryInterface[_T]], None
-        ] = None,
-    ) -> None:
-        self.service = service
-        self.override = override
-
-

Ancestors

-
    -
  • abc.ABC
  • -
  • typing.Generic
  • -
-
-
-class EmailDeliveryInterface -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class EmailDeliveryInterface(ABC, Generic[_T]):
-    @abstractmethod
-    async def send_email(self, template_vars: _T, user_context: Dict[str, Any]) -> None:
-        pass
-
-

Ancestors

-
    -
  • abc.ABC
  • -
  • typing.Generic
  • -
-

Subclasses

- -

Methods

-
-
-async def send_email(self, template_vars: _T, user_context: Dict[str, Any]) ‑> None -
-
-
-
- -Expand source code - -
@abstractmethod
-async def send_email(self, template_vars: _T, user_context: Dict[str, Any]) -> None:
-    pass
-
-
-
-
-
-class SMTPServiceInterface -(transporter: Transporter) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class SMTPServiceInterface(ABC, Generic[_T]):
-    def __init__(self, transporter: Transporter) -> None:
-        self.transporter = transporter
-
-    @abstractmethod
-    async def send_raw_email(
-        self, content: EmailContent, user_context: Dict[str, Any]
-    ) -> None:
-        pass
-
-    @abstractmethod
-    async def get_content(
-        self, template_vars: _T, user_context: Dict[str, Any]
-    ) -> EmailContent:
-        pass
-
-

Ancestors

-
    -
  • abc.ABC
  • -
  • typing.Generic
  • -
-

Subclasses

- -

Methods

-
-
-async def get_content(self, template_vars: _T, user_context: Dict[str, Any]) ‑> EmailContent -
-
-
-
- -Expand source code - -
@abstractmethod
-async def get_content(
-    self, template_vars: _T, user_context: Dict[str, Any]
-) -> EmailContent:
-    pass
-
-
-
-async def send_raw_email(self, content: EmailContent, user_context: Dict[str, Any]) ‑> None -
-
-
-
- -Expand source code - -
@abstractmethod
-async def send_raw_email(
-    self, content: EmailContent, user_context: Dict[str, Any]
-) -> None:
-    pass
-
-
-
-
-
-class SMTPSettings -(host: str, port: int, from_: SMTPSettingsFrom, password: Union[str, None] = None, secure: Union[bool, None] = None, username: Union[str, None] = None) -
-
-
-
- -Expand source code - -
class SMTPSettings:
-    def __init__(
-        self,
-        host: str,
-        port: int,
-        from_: SMTPSettingsFrom,
-        password: Union[str, None] = None,
-        secure: Union[bool, None] = None,
-        username: Union[str, None] = None,
-    ) -> None:
-        self.host = host
-        self.from_ = from_
-        self.password = password
-        self.port = port
-        self.secure = secure
-        self.username = username
-
-
-
-class SMTPSettingsFrom -(name: str, email: str) -
-
-
-
- -Expand source code - -
class SMTPSettingsFrom:
-    def __init__(self, name: str, email: str) -> None:
-        self.name = name
-        self.email = email
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/ingredients/index.html b/html/supertokens_python/ingredients/index.html deleted file mode 100644 index 04cc63a0f..000000000 --- a/html/supertokens_python/ingredients/index.html +++ /dev/null @@ -1,88 +0,0 @@ - - - - - - -supertokens_python.ingredients API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.ingredients

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-
-
-

Sub-modules

-
-
supertokens_python.ingredients.emaildelivery
-
-
-
-
supertokens_python.ingredients.smsdelivery
-
-
-
-
-
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/ingredients/smsdelivery/index.html b/html/supertokens_python/ingredients/smsdelivery/index.html deleted file mode 100644 index 3187249a3..000000000 --- a/html/supertokens_python/ingredients/smsdelivery/index.html +++ /dev/null @@ -1,166 +0,0 @@ - - - - - - -supertokens_python.ingredients.smsdelivery API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.ingredients.smsdelivery

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from typing import Generic, TypeVar
-
-from supertokens_python.ingredients.smsdelivery.types import (
-    SMSDeliveryConfigWithService,
-    SMSDeliveryInterface,
-)
-
-_T = TypeVar("_T")
-
-
-class SMSDeliveryIngredient(Generic[_T]):
-    ingredient_interface_impl: SMSDeliveryInterface[_T]
-
-    def __init__(self, config: SMSDeliveryConfigWithService[_T]) -> None:
-        self.ingredient_interface_impl = (
-            config.service
-            if config.override is None
-            else config.override(config.service)
-        )
-
-
-
-

Sub-modules

-
-
supertokens_python.ingredients.smsdelivery.services
-
-
-
-
supertokens_python.ingredients.smsdelivery.types
-
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class SMSDeliveryIngredient -(config: SMSDeliveryConfigWithService[~_T]) -
-
-

Abstract base class for generic types.

-

A generic type is typically declared by inheriting from -this class parameterized with one or more type variables. -For example, a generic mapping type might be defined as::

-

class Mapping(Generic[KT, VT]): -def getitem(self, key: KT) -> VT: -… -# Etc.

-

This class can then be used as follows::

-

def lookup_name(mapping: Mapping[KT, VT], key: KT, default: VT) -> VT: -try: -return mapping[key] -except KeyError: -return default

-
- -Expand source code - -
class SMSDeliveryIngredient(Generic[_T]):
-    ingredient_interface_impl: SMSDeliveryInterface[_T]
-
-    def __init__(self, config: SMSDeliveryConfigWithService[_T]) -> None:
-        self.ingredient_interface_impl = (
-            config.service
-            if config.override is None
-            else config.override(config.service)
-        )
-
-

Ancestors

-
    -
  • typing.Generic
  • -
-

Class variables

-
-
var ingredient_interface_implSMSDeliveryInterface[~_T]
-
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/ingredients/smsdelivery/services/index.html b/html/supertokens_python/ingredients/smsdelivery/services/index.html deleted file mode 100644 index 06233dd71..000000000 --- a/html/supertokens_python/ingredients/smsdelivery/services/index.html +++ /dev/null @@ -1,88 +0,0 @@ - - - - - - -supertokens_python.ingredients.smsdelivery.services API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.ingredients.smsdelivery.services

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-
-
-

Sub-modules

-
-
supertokens_python.ingredients.smsdelivery.services.supertokens
-
-
-
-
supertokens_python.ingredients.smsdelivery.services.twilio
-
-
-
-
-
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/ingredients/smsdelivery/services/supertokens.html b/html/supertokens_python/ingredients/smsdelivery/services/supertokens.html deleted file mode 100644 index bc4c7afd0..000000000 --- a/html/supertokens_python/ingredients/smsdelivery/services/supertokens.html +++ /dev/null @@ -1,73 +0,0 @@ - - - - - - -supertokens_python.ingredients.smsdelivery.services.supertokens API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.ingredients.smsdelivery.services.supertokens

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-SUPERTOKENS_SMS_SERVICE_URL = "https://api.supertokens.com/0/services/sms"
-
-
-
-
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/ingredients/smsdelivery/services/twilio.html b/html/supertokens_python/ingredients/smsdelivery/services/twilio.html deleted file mode 100644 index 17007753d..000000000 --- a/html/supertokens_python/ingredients/smsdelivery/services/twilio.html +++ /dev/null @@ -1,117 +0,0 @@ - - - - - - -supertokens_python.ingredients.smsdelivery.services.twilio API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.ingredients.smsdelivery.services.twilio

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from typing import TypeVar
-
-from supertokens_python.ingredients.smsdelivery.types import TwilioSettings
-
-_T = TypeVar("_T")
-
-
-def normalize_twilio_settings(twilio_settings: TwilioSettings) -> TwilioSettings:
-    from_ = twilio_settings.from_
-    messaging_service_sid = twilio_settings.messaging_service_sid
-
-    if (from_ and messaging_service_sid) or (not from_ and not messaging_service_sid):
-        raise Exception(
-            'Please pass exactly one of "from" and "messaging_service_sid" config for twilio_settings.'
-        )
-
-    return twilio_settings
-
-
-
-
-
-
-
-

Functions

-
-
-def normalize_twilio_settings(twilio_settings: TwilioSettings) ‑> TwilioSettings -
-
-
-
- -Expand source code - -
def normalize_twilio_settings(twilio_settings: TwilioSettings) -> TwilioSettings:
-    from_ = twilio_settings.from_
-    messaging_service_sid = twilio_settings.messaging_service_sid
-
-    if (from_ and messaging_service_sid) or (not from_ and not messaging_service_sid):
-        raise Exception(
-            'Please pass exactly one of "from" and "messaging_service_sid" config for twilio_settings.'
-        )
-
-    return twilio_settings
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/ingredients/smsdelivery/types.html b/html/supertokens_python/ingredients/smsdelivery/types.html deleted file mode 100644 index ee47376d6..000000000 --- a/html/supertokens_python/ingredients/smsdelivery/types.html +++ /dev/null @@ -1,419 +0,0 @@ - - - - - - -supertokens_python.ingredients.smsdelivery.types API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.ingredients.smsdelivery.types

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from abc import ABC, abstractmethod
-from typing import Any, Callable, Dict, Generic, TypeVar, Union
-
-from twilio.rest import Client  # type: ignore
-
-_T = TypeVar("_T")
-
-
-class SMSDeliveryInterface(ABC, Generic[_T]):
-    @abstractmethod
-    async def send_sms(self, template_vars: _T, user_context: Dict[str, Any]) -> None:
-        pass
-
-
-class SMSDeliveryConfig(ABC, Generic[_T]):
-    def __init__(
-        self,
-        service: Union[SMSDeliveryInterface[_T], None] = None,
-        override: Union[
-            Callable[[SMSDeliveryInterface[_T]], SMSDeliveryInterface[_T]], None
-        ] = None,
-    ) -> None:
-        self.service = service
-        self.override = override
-
-
-class SMSDeliveryConfigWithService(ABC, Generic[_T]):
-    def __init__(
-        self,
-        service: SMSDeliveryInterface[_T],
-        override: Union[
-            Callable[[SMSDeliveryInterface[_T]], SMSDeliveryInterface[_T]], None
-        ] = None,
-    ) -> None:
-        self.service = service
-        self.override = override
-
-
-class TwilioSettings:
-    def __init__(
-        self,
-        account_sid: str,
-        auth_token: str,
-        from_: Union[str, None] = None,
-        messaging_service_sid: Union[str, None] = None,
-        opts: Union[Dict[str, Any], None] = None,
-    ) -> None:
-        """
-        Note: `self.otps` can be used to override values passed to the Twilio Client.
-        Read docs from `twilio.rest.Client.__init__` to discover possible args.
-
-        For example, `opts = {"region": "...", "user_agent_extensions": ["..."], }`
-        """
-        self.account_sid = account_sid
-        self.auth_token = auth_token
-        self.from_ = from_
-        self.messaging_service_sid = messaging_service_sid
-        self.opts = opts
-
-
-class SMSContent:
-    def __init__(self, body: str, to_phone: str) -> None:
-        self.body = body
-        self.to_phone = to_phone
-
-
-class TwilioServiceInterface(ABC, Generic[_T]):
-    def __init__(self, twilio_client: Client) -> None:  # type: ignore
-        self.twilio_client = twilio_client  # type: ignore
-
-    @abstractmethod
-    async def send_raw_sms(
-        self,
-        content: SMSContent,
-        user_context: Dict[str, Any],
-        from_: Union[str, None] = None,
-        messaging_service_sid: Union[str, None] = None,
-    ) -> None:
-        pass
-
-    @abstractmethod
-    async def get_content(
-        self, template_vars: _T, user_context: Dict[str, Any]
-    ) -> SMSContent:
-        pass
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class SMSContent -(body: str, to_phone: str) -
-
-
-
- -Expand source code - -
class SMSContent:
-    def __init__(self, body: str, to_phone: str) -> None:
-        self.body = body
-        self.to_phone = to_phone
-
-
-
-class SMSDeliveryConfig -(service: Optional[SMSDeliveryInterface[~_T]] = None, override: Optional[Callable[[SMSDeliveryInterface[~_T]], SMSDeliveryInterface[~_T]]] = None) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class SMSDeliveryConfig(ABC, Generic[_T]):
-    def __init__(
-        self,
-        service: Union[SMSDeliveryInterface[_T], None] = None,
-        override: Union[
-            Callable[[SMSDeliveryInterface[_T]], SMSDeliveryInterface[_T]], None
-        ] = None,
-    ) -> None:
-        self.service = service
-        self.override = override
-
-

Ancestors

-
    -
  • abc.ABC
  • -
  • typing.Generic
  • -
-
-
-class SMSDeliveryConfigWithService -(service: SMSDeliveryInterface[~_T], override: Optional[Callable[[SMSDeliveryInterface[~_T]], SMSDeliveryInterface[~_T]]] = None) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class SMSDeliveryConfigWithService(ABC, Generic[_T]):
-    def __init__(
-        self,
-        service: SMSDeliveryInterface[_T],
-        override: Union[
-            Callable[[SMSDeliveryInterface[_T]], SMSDeliveryInterface[_T]], None
-        ] = None,
-    ) -> None:
-        self.service = service
-        self.override = override
-
-

Ancestors

-
    -
  • abc.ABC
  • -
  • typing.Generic
  • -
-
-
-class SMSDeliveryInterface -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class SMSDeliveryInterface(ABC, Generic[_T]):
-    @abstractmethod
-    async def send_sms(self, template_vars: _T, user_context: Dict[str, Any]) -> None:
-        pass
-
-

Ancestors

-
    -
  • abc.ABC
  • -
  • typing.Generic
  • -
-

Subclasses

- -

Methods

-
-
-async def send_sms(self, template_vars: ~_T, user_context: Dict[str, Any]) ‑> None -
-
-
-
- -Expand source code - -
@abstractmethod
-async def send_sms(self, template_vars: _T, user_context: Dict[str, Any]) -> None:
-    pass
-
-
-
-
-
-class TwilioServiceInterface -(twilio_client: twilio.rest.Client) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class TwilioServiceInterface(ABC, Generic[_T]):
-    def __init__(self, twilio_client: Client) -> None:  # type: ignore
-        self.twilio_client = twilio_client  # type: ignore
-
-    @abstractmethod
-    async def send_raw_sms(
-        self,
-        content: SMSContent,
-        user_context: Dict[str, Any],
-        from_: Union[str, None] = None,
-        messaging_service_sid: Union[str, None] = None,
-    ) -> None:
-        pass
-
-    @abstractmethod
-    async def get_content(
-        self, template_vars: _T, user_context: Dict[str, Any]
-    ) -> SMSContent:
-        pass
-
-

Ancestors

-
    -
  • abc.ABC
  • -
  • typing.Generic
  • -
-

Subclasses

- -

Methods

-
-
-async def get_content(self, template_vars: ~_T, user_context: Dict[str, Any]) ‑> SMSContent -
-
-
-
- -Expand source code - -
@abstractmethod
-async def get_content(
-    self, template_vars: _T, user_context: Dict[str, Any]
-) -> SMSContent:
-    pass
-
-
-
-async def send_raw_sms(self, content: SMSContent, user_context: Dict[str, Any], from_: Optional[str] = None, messaging_service_sid: Optional[str] = None) ‑> None -
-
-
-
- -Expand source code - -
@abstractmethod
-async def send_raw_sms(
-    self,
-    content: SMSContent,
-    user_context: Dict[str, Any],
-    from_: Union[str, None] = None,
-    messaging_service_sid: Union[str, None] = None,
-) -> None:
-    pass
-
-
-
-
-
-class TwilioSettings -(account_sid: str, auth_token: str, from_: Optional[str] = None, messaging_service_sid: Optional[str] = None, opts: Optional[Dict[str, Any]] = None) -
-
-

Note: self.otps can be used to override values passed to the Twilio Client. -Read docs from twilio.rest.Client.__init__ to discover possible args.

-

For example, opts = {"region": "...", "user_agent_extensions": ["..."], }

-
- -Expand source code - -
class TwilioSettings:
-    def __init__(
-        self,
-        account_sid: str,
-        auth_token: str,
-        from_: Union[str, None] = None,
-        messaging_service_sid: Union[str, None] = None,
-        opts: Union[Dict[str, Any], None] = None,
-    ) -> None:
-        """
-        Note: `self.otps` can be used to override values passed to the Twilio Client.
-        Read docs from `twilio.rest.Client.__init__` to discover possible args.
-
-        For example, `opts = {"region": "...", "user_agent_extensions": ["..."], }`
-        """
-        self.account_sid = account_sid
-        self.auth_token = auth_token
-        self.from_ = from_
-        self.messaging_service_sid = messaging_service_sid
-        self.opts = opts
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/interfaces.html b/html/supertokens_python/interfaces.html deleted file mode 100644 index 422cbb3e0..000000000 --- a/html/supertokens_python/interfaces.html +++ /dev/null @@ -1,254 +0,0 @@ - - - - - - -supertokens_python.interfaces API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.interfaces

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from typing import Optional
-
-from typing_extensions import Literal
-
-
-class UnknownSupertokensUserIDError:
-    pass
-
-
-class CreateUserIdMappingOkResult:
-    pass
-
-
-class UserIdMappingAlreadyExistsError:
-    def __init__(
-        self, does_super_tokens_user_id_exist: bool, does_external_user_id_exist: str
-    ):
-        self.does_super_tokens_user_id_exist = does_super_tokens_user_id_exist
-        self.does_external_user_id_exist = does_external_user_id_exist
-
-
-UserIDTypes = Literal["SUPERTOKENS", "EXTERNAL", "ANY"]
-
-
-class GetUserIdMappingOkResult:
-    def __init__(
-        self,
-        supertokens_user_id: str,
-        external_user_id: str,
-        external_user_info: Optional[str] = None,
-    ):
-        self.supertokens_user_id = supertokens_user_id
-        self.external_user_id = external_user_id
-        self.external_user_info = external_user_info
-
-
-class UnknownMappingError:
-    pass
-
-
-class DeleteUserIdMappingOkResult:
-    def __init__(self, did_mapping_exist: bool):
-        self.did_mapping_exist = did_mapping_exist
-
-
-class UpdateOrDeleteUserIdMappingInfoOkResult:
-    pass
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class CreateUserIdMappingOkResult -
-
-
-
- -Expand source code - -
class CreateUserIdMappingOkResult:
-    pass
-
-
-
-class DeleteUserIdMappingOkResult -(did_mapping_exist: bool) -
-
-
-
- -Expand source code - -
class DeleteUserIdMappingOkResult:
-    def __init__(self, did_mapping_exist: bool):
-        self.did_mapping_exist = did_mapping_exist
-
-
-
-class GetUserIdMappingOkResult -(supertokens_user_id: str, external_user_id: str, external_user_info: Optional[str] = None) -
-
-
-
- -Expand source code - -
class GetUserIdMappingOkResult:
-    def __init__(
-        self,
-        supertokens_user_id: str,
-        external_user_id: str,
-        external_user_info: Optional[str] = None,
-    ):
-        self.supertokens_user_id = supertokens_user_id
-        self.external_user_id = external_user_id
-        self.external_user_info = external_user_info
-
-
-
-class UnknownMappingError -
-
-
-
- -Expand source code - -
class UnknownMappingError:
-    pass
-
-
-
-class UnknownSupertokensUserIDError -
-
-
-
- -Expand source code - -
class UnknownSupertokensUserIDError:
-    pass
-
-
-
-class UpdateOrDeleteUserIdMappingInfoOkResult -
-
-
-
- -Expand source code - -
class UpdateOrDeleteUserIdMappingInfoOkResult:
-    pass
-
-
-
-class UserIdMappingAlreadyExistsError -(does_super_tokens_user_id_exist: bool, does_external_user_id_exist: str) -
-
-
-
- -Expand source code - -
class UserIdMappingAlreadyExistsError:
-    def __init__(
-        self, does_super_tokens_user_id_exist: bool, does_external_user_id_exist: str
-    ):
-        self.does_super_tokens_user_id_exist = does_super_tokens_user_id_exist
-        self.does_external_user_id_exist = does_external_user_id_exist
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/logger.html b/html/supertokens_python/logger.html deleted file mode 100644 index cb653e598..000000000 --- a/html/supertokens_python/logger.html +++ /dev/null @@ -1,257 +0,0 @@ - - - - - - -supertokens_python.logger API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.logger

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-import json
-import logging
-from datetime import datetime
-from os import getenv, path
-from typing import Union
-
-from .constants import VERSION
-
-NAMESPACE = "com.supertokens"
-DEBUG_ENV_VAR = "SUPERTOKENS_DEBUG"
-
-supertokens_dir = path.dirname(__file__)
-
-
-def enable_debug_logging():
-    _logger.setLevel(logging.DEBUG)
-
-
-# Configure logger
-_logger = logging.getLogger(NAMESPACE)
-debug_env = getenv(DEBUG_ENV_VAR, "").lower()
-if debug_env == "1":
-    enable_debug_logging()
-
-
-def _get_log_timestamp() -> str:
-    return datetime.utcnow().isoformat()[:-3] + "Z"
-
-
-class CustomStreamHandler(logging.StreamHandler):  # type: ignore
-    def emit(self, record: logging.LogRecord):
-        relative_path = path.relpath(record.pathname, supertokens_dir)
-
-        record.msg = json.dumps(
-            {
-                "t": _get_log_timestamp(),
-                "sdkVer": VERSION,
-                "message": record.msg,
-                "file": f"{relative_path}:{record.lineno}",
-            }
-        )
-
-        return super().emit(record)
-
-
-# Add stream handler and format
-streamHandler = CustomStreamHandler()
-streamFormatter = logging.Formatter("{name} {message}\n", style="{")
-streamHandler.setFormatter(streamFormatter)
-_logger.addHandler(streamHandler)
-
-
-# The debug logger can be used like this:
-# log_debug_message("Hello")
-# Output log format:
-# com.supertokens {"t": "2022-03-24T06:28:33.659Z", "sdkVer": "0.5.1", "message": "Hello", "file": "logger.py:73"}
-
-# Export logger.debug as log_debug_message function
-log_debug_message = _logger.debug
-
-
-def get_maybe_none_as_str(o: Union[str, None]) -> str:
-    if o is None:
-        return "None"
-    return o
-
-
-
-
-
-
-
-

Functions

-
-
-def enable_debug_logging() -
-
-
-
- -Expand source code - -
def enable_debug_logging():
-    _logger.setLevel(logging.DEBUG)
-
-
-
-def get_maybe_none_as_str(o: Optional[str]) ‑> str -
-
-
-
- -Expand source code - -
def get_maybe_none_as_str(o: Union[str, None]) -> str:
-    if o is None:
-        return "None"
-    return o
-
-
-
-
-
-

Classes

-
-
-class CustomStreamHandler -(stream=None) -
-
-

A handler class which writes logging records, appropriately formatted, -to a stream. Note that this class does not close the stream, as -sys.stdout or sys.stderr may be used.

-

Initialize the handler.

-

If stream is not specified, sys.stderr is used.

-
- -Expand source code - -
class CustomStreamHandler(logging.StreamHandler):  # type: ignore
-    def emit(self, record: logging.LogRecord):
-        relative_path = path.relpath(record.pathname, supertokens_dir)
-
-        record.msg = json.dumps(
-            {
-                "t": _get_log_timestamp(),
-                "sdkVer": VERSION,
-                "message": record.msg,
-                "file": f"{relative_path}:{record.lineno}",
-            }
-        )
-
-        return super().emit(record)
-
-

Ancestors

-
    -
  • logging.StreamHandler
  • -
  • logging.Handler
  • -
  • logging.Filterer
  • -
-

Methods

-
-
-def emit(self, record: logging.LogRecord) -
-
-

Emit a record.

-

If a formatter is specified, it is used to format the record. -The record is then written to the stream with a trailing newline. -If -exception information is present, it is formatted using -traceback.print_exception and appended to the stream. -If the stream -has an 'encoding' attribute, it is used to determine how to do the -output to the stream.

-
- -Expand source code - -
def emit(self, record: logging.LogRecord):
-    relative_path = path.relpath(record.pathname, supertokens_dir)
-
-    record.msg = json.dumps(
-        {
-            "t": _get_log_timestamp(),
-            "sdkVer": VERSION,
-            "message": record.msg,
-            "file": f"{relative_path}:{record.lineno}",
-        }
-    )
-
-    return super().emit(record)
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/normalised_url_domain.html b/html/supertokens_python/normalised_url_domain.html deleted file mode 100644 index 392a985df..000000000 --- a/html/supertokens_python/normalised_url_domain.html +++ /dev/null @@ -1,253 +0,0 @@ - - - - - - -supertokens_python.normalised_url_domain API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.normalised_url_domain

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from typing import TYPE_CHECKING
-from urllib.parse import urlparse
-
-from .utils import is_an_ip_address
-
-if TYPE_CHECKING:
-    pass
-from .exceptions import raise_general_exception
-
-
-class NormalisedURLDomain:
-    def __init__(self, url: str):
-        self.__value = normalise_domain_path_or_throw_error(url)
-
-    def get_as_string_dangerous(self):
-        return self.__value
-
-
-def normalise_domain_path_or_throw_error(
-    input_str: str, ignore_protocol: bool = False
-) -> str:
-    input_str = input_str.strip().lower()
-
-    try:
-        if (
-            (not input_str.startswith("http://"))
-            and (not input_str.startswith("https://"))
-            and (not input_str.startswith("supertokens://"))
-        ):
-            raise Exception("converting to proper URL")
-        url_obj = urlparse(input_str)
-
-        if ignore_protocol:
-            if url_obj.hostname is None:
-                raise Exception("Should never come here")
-            if url_obj.hostname.startswith("localhost") or is_an_ip_address(
-                url_obj.hostname
-            ):
-                input_str = "http://" + url_obj.netloc
-            else:
-                input_str = "https://" + url_obj.netloc
-        else:
-            input_str = url_obj.scheme + "://" + url_obj.netloc
-
-        return input_str
-    except Exception:
-        pass
-
-    if input_str.startswith("/"):
-        raise_general_exception("Please provide a valid domain name")
-
-    if input_str.startswith("."):
-        input_str = input_str[1:]
-
-    if (
-        ("." in input_str or input_str.startswith("localhost"))
-        and (not input_str.startswith("http://"))
-        and (not input_str.startswith("https://"))
-    ):
-        input_str = "https://" + input_str
-        try:
-            urlparse(input_str)
-            return normalise_domain_path_or_throw_error(input_str, True)
-        except Exception:
-            pass
-    raise_general_exception("Please provide a valid domain name")
-
-
-
-
-
-
-
-

Functions

-
-
-def normalise_domain_path_or_throw_error(input_str: str, ignore_protocol: bool = False) ‑> str -
-
-
-
- -Expand source code - -
def normalise_domain_path_or_throw_error(
-    input_str: str, ignore_protocol: bool = False
-) -> str:
-    input_str = input_str.strip().lower()
-
-    try:
-        if (
-            (not input_str.startswith("http://"))
-            and (not input_str.startswith("https://"))
-            and (not input_str.startswith("supertokens://"))
-        ):
-            raise Exception("converting to proper URL")
-        url_obj = urlparse(input_str)
-
-        if ignore_protocol:
-            if url_obj.hostname is None:
-                raise Exception("Should never come here")
-            if url_obj.hostname.startswith("localhost") or is_an_ip_address(
-                url_obj.hostname
-            ):
-                input_str = "http://" + url_obj.netloc
-            else:
-                input_str = "https://" + url_obj.netloc
-        else:
-            input_str = url_obj.scheme + "://" + url_obj.netloc
-
-        return input_str
-    except Exception:
-        pass
-
-    if input_str.startswith("/"):
-        raise_general_exception("Please provide a valid domain name")
-
-    if input_str.startswith("."):
-        input_str = input_str[1:]
-
-    if (
-        ("." in input_str or input_str.startswith("localhost"))
-        and (not input_str.startswith("http://"))
-        and (not input_str.startswith("https://"))
-    ):
-        input_str = "https://" + input_str
-        try:
-            urlparse(input_str)
-            return normalise_domain_path_or_throw_error(input_str, True)
-        except Exception:
-            pass
-    raise_general_exception("Please provide a valid domain name")
-
-
-
-
-
-

Classes

-
-
-class NormalisedURLDomain -(url: str) -
-
-
-
- -Expand source code - -
class NormalisedURLDomain:
-    def __init__(self, url: str):
-        self.__value = normalise_domain_path_or_throw_error(url)
-
-    def get_as_string_dangerous(self):
-        return self.__value
-
-

Methods

-
-
-def get_as_string_dangerous(self) -
-
-
-
- -Expand source code - -
def get_as_string_dangerous(self):
-    return self.__value
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/normalised_url_path.html b/html/supertokens_python/normalised_url_path.html deleted file mode 100644 index d36078bb8..000000000 --- a/html/supertokens_python/normalised_url_path.html +++ /dev/null @@ -1,369 +0,0 @@ - - - - - - -supertokens_python.normalised_url_path API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.normalised_url_path

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from __future__ import annotations
-
-from typing import TYPE_CHECKING
-from urllib.parse import urlparse
-
-if TYPE_CHECKING:
-    pass
-from .exceptions import raise_general_exception
-
-
-class NormalisedURLPath:
-    def __init__(self, url: str):
-        self.__value = normalise_url_path_or_throw_error(url)
-
-    def startswith(self, other: NormalisedURLPath) -> bool:
-        return self.__value.startswith(other.get_as_string_dangerous())
-
-    def append(self, other: NormalisedURLPath) -> NormalisedURLPath:
-        return NormalisedURLPath(self.__value + other.get_as_string_dangerous())
-
-    def get_as_string_dangerous(self) -> str:
-        return self.__value
-
-    def equals(self, other: NormalisedURLPath) -> bool:
-        return self.__value == other.get_as_string_dangerous()
-
-    def is_a_recipe_path(self) -> bool:
-        parts = self.__value.split("/")
-        return (len(parts) > 1 and parts[1] == "recipe") or (
-            len(parts) > 2 and parts[2] == "recipe"
-        )
-
-
-def normalise_url_path_or_throw_error(input_str: str) -> str:
-    input_str = input_str.strip().lower()
-
-    try:
-        if (not input_str.startswith("http://")) and (
-            not input_str.startswith("https://")
-        ):
-            raise Exception("converting to proper URL")
-        url_obj = urlparse(input_str)
-        input_str = url_obj.path
-
-        if input_str.endswith("/"):
-            return input_str[:-1]
-
-        return input_str
-    except Exception:
-        pass
-
-    if (
-        (domain_given(input_str) or input_str.startswith("localhost"))
-        and (not input_str.startswith("http://"))
-        and (not input_str.startswith("https://"))
-    ):
-        input_str = "http://" + input_str
-        return normalise_url_path_or_throw_error(input_str)
-
-    if not input_str.startswith("/"):
-        input_str = "/" + input_str
-
-    try:
-        urlparse("http://example.com" + input_str)
-        return normalise_url_path_or_throw_error("http://example.com" + input_str)
-    except Exception:
-        raise_general_exception("Please provide a valid URL path")
-
-
-def domain_given(input_str: str) -> bool:
-    if ("." not in input_str) or (input_str.startswith("/")):
-        return False
-
-    try:
-        url = urlparse(input_str)
-        if url.hostname is None:
-            raise Exception("Should never come here")
-        return url.hostname.find(".") != -1
-    except Exception:
-        pass
-
-    try:
-        url = urlparse("http://" + input_str)
-        if url.hostname is None:
-            raise Exception("Should never come here")
-        return url.hostname.find(".") != -1
-    except Exception:
-        pass
-
-    return False
-
-
-
-
-
-
-
-

Functions

-
-
-def domain_given(input_str: str) ‑> bool -
-
-
-
- -Expand source code - -
def domain_given(input_str: str) -> bool:
-    if ("." not in input_str) or (input_str.startswith("/")):
-        return False
-
-    try:
-        url = urlparse(input_str)
-        if url.hostname is None:
-            raise Exception("Should never come here")
-        return url.hostname.find(".") != -1
-    except Exception:
-        pass
-
-    try:
-        url = urlparse("http://" + input_str)
-        if url.hostname is None:
-            raise Exception("Should never come here")
-        return url.hostname.find(".") != -1
-    except Exception:
-        pass
-
-    return False
-
-
-
-def normalise_url_path_or_throw_error(input_str: str) ‑> str -
-
-
-
- -Expand source code - -
def normalise_url_path_or_throw_error(input_str: str) -> str:
-    input_str = input_str.strip().lower()
-
-    try:
-        if (not input_str.startswith("http://")) and (
-            not input_str.startswith("https://")
-        ):
-            raise Exception("converting to proper URL")
-        url_obj = urlparse(input_str)
-        input_str = url_obj.path
-
-        if input_str.endswith("/"):
-            return input_str[:-1]
-
-        return input_str
-    except Exception:
-        pass
-
-    if (
-        (domain_given(input_str) or input_str.startswith("localhost"))
-        and (not input_str.startswith("http://"))
-        and (not input_str.startswith("https://"))
-    ):
-        input_str = "http://" + input_str
-        return normalise_url_path_or_throw_error(input_str)
-
-    if not input_str.startswith("/"):
-        input_str = "/" + input_str
-
-    try:
-        urlparse("http://example.com" + input_str)
-        return normalise_url_path_or_throw_error("http://example.com" + input_str)
-    except Exception:
-        raise_general_exception("Please provide a valid URL path")
-
-
-
-
-
-

Classes

-
-
-class NormalisedURLPath -(url: str) -
-
-
-
- -Expand source code - -
class NormalisedURLPath:
-    def __init__(self, url: str):
-        self.__value = normalise_url_path_or_throw_error(url)
-
-    def startswith(self, other: NormalisedURLPath) -> bool:
-        return self.__value.startswith(other.get_as_string_dangerous())
-
-    def append(self, other: NormalisedURLPath) -> NormalisedURLPath:
-        return NormalisedURLPath(self.__value + other.get_as_string_dangerous())
-
-    def get_as_string_dangerous(self) -> str:
-        return self.__value
-
-    def equals(self, other: NormalisedURLPath) -> bool:
-        return self.__value == other.get_as_string_dangerous()
-
-    def is_a_recipe_path(self) -> bool:
-        parts = self.__value.split("/")
-        return (len(parts) > 1 and parts[1] == "recipe") or (
-            len(parts) > 2 and parts[2] == "recipe"
-        )
-
-

Methods

-
-
-def append(self, other: NormalisedURLPath) ‑> NormalisedURLPath -
-
-
-
- -Expand source code - -
def append(self, other: NormalisedURLPath) -> NormalisedURLPath:
-    return NormalisedURLPath(self.__value + other.get_as_string_dangerous())
-
-
-
-def equals(self, other: NormalisedURLPath) ‑> bool -
-
-
-
- -Expand source code - -
def equals(self, other: NormalisedURLPath) -> bool:
-    return self.__value == other.get_as_string_dangerous()
-
-
-
-def get_as_string_dangerous(self) ‑> str -
-
-
-
- -Expand source code - -
def get_as_string_dangerous(self) -> str:
-    return self.__value
-
-
-
-def is_a_recipe_path(self) ‑> bool -
-
-
-
- -Expand source code - -
def is_a_recipe_path(self) -> bool:
-    parts = self.__value.split("/")
-    return (len(parts) > 1 and parts[1] == "recipe") or (
-        len(parts) > 2 and parts[2] == "recipe"
-    )
-
-
-
-def startswith(self, other: NormalisedURLPath) ‑> bool -
-
-
-
- -Expand source code - -
def startswith(self, other: NormalisedURLPath) -> bool:
-    return self.__value.startswith(other.get_as_string_dangerous())
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/post_init_callbacks.html b/html/supertokens_python/post_init_callbacks.html deleted file mode 100644 index d474456f6..000000000 --- a/html/supertokens_python/post_init_callbacks.html +++ /dev/null @@ -1,172 +0,0 @@ - - - - - - -supertokens_python.post_init_callbacks API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.post_init_callbacks

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from typing import Callable, List
-
-
-class PostSTInitCallbacks:
-    """Callbacks that are called after the SuperTokens instance is initialized."""
-
-    callbacks: List[Callable[[], None]] = []
-
-    @staticmethod
-    def add_post_init_callback(cb: Callable[[], None]) -> None:
-        PostSTInitCallbacks.callbacks.append(cb)
-
-    @staticmethod
-    def run_post_init_callbacks() -> None:
-        for cb in PostSTInitCallbacks.callbacks:
-            cb()
-
-        PostSTInitCallbacks.callbacks = []
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class PostSTInitCallbacks -
-
-

Callbacks that are called after the SuperTokens instance is initialized.

-
- -Expand source code - -
class PostSTInitCallbacks:
-    """Callbacks that are called after the SuperTokens instance is initialized."""
-
-    callbacks: List[Callable[[], None]] = []
-
-    @staticmethod
-    def add_post_init_callback(cb: Callable[[], None]) -> None:
-        PostSTInitCallbacks.callbacks.append(cb)
-
-    @staticmethod
-    def run_post_init_callbacks() -> None:
-        for cb in PostSTInitCallbacks.callbacks:
-            cb()
-
-        PostSTInitCallbacks.callbacks = []
-
-

Class variables

-
-
var callbacks : List[Callable[[], None]]
-
-
-
-
-

Static methods

-
-
-def add_post_init_callback(cb: Callable[[], None]) ‑> None -
-
-
-
- -Expand source code - -
@staticmethod
-def add_post_init_callback(cb: Callable[[], None]) -> None:
-    PostSTInitCallbacks.callbacks.append(cb)
-
-
-
-def run_post_init_callbacks() ‑> None -
-
-
-
- -Expand source code - -
@staticmethod
-def run_post_init_callbacks() -> None:
-    for cb in PostSTInitCallbacks.callbacks:
-        cb()
-
-    PostSTInitCallbacks.callbacks = []
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/process_state.html b/html/supertokens_python/process_state.html deleted file mode 100644 index fbde96fd3..000000000 --- a/html/supertokens_python/process_state.html +++ /dev/null @@ -1,243 +0,0 @@ - - - - - - -supertokens_python.process_state API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.process_state

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from os import environ
-from typing import List
-from enum import Enum
-
-
-class AllowedProcessStates(Enum):
-    CALLING_SERVICE_IN_VERIFY = 1
-    CALLING_SERVICE_IN_GET_HANDSHAKE_INFO = 2
-    CALLING_SERVICE_IN_GET_API_VERSION = 3
-    CALLING_SERVICE_IN_REQUEST_HELPER = 4
-
-
-class ProcessState:
-    __instance = None
-
-    def __init__(self):
-        self.history: List[AllowedProcessStates] = []
-
-    @staticmethod
-    def get_instance():
-        if ProcessState.__instance is None:
-            ProcessState.__instance = ProcessState()
-        return ProcessState.__instance
-
-    def add_state(self, state: AllowedProcessStates):
-        if ("SUPERTOKENS_ENV" in environ) and (environ["SUPERTOKENS_ENV"] == "testing"):
-            self.history.append(state)
-
-    def reset(self):
-        self.history = []
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class AllowedProcessStates -(value, names=None, *, module=None, qualname=None, type=None, start=1) -
-
-

An enumeration.

-
- -Expand source code - -
class AllowedProcessStates(Enum):
-    CALLING_SERVICE_IN_VERIFY = 1
-    CALLING_SERVICE_IN_GET_HANDSHAKE_INFO = 2
-    CALLING_SERVICE_IN_GET_API_VERSION = 3
-    CALLING_SERVICE_IN_REQUEST_HELPER = 4
-
-

Ancestors

-
    -
  • enum.Enum
  • -
-

Class variables

-
-
var CALLING_SERVICE_IN_GET_API_VERSION
-
-
-
-
var CALLING_SERVICE_IN_GET_HANDSHAKE_INFO
-
-
-
-
var CALLING_SERVICE_IN_REQUEST_HELPER
-
-
-
-
var CALLING_SERVICE_IN_VERIFY
-
-
-
-
-
-
-class ProcessState -
-
-
-
- -Expand source code - -
class ProcessState:
-    __instance = None
-
-    def __init__(self):
-        self.history: List[AllowedProcessStates] = []
-
-    @staticmethod
-    def get_instance():
-        if ProcessState.__instance is None:
-            ProcessState.__instance = ProcessState()
-        return ProcessState.__instance
-
-    def add_state(self, state: AllowedProcessStates):
-        if ("SUPERTOKENS_ENV" in environ) and (environ["SUPERTOKENS_ENV"] == "testing"):
-            self.history.append(state)
-
-    def reset(self):
-        self.history = []
-
-

Static methods

-
-
-def get_instance() -
-
-
-
- -Expand source code - -
@staticmethod
-def get_instance():
-    if ProcessState.__instance is None:
-        ProcessState.__instance = ProcessState()
-    return ProcessState.__instance
-
-
-
-

Methods

-
-
-def add_state(self, state: AllowedProcessStates) -
-
-
-
- -Expand source code - -
def add_state(self, state: AllowedProcessStates):
-    if ("SUPERTOKENS_ENV" in environ) and (environ["SUPERTOKENS_ENV"] == "testing"):
-        self.history.append(state)
-
-
-
-def reset(self) -
-
-
-
- -Expand source code - -
def reset(self):
-    self.history = []
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/querier.html b/html/supertokens_python/querier.html deleted file mode 100644 index 6b3b7a147..000000000 --- a/html/supertokens_python/querier.html +++ /dev/null @@ -1,1661 +0,0 @@ - - - - - - -supertokens_python.querier API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.querier

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-import asyncio
-from json import JSONDecodeError
-from os import environ
-from typing import TYPE_CHECKING, Any, Awaitable, Callable, Dict, Optional, Tuple
-
-from httpx import AsyncClient, ConnectTimeout, NetworkError, Response
-
-from .constants import (
-    API_KEY_HEADER,
-    API_VERSION,
-    API_VERSION_HEADER,
-    RID_KEY_HEADER,
-    SUPPORTED_CDI_VERSIONS,
-    RATE_LIMIT_STATUS_CODE,
-)
-from .normalised_url_path import NormalisedURLPath
-
-if TYPE_CHECKING:
-    from .supertokens import Host
-
-from typing import List, Set, Union
-
-from .process_state import AllowedProcessStates, ProcessState
-from .utils import find_max_version, is_4xx_error, is_5xx_error
-from sniffio import AsyncLibraryNotFoundError
-from supertokens_python.async_to_sync_wrapper import create_or_get_event_loop
-from supertokens_python.utils import get_timestamp_ms
-
-
-class Querier:
-    __init_called = False
-    __hosts: List[Host] = []
-    __api_key: Union[None, str] = None
-    api_version = None
-    __last_tried_index: int = 0
-    __hosts_alive_for_testing: Set[str] = set()
-    network_interceptor: Optional[
-        Callable[
-            [
-                str,
-                str,
-                Dict[str, Any],
-                Optional[Dict[str, Any]],
-                Optional[Dict[str, Any]],
-                Optional[Dict[str, Any]],
-            ],
-            Tuple[
-                str,
-                str,
-                Dict[str, Any],
-                Optional[Dict[str, Any]],
-                Optional[Dict[str, Any]],
-            ],
-        ]
-    ] = None
-    __global_cache_tag = get_timestamp_ms()
-    __disable_cache = False
-
-    def __init__(self, hosts: List[Host], rid_to_core: Union[None, str] = None):
-        self.__hosts = hosts
-        self.__rid_to_core = None
-        self.__global_cache_tag = get_timestamp_ms()
-        if rid_to_core is not None:
-            self.__rid_to_core = rid_to_core
-
-    @staticmethod
-    def reset():
-        if ("SUPERTOKENS_ENV" not in environ) or (
-            environ["SUPERTOKENS_ENV"] != "testing"
-        ):
-            raise Exception("calling testing function in non testing env")
-        Querier.__init_called = False
-
-    @staticmethod
-    def get_hosts_alive_for_testing():
-        if ("SUPERTOKENS_ENV" not in environ) or (
-            environ["SUPERTOKENS_ENV"] != "testing"
-        ):
-            raise Exception("calling testing function in non testing env")
-        return Querier.__hosts_alive_for_testing
-
-    async def api_request(
-        self,
-        url: str,
-        method: str,
-        attempts_remaining: int,
-        *args: Any,
-        **kwargs: Any,
-    ) -> Response:
-        if attempts_remaining == 0:
-            raise Exception("Retry request failed")
-
-        try:
-            async with AsyncClient(timeout=30.0) as client:
-                if method == "GET":
-                    return await client.get(url, *args, **kwargs)  # type: ignore
-                if method == "POST":
-                    return await client.post(url, *args, **kwargs)  # type: ignore
-                if method == "PUT":
-                    return await client.put(url, *args, **kwargs)  # type: ignore
-                if method == "DELETE":
-                    return await client.delete(url, *args, **kwargs)  # type: ignore
-                raise Exception("Shouldn't come here")
-        except AsyncLibraryNotFoundError:
-            # Retry
-            loop = create_or_get_event_loop()
-            return loop.run_until_complete(
-                self.api_request(url, method, attempts_remaining - 1, *args, **kwargs)
-            )
-
-    async def get_api_version(self, user_context: Union[Dict[str, Any], None] = None):
-        if user_context is None:
-            user_context = {}
-
-        if Querier.api_version is not None:
-            return Querier.api_version
-
-        ProcessState.get_instance().add_state(
-            AllowedProcessStates.CALLING_SERVICE_IN_GET_API_VERSION
-        )
-
-        async def f(url: str, method: str) -> Response:
-            from supertokens_python.supertokens import (
-                Supertokens,
-                get_request_from_user_context,
-            )
-
-            headers = {}
-            if Querier.__api_key is not None:
-                headers = {API_KEY_HEADER: Querier.__api_key}
-
-            # Get app info
-            app_info = Supertokens.get_instance().app_info
-
-            req = get_request_from_user_context(user_context)
-            website_domain = app_info.get_origin(req, user_context)
-            # Prepare query parameters
-            query_params = {
-                "apiDomain": app_info.api_domain.get_as_string_dangerous(),
-                "websiteDomain": website_domain.get_as_string_dangerous(),
-            }
-
-            if Querier.network_interceptor is not None:
-                (
-                    url,
-                    method,
-                    headers,
-                    query_params,
-                    _,
-                ) = Querier.network_interceptor(  # pylint:disable=not-callable
-                    url, method, headers, query_params, {}, user_context
-                )
-
-            return await self.api_request(
-                url, method, 2, headers=headers, params=query_params
-            )
-
-        response = await self.__send_request_helper(
-            NormalisedURLPath(API_VERSION), "GET", f, len(self.__hosts)
-        )
-        cdi_supported_by_server = response["versions"]
-        api_version = find_max_version(cdi_supported_by_server, SUPPORTED_CDI_VERSIONS)
-
-        if api_version is None:
-            raise Exception(
-                "The running SuperTokens core version is not compatible with this python "
-                "SDK. Please visit https://supertokens.io/docs/community/compatibility-table "
-                "to find the right versions"
-            )
-
-        Querier.api_version = api_version
-        return Querier.api_version
-
-    @staticmethod
-    def get_instance(rid_to_core: Union[str, None] = None):
-        if not Querier.__init_called:
-            raise Exception(
-                "Please call the supertokens.init function before using SuperTokens"
-            )
-        return Querier(Querier.__hosts, rid_to_core)
-
-    @staticmethod
-    def init(
-        hosts: List[Host],
-        api_key: Union[str, None] = None,
-        network_interceptor: Optional[
-            Callable[
-                [
-                    str,
-                    str,
-                    Dict[str, Any],
-                    Optional[Dict[str, Any]],
-                    Optional[Dict[str, Any]],
-                    Optional[Dict[str, Any]],
-                ],
-                Tuple[
-                    str,
-                    str,
-                    Dict[str, Any],
-                    Optional[Dict[str, Any]],
-                    Optional[Dict[str, Any]],
-                ],
-            ]
-        ] = None,
-        disable_cache: bool = False,
-    ):
-        if not Querier.__init_called:
-            Querier.__init_called = True
-            Querier.__hosts = hosts
-            Querier.__api_key = api_key
-            Querier.api_version = None
-            Querier.__last_tried_index = 0
-            Querier.__hosts_alive_for_testing = set()
-            Querier.network_interceptor = network_interceptor
-            Querier.__disable_cache = disable_cache
-
-    async def __get_headers_with_api_version(
-        self, path: NormalisedURLPath, user_context: Union[Dict[str, Any], None]
-    ):
-        headers = {API_VERSION_HEADER: await self.get_api_version(user_context)}
-        if Querier.__api_key is not None:
-            headers = {**headers, API_KEY_HEADER: Querier.__api_key}
-        if path.is_a_recipe_path() and self.__rid_to_core is not None:
-            headers = {**headers, RID_KEY_HEADER: self.__rid_to_core}
-        return headers
-
-    async def send_get_request(
-        self,
-        path: NormalisedURLPath,
-        params: Union[Dict[str, Any], None],
-        user_context: Union[Dict[str, Any], None],
-    ) -> Dict[str, Any]:
-        if params is None:
-            params = {}
-
-        async def f(url: str, method: str) -> Response:
-            headers = await self.__get_headers_with_api_version(path, user_context)
-            nonlocal params
-
-            assert params is not None
-
-            # Sort the keys for deterministic order
-            sorted_keys = sorted(params.keys())
-            sorted_header_keys = sorted(headers.keys())
-
-            # Start with the path as the unique key
-            unique_key = path.get_as_string_dangerous()
-
-            # Append sorted params to the unique key
-            for key in sorted_keys:
-                value = params[key]
-                unique_key += f";{key}={value}"
-
-            # Append a separator for headers
-            unique_key += ";hdrs"
-
-            # Append sorted headers to the unique key
-            for key in sorted_header_keys:
-                value = headers[key]
-                unique_key += f";{key}={value}"
-
-            if user_context is not None:
-                if (
-                    user_context.get("_default", {}).get("global_cache_tag", -1)
-                    != self.__global_cache_tag
-                ):
-                    self.invalidate_core_call_cache(user_context, False)
-
-                if not Querier.__disable_cache and unique_key in user_context.get(
-                    "_default", {}
-                ).get("core_call_cache", {}):
-                    return user_context["_default"]["core_call_cache"][unique_key]
-
-            if Querier.network_interceptor is not None:
-                (
-                    url,
-                    method,
-                    headers,
-                    params,
-                    _,
-                ) = Querier.network_interceptor(  # pylint:disable=not-callable
-                    url, method, headers, params, {}, user_context
-                )
-
-            response = await self.api_request(
-                url,
-                method,
-                2,
-                headers=headers,
-                params=params,
-            )
-
-            if (
-                response.status_code == 200
-                and not Querier.__disable_cache
-                and user_context is not None
-            ):
-                user_context["_default"] = {
-                    **user_context.get("_default", {}),
-                    "core_call_cache": {
-                        **user_context.get("_default", {}).get("core_call_cache", {}),
-                        unique_key: response,
-                    },
-                    "global_cache_tag": self.__global_cache_tag,
-                }
-
-            return response
-
-        return await self.__send_request_helper(path, "GET", f, len(self.__hosts))
-
-    async def send_post_request(
-        self,
-        path: NormalisedURLPath,
-        data: Union[Dict[str, Any], None],
-        user_context: Union[Dict[str, Any], None],
-        test: bool = False,
-    ) -> Dict[str, Any]:
-        self.invalidate_core_call_cache(user_context)
-        if data is None:
-            data = {}
-
-        if (
-            ("SUPERTOKENS_ENV" in environ)
-            and (environ["SUPERTOKENS_ENV"] == "testing")
-            and test
-        ):
-            return data
-
-        headers = await self.__get_headers_with_api_version(path, user_context)
-        headers["content-type"] = "application/json; charset=utf-8"
-
-        async def f(url: str, method: str) -> Response:
-            nonlocal headers, data
-            if Querier.network_interceptor is not None:
-                (
-                    url,
-                    method,
-                    headers,
-                    _,
-                    data,
-                ) = Querier.network_interceptor(  # pylint:disable=not-callable
-                    url, method, headers, {}, data, user_context
-                )
-            return await self.api_request(
-                url,
-                method,
-                2,
-                headers=headers,
-                json=data,
-            )
-
-        return await self.__send_request_helper(path, "POST", f, len(self.__hosts))
-
-    async def send_delete_request(
-        self,
-        path: NormalisedURLPath,
-        params: Union[Dict[str, Any], None],
-        user_context: Union[Dict[str, Any], None],
-    ) -> Dict[str, Any]:
-        self.invalidate_core_call_cache(user_context)
-        if params is None:
-            params = {}
-
-        async def f(url: str, method: str) -> Response:
-            headers = await self.__get_headers_with_api_version(path, user_context)
-            nonlocal params
-            if Querier.network_interceptor is not None:
-                (
-                    url,
-                    method,
-                    headers,
-                    params,
-                    _,
-                ) = Querier.network_interceptor(  # pylint:disable=not-callable
-                    url, method, headers, params, {}, user_context
-                )
-            return await self.api_request(
-                url,
-                method,
-                2,
-                headers=headers,
-                params=params,
-            )
-
-        return await self.__send_request_helper(path, "DELETE", f, len(self.__hosts))
-
-    async def send_put_request(
-        self,
-        path: NormalisedURLPath,
-        data: Union[Dict[str, Any], None],
-        user_context: Union[Dict[str, Any], None],
-    ) -> Dict[str, Any]:
-        self.invalidate_core_call_cache(user_context)
-        if data is None:
-            data = {}
-
-        headers = await self.__get_headers_with_api_version(path, user_context)
-        headers["content-type"] = "application/json; charset=utf-8"
-
-        async def f(url: str, method: str) -> Response:
-            nonlocal headers, data
-            if Querier.network_interceptor is not None:
-                (
-                    url,
-                    method,
-                    headers,
-                    _,
-                    data,
-                ) = Querier.network_interceptor(  # pylint:disable=not-callable
-                    url, method, headers, {}, data, user_context
-                )
-            return await self.api_request(url, method, 2, headers=headers, json=data)
-
-        return await self.__send_request_helper(path, "PUT", f, len(self.__hosts))
-
-    def invalidate_core_call_cache(
-        self,
-        user_context: Union[Dict[str, Any], None],
-        upd_global_cache_tag_if_necessary: bool = True,
-    ):
-        if user_context is None:
-            # this is done so that the code below runs as expected.
-            # It will reset the __global_cache_tag if needed, and the
-            # stuff we assign to the user_context will just be ignored (as expected)
-            user_context = {}
-
-        if upd_global_cache_tag_if_necessary and (
-            user_context.get("_default", {}).get("keep_cache_alive", False) is not True
-        ):
-            # there can be race conditions here, but i think we can ignore them.
-            self.__global_cache_tag = get_timestamp_ms()
-
-        user_context["_default"] = {
-            **user_context.get("_default", {}),
-            "core_call_cache": {},
-        }
-
-    def get_all_core_urls_for_path(self, path: str) -> List[str]:
-        normalized_path = NormalisedURLPath(path)
-
-        result: List[str] = []
-
-        for h in self.__hosts:
-            current_domain = h.domain.get_as_string_dangerous()
-            current_base_path = h.base_path.get_as_string_dangerous()
-
-            result.append(
-                current_domain
-                + current_base_path
-                + normalized_path.get_as_string_dangerous()
-            )
-        return result
-
-    async def __send_request_helper(
-        self,
-        path: NormalisedURLPath,
-        method: str,
-        http_function: Callable[[str, str], Awaitable[Response]],
-        no_of_tries: int,
-        retry_info_map: Optional[Dict[str, int]] = None,
-    ) -> Dict[str, Any]:
-        if no_of_tries == 0:
-            raise Exception("No SuperTokens core available to query")
-
-        try:
-            current_host_domain = self.__hosts[
-                Querier.__last_tried_index
-            ].domain.get_as_string_dangerous()
-            current_host_base_path = self.__hosts[
-                Querier.__last_tried_index
-            ].base_path.get_as_string_dangerous()
-            current_host: str = current_host_domain + current_host_base_path
-            Querier.__last_tried_index += 1
-            Querier.__last_tried_index %= len(self.__hosts)
-            url = current_host + path.get_as_string_dangerous()
-
-            max_retries = 5
-
-            if retry_info_map is None:
-                retry_info_map = {}
-
-            if retry_info_map.get(url) is None:
-                retry_info_map[url] = max_retries
-
-            ProcessState.get_instance().add_state(
-                AllowedProcessStates.CALLING_SERVICE_IN_REQUEST_HELPER
-            )
-            response = await http_function(url, method)
-            if ("SUPERTOKENS_ENV" in environ) and (
-                environ["SUPERTOKENS_ENV"] == "testing"
-            ):
-                Querier.__hosts_alive_for_testing.add(current_host)
-
-            if response.status_code == RATE_LIMIT_STATUS_CODE:
-                retries_left = retry_info_map[url]
-
-                if retries_left > 0:
-                    retry_info_map[url] = retries_left - 1
-
-                    attempts_made = max_retries - retries_left
-                    delay = (10 + attempts_made * 250) / 1000
-
-                    await asyncio.sleep(delay)
-                    return await self.__send_request_helper(
-                        path, method, http_function, no_of_tries, retry_info_map
-                    )
-
-            if is_4xx_error(response.status_code) or is_5xx_error(response.status_code):  # type: ignore
-                raise Exception(
-                    "SuperTokens core threw an error for a "
-                    + method
-                    + " request to path: "
-                    + path.get_as_string_dangerous()
-                    + " with status code: "
-                    + str(response.status_code)
-                    + " and message: "
-                    + response.text  # type: ignore
-                )
-
-            res: Dict[str, Any] = {"_headers": dict(response.headers)}
-
-            try:
-                res.update(response.json())
-            except JSONDecodeError:
-                res["_text"] = response.text
-
-            return res
-
-        except (ConnectionError, NetworkError, ConnectTimeout) as _:
-            return await self.__send_request_helper(
-                path, method, http_function, no_of_tries - 1, retry_info_map
-            )
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class Querier -(hosts: List[Host], rid_to_core: Union[None, str] = None) -
-
-
-
- -Expand source code - -
class Querier:
-    __init_called = False
-    __hosts: List[Host] = []
-    __api_key: Union[None, str] = None
-    api_version = None
-    __last_tried_index: int = 0
-    __hosts_alive_for_testing: Set[str] = set()
-    network_interceptor: Optional[
-        Callable[
-            [
-                str,
-                str,
-                Dict[str, Any],
-                Optional[Dict[str, Any]],
-                Optional[Dict[str, Any]],
-                Optional[Dict[str, Any]],
-            ],
-            Tuple[
-                str,
-                str,
-                Dict[str, Any],
-                Optional[Dict[str, Any]],
-                Optional[Dict[str, Any]],
-            ],
-        ]
-    ] = None
-    __global_cache_tag = get_timestamp_ms()
-    __disable_cache = False
-
-    def __init__(self, hosts: List[Host], rid_to_core: Union[None, str] = None):
-        self.__hosts = hosts
-        self.__rid_to_core = None
-        self.__global_cache_tag = get_timestamp_ms()
-        if rid_to_core is not None:
-            self.__rid_to_core = rid_to_core
-
-    @staticmethod
-    def reset():
-        if ("SUPERTOKENS_ENV" not in environ) or (
-            environ["SUPERTOKENS_ENV"] != "testing"
-        ):
-            raise Exception("calling testing function in non testing env")
-        Querier.__init_called = False
-
-    @staticmethod
-    def get_hosts_alive_for_testing():
-        if ("SUPERTOKENS_ENV" not in environ) or (
-            environ["SUPERTOKENS_ENV"] != "testing"
-        ):
-            raise Exception("calling testing function in non testing env")
-        return Querier.__hosts_alive_for_testing
-
-    async def api_request(
-        self,
-        url: str,
-        method: str,
-        attempts_remaining: int,
-        *args: Any,
-        **kwargs: Any,
-    ) -> Response:
-        if attempts_remaining == 0:
-            raise Exception("Retry request failed")
-
-        try:
-            async with AsyncClient(timeout=30.0) as client:
-                if method == "GET":
-                    return await client.get(url, *args, **kwargs)  # type: ignore
-                if method == "POST":
-                    return await client.post(url, *args, **kwargs)  # type: ignore
-                if method == "PUT":
-                    return await client.put(url, *args, **kwargs)  # type: ignore
-                if method == "DELETE":
-                    return await client.delete(url, *args, **kwargs)  # type: ignore
-                raise Exception("Shouldn't come here")
-        except AsyncLibraryNotFoundError:
-            # Retry
-            loop = create_or_get_event_loop()
-            return loop.run_until_complete(
-                self.api_request(url, method, attempts_remaining - 1, *args, **kwargs)
-            )
-
-    async def get_api_version(self, user_context: Union[Dict[str, Any], None] = None):
-        if user_context is None:
-            user_context = {}
-
-        if Querier.api_version is not None:
-            return Querier.api_version
-
-        ProcessState.get_instance().add_state(
-            AllowedProcessStates.CALLING_SERVICE_IN_GET_API_VERSION
-        )
-
-        async def f(url: str, method: str) -> Response:
-            from supertokens_python.supertokens import (
-                Supertokens,
-                get_request_from_user_context,
-            )
-
-            headers = {}
-            if Querier.__api_key is not None:
-                headers = {API_KEY_HEADER: Querier.__api_key}
-
-            # Get app info
-            app_info = Supertokens.get_instance().app_info
-
-            req = get_request_from_user_context(user_context)
-            website_domain = app_info.get_origin(req, user_context)
-            # Prepare query parameters
-            query_params = {
-                "apiDomain": app_info.api_domain.get_as_string_dangerous(),
-                "websiteDomain": website_domain.get_as_string_dangerous(),
-            }
-
-            if Querier.network_interceptor is not None:
-                (
-                    url,
-                    method,
-                    headers,
-                    query_params,
-                    _,
-                ) = Querier.network_interceptor(  # pylint:disable=not-callable
-                    url, method, headers, query_params, {}, user_context
-                )
-
-            return await self.api_request(
-                url, method, 2, headers=headers, params=query_params
-            )
-
-        response = await self.__send_request_helper(
-            NormalisedURLPath(API_VERSION), "GET", f, len(self.__hosts)
-        )
-        cdi_supported_by_server = response["versions"]
-        api_version = find_max_version(cdi_supported_by_server, SUPPORTED_CDI_VERSIONS)
-
-        if api_version is None:
-            raise Exception(
-                "The running SuperTokens core version is not compatible with this python "
-                "SDK. Please visit https://supertokens.io/docs/community/compatibility-table "
-                "to find the right versions"
-            )
-
-        Querier.api_version = api_version
-        return Querier.api_version
-
-    @staticmethod
-    def get_instance(rid_to_core: Union[str, None] = None):
-        if not Querier.__init_called:
-            raise Exception(
-                "Please call the supertokens.init function before using SuperTokens"
-            )
-        return Querier(Querier.__hosts, rid_to_core)
-
-    @staticmethod
-    def init(
-        hosts: List[Host],
-        api_key: Union[str, None] = None,
-        network_interceptor: Optional[
-            Callable[
-                [
-                    str,
-                    str,
-                    Dict[str, Any],
-                    Optional[Dict[str, Any]],
-                    Optional[Dict[str, Any]],
-                    Optional[Dict[str, Any]],
-                ],
-                Tuple[
-                    str,
-                    str,
-                    Dict[str, Any],
-                    Optional[Dict[str, Any]],
-                    Optional[Dict[str, Any]],
-                ],
-            ]
-        ] = None,
-        disable_cache: bool = False,
-    ):
-        if not Querier.__init_called:
-            Querier.__init_called = True
-            Querier.__hosts = hosts
-            Querier.__api_key = api_key
-            Querier.api_version = None
-            Querier.__last_tried_index = 0
-            Querier.__hosts_alive_for_testing = set()
-            Querier.network_interceptor = network_interceptor
-            Querier.__disable_cache = disable_cache
-
-    async def __get_headers_with_api_version(
-        self, path: NormalisedURLPath, user_context: Union[Dict[str, Any], None]
-    ):
-        headers = {API_VERSION_HEADER: await self.get_api_version(user_context)}
-        if Querier.__api_key is not None:
-            headers = {**headers, API_KEY_HEADER: Querier.__api_key}
-        if path.is_a_recipe_path() and self.__rid_to_core is not None:
-            headers = {**headers, RID_KEY_HEADER: self.__rid_to_core}
-        return headers
-
-    async def send_get_request(
-        self,
-        path: NormalisedURLPath,
-        params: Union[Dict[str, Any], None],
-        user_context: Union[Dict[str, Any], None],
-    ) -> Dict[str, Any]:
-        if params is None:
-            params = {}
-
-        async def f(url: str, method: str) -> Response:
-            headers = await self.__get_headers_with_api_version(path, user_context)
-            nonlocal params
-
-            assert params is not None
-
-            # Sort the keys for deterministic order
-            sorted_keys = sorted(params.keys())
-            sorted_header_keys = sorted(headers.keys())
-
-            # Start with the path as the unique key
-            unique_key = path.get_as_string_dangerous()
-
-            # Append sorted params to the unique key
-            for key in sorted_keys:
-                value = params[key]
-                unique_key += f";{key}={value}"
-
-            # Append a separator for headers
-            unique_key += ";hdrs"
-
-            # Append sorted headers to the unique key
-            for key in sorted_header_keys:
-                value = headers[key]
-                unique_key += f";{key}={value}"
-
-            if user_context is not None:
-                if (
-                    user_context.get("_default", {}).get("global_cache_tag", -1)
-                    != self.__global_cache_tag
-                ):
-                    self.invalidate_core_call_cache(user_context, False)
-
-                if not Querier.__disable_cache and unique_key in user_context.get(
-                    "_default", {}
-                ).get("core_call_cache", {}):
-                    return user_context["_default"]["core_call_cache"][unique_key]
-
-            if Querier.network_interceptor is not None:
-                (
-                    url,
-                    method,
-                    headers,
-                    params,
-                    _,
-                ) = Querier.network_interceptor(  # pylint:disable=not-callable
-                    url, method, headers, params, {}, user_context
-                )
-
-            response = await self.api_request(
-                url,
-                method,
-                2,
-                headers=headers,
-                params=params,
-            )
-
-            if (
-                response.status_code == 200
-                and not Querier.__disable_cache
-                and user_context is not None
-            ):
-                user_context["_default"] = {
-                    **user_context.get("_default", {}),
-                    "core_call_cache": {
-                        **user_context.get("_default", {}).get("core_call_cache", {}),
-                        unique_key: response,
-                    },
-                    "global_cache_tag": self.__global_cache_tag,
-                }
-
-            return response
-
-        return await self.__send_request_helper(path, "GET", f, len(self.__hosts))
-
-    async def send_post_request(
-        self,
-        path: NormalisedURLPath,
-        data: Union[Dict[str, Any], None],
-        user_context: Union[Dict[str, Any], None],
-        test: bool = False,
-    ) -> Dict[str, Any]:
-        self.invalidate_core_call_cache(user_context)
-        if data is None:
-            data = {}
-
-        if (
-            ("SUPERTOKENS_ENV" in environ)
-            and (environ["SUPERTOKENS_ENV"] == "testing")
-            and test
-        ):
-            return data
-
-        headers = await self.__get_headers_with_api_version(path, user_context)
-        headers["content-type"] = "application/json; charset=utf-8"
-
-        async def f(url: str, method: str) -> Response:
-            nonlocal headers, data
-            if Querier.network_interceptor is not None:
-                (
-                    url,
-                    method,
-                    headers,
-                    _,
-                    data,
-                ) = Querier.network_interceptor(  # pylint:disable=not-callable
-                    url, method, headers, {}, data, user_context
-                )
-            return await self.api_request(
-                url,
-                method,
-                2,
-                headers=headers,
-                json=data,
-            )
-
-        return await self.__send_request_helper(path, "POST", f, len(self.__hosts))
-
-    async def send_delete_request(
-        self,
-        path: NormalisedURLPath,
-        params: Union[Dict[str, Any], None],
-        user_context: Union[Dict[str, Any], None],
-    ) -> Dict[str, Any]:
-        self.invalidate_core_call_cache(user_context)
-        if params is None:
-            params = {}
-
-        async def f(url: str, method: str) -> Response:
-            headers = await self.__get_headers_with_api_version(path, user_context)
-            nonlocal params
-            if Querier.network_interceptor is not None:
-                (
-                    url,
-                    method,
-                    headers,
-                    params,
-                    _,
-                ) = Querier.network_interceptor(  # pylint:disable=not-callable
-                    url, method, headers, params, {}, user_context
-                )
-            return await self.api_request(
-                url,
-                method,
-                2,
-                headers=headers,
-                params=params,
-            )
-
-        return await self.__send_request_helper(path, "DELETE", f, len(self.__hosts))
-
-    async def send_put_request(
-        self,
-        path: NormalisedURLPath,
-        data: Union[Dict[str, Any], None],
-        user_context: Union[Dict[str, Any], None],
-    ) -> Dict[str, Any]:
-        self.invalidate_core_call_cache(user_context)
-        if data is None:
-            data = {}
-
-        headers = await self.__get_headers_with_api_version(path, user_context)
-        headers["content-type"] = "application/json; charset=utf-8"
-
-        async def f(url: str, method: str) -> Response:
-            nonlocal headers, data
-            if Querier.network_interceptor is not None:
-                (
-                    url,
-                    method,
-                    headers,
-                    _,
-                    data,
-                ) = Querier.network_interceptor(  # pylint:disable=not-callable
-                    url, method, headers, {}, data, user_context
-                )
-            return await self.api_request(url, method, 2, headers=headers, json=data)
-
-        return await self.__send_request_helper(path, "PUT", f, len(self.__hosts))
-
-    def invalidate_core_call_cache(
-        self,
-        user_context: Union[Dict[str, Any], None],
-        upd_global_cache_tag_if_necessary: bool = True,
-    ):
-        if user_context is None:
-            # this is done so that the code below runs as expected.
-            # It will reset the __global_cache_tag if needed, and the
-            # stuff we assign to the user_context will just be ignored (as expected)
-            user_context = {}
-
-        if upd_global_cache_tag_if_necessary and (
-            user_context.get("_default", {}).get("keep_cache_alive", False) is not True
-        ):
-            # there can be race conditions here, but i think we can ignore them.
-            self.__global_cache_tag = get_timestamp_ms()
-
-        user_context["_default"] = {
-            **user_context.get("_default", {}),
-            "core_call_cache": {},
-        }
-
-    def get_all_core_urls_for_path(self, path: str) -> List[str]:
-        normalized_path = NormalisedURLPath(path)
-
-        result: List[str] = []
-
-        for h in self.__hosts:
-            current_domain = h.domain.get_as_string_dangerous()
-            current_base_path = h.base_path.get_as_string_dangerous()
-
-            result.append(
-                current_domain
-                + current_base_path
-                + normalized_path.get_as_string_dangerous()
-            )
-        return result
-
-    async def __send_request_helper(
-        self,
-        path: NormalisedURLPath,
-        method: str,
-        http_function: Callable[[str, str], Awaitable[Response]],
-        no_of_tries: int,
-        retry_info_map: Optional[Dict[str, int]] = None,
-    ) -> Dict[str, Any]:
-        if no_of_tries == 0:
-            raise Exception("No SuperTokens core available to query")
-
-        try:
-            current_host_domain = self.__hosts[
-                Querier.__last_tried_index
-            ].domain.get_as_string_dangerous()
-            current_host_base_path = self.__hosts[
-                Querier.__last_tried_index
-            ].base_path.get_as_string_dangerous()
-            current_host: str = current_host_domain + current_host_base_path
-            Querier.__last_tried_index += 1
-            Querier.__last_tried_index %= len(self.__hosts)
-            url = current_host + path.get_as_string_dangerous()
-
-            max_retries = 5
-
-            if retry_info_map is None:
-                retry_info_map = {}
-
-            if retry_info_map.get(url) is None:
-                retry_info_map[url] = max_retries
-
-            ProcessState.get_instance().add_state(
-                AllowedProcessStates.CALLING_SERVICE_IN_REQUEST_HELPER
-            )
-            response = await http_function(url, method)
-            if ("SUPERTOKENS_ENV" in environ) and (
-                environ["SUPERTOKENS_ENV"] == "testing"
-            ):
-                Querier.__hosts_alive_for_testing.add(current_host)
-
-            if response.status_code == RATE_LIMIT_STATUS_CODE:
-                retries_left = retry_info_map[url]
-
-                if retries_left > 0:
-                    retry_info_map[url] = retries_left - 1
-
-                    attempts_made = max_retries - retries_left
-                    delay = (10 + attempts_made * 250) / 1000
-
-                    await asyncio.sleep(delay)
-                    return await self.__send_request_helper(
-                        path, method, http_function, no_of_tries, retry_info_map
-                    )
-
-            if is_4xx_error(response.status_code) or is_5xx_error(response.status_code):  # type: ignore
-                raise Exception(
-                    "SuperTokens core threw an error for a "
-                    + method
-                    + " request to path: "
-                    + path.get_as_string_dangerous()
-                    + " with status code: "
-                    + str(response.status_code)
-                    + " and message: "
-                    + response.text  # type: ignore
-                )
-
-            res: Dict[str, Any] = {"_headers": dict(response.headers)}
-
-            try:
-                res.update(response.json())
-            except JSONDecodeError:
-                res["_text"] = response.text
-
-            return res
-
-        except (ConnectionError, NetworkError, ConnectTimeout) as _:
-            return await self.__send_request_helper(
-                path, method, http_function, no_of_tries - 1, retry_info_map
-            )
-
-

Class variables

-
-
var api_version
-
-
-
-
var network_interceptor : Optional[Callable[[str, str, Dict[str, Any], Optional[Dict[str, Any]], Optional[Dict[str, Any]], Optional[Dict[str, Any]]], Tuple[str, str, Dict[str, Any], Optional[Dict[str, Any]], Optional[Dict[str, Any]]]]]
-
-
-
-
-

Static methods

-
-
-def get_hosts_alive_for_testing() -
-
-
-
- -Expand source code - -
@staticmethod
-def get_hosts_alive_for_testing():
-    if ("SUPERTOKENS_ENV" not in environ) or (
-        environ["SUPERTOKENS_ENV"] != "testing"
-    ):
-        raise Exception("calling testing function in non testing env")
-    return Querier.__hosts_alive_for_testing
-
-
-
-def get_instance(rid_to_core: Union[str, None] = None) -
-
-
-
- -Expand source code - -
@staticmethod
-def get_instance(rid_to_core: Union[str, None] = None):
-    if not Querier.__init_called:
-        raise Exception(
-            "Please call the supertokens.init function before using SuperTokens"
-        )
-    return Querier(Querier.__hosts, rid_to_core)
-
-
-
-def init(hosts: List[Host], api_key: Union[str, None] = None, network_interceptor: Optional[Callable[[str, str, Dict[str, Any], Optional[Dict[str, Any]], Optional[Dict[str, Any]], Optional[Dict[str, Any]]], Tuple[str, str, Dict[str, Any], Optional[Dict[str, Any]], Optional[Dict[str, Any]]]]] = None, disable_cache: bool = False) -
-
-
-
- -Expand source code - -
@staticmethod
-def init(
-    hosts: List[Host],
-    api_key: Union[str, None] = None,
-    network_interceptor: Optional[
-        Callable[
-            [
-                str,
-                str,
-                Dict[str, Any],
-                Optional[Dict[str, Any]],
-                Optional[Dict[str, Any]],
-                Optional[Dict[str, Any]],
-            ],
-            Tuple[
-                str,
-                str,
-                Dict[str, Any],
-                Optional[Dict[str, Any]],
-                Optional[Dict[str, Any]],
-            ],
-        ]
-    ] = None,
-    disable_cache: bool = False,
-):
-    if not Querier.__init_called:
-        Querier.__init_called = True
-        Querier.__hosts = hosts
-        Querier.__api_key = api_key
-        Querier.api_version = None
-        Querier.__last_tried_index = 0
-        Querier.__hosts_alive_for_testing = set()
-        Querier.network_interceptor = network_interceptor
-        Querier.__disable_cache = disable_cache
-
-
-
-def reset() -
-
-
-
- -Expand source code - -
@staticmethod
-def reset():
-    if ("SUPERTOKENS_ENV" not in environ) or (
-        environ["SUPERTOKENS_ENV"] != "testing"
-    ):
-        raise Exception("calling testing function in non testing env")
-    Querier.__init_called = False
-
-
-
-

Methods

-
-
-async def api_request(self, url: str, method: str, attempts_remaining: int, *args: Any, **kwargs: Any) ‑> httpx.Response -
-
-
-
- -Expand source code - -
async def api_request(
-    self,
-    url: str,
-    method: str,
-    attempts_remaining: int,
-    *args: Any,
-    **kwargs: Any,
-) -> Response:
-    if attempts_remaining == 0:
-        raise Exception("Retry request failed")
-
-    try:
-        async with AsyncClient(timeout=30.0) as client:
-            if method == "GET":
-                return await client.get(url, *args, **kwargs)  # type: ignore
-            if method == "POST":
-                return await client.post(url, *args, **kwargs)  # type: ignore
-            if method == "PUT":
-                return await client.put(url, *args, **kwargs)  # type: ignore
-            if method == "DELETE":
-                return await client.delete(url, *args, **kwargs)  # type: ignore
-            raise Exception("Shouldn't come here")
-    except AsyncLibraryNotFoundError:
-        # Retry
-        loop = create_or_get_event_loop()
-        return loop.run_until_complete(
-            self.api_request(url, method, attempts_remaining - 1, *args, **kwargs)
-        )
-
-
-
-def get_all_core_urls_for_path(self, path: str) ‑> List[str] -
-
-
-
- -Expand source code - -
def get_all_core_urls_for_path(self, path: str) -> List[str]:
-    normalized_path = NormalisedURLPath(path)
-
-    result: List[str] = []
-
-    for h in self.__hosts:
-        current_domain = h.domain.get_as_string_dangerous()
-        current_base_path = h.base_path.get_as_string_dangerous()
-
-        result.append(
-            current_domain
-            + current_base_path
-            + normalized_path.get_as_string_dangerous()
-        )
-    return result
-
-
-
-async def get_api_version(self, user_context: Union[Dict[str, Any], None] = None) -
-
-
-
- -Expand source code - -
async def get_api_version(self, user_context: Union[Dict[str, Any], None] = None):
-    if user_context is None:
-        user_context = {}
-
-    if Querier.api_version is not None:
-        return Querier.api_version
-
-    ProcessState.get_instance().add_state(
-        AllowedProcessStates.CALLING_SERVICE_IN_GET_API_VERSION
-    )
-
-    async def f(url: str, method: str) -> Response:
-        from supertokens_python.supertokens import (
-            Supertokens,
-            get_request_from_user_context,
-        )
-
-        headers = {}
-        if Querier.__api_key is not None:
-            headers = {API_KEY_HEADER: Querier.__api_key}
-
-        # Get app info
-        app_info = Supertokens.get_instance().app_info
-
-        req = get_request_from_user_context(user_context)
-        website_domain = app_info.get_origin(req, user_context)
-        # Prepare query parameters
-        query_params = {
-            "apiDomain": app_info.api_domain.get_as_string_dangerous(),
-            "websiteDomain": website_domain.get_as_string_dangerous(),
-        }
-
-        if Querier.network_interceptor is not None:
-            (
-                url,
-                method,
-                headers,
-                query_params,
-                _,
-            ) = Querier.network_interceptor(  # pylint:disable=not-callable
-                url, method, headers, query_params, {}, user_context
-            )
-
-        return await self.api_request(
-            url, method, 2, headers=headers, params=query_params
-        )
-
-    response = await self.__send_request_helper(
-        NormalisedURLPath(API_VERSION), "GET", f, len(self.__hosts)
-    )
-    cdi_supported_by_server = response["versions"]
-    api_version = find_max_version(cdi_supported_by_server, SUPPORTED_CDI_VERSIONS)
-
-    if api_version is None:
-        raise Exception(
-            "The running SuperTokens core version is not compatible with this python "
-            "SDK. Please visit https://supertokens.io/docs/community/compatibility-table "
-            "to find the right versions"
-        )
-
-    Querier.api_version = api_version
-    return Querier.api_version
-
-
-
-def invalidate_core_call_cache(self, user_context: Union[Dict[str, Any], None], upd_global_cache_tag_if_necessary: bool = True) -
-
-
-
- -Expand source code - -
def invalidate_core_call_cache(
-    self,
-    user_context: Union[Dict[str, Any], None],
-    upd_global_cache_tag_if_necessary: bool = True,
-):
-    if user_context is None:
-        # this is done so that the code below runs as expected.
-        # It will reset the __global_cache_tag if needed, and the
-        # stuff we assign to the user_context will just be ignored (as expected)
-        user_context = {}
-
-    if upd_global_cache_tag_if_necessary and (
-        user_context.get("_default", {}).get("keep_cache_alive", False) is not True
-    ):
-        # there can be race conditions here, but i think we can ignore them.
-        self.__global_cache_tag = get_timestamp_ms()
-
-    user_context["_default"] = {
-        **user_context.get("_default", {}),
-        "core_call_cache": {},
-    }
-
-
-
-async def send_delete_request(self, path: NormalisedURLPath, params: Union[Dict[str, Any], None], user_context: Union[Dict[str, Any], None]) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
async def send_delete_request(
-    self,
-    path: NormalisedURLPath,
-    params: Union[Dict[str, Any], None],
-    user_context: Union[Dict[str, Any], None],
-) -> Dict[str, Any]:
-    self.invalidate_core_call_cache(user_context)
-    if params is None:
-        params = {}
-
-    async def f(url: str, method: str) -> Response:
-        headers = await self.__get_headers_with_api_version(path, user_context)
-        nonlocal params
-        if Querier.network_interceptor is not None:
-            (
-                url,
-                method,
-                headers,
-                params,
-                _,
-            ) = Querier.network_interceptor(  # pylint:disable=not-callable
-                url, method, headers, params, {}, user_context
-            )
-        return await self.api_request(
-            url,
-            method,
-            2,
-            headers=headers,
-            params=params,
-        )
-
-    return await self.__send_request_helper(path, "DELETE", f, len(self.__hosts))
-
-
-
-async def send_get_request(self, path: NormalisedURLPath, params: Union[Dict[str, Any], None], user_context: Union[Dict[str, Any], None]) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
async def send_get_request(
-    self,
-    path: NormalisedURLPath,
-    params: Union[Dict[str, Any], None],
-    user_context: Union[Dict[str, Any], None],
-) -> Dict[str, Any]:
-    if params is None:
-        params = {}
-
-    async def f(url: str, method: str) -> Response:
-        headers = await self.__get_headers_with_api_version(path, user_context)
-        nonlocal params
-
-        assert params is not None
-
-        # Sort the keys for deterministic order
-        sorted_keys = sorted(params.keys())
-        sorted_header_keys = sorted(headers.keys())
-
-        # Start with the path as the unique key
-        unique_key = path.get_as_string_dangerous()
-
-        # Append sorted params to the unique key
-        for key in sorted_keys:
-            value = params[key]
-            unique_key += f";{key}={value}"
-
-        # Append a separator for headers
-        unique_key += ";hdrs"
-
-        # Append sorted headers to the unique key
-        for key in sorted_header_keys:
-            value = headers[key]
-            unique_key += f";{key}={value}"
-
-        if user_context is not None:
-            if (
-                user_context.get("_default", {}).get("global_cache_tag", -1)
-                != self.__global_cache_tag
-            ):
-                self.invalidate_core_call_cache(user_context, False)
-
-            if not Querier.__disable_cache and unique_key in user_context.get(
-                "_default", {}
-            ).get("core_call_cache", {}):
-                return user_context["_default"]["core_call_cache"][unique_key]
-
-        if Querier.network_interceptor is not None:
-            (
-                url,
-                method,
-                headers,
-                params,
-                _,
-            ) = Querier.network_interceptor(  # pylint:disable=not-callable
-                url, method, headers, params, {}, user_context
-            )
-
-        response = await self.api_request(
-            url,
-            method,
-            2,
-            headers=headers,
-            params=params,
-        )
-
-        if (
-            response.status_code == 200
-            and not Querier.__disable_cache
-            and user_context is not None
-        ):
-            user_context["_default"] = {
-                **user_context.get("_default", {}),
-                "core_call_cache": {
-                    **user_context.get("_default", {}).get("core_call_cache", {}),
-                    unique_key: response,
-                },
-                "global_cache_tag": self.__global_cache_tag,
-            }
-
-        return response
-
-    return await self.__send_request_helper(path, "GET", f, len(self.__hosts))
-
-
-
-async def send_post_request(self, path: NormalisedURLPath, data: Union[Dict[str, Any], None], user_context: Union[Dict[str, Any], None], test: bool = False) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
async def send_post_request(
-    self,
-    path: NormalisedURLPath,
-    data: Union[Dict[str, Any], None],
-    user_context: Union[Dict[str, Any], None],
-    test: bool = False,
-) -> Dict[str, Any]:
-    self.invalidate_core_call_cache(user_context)
-    if data is None:
-        data = {}
-
-    if (
-        ("SUPERTOKENS_ENV" in environ)
-        and (environ["SUPERTOKENS_ENV"] == "testing")
-        and test
-    ):
-        return data
-
-    headers = await self.__get_headers_with_api_version(path, user_context)
-    headers["content-type"] = "application/json; charset=utf-8"
-
-    async def f(url: str, method: str) -> Response:
-        nonlocal headers, data
-        if Querier.network_interceptor is not None:
-            (
-                url,
-                method,
-                headers,
-                _,
-                data,
-            ) = Querier.network_interceptor(  # pylint:disable=not-callable
-                url, method, headers, {}, data, user_context
-            )
-        return await self.api_request(
-            url,
-            method,
-            2,
-            headers=headers,
-            json=data,
-        )
-
-    return await self.__send_request_helper(path, "POST", f, len(self.__hosts))
-
-
-
-async def send_put_request(self, path: NormalisedURLPath, data: Union[Dict[str, Any], None], user_context: Union[Dict[str, Any], None]) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
async def send_put_request(
-    self,
-    path: NormalisedURLPath,
-    data: Union[Dict[str, Any], None],
-    user_context: Union[Dict[str, Any], None],
-) -> Dict[str, Any]:
-    self.invalidate_core_call_cache(user_context)
-    if data is None:
-        data = {}
-
-    headers = await self.__get_headers_with_api_version(path, user_context)
-    headers["content-type"] = "application/json; charset=utf-8"
-
-    async def f(url: str, method: str) -> Response:
-        nonlocal headers, data
-        if Querier.network_interceptor is not None:
-            (
-                url,
-                method,
-                headers,
-                _,
-                data,
-            ) = Querier.network_interceptor(  # pylint:disable=not-callable
-                url, method, headers, {}, data, user_context
-            )
-        return await self.api_request(url, method, 2, headers=headers, json=data)
-
-    return await self.__send_request_helper(path, "PUT", f, len(self.__hosts))
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/dashboard/api/analytics.html b/html/supertokens_python/recipe/dashboard/api/analytics.html deleted file mode 100644 index 61801cbf1..000000000 --- a/html/supertokens_python/recipe/dashboard/api/analytics.html +++ /dev/null @@ -1,253 +0,0 @@ - - - - - - -supertokens_python.recipe.dashboard.api.analytics API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.dashboard.api.analytics

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from __future__ import annotations
-
-from typing import TYPE_CHECKING, Dict, Any
-
-from httpx import AsyncClient
-
-from supertokens_python import Supertokens
-from supertokens_python.constants import (
-    TELEMETRY_SUPERTOKENS_API_URL,
-    TELEMETRY_SUPERTOKENS_API_VERSION,
-)
-from supertokens_python.constants import VERSION as SDKVersion
-from supertokens_python.exceptions import raise_bad_input_exception
-from supertokens_python.normalised_url_path import NormalisedURLPath
-from supertokens_python.querier import Querier
-
-from ..interfaces import AnalyticsResponse
-
-if TYPE_CHECKING:
-    from supertokens_python.recipe.dashboard.interfaces import APIInterface, APIOptions
-
-
-async def handle_analytics_post(
-    _: APIInterface,
-    _tenant_id: str,
-    api_options: APIOptions,
-    _user_context: Dict[str, Any],
-) -> AnalyticsResponse:
-    if not Supertokens.get_instance().telemetry:
-        return AnalyticsResponse()
-    body = await api_options.request.json()
-    if body is None:
-        raise_bad_input_exception("Please send body")
-    email = body.get("email")
-    dashboard_version = body.get("dashboardVersion")
-
-    if email is None:
-        raise_bad_input_exception("Missing required property 'email'")
-    if dashboard_version is None:
-        raise_bad_input_exception("Missing required property 'dashboardVersion'")
-
-    telemetry_id = None
-
-    try:
-        response = await Querier.get_instance().send_get_request(
-            NormalisedURLPath("/telemetry"),
-            None,
-            _user_context,
-        )
-        if "exists" in response and response["exists"] and "telemetryId" in response:
-            telemetry_id = response["telemetryId"]
-
-        number_of_users = await Supertokens.get_instance().get_user_count(
-            include_recipe_ids=None
-        )
-
-    except Exception as __:
-        # If either telemetry id API or user count fetch fails, no event should be sent
-        return AnalyticsResponse()
-
-    apiDomain, websiteDomain, appName = (
-        api_options.app_info.api_domain,
-        api_options.app_info.get_origin(api_options.request, {}),
-        api_options.app_info.app_name,
-    )
-
-    data = {
-        "websiteDomain": websiteDomain.get_as_string_dangerous(),
-        "apiDomain": apiDomain.get_as_string_dangerous(),
-        "appName": appName,
-        "sdk": "python",
-        "sdkVersion": SDKVersion,
-        "numberOfUsers": number_of_users,
-        "email": email,
-        "dashboardVersion": dashboard_version,
-    }
-
-    if telemetry_id is not None:
-        data["telemetryId"] = telemetry_id
-
-    try:
-        async with AsyncClient(timeout=30.0) as client:
-            await client.post(  # type: ignore
-                url=TELEMETRY_SUPERTOKENS_API_URL,
-                json=data,
-                headers={"api-version": TELEMETRY_SUPERTOKENS_API_VERSION},
-            )
-    except Exception as __:
-        # If telemetry event fails, no error should be thrown
-        pass
-
-    return AnalyticsResponse()
-
-
-
-
-
-
-
-

Functions

-
-
-async def handle_analytics_post(_: APIInterface, _tenant_id: str, api_options: APIOptions, _user_context: Dict[str, Any]) ‑> AnalyticsResponse -
-
-
-
- -Expand source code - -
async def handle_analytics_post(
-    _: APIInterface,
-    _tenant_id: str,
-    api_options: APIOptions,
-    _user_context: Dict[str, Any],
-) -> AnalyticsResponse:
-    if not Supertokens.get_instance().telemetry:
-        return AnalyticsResponse()
-    body = await api_options.request.json()
-    if body is None:
-        raise_bad_input_exception("Please send body")
-    email = body.get("email")
-    dashboard_version = body.get("dashboardVersion")
-
-    if email is None:
-        raise_bad_input_exception("Missing required property 'email'")
-    if dashboard_version is None:
-        raise_bad_input_exception("Missing required property 'dashboardVersion'")
-
-    telemetry_id = None
-
-    try:
-        response = await Querier.get_instance().send_get_request(
-            NormalisedURLPath("/telemetry"),
-            None,
-            _user_context,
-        )
-        if "exists" in response and response["exists"] and "telemetryId" in response:
-            telemetry_id = response["telemetryId"]
-
-        number_of_users = await Supertokens.get_instance().get_user_count(
-            include_recipe_ids=None
-        )
-
-    except Exception as __:
-        # If either telemetry id API or user count fetch fails, no event should be sent
-        return AnalyticsResponse()
-
-    apiDomain, websiteDomain, appName = (
-        api_options.app_info.api_domain,
-        api_options.app_info.get_origin(api_options.request, {}),
-        api_options.app_info.app_name,
-    )
-
-    data = {
-        "websiteDomain": websiteDomain.get_as_string_dangerous(),
-        "apiDomain": apiDomain.get_as_string_dangerous(),
-        "appName": appName,
-        "sdk": "python",
-        "sdkVersion": SDKVersion,
-        "numberOfUsers": number_of_users,
-        "email": email,
-        "dashboardVersion": dashboard_version,
-    }
-
-    if telemetry_id is not None:
-        data["telemetryId"] = telemetry_id
-
-    try:
-        async with AsyncClient(timeout=30.0) as client:
-            await client.post(  # type: ignore
-                url=TELEMETRY_SUPERTOKENS_API_URL,
-                json=data,
-                headers={"api-version": TELEMETRY_SUPERTOKENS_API_VERSION},
-            )
-    except Exception as __:
-        # If telemetry event fails, no error should be thrown
-        pass
-
-    return AnalyticsResponse()
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/dashboard/api/dashboard.html b/html/supertokens_python/recipe/dashboard/api/dashboard.html deleted file mode 100644 index 95bbe7535..000000000 --- a/html/supertokens_python/recipe/dashboard/api/dashboard.html +++ /dev/null @@ -1,127 +0,0 @@ - - - - - - -supertokens_python.recipe.dashboard.api.dashboard API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.dashboard.api.dashboard

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from typing import TYPE_CHECKING, Optional, Dict, Any
-
-from supertokens_python.framework import BaseResponse
-
-if TYPE_CHECKING:
-    from supertokens_python.recipe.dashboard.interfaces import (
-        APIOptions,
-        APIInterface,
-    )
-
-
-async def handle_dashboard_api(
-    api_implementation: APIInterface,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-) -> Optional[BaseResponse]:
-    if api_implementation.dashboard_get is None:
-        return None
-
-    html_str = await api_implementation.dashboard_get(api_options, user_context)
-
-    api_options.response.set_html_content(html_str)
-    return api_options.response
-
-
-
-
-
-
-
-

Functions

-
-
-async def handle_dashboard_api(api_implementation: APIInterface, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Optional[BaseResponse] -
-
-
-
- -Expand source code - -
async def handle_dashboard_api(
-    api_implementation: APIInterface,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-) -> Optional[BaseResponse]:
-    if api_implementation.dashboard_get is None:
-        return None
-
-    html_str = await api_implementation.dashboard_get(api_options, user_context)
-
-    api_options.response.set_html_content(html_str)
-    return api_options.response
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/dashboard/api/implementation.html b/html/supertokens_python/recipe/dashboard/api/implementation.html deleted file mode 100644 index 3f76cdfa5..000000000 --- a/html/supertokens_python/recipe/dashboard/api/implementation.html +++ /dev/null @@ -1,271 +0,0 @@ - - - - - - -supertokens_python.recipe.dashboard.api.implementation API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.dashboard.api.implementation

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from string import Template
-from textwrap import dedent
-from typing import TYPE_CHECKING, Any, Dict
-
-from supertokens_python import Supertokens
-from supertokens_python.normalised_url_domain import NormalisedURLDomain
-from supertokens_python.normalised_url_path import NormalisedURLPath
-from supertokens_python.querier import Querier
-from supertokens_python.utils import is_version_gte
-
-from ..constants import DASHBOARD_API
-from ..interfaces import APIInterface
-
-if TYPE_CHECKING:
-    from ..interfaces import APIOptions
-
-
-class APIImplementation(APIInterface):
-    def __init__(self):
-        super().__init__()
-
-        async def dashboard_get(
-            options: APIOptions, user_context: Dict[str, Any]
-        ) -> str:
-            bundle_base_path_string = (
-                await options.recipe_implementation.get_dashboard_bundle_location(
-                    user_context
-                )
-            )
-            bundle_domain = (
-                NormalisedURLDomain(bundle_base_path_string).get_as_string_dangerous()
-                + NormalisedURLPath(bundle_base_path_string).get_as_string_dangerous()
-            )
-
-            connection_uri = ""
-            super_tokens_instance = Supertokens.get_instance()
-            auth_mode = options.config.auth_mode
-            connection_uri = (
-                NormalisedURLDomain(
-                    super_tokens_instance.supertokens_config.connection_uri.split(";")[
-                        0
-                    ]
-                ).get_as_string_dangerous()
-                + NormalisedURLPath(
-                    super_tokens_instance.supertokens_config.connection_uri.split(";")[
-                        0
-                    ]
-                ).get_as_string_dangerous()
-            )
-
-            dashboard_path = options.app_info.api_base_path.append(
-                NormalisedURLPath(DASHBOARD_API)
-            ).get_as_string_dangerous()
-
-            is_search_enabled: bool = False
-            querier = Querier.get_instance(options.recipe_id)
-            cdiVersion = await querier.get_api_version(user_context)
-            if not cdiVersion:
-                is_search_enabled = True
-            elif is_version_gte(cdiVersion, "2.20"):
-                is_search_enabled = True
-
-            return Template(
-                dedent(
-                    """
-                <html>
-                    <head>
-                        <meta name="viewport" content="width=device-width, initial-scale=1.0">
-                        <script>
-                            window.staticBasePath = "${bundleDomain}/static"
-                            window.dashboardAppPath = "${dashboardPath}"
-                            window.connectionURI = "${connectionURI}"
-                            window.authMode = "${authMode}"
-                            window.isSearchEnabled = "${isSearchEnabled}"
-                        </script>
-                        <script defer src="${bundleDomain}/static/js/bundle.js"></script></head>
-                        <link href="${bundleDomain}/static/css/main.css" rel="stylesheet" type="text/css">
-                        <link rel="icon" type="image/x-icon" href="${bundleDomain}/static/media/favicon.ico">
-                    </head>
-                    <body>
-                        <noscript>You need to enable JavaScript to run this app.</noscript>
-                        <div id="root"></div>
-                    </body>
-                </html>
-                """
-                )
-            ).substitute(
-                bundleDomain=bundle_domain,
-                dashboardPath=dashboard_path,
-                connectionURI=connection_uri,
-                authMode=auth_mode,
-                isSearchEnabled=str(is_search_enabled).lower(),
-            )
-
-        self.dashboard_get = dashboard_get
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class APIImplementation -
-
-
-
- -Expand source code - -
class APIImplementation(APIInterface):
-    def __init__(self):
-        super().__init__()
-
-        async def dashboard_get(
-            options: APIOptions, user_context: Dict[str, Any]
-        ) -> str:
-            bundle_base_path_string = (
-                await options.recipe_implementation.get_dashboard_bundle_location(
-                    user_context
-                )
-            )
-            bundle_domain = (
-                NormalisedURLDomain(bundle_base_path_string).get_as_string_dangerous()
-                + NormalisedURLPath(bundle_base_path_string).get_as_string_dangerous()
-            )
-
-            connection_uri = ""
-            super_tokens_instance = Supertokens.get_instance()
-            auth_mode = options.config.auth_mode
-            connection_uri = (
-                NormalisedURLDomain(
-                    super_tokens_instance.supertokens_config.connection_uri.split(";")[
-                        0
-                    ]
-                ).get_as_string_dangerous()
-                + NormalisedURLPath(
-                    super_tokens_instance.supertokens_config.connection_uri.split(";")[
-                        0
-                    ]
-                ).get_as_string_dangerous()
-            )
-
-            dashboard_path = options.app_info.api_base_path.append(
-                NormalisedURLPath(DASHBOARD_API)
-            ).get_as_string_dangerous()
-
-            is_search_enabled: bool = False
-            querier = Querier.get_instance(options.recipe_id)
-            cdiVersion = await querier.get_api_version(user_context)
-            if not cdiVersion:
-                is_search_enabled = True
-            elif is_version_gte(cdiVersion, "2.20"):
-                is_search_enabled = True
-
-            return Template(
-                dedent(
-                    """
-                <html>
-                    <head>
-                        <meta name="viewport" content="width=device-width, initial-scale=1.0">
-                        <script>
-                            window.staticBasePath = "${bundleDomain}/static"
-                            window.dashboardAppPath = "${dashboardPath}"
-                            window.connectionURI = "${connectionURI}"
-                            window.authMode = "${authMode}"
-                            window.isSearchEnabled = "${isSearchEnabled}"
-                        </script>
-                        <script defer src="${bundleDomain}/static/js/bundle.js"></script></head>
-                        <link href="${bundleDomain}/static/css/main.css" rel="stylesheet" type="text/css">
-                        <link rel="icon" type="image/x-icon" href="${bundleDomain}/static/media/favicon.ico">
-                    </head>
-                    <body>
-                        <noscript>You need to enable JavaScript to run this app.</noscript>
-                        <div id="root"></div>
-                    </body>
-                </html>
-                """
-                )
-            ).substitute(
-                bundleDomain=bundle_domain,
-                dashboardPath=dashboard_path,
-                connectionURI=connection_uri,
-                authMode=auth_mode,
-                isSearchEnabled=str(is_search_enabled).lower(),
-            )
-
-        self.dashboard_get = dashboard_get
-
-

Ancestors

- -
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/dashboard/api/index.html b/html/supertokens_python/recipe/dashboard/api/index.html deleted file mode 100644 index b64b53d69..000000000 --- a/html/supertokens_python/recipe/dashboard/api/index.html +++ /dev/null @@ -1,1174 +0,0 @@ - - - - - - -supertokens_python.recipe.dashboard.api API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.dashboard.api

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from .analytics import handle_analytics_post
-from .api_key_protector import api_key_protector
-from .dashboard import handle_dashboard_api
-from .search.getTags import handle_get_tags
-from .signin import handle_emailpassword_signin_api
-from .signout import handle_emailpassword_signout_api
-from .userdetails.user_delete import handle_user_delete
-from .userdetails.user_email_verify_get import handle_user_email_verify_get
-from .userdetails.user_email_verify_put import handle_user_email_verify_put
-from .userdetails.user_email_verify_token_post import handle_email_verify_token_post
-from .userdetails.user_get import handle_user_get
-from .userdetails.user_metadata_get import handle_metadata_get
-from .userdetails.user_metadata_put import handle_metadata_put
-from .userdetails.user_password_put import handle_user_password_put
-from .userdetails.user_put import handle_user_put
-from .userdetails.user_sessions_get import handle_sessions_get
-from .userdetails.user_sessions_post import handle_user_sessions_post
-from .users_count_get import handle_users_count_get_api
-from .users_get import handle_users_get_api
-from .validate_key import handle_validate_key_api
-from .list_tenants import handle_list_tenants_api
-
-__all__ = [
-    "handle_dashboard_api",
-    "api_key_protector",
-    "handle_users_count_get_api",
-    "handle_users_get_api",
-    "handle_validate_key_api",
-    "handle_user_email_verify_get",
-    "handle_user_get",
-    "handle_metadata_get",
-    "handle_sessions_get",
-    "handle_user_delete",
-    "handle_user_put",
-    "handle_user_email_verify_put",
-    "handle_metadata_put",
-    "handle_user_sessions_post",
-    "handle_user_password_put",
-    "handle_email_verify_token_post",
-    "handle_emailpassword_signin_api",
-    "handle_emailpassword_signout_api",
-    "handle_get_tags",
-    "handle_analytics_post",
-    "handle_list_tenants_api",
-]
-
-
-
-

Sub-modules

-
-
supertokens_python.recipe.dashboard.api.analytics
-
-
-
-
supertokens_python.recipe.dashboard.api.dashboard
-
-
-
-
supertokens_python.recipe.dashboard.api.implementation
-
-
-
-
supertokens_python.recipe.dashboard.api.list_tenants
-
-
-
-
supertokens_python.recipe.dashboard.api.search
-
-
-
-
supertokens_python.recipe.dashboard.api.signin
-
-
-
-
supertokens_python.recipe.dashboard.api.signout
-
-
-
-
supertokens_python.recipe.dashboard.api.userdetails
-
-
-
-
supertokens_python.recipe.dashboard.api.users_count_get
-
-
-
-
supertokens_python.recipe.dashboard.api.users_get
-
-
-
-
supertokens_python.recipe.dashboard.api.validate_key
-
-
-
-
-
-
-
-
-

Functions

-
-
-async def api_key_protector(api_implementation: APIInterface, tenant_id: str, api_options: APIOptions, api_function: Callable[[APIInterface, str, APIOptions, Dict[str, Any]], Awaitable[APIResponse]], user_context: Dict[str, Any]) ‑> Optional[BaseResponse] -
-
-
-
- -Expand source code - -
async def api_key_protector(
-    api_implementation: APIInterface,
-    tenant_id: str,
-    api_options: APIOptions,
-    api_function: Callable[
-        [APIInterface, str, APIOptions, Dict[str, Any]], Awaitable[APIResponse]
-    ],
-    user_context: Dict[str, Any],
-) -> Optional[BaseResponse]:
-    should_allow_access = False
-
-    try:
-        should_allow_access = (
-            await api_options.recipe_implementation.should_allow_access(
-                api_options.request, api_options.config, user_context
-            )
-        )
-    except DashboardOperationNotAllowedError as _:
-        return send_non_200_response_with_message(
-            "You are not permitted to perform this operation",
-            403,
-            api_options.response,
-        )
-
-    if should_allow_access is False:
-        return send_non_200_response_with_message(
-            "Unauthorised access", 401, api_options.response
-        )
-
-    response = await api_function(
-        api_implementation, tenant_id, api_options, user_context
-    )
-    return send_200_response(response.to_json(), api_options.response)
-
-
-
-async def handle_analytics_post(_: APIInterface, _tenant_id: str, api_options: APIOptions, _user_context: Dict[str, Any]) ‑> AnalyticsResponse -
-
-
-
- -Expand source code - -
async def handle_analytics_post(
-    _: APIInterface,
-    _tenant_id: str,
-    api_options: APIOptions,
-    _user_context: Dict[str, Any],
-) -> AnalyticsResponse:
-    if not Supertokens.get_instance().telemetry:
-        return AnalyticsResponse()
-    body = await api_options.request.json()
-    if body is None:
-        raise_bad_input_exception("Please send body")
-    email = body.get("email")
-    dashboard_version = body.get("dashboardVersion")
-
-    if email is None:
-        raise_bad_input_exception("Missing required property 'email'")
-    if dashboard_version is None:
-        raise_bad_input_exception("Missing required property 'dashboardVersion'")
-
-    telemetry_id = None
-
-    try:
-        response = await Querier.get_instance().send_get_request(
-            NormalisedURLPath("/telemetry"),
-            None,
-            _user_context,
-        )
-        if "exists" in response and response["exists"] and "telemetryId" in response:
-            telemetry_id = response["telemetryId"]
-
-        number_of_users = await Supertokens.get_instance().get_user_count(
-            include_recipe_ids=None
-        )
-
-    except Exception as __:
-        # If either telemetry id API or user count fetch fails, no event should be sent
-        return AnalyticsResponse()
-
-    apiDomain, websiteDomain, appName = (
-        api_options.app_info.api_domain,
-        api_options.app_info.get_origin(api_options.request, {}),
-        api_options.app_info.app_name,
-    )
-
-    data = {
-        "websiteDomain": websiteDomain.get_as_string_dangerous(),
-        "apiDomain": apiDomain.get_as_string_dangerous(),
-        "appName": appName,
-        "sdk": "python",
-        "sdkVersion": SDKVersion,
-        "numberOfUsers": number_of_users,
-        "email": email,
-        "dashboardVersion": dashboard_version,
-    }
-
-    if telemetry_id is not None:
-        data["telemetryId"] = telemetry_id
-
-    try:
-        async with AsyncClient(timeout=30.0) as client:
-            await client.post(  # type: ignore
-                url=TELEMETRY_SUPERTOKENS_API_URL,
-                json=data,
-                headers={"api-version": TELEMETRY_SUPERTOKENS_API_VERSION},
-            )
-    except Exception as __:
-        # If telemetry event fails, no error should be thrown
-        pass
-
-    return AnalyticsResponse()
-
-
-
-async def handle_dashboard_api(api_implementation: APIInterface, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Optional[BaseResponse] -
-
-
-
- -Expand source code - -
async def handle_dashboard_api(
-    api_implementation: APIInterface,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-) -> Optional[BaseResponse]:
-    if api_implementation.dashboard_get is None:
-        return None
-
-    html_str = await api_implementation.dashboard_get(api_options, user_context)
-
-    api_options.response.set_html_content(html_str)
-    return api_options.response
-
-
-
-async def handle_email_verify_token_post(_api_interface: APIInterface, tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[UserEmailVerifyTokenPostAPIOkResponseUserEmailVerifyTokenPostAPIEmailAlreadyVerifiedErrorResponse] -
-
-
-
- -Expand source code - -
async def handle_email_verify_token_post(
-    _api_interface: APIInterface,
-    tenant_id: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-) -> Union[
-    UserEmailVerifyTokenPostAPIOkResponse,
-    UserEmailVerifyTokenPostAPIEmailAlreadyVerifiedErrorResponse,
-]:
-    request_body: Dict[str, Any] = await api_options.request.json()  # type: ignore
-    user_id = request_body.get("userId")
-
-    if user_id is None or not isinstance(user_id, str):
-        raise_bad_input_exception(
-            "Required parameter 'userId' is missing or has an invalid type"
-        )
-
-    res = await send_email_verification_email(
-        tenant_id=tenant_id, user_id=user_id, email=None, user_context=user_context
-    )
-
-    if isinstance(res, SendEmailVerificationEmailAlreadyVerifiedError):
-        return UserEmailVerifyTokenPostAPIEmailAlreadyVerifiedErrorResponse()
-
-    return UserEmailVerifyTokenPostAPIOkResponse()
-
-
-
-async def handle_emailpassword_signin_api(_: APIInterface, api_options: APIOptions, _user_context: Dict[str, Any]) -
-
-
-
- -Expand source code - -
async def handle_emailpassword_signin_api(
-    _: APIInterface, api_options: APIOptions, _user_context: Dict[str, Any]
-):
-    body = await api_options.request.json()
-    if body is None:
-        raise_bad_input_exception("Please send body")
-    email = body.get("email")
-    password = body.get("password")
-
-    if email is None or not isinstance(email, str):
-        raise_bad_input_exception("Missing required parameter 'email'")
-    if password is None or not isinstance(password, str):
-        raise_bad_input_exception("Missing required parameter 'password'")
-    response = await Querier.get_instance().send_post_request(
-        NormalisedURLPath("/recipe/dashboard/signin"),
-        {"email": email, "password": password},
-        user_context=_user_context,
-    )
-
-    if "status" in response and response["status"] == "OK":
-        return send_200_response(
-            {"status": "OK", "sessionId": response["sessionId"]}, api_options.response
-        )
-    if "status" in response and response["status"] == "INVALID_CREDENTIALS_ERROR":
-        return send_200_response(
-            {"status": "INVALID_CREDENTIALS_ERROR"},
-            api_options.response,
-        )
-    if "status" in response and response["status"] == "USER_SUSPENDED_ERROR":
-        return send_200_response(
-            {"status": "USER_SUSPENDED_ERROR", "message": response["message"]},
-            api_options.response,
-        )
-
-
-
-async def handle_emailpassword_signout_api(_: APIInterface, _tenant_id: str, api_options: APIOptions, _user_context: Dict[str, Any]) ‑> SignOutOK -
-
-
-
- -Expand source code - -
async def handle_emailpassword_signout_api(
-    _: APIInterface,
-    _tenant_id: str,
-    api_options: APIOptions,
-    _user_context: Dict[str, Any],
-) -> SignOutOK:
-    if api_options.config.auth_mode == "api-key":
-        return SignOutOK()
-    session_id_form_auth_header = api_options.request.get_header("authorization")
-    if not session_id_form_auth_header:
-        return raise_bad_input_exception(
-            "Neither 'API Key' nor 'Authorization' header was found"
-        )
-    session_id_form_auth_header = session_id_form_auth_header.split()[1]
-    await Querier.get_instance().send_delete_request(
-        NormalisedURLPath("/recipe/dashboard/session"),
-        {"sessionId": session_id_form_auth_header},
-        user_context=_user_context,
-    )
-    return SignOutOK()
-
-
-
-async def handle_get_tags(_: APIInterface, _tenant_id: str, __: APIOptions, _user_context: Dict[str, Any]) ‑> SearchTagsOK -
-
-
-
- -Expand source code - -
async def handle_get_tags(
-    _: APIInterface, _tenant_id: str, __: APIOptions, _user_context: Dict[str, Any]
-) -> SearchTagsOK:
-    response = await Querier.get_instance().send_get_request(
-        NormalisedURLPath("/user/search/tags"), None, _user_context
-    )
-    return SearchTagsOK(tags=response["tags"])
-
-
-
-async def handle_list_tenants_api(_api_implementation: APIInterface, _tenant_id: str, _api_options: APIOptions, user_context: Dict[str, Any]) ‑> APIResponse -
-
-
-
- -Expand source code - -
async def handle_list_tenants_api(
-    _api_implementation: APIInterface,
-    _tenant_id: str,
-    _api_options: APIOptions,
-    user_context: Dict[str, Any],
-) -> APIResponse:
-    tenants = await list_all_tenants(user_context)
-
-    final_tenants: List[DashboardListTenantItem] = []
-
-    for current_tenant in tenants.tenants:
-        dashboard_tenant = DashboardListTenantItem(
-            tenant_id=current_tenant.tenant_id,
-            emailpassword=current_tenant.emailpassword,
-            passwordless=current_tenant.passwordless,
-            third_party=current_tenant.third_party,
-        )
-        final_tenants.append(dashboard_tenant)
-
-    return DashboardListTenantsGetResponse(final_tenants)
-
-
-
-async def handle_metadata_get(_api_interface: APIInterface, _tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[UserMetadataGetAPIOkResponseFeatureNotEnabledError] -
-
-
-
- -Expand source code - -
async def handle_metadata_get(
-    _api_interface: APIInterface,
-    _tenant_id: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-) -> Union[UserMetadataGetAPIOkResponse, FeatureNotEnabledError]:
-    user_id = api_options.request.get_query_param("userId")
-
-    if user_id is None:
-        raise_bad_input_exception("Missing required parameter 'userId'")
-
-    try:
-        UserMetadataRecipe.get_instance()
-    except Exception:
-        return FeatureNotEnabledError()
-
-    metadata_response = await get_user_metadata(user_id, user_context=user_context)
-    return UserMetadataGetAPIOkResponse(metadata_response.metadata)
-
-
-
-async def handle_metadata_put(_api_interface: APIInterface, _tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> UserMetadataPutAPIResponse -
-
-
-
- -Expand source code - -
async def handle_metadata_put(
-    _api_interface: APIInterface,
-    _tenant_id: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-) -> UserMetadataPutAPIResponse:
-    request_body: Dict[str, Any] = await api_options.request.json()  # type: ignore
-    user_id = request_body.get("userId")
-    data = request_body.get("data")
-
-    # This is to throw an error early in case the recipe has not been initialised
-    UserMetadataRecipe.get_instance()
-
-    if user_id is None or isinstance(user_id, str) is False:
-        raise_bad_input_exception(
-            "Required parameter 'userId' is missing or has an invalid type"
-        )
-
-    if data is None or isinstance(data, str) is False:
-        raise_bad_input_exception(
-            "Required parameter 'data' is missing or has an invalid type"
-        )
-
-    parsed_data: Dict[str, Any] = {}
-    try:
-        parsed_data = json.loads(data)
-        if not isinstance(parsed_data, dict):  # type: ignore
-            raise Exception()
-
-    except Exception:
-        raise_bad_input_exception("'data' must be a valid JSON body")
-
-    # This API is meant to set the user metadata of a user. We delete the existing data
-    # before updating it because we want to make sure that shallow merging does not result
-    # in the data being incorrect
-    #
-    # For example if the old data is {test: "test", test2: "test2"} and the user wants to delete
-    # test2 from the data simply calling updateUserMetadata with {test: "test"} would not remove
-    # test2 because of shallow merging.
-    #
-    # Removing first ensures that the final data is exactly what the user wanted it to be
-
-    await clear_user_metadata(user_id, user_context)
-    await update_user_metadata(user_id, parsed_data, user_context)
-
-    return UserMetadataPutAPIResponse()
-
-
-
-async def handle_sessions_get(_api_interface: APIInterface, _tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> UserSessionsGetAPIResponse -
-
-
-
- -Expand source code - -
async def handle_sessions_get(
-    _api_interface: APIInterface,
-    _tenant_id: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-) -> UserSessionsGetAPIResponse:
-    user_id = api_options.request.get_query_param("userId")
-
-    if user_id is None:
-        raise_bad_input_exception("Missing required parameter 'userId'")
-
-    # Passing tenant id as None sets fetch_across_all_tenants to True
-    # which is what we want here.
-    session_handles = await get_all_session_handles_for_user(
-        user_id, None, user_context
-    )
-    sessions: List[Optional[SessionInfo]] = [None for _ in session_handles]
-
-    async def call_(i: int, session_handle: str):
-        try:
-            session_response = await get_session_information(
-                session_handle, user_context
-            )
-            if session_response is not None:
-                sessions[i] = SessionInfo(session_response)
-        except Exception:
-            sessions[i] = None
-
-    session_info_promises = [
-        call_(i, handle) for i, handle in enumerate(session_handles)
-    ]
-
-    await asyncio.gather(*session_info_promises)
-
-    return UserSessionsGetAPIResponse([s for s in sessions if s is not None])
-
-
-
-async def handle_user_delete(_api_interface: APIInterface, _tenant_id: str, api_options: APIOptions, _user_context: Dict[str, Any]) ‑> UserDeleteAPIResponse -
-
-
-
- -Expand source code - -
async def handle_user_delete(
-    _api_interface: APIInterface,
-    _tenant_id: str,
-    api_options: APIOptions,
-    _user_context: Dict[str, Any],
-) -> UserDeleteAPIResponse:
-    user_id = api_options.request.get_query_param("userId")
-
-    if user_id is None:
-        raise_bad_input_exception("Missing required parameter 'userId'")
-
-    await Supertokens.get_instance().delete_user(user_id, _user_context)
-
-    return UserDeleteAPIResponse()
-
-
-
-async def handle_user_email_verify_get(_api_interface: APIInterface, _tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[UserEmailVerifyGetAPIResponseFeatureNotEnabledError] -
-
-
-
- -Expand source code - -
async def handle_user_email_verify_get(
-    _api_interface: APIInterface,
-    _tenant_id: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-) -> Union[UserEmailVerifyGetAPIResponse, FeatureNotEnabledError]:
-    req = api_options.request
-    user_id = req.get_query_param("userId")
-
-    if user_id is None:
-        raise_bad_input_exception("Missing required parameter 'userId'")
-
-    try:
-        EmailVerificationRecipe.get_instance()
-    except Exception:
-        return FeatureNotEnabledError()
-
-    is_verified = await is_email_verified(user_id, user_context=user_context)
-    return UserEmailVerifyGetAPIResponse(is_verified)
-
-
-
-async def handle_user_email_verify_put(_api_interface: APIInterface, tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> UserEmailVerifyPutAPIResponse -
-
-
-
- -Expand source code - -
async def handle_user_email_verify_put(
-    _api_interface: APIInterface,
-    tenant_id: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-) -> UserEmailVerifyPutAPIResponse:
-    request_body: Dict[str, Any] = await api_options.request.json()  # type: ignore
-    user_id = request_body.get("userId")
-    verified = request_body.get("verified")
-
-    if user_id is None or not isinstance(user_id, str):
-        raise_bad_input_exception(
-            "Required parameter 'userId' is missing or has an invalid type"
-        )
-
-    if verified is None or not isinstance(verified, bool):
-        raise_bad_input_exception(
-            "Required parameter 'verified' is missing or has an invalid type"
-        )
-
-    if verified:
-        token_response = await create_email_verification_token(
-            tenant_id=tenant_id, user_id=user_id, email=None, user_context=user_context
-        )
-
-        if isinstance(
-            token_response, CreateEmailVerificationTokenEmailAlreadyVerifiedError
-        ):
-            return UserEmailVerifyPutAPIResponse()
-
-        verify_response = await verify_email_using_token(
-            tenant_id=tenant_id, token=token_response.token, user_context=user_context
-        )
-
-        if isinstance(verify_response, VerifyEmailUsingTokenInvalidTokenError):
-            # This should never happen because we consume the token immediately after creating it
-            raise Exception("Should not come here")
-
-    else:
-        await unverify_email(user_id, user_context=user_context)
-
-    return UserEmailVerifyPutAPIResponse()
-
-
-
-async def handle_user_get(_api_interface: APIInterface, _tenant_id: str, api_options: APIOptions, _user_context: Dict[str, Any]) ‑> Union[UserGetAPINoUserFoundErrorUserGetAPIOkResponseUserGetAPIRecipeNotInitialisedError] -
-
-
-
- -Expand source code - -
async def handle_user_get(
-    _api_interface: APIInterface,
-    _tenant_id: str,
-    api_options: APIOptions,
-    _user_context: Dict[str, Any],
-) -> Union[
-    UserGetAPINoUserFoundError,
-    UserGetAPIOkResponse,
-    UserGetAPIRecipeNotInitialisedError,
-]:
-    user_id = api_options.request.get_query_param("userId")
-    recipe_id = api_options.request.get_query_param("recipeId")
-
-    if user_id is None:
-        raise_bad_input_exception("Missing required parameter 'userId'")
-
-    if recipe_id is None:
-        raise_bad_input_exception("Missing required parameter 'recipeId'")
-
-    if not is_valid_recipe_id(recipe_id):
-        raise_bad_input_exception("Invalid recipe id")
-
-    if not is_recipe_initialised(recipe_id):
-        return UserGetAPIRecipeNotInitialisedError()
-
-    user_response = await get_user_for_recipe_id(user_id, recipe_id)
-    if user_response is None:
-        return UserGetAPINoUserFoundError()
-
-    user = user_response.user
-
-    try:
-        UserMetadataRecipe.get_instance()
-    except Exception:
-        user.first_name = "FEATURE_NOT_ENABLED"
-        user.last_name = "FEATURE_NOT_ENABLED"
-
-        return UserGetAPIOkResponse(recipe_id, user)
-
-    user_metadata = await get_user_metadata(user_id, user_context=_user_context)
-    first_name = user_metadata.metadata.get("first_name", "")
-    last_name = user_metadata.metadata.get("last_name", "")
-
-    user.first_name = first_name
-    user.last_name = last_name
-
-    return UserGetAPIOkResponse(recipe_id, user)
-
-
-
-async def handle_user_password_put(_api_interface: APIInterface, tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[UserPasswordPutAPIResponseUserPasswordPutAPIInvalidPasswordErrorResponse] -
-
-
-
- -Expand source code - -
async def handle_user_password_put(
-    _api_interface: APIInterface,
-    tenant_id: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-) -> Union[UserPasswordPutAPIResponse, UserPasswordPutAPIInvalidPasswordErrorResponse]:
-    request_body: Dict[str, Any] = await api_options.request.json()  # type: ignore
-    user_id = request_body.get("userId")
-    new_password = request_body.get("newPassword")
-
-    if user_id is None or not isinstance(user_id, str):
-        raise_bad_input_exception("Missing required parameter 'userId'")
-
-    if new_password is None or not isinstance(new_password, str):
-        raise_bad_input_exception("Missing required parameter 'newPassword'")
-
-    async def reset_password(
-        form_fields: List[NormalisedFormField],
-        create_reset_password_token: Callable[
-            [str, str, Dict[str, Any]],
-            Awaitable[
-                Union[CreateResetPasswordOkResult, CreateResetPasswordWrongUserIdError]
-            ],
-        ],
-        reset_password_using_token: Callable[
-            [str, str, str, Dict[str, Any]],
-            Awaitable[
-                Union[
-                    ResetPasswordUsingTokenOkResult,
-                    ResetPasswordUsingTokenInvalidTokenError,
-                ]
-            ],
-        ],
-    ) -> Union[
-        UserPasswordPutAPIResponse, UserPasswordPutAPIInvalidPasswordErrorResponse
-    ]:
-        password_form_field = [
-            field for field in form_fields if field.id == FORM_FIELD_PASSWORD_ID
-        ][0]
-
-        password_validation_error = await password_form_field.validate(
-            new_password, tenant_id
-        )
-
-        if password_validation_error is not None:
-            return UserPasswordPutAPIInvalidPasswordErrorResponse(
-                password_validation_error
-            )
-
-        password_reset_token = await create_reset_password_token(
-            tenant_id, user_id, user_context
-        )
-
-        if isinstance(password_reset_token, CreateResetPasswordWrongUserIdError):
-            # Techincally it can but its an edge case so we assume that it wont
-            # UNKNOWN_USER_ID_ERROR
-            raise Exception("Should never come here")
-
-        password_reset_response = await reset_password_using_token(
-            tenant_id, password_reset_token.token, new_password, user_context
-        )
-
-        if isinstance(
-            password_reset_response, ResetPasswordUsingTokenInvalidTokenError
-        ):
-            # RESET_PASSWORD_INVALID_TOKEN_ERROR
-            raise Exception("Should not come here")
-
-        return UserPasswordPutAPIResponse()
-
-    return await reset_password(
-        EmailPasswordRecipe.get_instance().config.sign_up_feature.form_fields,
-        ep_create_reset_password_token,
-        ep_reset_password_using_token,
-    )
-
-
-
-async def handle_user_put(_api_interface: APIInterface, tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[UserPutAPIOkResponseUserPutAPIInvalidEmailErrorResponseUserPutAPIEmailAlreadyExistsErrorResponseUserPutAPIInvalidPhoneErrorResponseUserPutPhoneAlreadyExistsAPIResponse] -
-
-
-
- -Expand source code - -
async def handle_user_put(
-    _api_interface: APIInterface,
-    tenant_id: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-) -> Union[
-    UserPutAPIOkResponse,
-    UserPutAPIInvalidEmailErrorResponse,
-    UserPutAPIEmailAlreadyExistsErrorResponse,
-    UserPutAPIInvalidPhoneErrorResponse,
-    UserPutPhoneAlreadyExistsAPIResponse,
-]:
-    request_body: Dict[str, Any] = await api_options.request.json()  # type: ignore
-    user_id: Optional[str] = request_body.get("userId")
-    recipe_id: Optional[str] = request_body.get("recipeId")
-    first_name: Optional[str] = request_body.get("firstName")
-    last_name: Optional[str] = request_body.get("lastName")
-    email: Optional[str] = request_body.get("email")
-    phone: Optional[str] = request_body.get("phone")
-
-    if not isinstance(user_id, str):
-        return raise_bad_input_exception(
-            "Required parameter 'userId' is missing or has an invalid type"
-        )
-
-    if not isinstance(recipe_id, str):
-        return raise_bad_input_exception(
-            "Required parameter 'recipeId' is missing or has an invalid type"
-        )
-
-    if not is_valid_recipe_id(recipe_id):
-        raise_bad_input_exception("Invalid recipe id")
-
-    if first_name is None and not isinstance(first_name, str):
-        raise_bad_input_exception(
-            "Required parameter 'firstName' is missing or has an invalid type"
-        )
-
-    if last_name is None and not isinstance(last_name, str):
-        raise_bad_input_exception(
-            "Required parameter 'lastName' is missing or has an invalid type"
-        )
-
-    if email is None and not isinstance(email, str):
-        raise_bad_input_exception(
-            "Required parameter 'email' is missing or has an invalid type"
-        )
-
-    if phone is None and not isinstance(phone, str):
-        raise_bad_input_exception(
-            "Required parameter 'phone' is missing or has an invalid type"
-        )
-
-    user_response = await get_user_for_recipe_id(user_id, recipe_id)
-
-    if user_response is None:
-        raise Exception("Should never come here")
-
-    first_name = first_name.strip()
-    last_name = last_name.strip()
-    email = email.strip()
-    phone = phone.strip()
-
-    if first_name != "" or last_name != "":
-        is_recipe_initialized = False
-
-        try:
-            UserMetadataRecipe.get_instance()
-            is_recipe_initialized = True
-        except Exception:
-            pass
-
-        if is_recipe_initialized:
-            metadata_update: Dict[str, Any] = {}
-
-            if first_name != "":
-                metadata_update["first_name"] = first_name
-
-            if last_name != "":
-                metadata_update["last_name"] = last_name
-
-            await update_user_metadata(user_id, metadata_update, user_context)
-
-    if email != "":
-        email_update_response = await update_email_for_recipe_id(
-            user_response.recipe, user_id, email, tenant_id, user_context
-        )
-
-        if not isinstance(email_update_response, UserPutAPIOkResponse):
-            return email_update_response
-
-    if phone != "":
-        phone_update_response = await update_phone_for_recipe_id(
-            user_response.recipe, user_id, phone, tenant_id, user_context
-        )
-
-        if not isinstance(phone_update_response, UserPutAPIOkResponse):
-            return phone_update_response
-
-    return UserPutAPIOkResponse()
-
-
-
-async def handle_user_sessions_post(_api_interface: APIInterface, _tenant_id: str, api_options: APIOptions, _user_context: Dict[str, Any]) ‑> UserSessionsPostAPIResponse -
-
-
-
- -Expand source code - -
async def handle_user_sessions_post(
-    _api_interface: APIInterface,
-    _tenant_id: str,
-    api_options: APIOptions,
-    _user_context: Dict[str, Any],
-) -> UserSessionsPostAPIResponse:
-    request_body = await api_options.request.json()  # type: ignore
-    session_handles: Optional[List[str]] = request_body.get("sessionHandles")  # type: ignore
-
-    if not isinstance(session_handles, list):
-        raise_bad_input_exception(
-            "Required parameter 'sessionHandles' is missing or has an invalid type"
-        )
-
-    await revoke_multiple_sessions(session_handles, _user_context)
-    return UserSessionsPostAPIResponse()
-
-
-
-async def handle_users_count_get_api(_: APIInterface, tenant_id: str, _api_options: APIOptions, _user_context: Dict[str, Any]) ‑> UserCountGetAPIResponse -
-
-
-
- -Expand source code - -
async def handle_users_count_get_api(
-    _: APIInterface,
-    tenant_id: str,
-    _api_options: APIOptions,
-    _user_context: Dict[str, Any],
-) -> UserCountGetAPIResponse:
-    count = await Supertokens.get_instance().get_user_count(
-        None,
-        tenant_id,
-    )
-    return UserCountGetAPIResponse(count=count)
-
-
-
-async def handle_users_get_api(api_implementation: APIInterface, tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> APIResponse -
-
-
-
- -Expand source code - -
async def handle_users_get_api(
-    api_implementation: APIInterface,
-    tenant_id: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-) -> APIResponse:
-    _ = api_implementation
-
-    limit = api_options.request.get_query_param("limit")
-    if limit is None:
-        raise_bad_input_exception("Missing required parameter 'limit'")
-
-    time_joined_order: Literal["ASC", "DESC"] = api_options.request.get_query_param(  # type: ignore
-        "timeJoinedOrder", "DESC"
-    )
-    if time_joined_order not in ["ASC", "DESC"]:
-        raise_bad_input_exception("Invalid value recieved for 'timeJoinedOrder'")
-
-    pagination_token = api_options.request.get_query_param("paginationToken")
-
-    users_response = await Supertokens.get_instance().get_users(
-        tenant_id,
-        time_joined_order=time_joined_order,
-        limit=int(limit),
-        pagination_token=pagination_token,
-        include_recipe_ids=None,
-        query=api_options.request.get_query_params(),
-        user_context=user_context,
-    )
-
-    # user metadata bulk fetch with batches:
-
-    try:
-        UserMetadataRecipe.get_instance()
-    except GeneralError:
-        return DashboardUsersGetResponse(
-            users_response.users, users_response.next_pagination_token
-        )
-
-    users_with_metadata: List[UserWithMetadata] = [
-        UserWithMetadata().from_user(user) for user in users_response.users
-    ]
-    metadata_fetch_awaitables: List[Awaitable[Any]] = []
-
-    async def get_user_metadata_and_update_user(user_idx: int) -> None:
-        user = users_response.users[user_idx]
-        user_metadata = await get_user_metadata(user.user_id, user_context)
-        first_name = user_metadata.metadata.get("first_name")
-        last_name = user_metadata.metadata.get("last_name")
-
-        # None becomes null which is acceptable for the dashboard.
-        users_with_metadata[user_idx].first_name = first_name
-        users_with_metadata[user_idx].last_name = last_name
-
-    # Batch calls to get user metadata:
-    for i, _ in enumerate(users_response.users):
-        metadata_fetch_awaitables.append(get_user_metadata_and_update_user(i))
-
-    promise_arr_start_position = 0
-    batch_size = 5
-
-    while promise_arr_start_position < len(metadata_fetch_awaitables):
-        # We want to query only 5 in parallel at a time
-        promises_to_call = [
-            metadata_fetch_awaitables[i]
-            for i in range(
-                promise_arr_start_position,
-                min(
-                    promise_arr_start_position + batch_size,
-                    len(metadata_fetch_awaitables),
-                ),
-            )
-        ]
-        await asyncio.gather(*promises_to_call)
-
-        promise_arr_start_position += batch_size
-
-    return DashboardUsersGetResponse(
-        users_with_metadata,
-        users_response.next_pagination_token,
-    )
-
-
-
-async def handle_validate_key_api(_api_implementation: APIInterface, api_options: APIOptions, user_context: Dict[str, Any]) -
-
-
-
- -Expand source code - -
async def handle_validate_key_api(
-    _api_implementation: APIInterface,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-):
-
-    is_valid_key = await validate_api_key(
-        api_options.request, api_options.config, user_context
-    )
-
-    if is_valid_key:
-        return send_200_response({"status": "OK"}, api_options.response)
-    return send_non_200_response_with_message("Unauthorised", 401, api_options.response)
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/dashboard/api/list_tenants.html b/html/supertokens_python/recipe/dashboard/api/list_tenants.html deleted file mode 100644 index aff46e79b..000000000 --- a/html/supertokens_python/recipe/dashboard/api/list_tenants.html +++ /dev/null @@ -1,150 +0,0 @@ - - - - - - -supertokens_python.recipe.dashboard.api.list_tenants API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.dashboard.api.list_tenants

-
-
-
- -Expand source code - -
# Copyright (c) 2023, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from __future__ import annotations
-
-from typing import TYPE_CHECKING, Any, Dict, List
-
-from supertokens_python.recipe.dashboard.interfaces import DashboardListTenantItem
-
-if TYPE_CHECKING:
-    from supertokens_python.recipe.dashboard.interfaces import (
-        APIOptions,
-        APIInterface,
-    )
-    from supertokens_python.types import APIResponse
-
-from supertokens_python.recipe.multitenancy.asyncio import list_all_tenants
-from supertokens_python.recipe.dashboard.interfaces import (
-    DashboardListTenantsGetResponse,
-)
-
-
-async def handle_list_tenants_api(
-    _api_implementation: APIInterface,
-    _tenant_id: str,
-    _api_options: APIOptions,
-    user_context: Dict[str, Any],
-) -> APIResponse:
-    tenants = await list_all_tenants(user_context)
-
-    final_tenants: List[DashboardListTenantItem] = []
-
-    for current_tenant in tenants.tenants:
-        dashboard_tenant = DashboardListTenantItem(
-            tenant_id=current_tenant.tenant_id,
-            emailpassword=current_tenant.emailpassword,
-            passwordless=current_tenant.passwordless,
-            third_party=current_tenant.third_party,
-        )
-        final_tenants.append(dashboard_tenant)
-
-    return DashboardListTenantsGetResponse(final_tenants)
-
-
-
-
-
-
-
-

Functions

-
-
-async def handle_list_tenants_api(_api_implementation: APIInterface, _tenant_id: str, _api_options: APIOptions, user_context: Dict[str, Any]) ‑> APIResponse -
-
-
-
- -Expand source code - -
async def handle_list_tenants_api(
-    _api_implementation: APIInterface,
-    _tenant_id: str,
-    _api_options: APIOptions,
-    user_context: Dict[str, Any],
-) -> APIResponse:
-    tenants = await list_all_tenants(user_context)
-
-    final_tenants: List[DashboardListTenantItem] = []
-
-    for current_tenant in tenants.tenants:
-        dashboard_tenant = DashboardListTenantItem(
-            tenant_id=current_tenant.tenant_id,
-            emailpassword=current_tenant.emailpassword,
-            passwordless=current_tenant.passwordless,
-            third_party=current_tenant.third_party,
-        )
-        final_tenants.append(dashboard_tenant)
-
-    return DashboardListTenantsGetResponse(final_tenants)
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/dashboard/api/search/getTags.html b/html/supertokens_python/recipe/dashboard/api/search/getTags.html deleted file mode 100644 index cefc743c7..000000000 --- a/html/supertokens_python/recipe/dashboard/api/search/getTags.html +++ /dev/null @@ -1,116 +0,0 @@ - - - - - - -supertokens_python.recipe.dashboard.api.search.getTags API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.dashboard.api.search.getTags

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from typing import TYPE_CHECKING, Dict, Any
-
-if TYPE_CHECKING:
-    from supertokens_python.recipe.dashboard.interfaces import APIInterface, APIOptions
-
-from supertokens_python.normalised_url_path import NormalisedURLPath
-from supertokens_python.querier import Querier
-from supertokens_python.recipe.dashboard.interfaces import SearchTagsOK
-
-
-async def handle_get_tags(
-    _: APIInterface, _tenant_id: str, __: APIOptions, _user_context: Dict[str, Any]
-) -> SearchTagsOK:
-    response = await Querier.get_instance().send_get_request(
-        NormalisedURLPath("/user/search/tags"), None, _user_context
-    )
-    return SearchTagsOK(tags=response["tags"])
-
-
-
-
-
-
-
-

Functions

-
-
-async def handle_get_tags(_: APIInterface, _tenant_id: str, __: APIOptions, _user_context: Dict[str, Any]) ‑> SearchTagsOK -
-
-
-
- -Expand source code - -
async def handle_get_tags(
-    _: APIInterface, _tenant_id: str, __: APIOptions, _user_context: Dict[str, Any]
-) -> SearchTagsOK:
-    response = await Querier.get_instance().send_get_request(
-        NormalisedURLPath("/user/search/tags"), None, _user_context
-    )
-    return SearchTagsOK(tags=response["tags"])
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/dashboard/api/search/index.html b/html/supertokens_python/recipe/dashboard/api/search/index.html deleted file mode 100644 index 1ef92da40..000000000 --- a/html/supertokens_python/recipe/dashboard/api/search/index.html +++ /dev/null @@ -1,65 +0,0 @@ - - - - - - -supertokens_python.recipe.dashboard.api.search API documentation - - - - - - - - - - - -
- - -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/dashboard/api/signin.html b/html/supertokens_python/recipe/dashboard/api/signin.html deleted file mode 100644 index 508a1f47f..000000000 --- a/html/supertokens_python/recipe/dashboard/api/signin.html +++ /dev/null @@ -1,169 +0,0 @@ - - - - - - -supertokens_python.recipe.dashboard.api.signin API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.dashboard.api.signin

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from typing import TYPE_CHECKING, Dict, Any
-
-if TYPE_CHECKING:
-    from supertokens_python.recipe.dashboard.interfaces import APIInterface, APIOptions
-
-from supertokens_python.exceptions import raise_bad_input_exception
-from supertokens_python.normalised_url_path import NormalisedURLPath
-from supertokens_python.querier import Querier
-from supertokens_python.utils import send_200_response
-
-
-async def handle_emailpassword_signin_api(
-    _: APIInterface, api_options: APIOptions, _user_context: Dict[str, Any]
-):
-    body = await api_options.request.json()
-    if body is None:
-        raise_bad_input_exception("Please send body")
-    email = body.get("email")
-    password = body.get("password")
-
-    if email is None or not isinstance(email, str):
-        raise_bad_input_exception("Missing required parameter 'email'")
-    if password is None or not isinstance(password, str):
-        raise_bad_input_exception("Missing required parameter 'password'")
-    response = await Querier.get_instance().send_post_request(
-        NormalisedURLPath("/recipe/dashboard/signin"),
-        {"email": email, "password": password},
-        user_context=_user_context,
-    )
-
-    if "status" in response and response["status"] == "OK":
-        return send_200_response(
-            {"status": "OK", "sessionId": response["sessionId"]}, api_options.response
-        )
-    if "status" in response and response["status"] == "INVALID_CREDENTIALS_ERROR":
-        return send_200_response(
-            {"status": "INVALID_CREDENTIALS_ERROR"},
-            api_options.response,
-        )
-    if "status" in response and response["status"] == "USER_SUSPENDED_ERROR":
-        return send_200_response(
-            {"status": "USER_SUSPENDED_ERROR", "message": response["message"]},
-            api_options.response,
-        )
-
-
-
-
-
-
-
-

Functions

-
-
-async def handle_emailpassword_signin_api(_: APIInterface, api_options: APIOptions, _user_context: Dict[str, Any]) -
-
-
-
- -Expand source code - -
async def handle_emailpassword_signin_api(
-    _: APIInterface, api_options: APIOptions, _user_context: Dict[str, Any]
-):
-    body = await api_options.request.json()
-    if body is None:
-        raise_bad_input_exception("Please send body")
-    email = body.get("email")
-    password = body.get("password")
-
-    if email is None or not isinstance(email, str):
-        raise_bad_input_exception("Missing required parameter 'email'")
-    if password is None or not isinstance(password, str):
-        raise_bad_input_exception("Missing required parameter 'password'")
-    response = await Querier.get_instance().send_post_request(
-        NormalisedURLPath("/recipe/dashboard/signin"),
-        {"email": email, "password": password},
-        user_context=_user_context,
-    )
-
-    if "status" in response and response["status"] == "OK":
-        return send_200_response(
-            {"status": "OK", "sessionId": response["sessionId"]}, api_options.response
-        )
-    if "status" in response and response["status"] == "INVALID_CREDENTIALS_ERROR":
-        return send_200_response(
-            {"status": "INVALID_CREDENTIALS_ERROR"},
-            api_options.response,
-        )
-    if "status" in response and response["status"] == "USER_SUSPENDED_ERROR":
-        return send_200_response(
-            {"status": "USER_SUSPENDED_ERROR", "message": response["message"]},
-            api_options.response,
-        )
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/dashboard/api/signout.html b/html/supertokens_python/recipe/dashboard/api/signout.html deleted file mode 100644 index ed886c577..000000000 --- a/html/supertokens_python/recipe/dashboard/api/signout.html +++ /dev/null @@ -1,144 +0,0 @@ - - - - - - -supertokens_python.recipe.dashboard.api.signout API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.dashboard.api.signout

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from typing import TYPE_CHECKING, Dict, Any
-
-if TYPE_CHECKING:
-    from supertokens_python.recipe.dashboard.interfaces import APIInterface, APIOptions
-
-from supertokens_python.exceptions import raise_bad_input_exception
-from supertokens_python.normalised_url_path import NormalisedURLPath
-from supertokens_python.querier import Querier
-
-from ..interfaces import SignOutOK
-
-
-async def handle_emailpassword_signout_api(
-    _: APIInterface,
-    _tenant_id: str,
-    api_options: APIOptions,
-    _user_context: Dict[str, Any],
-) -> SignOutOK:
-    if api_options.config.auth_mode == "api-key":
-        return SignOutOK()
-    session_id_form_auth_header = api_options.request.get_header("authorization")
-    if not session_id_form_auth_header:
-        return raise_bad_input_exception(
-            "Neither 'API Key' nor 'Authorization' header was found"
-        )
-    session_id_form_auth_header = session_id_form_auth_header.split()[1]
-    await Querier.get_instance().send_delete_request(
-        NormalisedURLPath("/recipe/dashboard/session"),
-        {"sessionId": session_id_form_auth_header},
-        user_context=_user_context,
-    )
-    return SignOutOK()
-
-
-
-
-
-
-
-

Functions

-
-
-async def handle_emailpassword_signout_api(_: APIInterface, _tenant_id: str, api_options: APIOptions, _user_context: Dict[str, Any]) ‑> SignOutOK -
-
-
-
- -Expand source code - -
async def handle_emailpassword_signout_api(
-    _: APIInterface,
-    _tenant_id: str,
-    api_options: APIOptions,
-    _user_context: Dict[str, Any],
-) -> SignOutOK:
-    if api_options.config.auth_mode == "api-key":
-        return SignOutOK()
-    session_id_form_auth_header = api_options.request.get_header("authorization")
-    if not session_id_form_auth_header:
-        return raise_bad_input_exception(
-            "Neither 'API Key' nor 'Authorization' header was found"
-        )
-    session_id_form_auth_header = session_id_form_auth_header.split()[1]
-    await Querier.get_instance().send_delete_request(
-        NormalisedURLPath("/recipe/dashboard/session"),
-        {"sessionId": session_id_form_auth_header},
-        user_context=_user_context,
-    )
-    return SignOutOK()
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/dashboard/api/userdetails/index.html b/html/supertokens_python/recipe/dashboard/api/userdetails/index.html deleted file mode 100644 index dc3547f9f..000000000 --- a/html/supertokens_python/recipe/dashboard/api/userdetails/index.html +++ /dev/null @@ -1,115 +0,0 @@ - - - - - - -supertokens_python.recipe.dashboard.api.userdetails API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.dashboard.api.userdetails

-
-
-
-
-

Sub-modules

-
-
supertokens_python.recipe.dashboard.api.userdetails.user_delete
-
-
-
-
supertokens_python.recipe.dashboard.api.userdetails.user_email_verify_get
-
-
-
-
supertokens_python.recipe.dashboard.api.userdetails.user_email_verify_put
-
-
-
-
supertokens_python.recipe.dashboard.api.userdetails.user_email_verify_token_post
-
-
-
-
supertokens_python.recipe.dashboard.api.userdetails.user_get
-
-
-
-
supertokens_python.recipe.dashboard.api.userdetails.user_metadata_get
-
-
-
-
supertokens_python.recipe.dashboard.api.userdetails.user_metadata_put
-
-
-
-
supertokens_python.recipe.dashboard.api.userdetails.user_password_put
-
-
-
-
supertokens_python.recipe.dashboard.api.userdetails.user_put
-
-
-
-
supertokens_python.recipe.dashboard.api.userdetails.user_sessions_get
-
-
-
-
supertokens_python.recipe.dashboard.api.userdetails.user_sessions_post
-
-
-
-
-
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/dashboard/api/userdetails/user_delete.html b/html/supertokens_python/recipe/dashboard/api/userdetails/user_delete.html deleted file mode 100644 index 0bb3f956f..000000000 --- a/html/supertokens_python/recipe/dashboard/api/userdetails/user_delete.html +++ /dev/null @@ -1,112 +0,0 @@ - - - - - - -supertokens_python.recipe.dashboard.api.userdetails.user_delete API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.dashboard.api.userdetails.user_delete

-
-
-
- -Expand source code - -
from typing import Any, Dict
-
-from ...interfaces import APIInterface, APIOptions, UserDeleteAPIResponse
-from supertokens_python.exceptions import raise_bad_input_exception
-from supertokens_python import Supertokens
-
-
-async def handle_user_delete(
-    _api_interface: APIInterface,
-    _tenant_id: str,
-    api_options: APIOptions,
-    _user_context: Dict[str, Any],
-) -> UserDeleteAPIResponse:
-    user_id = api_options.request.get_query_param("userId")
-
-    if user_id is None:
-        raise_bad_input_exception("Missing required parameter 'userId'")
-
-    await Supertokens.get_instance().delete_user(user_id, _user_context)
-
-    return UserDeleteAPIResponse()
-
-
-
-
-
-
-
-

Functions

-
-
-async def handle_user_delete(_api_interface: APIInterface, _tenant_id: str, api_options: APIOptions, _user_context: Dict[str, Any]) ‑> UserDeleteAPIResponse -
-
-
-
- -Expand source code - -
async def handle_user_delete(
-    _api_interface: APIInterface,
-    _tenant_id: str,
-    api_options: APIOptions,
-    _user_context: Dict[str, Any],
-) -> UserDeleteAPIResponse:
-    user_id = api_options.request.get_query_param("userId")
-
-    if user_id is None:
-        raise_bad_input_exception("Missing required parameter 'userId'")
-
-    await Supertokens.get_instance().delete_user(user_id, _user_context)
-
-    return UserDeleteAPIResponse()
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/dashboard/api/userdetails/user_email_verify_get.html b/html/supertokens_python/recipe/dashboard/api/userdetails/user_email_verify_get.html deleted file mode 100644 index bb41398b2..000000000 --- a/html/supertokens_python/recipe/dashboard/api/userdetails/user_email_verify_get.html +++ /dev/null @@ -1,128 +0,0 @@ - - - - - - -supertokens_python.recipe.dashboard.api.userdetails.user_email_verify_get API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.dashboard.api.userdetails.user_email_verify_get

-
-
-
- -Expand source code - -
from supertokens_python.exceptions import raise_bad_input_exception
-from supertokens_python.recipe.emailverification import EmailVerificationRecipe
-from supertokens_python.recipe.emailverification.asyncio import is_email_verified
-from ...interfaces import (
-    APIInterface,
-    APIOptions,
-    UserEmailVerifyGetAPIResponse,
-    FeatureNotEnabledError,
-)
-
-from typing import Union, Dict, Any
-
-
-async def handle_user_email_verify_get(
-    _api_interface: APIInterface,
-    _tenant_id: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-) -> Union[UserEmailVerifyGetAPIResponse, FeatureNotEnabledError]:
-    req = api_options.request
-    user_id = req.get_query_param("userId")
-
-    if user_id is None:
-        raise_bad_input_exception("Missing required parameter 'userId'")
-
-    try:
-        EmailVerificationRecipe.get_instance()
-    except Exception:
-        return FeatureNotEnabledError()
-
-    is_verified = await is_email_verified(user_id, user_context=user_context)
-    return UserEmailVerifyGetAPIResponse(is_verified)
-
-
-
-
-
-
-
-

Functions

-
-
-async def handle_user_email_verify_get(_api_interface: APIInterface, _tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[UserEmailVerifyGetAPIResponseFeatureNotEnabledError] -
-
-
-
- -Expand source code - -
async def handle_user_email_verify_get(
-    _api_interface: APIInterface,
-    _tenant_id: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-) -> Union[UserEmailVerifyGetAPIResponse, FeatureNotEnabledError]:
-    req = api_options.request
-    user_id = req.get_query_param("userId")
-
-    if user_id is None:
-        raise_bad_input_exception("Missing required parameter 'userId'")
-
-    try:
-        EmailVerificationRecipe.get_instance()
-    except Exception:
-        return FeatureNotEnabledError()
-
-    is_verified = await is_email_verified(user_id, user_context=user_context)
-    return UserEmailVerifyGetAPIResponse(is_verified)
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/dashboard/api/userdetails/user_email_verify_put.html b/html/supertokens_python/recipe/dashboard/api/userdetails/user_email_verify_put.html deleted file mode 100644 index a3dfba940..000000000 --- a/html/supertokens_python/recipe/dashboard/api/userdetails/user_email_verify_put.html +++ /dev/null @@ -1,181 +0,0 @@ - - - - - - -supertokens_python.recipe.dashboard.api.userdetails.user_email_verify_put API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.dashboard.api.userdetails.user_email_verify_put

-
-
-
- -Expand source code - -
from typing import Any, Dict
-
-from supertokens_python.exceptions import raise_bad_input_exception
-from supertokens_python.recipe.emailverification.asyncio import (
-    create_email_verification_token,
-    unverify_email,
-    verify_email_using_token,
-)
-from supertokens_python.recipe.emailverification.interfaces import (
-    CreateEmailVerificationTokenEmailAlreadyVerifiedError,
-    VerifyEmailUsingTokenInvalidTokenError,
-)
-
-from ...interfaces import (
-    APIInterface,
-    APIOptions,
-    UserEmailVerifyPutAPIResponse,
-)
-
-
-async def handle_user_email_verify_put(
-    _api_interface: APIInterface,
-    tenant_id: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-) -> UserEmailVerifyPutAPIResponse:
-    request_body: Dict[str, Any] = await api_options.request.json()  # type: ignore
-    user_id = request_body.get("userId")
-    verified = request_body.get("verified")
-
-    if user_id is None or not isinstance(user_id, str):
-        raise_bad_input_exception(
-            "Required parameter 'userId' is missing or has an invalid type"
-        )
-
-    if verified is None or not isinstance(verified, bool):
-        raise_bad_input_exception(
-            "Required parameter 'verified' is missing or has an invalid type"
-        )
-
-    if verified:
-        token_response = await create_email_verification_token(
-            tenant_id=tenant_id, user_id=user_id, email=None, user_context=user_context
-        )
-
-        if isinstance(
-            token_response, CreateEmailVerificationTokenEmailAlreadyVerifiedError
-        ):
-            return UserEmailVerifyPutAPIResponse()
-
-        verify_response = await verify_email_using_token(
-            tenant_id=tenant_id, token=token_response.token, user_context=user_context
-        )
-
-        if isinstance(verify_response, VerifyEmailUsingTokenInvalidTokenError):
-            # This should never happen because we consume the token immediately after creating it
-            raise Exception("Should not come here")
-
-    else:
-        await unverify_email(user_id, user_context=user_context)
-
-    return UserEmailVerifyPutAPIResponse()
-
-
-
-
-
-
-
-

Functions

-
-
-async def handle_user_email_verify_put(_api_interface: APIInterface, tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> UserEmailVerifyPutAPIResponse -
-
-
-
- -Expand source code - -
async def handle_user_email_verify_put(
-    _api_interface: APIInterface,
-    tenant_id: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-) -> UserEmailVerifyPutAPIResponse:
-    request_body: Dict[str, Any] = await api_options.request.json()  # type: ignore
-    user_id = request_body.get("userId")
-    verified = request_body.get("verified")
-
-    if user_id is None or not isinstance(user_id, str):
-        raise_bad_input_exception(
-            "Required parameter 'userId' is missing or has an invalid type"
-        )
-
-    if verified is None or not isinstance(verified, bool):
-        raise_bad_input_exception(
-            "Required parameter 'verified' is missing or has an invalid type"
-        )
-
-    if verified:
-        token_response = await create_email_verification_token(
-            tenant_id=tenant_id, user_id=user_id, email=None, user_context=user_context
-        )
-
-        if isinstance(
-            token_response, CreateEmailVerificationTokenEmailAlreadyVerifiedError
-        ):
-            return UserEmailVerifyPutAPIResponse()
-
-        verify_response = await verify_email_using_token(
-            tenant_id=tenant_id, token=token_response.token, user_context=user_context
-        )
-
-        if isinstance(verify_response, VerifyEmailUsingTokenInvalidTokenError):
-            # This should never happen because we consume the token immediately after creating it
-            raise Exception("Should not come here")
-
-    else:
-        await unverify_email(user_id, user_context=user_context)
-
-    return UserEmailVerifyPutAPIResponse()
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/dashboard/api/userdetails/user_email_verify_token_post.html b/html/supertokens_python/recipe/dashboard/api/userdetails/user_email_verify_token_post.html deleted file mode 100644 index 985e177e7..000000000 --- a/html/supertokens_python/recipe/dashboard/api/userdetails/user_email_verify_token_post.html +++ /dev/null @@ -1,143 +0,0 @@ - - - - - - -supertokens_python.recipe.dashboard.api.userdetails.user_email_verify_token_post API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.dashboard.api.userdetails.user_email_verify_token_post

-
-
-
- -Expand source code - -
from typing import Any, Dict, Union
-
-from supertokens_python.exceptions import raise_bad_input_exception
-from supertokens_python.recipe.emailverification.asyncio import (
-    send_email_verification_email,
-    SendEmailVerificationEmailAlreadyVerifiedError,
-)
-
-from ...interfaces import (
-    APIInterface,
-    APIOptions,
-    UserEmailVerifyTokenPostAPIOkResponse,
-    UserEmailVerifyTokenPostAPIEmailAlreadyVerifiedErrorResponse,
-)
-
-
-async def handle_email_verify_token_post(
-    _api_interface: APIInterface,
-    tenant_id: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-) -> Union[
-    UserEmailVerifyTokenPostAPIOkResponse,
-    UserEmailVerifyTokenPostAPIEmailAlreadyVerifiedErrorResponse,
-]:
-    request_body: Dict[str, Any] = await api_options.request.json()  # type: ignore
-    user_id = request_body.get("userId")
-
-    if user_id is None or not isinstance(user_id, str):
-        raise_bad_input_exception(
-            "Required parameter 'userId' is missing or has an invalid type"
-        )
-
-    res = await send_email_verification_email(
-        tenant_id=tenant_id, user_id=user_id, email=None, user_context=user_context
-    )
-
-    if isinstance(res, SendEmailVerificationEmailAlreadyVerifiedError):
-        return UserEmailVerifyTokenPostAPIEmailAlreadyVerifiedErrorResponse()
-
-    return UserEmailVerifyTokenPostAPIOkResponse()
-
-
-
-
-
-
-
-

Functions

-
-
-async def handle_email_verify_token_post(_api_interface: APIInterface, tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[UserEmailVerifyTokenPostAPIOkResponseUserEmailVerifyTokenPostAPIEmailAlreadyVerifiedErrorResponse] -
-
-
-
- -Expand source code - -
async def handle_email_verify_token_post(
-    _api_interface: APIInterface,
-    tenant_id: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-) -> Union[
-    UserEmailVerifyTokenPostAPIOkResponse,
-    UserEmailVerifyTokenPostAPIEmailAlreadyVerifiedErrorResponse,
-]:
-    request_body: Dict[str, Any] = await api_options.request.json()  # type: ignore
-    user_id = request_body.get("userId")
-
-    if user_id is None or not isinstance(user_id, str):
-        raise_bad_input_exception(
-            "Required parameter 'userId' is missing or has an invalid type"
-        )
-
-    res = await send_email_verification_email(
-        tenant_id=tenant_id, user_id=user_id, email=None, user_context=user_context
-    )
-
-    if isinstance(res, SendEmailVerificationEmailAlreadyVerifiedError):
-        return UserEmailVerifyTokenPostAPIEmailAlreadyVerifiedErrorResponse()
-
-    return UserEmailVerifyTokenPostAPIOkResponse()
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/dashboard/api/userdetails/user_get.html b/html/supertokens_python/recipe/dashboard/api/userdetails/user_get.html deleted file mode 100644 index fb168d95a..000000000 --- a/html/supertokens_python/recipe/dashboard/api/userdetails/user_get.html +++ /dev/null @@ -1,188 +0,0 @@ - - - - - - -supertokens_python.recipe.dashboard.api.userdetails.user_get API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.dashboard.api.userdetails.user_get

-
-
-
- -Expand source code - -
from typing import Union, Dict, Any
-
-from supertokens_python.exceptions import raise_bad_input_exception
-from supertokens_python.recipe.dashboard.utils import get_user_for_recipe_id
-from supertokens_python.recipe.usermetadata import UserMetadataRecipe
-from supertokens_python.recipe.usermetadata.asyncio import get_user_metadata
-
-from ...interfaces import (
-    APIInterface,
-    APIOptions,
-    UserGetAPINoUserFoundError,
-    UserGetAPIOkResponse,
-    UserGetAPIRecipeNotInitialisedError,
-)
-from ...utils import is_recipe_initialised, is_valid_recipe_id
-
-
-async def handle_user_get(
-    _api_interface: APIInterface,
-    _tenant_id: str,
-    api_options: APIOptions,
-    _user_context: Dict[str, Any],
-) -> Union[
-    UserGetAPINoUserFoundError,
-    UserGetAPIOkResponse,
-    UserGetAPIRecipeNotInitialisedError,
-]:
-    user_id = api_options.request.get_query_param("userId")
-    recipe_id = api_options.request.get_query_param("recipeId")
-
-    if user_id is None:
-        raise_bad_input_exception("Missing required parameter 'userId'")
-
-    if recipe_id is None:
-        raise_bad_input_exception("Missing required parameter 'recipeId'")
-
-    if not is_valid_recipe_id(recipe_id):
-        raise_bad_input_exception("Invalid recipe id")
-
-    if not is_recipe_initialised(recipe_id):
-        return UserGetAPIRecipeNotInitialisedError()
-
-    user_response = await get_user_for_recipe_id(user_id, recipe_id)
-    if user_response is None:
-        return UserGetAPINoUserFoundError()
-
-    user = user_response.user
-
-    try:
-        UserMetadataRecipe.get_instance()
-    except Exception:
-        user.first_name = "FEATURE_NOT_ENABLED"
-        user.last_name = "FEATURE_NOT_ENABLED"
-
-        return UserGetAPIOkResponse(recipe_id, user)
-
-    user_metadata = await get_user_metadata(user_id, user_context=_user_context)
-    first_name = user_metadata.metadata.get("first_name", "")
-    last_name = user_metadata.metadata.get("last_name", "")
-
-    user.first_name = first_name
-    user.last_name = last_name
-
-    return UserGetAPIOkResponse(recipe_id, user)
-
-
-
-
-
-
-
-

Functions

-
-
-async def handle_user_get(_api_interface: APIInterface, _tenant_id: str, api_options: APIOptions, _user_context: Dict[str, Any]) ‑> Union[UserGetAPINoUserFoundErrorUserGetAPIOkResponseUserGetAPIRecipeNotInitialisedError] -
-
-
-
- -Expand source code - -
async def handle_user_get(
-    _api_interface: APIInterface,
-    _tenant_id: str,
-    api_options: APIOptions,
-    _user_context: Dict[str, Any],
-) -> Union[
-    UserGetAPINoUserFoundError,
-    UserGetAPIOkResponse,
-    UserGetAPIRecipeNotInitialisedError,
-]:
-    user_id = api_options.request.get_query_param("userId")
-    recipe_id = api_options.request.get_query_param("recipeId")
-
-    if user_id is None:
-        raise_bad_input_exception("Missing required parameter 'userId'")
-
-    if recipe_id is None:
-        raise_bad_input_exception("Missing required parameter 'recipeId'")
-
-    if not is_valid_recipe_id(recipe_id):
-        raise_bad_input_exception("Invalid recipe id")
-
-    if not is_recipe_initialised(recipe_id):
-        return UserGetAPIRecipeNotInitialisedError()
-
-    user_response = await get_user_for_recipe_id(user_id, recipe_id)
-    if user_response is None:
-        return UserGetAPINoUserFoundError()
-
-    user = user_response.user
-
-    try:
-        UserMetadataRecipe.get_instance()
-    except Exception:
-        user.first_name = "FEATURE_NOT_ENABLED"
-        user.last_name = "FEATURE_NOT_ENABLED"
-
-        return UserGetAPIOkResponse(recipe_id, user)
-
-    user_metadata = await get_user_metadata(user_id, user_context=_user_context)
-    first_name = user_metadata.metadata.get("first_name", "")
-    last_name = user_metadata.metadata.get("last_name", "")
-
-    user.first_name = first_name
-    user.last_name = last_name
-
-    return UserGetAPIOkResponse(recipe_id, user)
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/dashboard/api/userdetails/user_metadata_get.html b/html/supertokens_python/recipe/dashboard/api/userdetails/user_metadata_get.html deleted file mode 100644 index e7cd69a44..000000000 --- a/html/supertokens_python/recipe/dashboard/api/userdetails/user_metadata_get.html +++ /dev/null @@ -1,125 +0,0 @@ - - - - - - -supertokens_python.recipe.dashboard.api.userdetails.user_metadata_get API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.dashboard.api.userdetails.user_metadata_get

-
-
-
- -Expand source code - -
from ...interfaces import (
-    APIInterface,
-    APIOptions,
-    FeatureNotEnabledError,
-    UserMetadataGetAPIOkResponse,
-)
-from supertokens_python.exceptions import raise_bad_input_exception
-from supertokens_python.recipe.usermetadata import UserMetadataRecipe
-from supertokens_python.recipe.usermetadata.asyncio import get_user_metadata
-from typing import Union, Dict, Any
-
-
-async def handle_metadata_get(
-    _api_interface: APIInterface,
-    _tenant_id: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-) -> Union[UserMetadataGetAPIOkResponse, FeatureNotEnabledError]:
-    user_id = api_options.request.get_query_param("userId")
-
-    if user_id is None:
-        raise_bad_input_exception("Missing required parameter 'userId'")
-
-    try:
-        UserMetadataRecipe.get_instance()
-    except Exception:
-        return FeatureNotEnabledError()
-
-    metadata_response = await get_user_metadata(user_id, user_context=user_context)
-    return UserMetadataGetAPIOkResponse(metadata_response.metadata)
-
-
-
-
-
-
-
-

Functions

-
-
-async def handle_metadata_get(_api_interface: APIInterface, _tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[UserMetadataGetAPIOkResponseFeatureNotEnabledError] -
-
-
-
- -Expand source code - -
async def handle_metadata_get(
-    _api_interface: APIInterface,
-    _tenant_id: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-) -> Union[UserMetadataGetAPIOkResponse, FeatureNotEnabledError]:
-    user_id = api_options.request.get_query_param("userId")
-
-    if user_id is None:
-        raise_bad_input_exception("Missing required parameter 'userId'")
-
-    try:
-        UserMetadataRecipe.get_instance()
-    except Exception:
-        return FeatureNotEnabledError()
-
-    metadata_response = await get_user_metadata(user_id, user_context=user_context)
-    return UserMetadataGetAPIOkResponse(metadata_response.metadata)
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/dashboard/api/userdetails/user_metadata_put.html b/html/supertokens_python/recipe/dashboard/api/userdetails/user_metadata_put.html deleted file mode 100644 index 985ea2cfe..000000000 --- a/html/supertokens_python/recipe/dashboard/api/userdetails/user_metadata_put.html +++ /dev/null @@ -1,182 +0,0 @@ - - - - - - -supertokens_python.recipe.dashboard.api.userdetails.user_metadata_put API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.dashboard.api.userdetails.user_metadata_put

-
-
-
- -Expand source code - -
import json
-from typing import Any, Dict
-
-from supertokens_python.exceptions import raise_bad_input_exception
-from supertokens_python.recipe.usermetadata import UserMetadataRecipe
-from supertokens_python.recipe.usermetadata.asyncio import (
-    clear_user_metadata,
-    update_user_metadata,
-)
-
-from ...interfaces import APIInterface, APIOptions, UserMetadataPutAPIResponse
-
-
-async def handle_metadata_put(
-    _api_interface: APIInterface,
-    _tenant_id: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-) -> UserMetadataPutAPIResponse:
-    request_body: Dict[str, Any] = await api_options.request.json()  # type: ignore
-    user_id = request_body.get("userId")
-    data = request_body.get("data")
-
-    # This is to throw an error early in case the recipe has not been initialised
-    UserMetadataRecipe.get_instance()
-
-    if user_id is None or isinstance(user_id, str) is False:
-        raise_bad_input_exception(
-            "Required parameter 'userId' is missing or has an invalid type"
-        )
-
-    if data is None or isinstance(data, str) is False:
-        raise_bad_input_exception(
-            "Required parameter 'data' is missing or has an invalid type"
-        )
-
-    parsed_data: Dict[str, Any] = {}
-    try:
-        parsed_data = json.loads(data)
-        if not isinstance(parsed_data, dict):  # type: ignore
-            raise Exception()
-
-    except Exception:
-        raise_bad_input_exception("'data' must be a valid JSON body")
-
-    # This API is meant to set the user metadata of a user. We delete the existing data
-    # before updating it because we want to make sure that shallow merging does not result
-    # in the data being incorrect
-    #
-    # For example if the old data is {test: "test", test2: "test2"} and the user wants to delete
-    # test2 from the data simply calling updateUserMetadata with {test: "test"} would not remove
-    # test2 because of shallow merging.
-    #
-    # Removing first ensures that the final data is exactly what the user wanted it to be
-
-    await clear_user_metadata(user_id, user_context)
-    await update_user_metadata(user_id, parsed_data, user_context)
-
-    return UserMetadataPutAPIResponse()
-
-
-
-
-
-
-
-

Functions

-
-
-async def handle_metadata_put(_api_interface: APIInterface, _tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> UserMetadataPutAPIResponse -
-
-
-
- -Expand source code - -
async def handle_metadata_put(
-    _api_interface: APIInterface,
-    _tenant_id: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-) -> UserMetadataPutAPIResponse:
-    request_body: Dict[str, Any] = await api_options.request.json()  # type: ignore
-    user_id = request_body.get("userId")
-    data = request_body.get("data")
-
-    # This is to throw an error early in case the recipe has not been initialised
-    UserMetadataRecipe.get_instance()
-
-    if user_id is None or isinstance(user_id, str) is False:
-        raise_bad_input_exception(
-            "Required parameter 'userId' is missing or has an invalid type"
-        )
-
-    if data is None or isinstance(data, str) is False:
-        raise_bad_input_exception(
-            "Required parameter 'data' is missing or has an invalid type"
-        )
-
-    parsed_data: Dict[str, Any] = {}
-    try:
-        parsed_data = json.loads(data)
-        if not isinstance(parsed_data, dict):  # type: ignore
-            raise Exception()
-
-    except Exception:
-        raise_bad_input_exception("'data' must be a valid JSON body")
-
-    # This API is meant to set the user metadata of a user. We delete the existing data
-    # before updating it because we want to make sure that shallow merging does not result
-    # in the data being incorrect
-    #
-    # For example if the old data is {test: "test", test2: "test2"} and the user wants to delete
-    # test2 from the data simply calling updateUserMetadata with {test: "test"} would not remove
-    # test2 because of shallow merging.
-    #
-    # Removing first ensures that the final data is exactly what the user wanted it to be
-
-    await clear_user_metadata(user_id, user_context)
-    await update_user_metadata(user_id, parsed_data, user_context)
-
-    return UserMetadataPutAPIResponse()
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/dashboard/api/userdetails/user_password_put.html b/html/supertokens_python/recipe/dashboard/api/userdetails/user_password_put.html deleted file mode 100644 index 28da6cca0..000000000 --- a/html/supertokens_python/recipe/dashboard/api/userdetails/user_password_put.html +++ /dev/null @@ -1,256 +0,0 @@ - - - - - - -supertokens_python.recipe.dashboard.api.userdetails.user_password_put API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.dashboard.api.userdetails.user_password_put

-
-
-
- -Expand source code - -
from typing import Any, Callable, Dict, List, Union
-
-from supertokens_python.exceptions import raise_bad_input_exception
-from supertokens_python.recipe.emailpassword import EmailPasswordRecipe
-from supertokens_python.recipe.emailpassword.asyncio import (
-    create_reset_password_token as ep_create_reset_password_token,
-)
-from supertokens_python.recipe.emailpassword.asyncio import (
-    reset_password_using_token as ep_reset_password_using_token,
-)
-from supertokens_python.recipe.emailpassword.constants import FORM_FIELD_PASSWORD_ID
-from supertokens_python.recipe.emailpassword.interfaces import (
-    CreateResetPasswordOkResult,
-    CreateResetPasswordWrongUserIdError,
-    ResetPasswordUsingTokenInvalidTokenError,
-    ResetPasswordUsingTokenOkResult,
-)
-from supertokens_python.recipe.emailpassword.types import NormalisedFormField
-
-from supertokens_python.utils import Awaitable
-
-from ...interfaces import (
-    APIInterface,
-    APIOptions,
-    UserPasswordPutAPIInvalidPasswordErrorResponse,
-    UserPasswordPutAPIResponse,
-)
-
-
-async def handle_user_password_put(
-    _api_interface: APIInterface,
-    tenant_id: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-) -> Union[UserPasswordPutAPIResponse, UserPasswordPutAPIInvalidPasswordErrorResponse]:
-    request_body: Dict[str, Any] = await api_options.request.json()  # type: ignore
-    user_id = request_body.get("userId")
-    new_password = request_body.get("newPassword")
-
-    if user_id is None or not isinstance(user_id, str):
-        raise_bad_input_exception("Missing required parameter 'userId'")
-
-    if new_password is None or not isinstance(new_password, str):
-        raise_bad_input_exception("Missing required parameter 'newPassword'")
-
-    async def reset_password(
-        form_fields: List[NormalisedFormField],
-        create_reset_password_token: Callable[
-            [str, str, Dict[str, Any]],
-            Awaitable[
-                Union[CreateResetPasswordOkResult, CreateResetPasswordWrongUserIdError]
-            ],
-        ],
-        reset_password_using_token: Callable[
-            [str, str, str, Dict[str, Any]],
-            Awaitable[
-                Union[
-                    ResetPasswordUsingTokenOkResult,
-                    ResetPasswordUsingTokenInvalidTokenError,
-                ]
-            ],
-        ],
-    ) -> Union[
-        UserPasswordPutAPIResponse, UserPasswordPutAPIInvalidPasswordErrorResponse
-    ]:
-        password_form_field = [
-            field for field in form_fields if field.id == FORM_FIELD_PASSWORD_ID
-        ][0]
-
-        password_validation_error = await password_form_field.validate(
-            new_password, tenant_id
-        )
-
-        if password_validation_error is not None:
-            return UserPasswordPutAPIInvalidPasswordErrorResponse(
-                password_validation_error
-            )
-
-        password_reset_token = await create_reset_password_token(
-            tenant_id, user_id, user_context
-        )
-
-        if isinstance(password_reset_token, CreateResetPasswordWrongUserIdError):
-            # Techincally it can but its an edge case so we assume that it wont
-            # UNKNOWN_USER_ID_ERROR
-            raise Exception("Should never come here")
-
-        password_reset_response = await reset_password_using_token(
-            tenant_id, password_reset_token.token, new_password, user_context
-        )
-
-        if isinstance(
-            password_reset_response, ResetPasswordUsingTokenInvalidTokenError
-        ):
-            # RESET_PASSWORD_INVALID_TOKEN_ERROR
-            raise Exception("Should not come here")
-
-        return UserPasswordPutAPIResponse()
-
-    return await reset_password(
-        EmailPasswordRecipe.get_instance().config.sign_up_feature.form_fields,
-        ep_create_reset_password_token,
-        ep_reset_password_using_token,
-    )
-
-
-
-
-
-
-
-

Functions

-
-
-async def handle_user_password_put(_api_interface: APIInterface, tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[UserPasswordPutAPIResponseUserPasswordPutAPIInvalidPasswordErrorResponse] -
-
-
-
- -Expand source code - -
async def handle_user_password_put(
-    _api_interface: APIInterface,
-    tenant_id: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-) -> Union[UserPasswordPutAPIResponse, UserPasswordPutAPIInvalidPasswordErrorResponse]:
-    request_body: Dict[str, Any] = await api_options.request.json()  # type: ignore
-    user_id = request_body.get("userId")
-    new_password = request_body.get("newPassword")
-
-    if user_id is None or not isinstance(user_id, str):
-        raise_bad_input_exception("Missing required parameter 'userId'")
-
-    if new_password is None or not isinstance(new_password, str):
-        raise_bad_input_exception("Missing required parameter 'newPassword'")
-
-    async def reset_password(
-        form_fields: List[NormalisedFormField],
-        create_reset_password_token: Callable[
-            [str, str, Dict[str, Any]],
-            Awaitable[
-                Union[CreateResetPasswordOkResult, CreateResetPasswordWrongUserIdError]
-            ],
-        ],
-        reset_password_using_token: Callable[
-            [str, str, str, Dict[str, Any]],
-            Awaitable[
-                Union[
-                    ResetPasswordUsingTokenOkResult,
-                    ResetPasswordUsingTokenInvalidTokenError,
-                ]
-            ],
-        ],
-    ) -> Union[
-        UserPasswordPutAPIResponse, UserPasswordPutAPIInvalidPasswordErrorResponse
-    ]:
-        password_form_field = [
-            field for field in form_fields if field.id == FORM_FIELD_PASSWORD_ID
-        ][0]
-
-        password_validation_error = await password_form_field.validate(
-            new_password, tenant_id
-        )
-
-        if password_validation_error is not None:
-            return UserPasswordPutAPIInvalidPasswordErrorResponse(
-                password_validation_error
-            )
-
-        password_reset_token = await create_reset_password_token(
-            tenant_id, user_id, user_context
-        )
-
-        if isinstance(password_reset_token, CreateResetPasswordWrongUserIdError):
-            # Techincally it can but its an edge case so we assume that it wont
-            # UNKNOWN_USER_ID_ERROR
-            raise Exception("Should never come here")
-
-        password_reset_response = await reset_password_using_token(
-            tenant_id, password_reset_token.token, new_password, user_context
-        )
-
-        if isinstance(
-            password_reset_response, ResetPasswordUsingTokenInvalidTokenError
-        ):
-            # RESET_PASSWORD_INVALID_TOKEN_ERROR
-            raise Exception("Should not come here")
-
-        return UserPasswordPutAPIResponse()
-
-    return await reset_password(
-        EmailPasswordRecipe.get_instance().config.sign_up_feature.form_fields,
-        ep_create_reset_password_token,
-        ep_reset_password_using_token,
-    )
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/dashboard/api/userdetails/user_put.html b/html/supertokens_python/recipe/dashboard/api/userdetails/user_put.html deleted file mode 100644 index e79cadae7..000000000 --- a/html/supertokens_python/recipe/dashboard/api/userdetails/user_put.html +++ /dev/null @@ -1,584 +0,0 @@ - - - - - - -supertokens_python.recipe.dashboard.api.userdetails.user_put API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.dashboard.api.userdetails.user_put

-
-
-
- -Expand source code - -
from typing import Any, Dict, Optional, Union
-
-from supertokens_python.exceptions import raise_bad_input_exception
-from supertokens_python.recipe.dashboard.utils import (
-    get_user_for_recipe_id,
-    is_valid_recipe_id,
-)
-from supertokens_python.recipe.emailpassword import EmailPasswordRecipe
-from supertokens_python.recipe.emailpassword.asyncio import (
-    update_email_or_password as ep_update_email_or_password,
-)
-from supertokens_python.recipe.emailpassword.constants import FORM_FIELD_EMAIL_ID
-from supertokens_python.recipe.emailpassword.interfaces import (
-    UpdateEmailOrPasswordEmailAlreadyExistsError,
-)
-from supertokens_python.recipe.passwordless import PasswordlessRecipe
-from supertokens_python.recipe.passwordless.asyncio import (
-    update_user as pless_update_user,
-)
-from supertokens_python.recipe.passwordless.interfaces import (
-    UpdateUserEmailAlreadyExistsError as EmailAlreadyExistsErrorResponse,
-)
-from supertokens_python.recipe.passwordless.interfaces import (
-    UpdateUserPhoneNumberAlreadyExistsError as PhoneNumberAlreadyExistsError,
-)
-from supertokens_python.recipe.passwordless.interfaces import (
-    UpdateUserUnknownUserIdError as PlessUpdateUserUnknownUserIdError,
-)
-from supertokens_python.recipe.passwordless.utils import (
-    ContactEmailOnlyConfig,
-    ContactEmailOrPhoneConfig,
-    ContactPhoneOnlyConfig,
-    default_validate_email,
-    default_validate_phone_number,
-)
-from supertokens_python.recipe.usermetadata import UserMetadataRecipe
-from supertokens_python.recipe.usermetadata.asyncio import update_user_metadata
-
-from ...interfaces import (
-    APIInterface,
-    APIOptions,
-    UserPutAPIEmailAlreadyExistsErrorResponse,
-    UserPutAPIInvalidEmailErrorResponse,
-    UserPutAPIInvalidPhoneErrorResponse,
-    UserPutAPIOkResponse,
-    UserPutPhoneAlreadyExistsAPIResponse,
-)
-
-
-async def update_email_for_recipe_id(
-    recipe_id: str,
-    user_id: str,
-    email: str,
-    tenant_id: str,
-    user_context: Dict[str, Any],
-) -> Union[
-    UserPutAPIOkResponse,
-    UserPutAPIInvalidEmailErrorResponse,
-    UserPutAPIEmailAlreadyExistsErrorResponse,
-]:
-    validation_error: Optional[str] = None
-
-    if recipe_id == "emailpassword":
-        form_fields = (
-            EmailPasswordRecipe.get_instance().config.sign_up_feature.form_fields
-        )
-        email_form_fields = [
-            form_field
-            for form_field in form_fields
-            if form_field.id == FORM_FIELD_EMAIL_ID
-        ]
-
-        validation_error = await email_form_fields[0].validate(email, tenant_id)
-
-        if validation_error is not None:
-            return UserPutAPIInvalidEmailErrorResponse(validation_error)
-
-        email_update_response = await ep_update_email_or_password(
-            user_id, email, user_context=user_context
-        )
-
-        if isinstance(
-            email_update_response, UpdateEmailOrPasswordEmailAlreadyExistsError
-        ):
-            return UserPutAPIEmailAlreadyExistsErrorResponse()
-
-        return UserPutAPIOkResponse()
-
-    if recipe_id == "passwordless":
-        validation_error = None
-
-        passwordless_config = PasswordlessRecipe.get_instance().config.contact_config
-
-        if isinstance(passwordless_config.contact_method, ContactPhoneOnlyConfig):
-            validation_error = await default_validate_email(email, tenant_id)
-
-        elif isinstance(
-            passwordless_config, (ContactEmailOnlyConfig, ContactEmailOrPhoneConfig)
-        ):
-            validation_error = await passwordless_config.validate_email_address(
-                email, tenant_id
-            )
-
-        if validation_error is not None:
-            return UserPutAPIInvalidEmailErrorResponse(validation_error)
-
-        update_result = await pless_update_user(
-            user_id, email, user_context=user_context
-        )
-
-        if isinstance(update_result, PlessUpdateUserUnknownUserIdError):
-            raise Exception("Should never come here")
-
-        if isinstance(update_result, EmailAlreadyExistsErrorResponse):
-            return UserPutAPIEmailAlreadyExistsErrorResponse()
-
-        return UserPutAPIOkResponse()
-
-    # If it comes here then the user is a third party user in which case the UI should not have allowed this
-    raise Exception("Should never come here")
-
-
-async def update_phone_for_recipe_id(
-    recipe_id: str,
-    user_id: str,
-    phone: str,
-    tenant_id: str,
-    user_context: Dict[str, Any],
-) -> Union[
-    UserPutAPIOkResponse,
-    UserPutAPIInvalidPhoneErrorResponse,
-    UserPutPhoneAlreadyExistsAPIResponse,
-]:
-    validation_error: Optional[str] = None
-
-    if recipe_id == "passwordless":
-        validation_error = None
-
-        passwordless_config = PasswordlessRecipe.get_instance().config.contact_config
-
-        if isinstance(passwordless_config, ContactEmailOnlyConfig):
-            validation_error = await default_validate_phone_number(phone, tenant_id)
-        elif isinstance(
-            passwordless_config, (ContactPhoneOnlyConfig, ContactEmailOrPhoneConfig)
-        ):
-            validation_error = await passwordless_config.validate_phone_number(
-                phone, tenant_id
-            )
-
-        if validation_error is not None:
-            return UserPutAPIInvalidPhoneErrorResponse(validation_error)
-
-        update_result = await pless_update_user(
-            user_id, phone_number=phone, user_context=user_context
-        )
-
-        if isinstance(update_result, PlessUpdateUserUnknownUserIdError):
-            raise Exception("Should never come here")
-
-        if isinstance(update_result, PhoneNumberAlreadyExistsError):
-            return UserPutPhoneAlreadyExistsAPIResponse()
-
-        return UserPutAPIOkResponse()
-
-    # If it comes here then the user is a third party user in which case the UI should not have allowed this
-    raise Exception("Should never come here")
-
-
-async def handle_user_put(
-    _api_interface: APIInterface,
-    tenant_id: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-) -> Union[
-    UserPutAPIOkResponse,
-    UserPutAPIInvalidEmailErrorResponse,
-    UserPutAPIEmailAlreadyExistsErrorResponse,
-    UserPutAPIInvalidPhoneErrorResponse,
-    UserPutPhoneAlreadyExistsAPIResponse,
-]:
-    request_body: Dict[str, Any] = await api_options.request.json()  # type: ignore
-    user_id: Optional[str] = request_body.get("userId")
-    recipe_id: Optional[str] = request_body.get("recipeId")
-    first_name: Optional[str] = request_body.get("firstName")
-    last_name: Optional[str] = request_body.get("lastName")
-    email: Optional[str] = request_body.get("email")
-    phone: Optional[str] = request_body.get("phone")
-
-    if not isinstance(user_id, str):
-        return raise_bad_input_exception(
-            "Required parameter 'userId' is missing or has an invalid type"
-        )
-
-    if not isinstance(recipe_id, str):
-        return raise_bad_input_exception(
-            "Required parameter 'recipeId' is missing or has an invalid type"
-        )
-
-    if not is_valid_recipe_id(recipe_id):
-        raise_bad_input_exception("Invalid recipe id")
-
-    if first_name is None and not isinstance(first_name, str):
-        raise_bad_input_exception(
-            "Required parameter 'firstName' is missing or has an invalid type"
-        )
-
-    if last_name is None and not isinstance(last_name, str):
-        raise_bad_input_exception(
-            "Required parameter 'lastName' is missing or has an invalid type"
-        )
-
-    if email is None and not isinstance(email, str):
-        raise_bad_input_exception(
-            "Required parameter 'email' is missing or has an invalid type"
-        )
-
-    if phone is None and not isinstance(phone, str):
-        raise_bad_input_exception(
-            "Required parameter 'phone' is missing or has an invalid type"
-        )
-
-    user_response = await get_user_for_recipe_id(user_id, recipe_id)
-
-    if user_response is None:
-        raise Exception("Should never come here")
-
-    first_name = first_name.strip()
-    last_name = last_name.strip()
-    email = email.strip()
-    phone = phone.strip()
-
-    if first_name != "" or last_name != "":
-        is_recipe_initialized = False
-
-        try:
-            UserMetadataRecipe.get_instance()
-            is_recipe_initialized = True
-        except Exception:
-            pass
-
-        if is_recipe_initialized:
-            metadata_update: Dict[str, Any] = {}
-
-            if first_name != "":
-                metadata_update["first_name"] = first_name
-
-            if last_name != "":
-                metadata_update["last_name"] = last_name
-
-            await update_user_metadata(user_id, metadata_update, user_context)
-
-    if email != "":
-        email_update_response = await update_email_for_recipe_id(
-            user_response.recipe, user_id, email, tenant_id, user_context
-        )
-
-        if not isinstance(email_update_response, UserPutAPIOkResponse):
-            return email_update_response
-
-    if phone != "":
-        phone_update_response = await update_phone_for_recipe_id(
-            user_response.recipe, user_id, phone, tenant_id, user_context
-        )
-
-        if not isinstance(phone_update_response, UserPutAPIOkResponse):
-            return phone_update_response
-
-    return UserPutAPIOkResponse()
-
-
-
-
-
-
-
-

Functions

-
-
-async def handle_user_put(_api_interface: APIInterface, tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[UserPutAPIOkResponseUserPutAPIInvalidEmailErrorResponseUserPutAPIEmailAlreadyExistsErrorResponseUserPutAPIInvalidPhoneErrorResponseUserPutPhoneAlreadyExistsAPIResponse] -
-
-
-
- -Expand source code - -
async def handle_user_put(
-    _api_interface: APIInterface,
-    tenant_id: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-) -> Union[
-    UserPutAPIOkResponse,
-    UserPutAPIInvalidEmailErrorResponse,
-    UserPutAPIEmailAlreadyExistsErrorResponse,
-    UserPutAPIInvalidPhoneErrorResponse,
-    UserPutPhoneAlreadyExistsAPIResponse,
-]:
-    request_body: Dict[str, Any] = await api_options.request.json()  # type: ignore
-    user_id: Optional[str] = request_body.get("userId")
-    recipe_id: Optional[str] = request_body.get("recipeId")
-    first_name: Optional[str] = request_body.get("firstName")
-    last_name: Optional[str] = request_body.get("lastName")
-    email: Optional[str] = request_body.get("email")
-    phone: Optional[str] = request_body.get("phone")
-
-    if not isinstance(user_id, str):
-        return raise_bad_input_exception(
-            "Required parameter 'userId' is missing or has an invalid type"
-        )
-
-    if not isinstance(recipe_id, str):
-        return raise_bad_input_exception(
-            "Required parameter 'recipeId' is missing or has an invalid type"
-        )
-
-    if not is_valid_recipe_id(recipe_id):
-        raise_bad_input_exception("Invalid recipe id")
-
-    if first_name is None and not isinstance(first_name, str):
-        raise_bad_input_exception(
-            "Required parameter 'firstName' is missing or has an invalid type"
-        )
-
-    if last_name is None and not isinstance(last_name, str):
-        raise_bad_input_exception(
-            "Required parameter 'lastName' is missing or has an invalid type"
-        )
-
-    if email is None and not isinstance(email, str):
-        raise_bad_input_exception(
-            "Required parameter 'email' is missing or has an invalid type"
-        )
-
-    if phone is None and not isinstance(phone, str):
-        raise_bad_input_exception(
-            "Required parameter 'phone' is missing or has an invalid type"
-        )
-
-    user_response = await get_user_for_recipe_id(user_id, recipe_id)
-
-    if user_response is None:
-        raise Exception("Should never come here")
-
-    first_name = first_name.strip()
-    last_name = last_name.strip()
-    email = email.strip()
-    phone = phone.strip()
-
-    if first_name != "" or last_name != "":
-        is_recipe_initialized = False
-
-        try:
-            UserMetadataRecipe.get_instance()
-            is_recipe_initialized = True
-        except Exception:
-            pass
-
-        if is_recipe_initialized:
-            metadata_update: Dict[str, Any] = {}
-
-            if first_name != "":
-                metadata_update["first_name"] = first_name
-
-            if last_name != "":
-                metadata_update["last_name"] = last_name
-
-            await update_user_metadata(user_id, metadata_update, user_context)
-
-    if email != "":
-        email_update_response = await update_email_for_recipe_id(
-            user_response.recipe, user_id, email, tenant_id, user_context
-        )
-
-        if not isinstance(email_update_response, UserPutAPIOkResponse):
-            return email_update_response
-
-    if phone != "":
-        phone_update_response = await update_phone_for_recipe_id(
-            user_response.recipe, user_id, phone, tenant_id, user_context
-        )
-
-        if not isinstance(phone_update_response, UserPutAPIOkResponse):
-            return phone_update_response
-
-    return UserPutAPIOkResponse()
-
-
-
-async def update_email_for_recipe_id(recipe_id: str, user_id: str, email: str, tenant_id: str, user_context: Dict[str, Any]) ‑> Union[UserPutAPIOkResponseUserPutAPIInvalidEmailErrorResponseUserPutAPIEmailAlreadyExistsErrorResponse] -
-
-
-
- -Expand source code - -
async def update_email_for_recipe_id(
-    recipe_id: str,
-    user_id: str,
-    email: str,
-    tenant_id: str,
-    user_context: Dict[str, Any],
-) -> Union[
-    UserPutAPIOkResponse,
-    UserPutAPIInvalidEmailErrorResponse,
-    UserPutAPIEmailAlreadyExistsErrorResponse,
-]:
-    validation_error: Optional[str] = None
-
-    if recipe_id == "emailpassword":
-        form_fields = (
-            EmailPasswordRecipe.get_instance().config.sign_up_feature.form_fields
-        )
-        email_form_fields = [
-            form_field
-            for form_field in form_fields
-            if form_field.id == FORM_FIELD_EMAIL_ID
-        ]
-
-        validation_error = await email_form_fields[0].validate(email, tenant_id)
-
-        if validation_error is not None:
-            return UserPutAPIInvalidEmailErrorResponse(validation_error)
-
-        email_update_response = await ep_update_email_or_password(
-            user_id, email, user_context=user_context
-        )
-
-        if isinstance(
-            email_update_response, UpdateEmailOrPasswordEmailAlreadyExistsError
-        ):
-            return UserPutAPIEmailAlreadyExistsErrorResponse()
-
-        return UserPutAPIOkResponse()
-
-    if recipe_id == "passwordless":
-        validation_error = None
-
-        passwordless_config = PasswordlessRecipe.get_instance().config.contact_config
-
-        if isinstance(passwordless_config.contact_method, ContactPhoneOnlyConfig):
-            validation_error = await default_validate_email(email, tenant_id)
-
-        elif isinstance(
-            passwordless_config, (ContactEmailOnlyConfig, ContactEmailOrPhoneConfig)
-        ):
-            validation_error = await passwordless_config.validate_email_address(
-                email, tenant_id
-            )
-
-        if validation_error is not None:
-            return UserPutAPIInvalidEmailErrorResponse(validation_error)
-
-        update_result = await pless_update_user(
-            user_id, email, user_context=user_context
-        )
-
-        if isinstance(update_result, PlessUpdateUserUnknownUserIdError):
-            raise Exception("Should never come here")
-
-        if isinstance(update_result, EmailAlreadyExistsErrorResponse):
-            return UserPutAPIEmailAlreadyExistsErrorResponse()
-
-        return UserPutAPIOkResponse()
-
-    # If it comes here then the user is a third party user in which case the UI should not have allowed this
-    raise Exception("Should never come here")
-
-
-
-async def update_phone_for_recipe_id(recipe_id: str, user_id: str, phone: str, tenant_id: str, user_context: Dict[str, Any]) ‑> Union[UserPutAPIOkResponseUserPutAPIInvalidPhoneErrorResponseUserPutPhoneAlreadyExistsAPIResponse] -
-
-
-
- -Expand source code - -
async def update_phone_for_recipe_id(
-    recipe_id: str,
-    user_id: str,
-    phone: str,
-    tenant_id: str,
-    user_context: Dict[str, Any],
-) -> Union[
-    UserPutAPIOkResponse,
-    UserPutAPIInvalidPhoneErrorResponse,
-    UserPutPhoneAlreadyExistsAPIResponse,
-]:
-    validation_error: Optional[str] = None
-
-    if recipe_id == "passwordless":
-        validation_error = None
-
-        passwordless_config = PasswordlessRecipe.get_instance().config.contact_config
-
-        if isinstance(passwordless_config, ContactEmailOnlyConfig):
-            validation_error = await default_validate_phone_number(phone, tenant_id)
-        elif isinstance(
-            passwordless_config, (ContactPhoneOnlyConfig, ContactEmailOrPhoneConfig)
-        ):
-            validation_error = await passwordless_config.validate_phone_number(
-                phone, tenant_id
-            )
-
-        if validation_error is not None:
-            return UserPutAPIInvalidPhoneErrorResponse(validation_error)
-
-        update_result = await pless_update_user(
-            user_id, phone_number=phone, user_context=user_context
-        )
-
-        if isinstance(update_result, PlessUpdateUserUnknownUserIdError):
-            raise Exception("Should never come here")
-
-        if isinstance(update_result, PhoneNumberAlreadyExistsError):
-            return UserPutPhoneAlreadyExistsAPIResponse()
-
-        return UserPutAPIOkResponse()
-
-    # If it comes here then the user is a third party user in which case the UI should not have allowed this
-    raise Exception("Should never come here")
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/dashboard/api/userdetails/user_sessions_get.html b/html/supertokens_python/recipe/dashboard/api/userdetails/user_sessions_get.html deleted file mode 100644 index 593e2a594..000000000 --- a/html/supertokens_python/recipe/dashboard/api/userdetails/user_sessions_get.html +++ /dev/null @@ -1,164 +0,0 @@ - - - - - - -supertokens_python.recipe.dashboard.api.userdetails.user_sessions_get API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.dashboard.api.userdetails.user_sessions_get

-
-
-
- -Expand source code - -
import asyncio
-from typing import List, Optional, Dict, Any
-
-from supertokens_python.exceptions import raise_bad_input_exception
-from supertokens_python.recipe.session.asyncio import (
-    get_all_session_handles_for_user,
-    get_session_information,
-)
-
-from ...interfaces import (
-    APIInterface,
-    APIOptions,
-    SessionInfo,
-    UserSessionsGetAPIResponse,
-)
-
-
-async def handle_sessions_get(
-    _api_interface: APIInterface,
-    _tenant_id: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-) -> UserSessionsGetAPIResponse:
-    user_id = api_options.request.get_query_param("userId")
-
-    if user_id is None:
-        raise_bad_input_exception("Missing required parameter 'userId'")
-
-    # Passing tenant id as None sets fetch_across_all_tenants to True
-    # which is what we want here.
-    session_handles = await get_all_session_handles_for_user(
-        user_id, None, user_context
-    )
-    sessions: List[Optional[SessionInfo]] = [None for _ in session_handles]
-
-    async def call_(i: int, session_handle: str):
-        try:
-            session_response = await get_session_information(
-                session_handle, user_context
-            )
-            if session_response is not None:
-                sessions[i] = SessionInfo(session_response)
-        except Exception:
-            sessions[i] = None
-
-    session_info_promises = [
-        call_(i, handle) for i, handle in enumerate(session_handles)
-    ]
-
-    await asyncio.gather(*session_info_promises)
-
-    return UserSessionsGetAPIResponse([s for s in sessions if s is not None])
-
-
-
-
-
-
-
-

Functions

-
-
-async def handle_sessions_get(_api_interface: APIInterface, _tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> UserSessionsGetAPIResponse -
-
-
-
- -Expand source code - -
async def handle_sessions_get(
-    _api_interface: APIInterface,
-    _tenant_id: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-) -> UserSessionsGetAPIResponse:
-    user_id = api_options.request.get_query_param("userId")
-
-    if user_id is None:
-        raise_bad_input_exception("Missing required parameter 'userId'")
-
-    # Passing tenant id as None sets fetch_across_all_tenants to True
-    # which is what we want here.
-    session_handles = await get_all_session_handles_for_user(
-        user_id, None, user_context
-    )
-    sessions: List[Optional[SessionInfo]] = [None for _ in session_handles]
-
-    async def call_(i: int, session_handle: str):
-        try:
-            session_response = await get_session_information(
-                session_handle, user_context
-            )
-            if session_response is not None:
-                sessions[i] = SessionInfo(session_response)
-        except Exception:
-            sessions[i] = None
-
-    session_info_promises = [
-        call_(i, handle) for i, handle in enumerate(session_handles)
-    ]
-
-    await asyncio.gather(*session_info_promises)
-
-    return UserSessionsGetAPIResponse([s for s in sessions if s is not None])
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/dashboard/api/userdetails/user_sessions_post.html b/html/supertokens_python/recipe/dashboard/api/userdetails/user_sessions_post.html deleted file mode 100644 index 890241d67..000000000 --- a/html/supertokens_python/recipe/dashboard/api/userdetails/user_sessions_post.html +++ /dev/null @@ -1,116 +0,0 @@ - - - - - - -supertokens_python.recipe.dashboard.api.userdetails.user_sessions_post API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.dashboard.api.userdetails.user_sessions_post

-
-
-
- -Expand source code - -
from typing import Any, Dict, List, Optional
-
-from supertokens_python.exceptions import raise_bad_input_exception
-from supertokens_python.recipe.session.asyncio import revoke_multiple_sessions
-from ...interfaces import APIInterface, APIOptions, UserSessionsPostAPIResponse
-
-
-async def handle_user_sessions_post(
-    _api_interface: APIInterface,
-    _tenant_id: str,
-    api_options: APIOptions,
-    _user_context: Dict[str, Any],
-) -> UserSessionsPostAPIResponse:
-    request_body = await api_options.request.json()  # type: ignore
-    session_handles: Optional[List[str]] = request_body.get("sessionHandles")  # type: ignore
-
-    if not isinstance(session_handles, list):
-        raise_bad_input_exception(
-            "Required parameter 'sessionHandles' is missing or has an invalid type"
-        )
-
-    await revoke_multiple_sessions(session_handles, _user_context)
-    return UserSessionsPostAPIResponse()
-
-
-
-
-
-
-
-

Functions

-
-
-async def handle_user_sessions_post(_api_interface: APIInterface, _tenant_id: str, api_options: APIOptions, _user_context: Dict[str, Any]) ‑> UserSessionsPostAPIResponse -
-
-
-
- -Expand source code - -
async def handle_user_sessions_post(
-    _api_interface: APIInterface,
-    _tenant_id: str,
-    api_options: APIOptions,
-    _user_context: Dict[str, Any],
-) -> UserSessionsPostAPIResponse:
-    request_body = await api_options.request.json()  # type: ignore
-    session_handles: Optional[List[str]] = request_body.get("sessionHandles")  # type: ignore
-
-    if not isinstance(session_handles, list):
-        raise_bad_input_exception(
-            "Required parameter 'sessionHandles' is missing or has an invalid type"
-        )
-
-    await revoke_multiple_sessions(session_handles, _user_context)
-    return UserSessionsPostAPIResponse()
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/dashboard/api/users_count_get.html b/html/supertokens_python/recipe/dashboard/api/users_count_get.html deleted file mode 100644 index 5e7a5c477..000000000 --- a/html/supertokens_python/recipe/dashboard/api/users_count_get.html +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - -supertokens_python.recipe.dashboard.api.users_count_get API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.dashboard.api.users_count_get

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from typing import TYPE_CHECKING, Dict, Any
-
-from supertokens_python.supertokens import Supertokens
-from supertokens_python.recipe.dashboard.interfaces import UserCountGetAPIResponse
-
-if TYPE_CHECKING:
-    from supertokens_python.recipe.dashboard.interfaces import (
-        APIOptions,
-        APIInterface,
-    )
-
-
-async def handle_users_count_get_api(
-    _: APIInterface,
-    tenant_id: str,
-    _api_options: APIOptions,
-    _user_context: Dict[str, Any],
-) -> UserCountGetAPIResponse:
-    count = await Supertokens.get_instance().get_user_count(
-        None,
-        tenant_id,
-    )
-    return UserCountGetAPIResponse(count=count)
-
-
-
-
-
-
-
-

Functions

-
-
-async def handle_users_count_get_api(_: APIInterface, tenant_id: str, _api_options: APIOptions, _user_context: Dict[str, Any]) ‑> UserCountGetAPIResponse -
-
-
-
- -Expand source code - -
async def handle_users_count_get_api(
-    _: APIInterface,
-    tenant_id: str,
-    _api_options: APIOptions,
-    _user_context: Dict[str, Any],
-) -> UserCountGetAPIResponse:
-    count = await Supertokens.get_instance().get_user_count(
-        None,
-        tenant_id,
-    )
-    return UserCountGetAPIResponse(count=count)
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/dashboard/api/users_get.html b/html/supertokens_python/recipe/dashboard/api/users_get.html deleted file mode 100644 index fc984b181..000000000 --- a/html/supertokens_python/recipe/dashboard/api/users_get.html +++ /dev/null @@ -1,275 +0,0 @@ - - - - - - -supertokens_python.recipe.dashboard.api.users_get API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.dashboard.api.users_get

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-import asyncio
-from typing import TYPE_CHECKING, Any, Awaitable, List, Dict
-from typing_extensions import Literal
-
-from supertokens_python.supertokens import Supertokens
-
-from ...usermetadata import UserMetadataRecipe
-from ...usermetadata.asyncio import get_user_metadata
-from ..interfaces import DashboardUsersGetResponse
-from ..utils import UserWithMetadata
-
-if TYPE_CHECKING:
-    from supertokens_python.recipe.dashboard.interfaces import (
-        APIOptions,
-        APIInterface,
-    )
-    from supertokens_python.types import APIResponse
-
-from supertokens_python.exceptions import GeneralError, raise_bad_input_exception
-
-
-async def handle_users_get_api(
-    api_implementation: APIInterface,
-    tenant_id: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-) -> APIResponse:
-    _ = api_implementation
-
-    limit = api_options.request.get_query_param("limit")
-    if limit is None:
-        raise_bad_input_exception("Missing required parameter 'limit'")
-
-    time_joined_order: Literal["ASC", "DESC"] = api_options.request.get_query_param(  # type: ignore
-        "timeJoinedOrder", "DESC"
-    )
-    if time_joined_order not in ["ASC", "DESC"]:
-        raise_bad_input_exception("Invalid value recieved for 'timeJoinedOrder'")
-
-    pagination_token = api_options.request.get_query_param("paginationToken")
-
-    users_response = await Supertokens.get_instance().get_users(
-        tenant_id,
-        time_joined_order=time_joined_order,
-        limit=int(limit),
-        pagination_token=pagination_token,
-        include_recipe_ids=None,
-        query=api_options.request.get_query_params(),
-        user_context=user_context,
-    )
-
-    # user metadata bulk fetch with batches:
-
-    try:
-        UserMetadataRecipe.get_instance()
-    except GeneralError:
-        return DashboardUsersGetResponse(
-            users_response.users, users_response.next_pagination_token
-        )
-
-    users_with_metadata: List[UserWithMetadata] = [
-        UserWithMetadata().from_user(user) for user in users_response.users
-    ]
-    metadata_fetch_awaitables: List[Awaitable[Any]] = []
-
-    async def get_user_metadata_and_update_user(user_idx: int) -> None:
-        user = users_response.users[user_idx]
-        user_metadata = await get_user_metadata(user.user_id, user_context)
-        first_name = user_metadata.metadata.get("first_name")
-        last_name = user_metadata.metadata.get("last_name")
-
-        # None becomes null which is acceptable for the dashboard.
-        users_with_metadata[user_idx].first_name = first_name
-        users_with_metadata[user_idx].last_name = last_name
-
-    # Batch calls to get user metadata:
-    for i, _ in enumerate(users_response.users):
-        metadata_fetch_awaitables.append(get_user_metadata_and_update_user(i))
-
-    promise_arr_start_position = 0
-    batch_size = 5
-
-    while promise_arr_start_position < len(metadata_fetch_awaitables):
-        # We want to query only 5 in parallel at a time
-        promises_to_call = [
-            metadata_fetch_awaitables[i]
-            for i in range(
-                promise_arr_start_position,
-                min(
-                    promise_arr_start_position + batch_size,
-                    len(metadata_fetch_awaitables),
-                ),
-            )
-        ]
-        await asyncio.gather(*promises_to_call)
-
-        promise_arr_start_position += batch_size
-
-    return DashboardUsersGetResponse(
-        users_with_metadata,
-        users_response.next_pagination_token,
-    )
-
-
-
-
-
-
-
-

Functions

-
-
-async def handle_users_get_api(api_implementation: APIInterface, tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> APIResponse -
-
-
-
- -Expand source code - -
async def handle_users_get_api(
-    api_implementation: APIInterface,
-    tenant_id: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-) -> APIResponse:
-    _ = api_implementation
-
-    limit = api_options.request.get_query_param("limit")
-    if limit is None:
-        raise_bad_input_exception("Missing required parameter 'limit'")
-
-    time_joined_order: Literal["ASC", "DESC"] = api_options.request.get_query_param(  # type: ignore
-        "timeJoinedOrder", "DESC"
-    )
-    if time_joined_order not in ["ASC", "DESC"]:
-        raise_bad_input_exception("Invalid value recieved for 'timeJoinedOrder'")
-
-    pagination_token = api_options.request.get_query_param("paginationToken")
-
-    users_response = await Supertokens.get_instance().get_users(
-        tenant_id,
-        time_joined_order=time_joined_order,
-        limit=int(limit),
-        pagination_token=pagination_token,
-        include_recipe_ids=None,
-        query=api_options.request.get_query_params(),
-        user_context=user_context,
-    )
-
-    # user metadata bulk fetch with batches:
-
-    try:
-        UserMetadataRecipe.get_instance()
-    except GeneralError:
-        return DashboardUsersGetResponse(
-            users_response.users, users_response.next_pagination_token
-        )
-
-    users_with_metadata: List[UserWithMetadata] = [
-        UserWithMetadata().from_user(user) for user in users_response.users
-    ]
-    metadata_fetch_awaitables: List[Awaitable[Any]] = []
-
-    async def get_user_metadata_and_update_user(user_idx: int) -> None:
-        user = users_response.users[user_idx]
-        user_metadata = await get_user_metadata(user.user_id, user_context)
-        first_name = user_metadata.metadata.get("first_name")
-        last_name = user_metadata.metadata.get("last_name")
-
-        # None becomes null which is acceptable for the dashboard.
-        users_with_metadata[user_idx].first_name = first_name
-        users_with_metadata[user_idx].last_name = last_name
-
-    # Batch calls to get user metadata:
-    for i, _ in enumerate(users_response.users):
-        metadata_fetch_awaitables.append(get_user_metadata_and_update_user(i))
-
-    promise_arr_start_position = 0
-    batch_size = 5
-
-    while promise_arr_start_position < len(metadata_fetch_awaitables):
-        # We want to query only 5 in parallel at a time
-        promises_to_call = [
-            metadata_fetch_awaitables[i]
-            for i in range(
-                promise_arr_start_position,
-                min(
-                    promise_arr_start_position + batch_size,
-                    len(metadata_fetch_awaitables),
-                ),
-            )
-        ]
-        await asyncio.gather(*promises_to_call)
-
-        promise_arr_start_position += batch_size
-
-    return DashboardUsersGetResponse(
-        users_with_metadata,
-        users_response.next_pagination_token,
-    )
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/dashboard/api/validate_key.html b/html/supertokens_python/recipe/dashboard/api/validate_key.html deleted file mode 100644 index ffeab15b9..000000000 --- a/html/supertokens_python/recipe/dashboard/api/validate_key.html +++ /dev/null @@ -1,134 +0,0 @@ - - - - - - -supertokens_python.recipe.dashboard.api.validate_key API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.dashboard.api.validate_key

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from typing import TYPE_CHECKING, Dict, Any
-
-if TYPE_CHECKING:
-    from supertokens_python.recipe.dashboard.interfaces import (
-        APIOptions,
-        APIInterface,
-    )
-
-from supertokens_python.utils import (
-    send_200_response,
-    send_non_200_response_with_message,
-)
-
-from ..utils import validate_api_key
-
-
-async def handle_validate_key_api(
-    _api_implementation: APIInterface,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-):
-
-    is_valid_key = await validate_api_key(
-        api_options.request, api_options.config, user_context
-    )
-
-    if is_valid_key:
-        return send_200_response({"status": "OK"}, api_options.response)
-    return send_non_200_response_with_message("Unauthorised", 401, api_options.response)
-
-
-
-
-
-
-
-

Functions

-
-
-async def handle_validate_key_api(_api_implementation: APIInterface, api_options: APIOptions, user_context: Dict[str, Any]) -
-
-
-
- -Expand source code - -
async def handle_validate_key_api(
-    _api_implementation: APIInterface,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-):
-
-    is_valid_key = await validate_api_key(
-        api_options.request, api_options.config, user_context
-    )
-
-    if is_valid_key:
-        return send_200_response({"status": "OK"}, api_options.response)
-    return send_non_200_response_with_message("Unauthorised", 401, api_options.response)
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/dashboard/constants.html b/html/supertokens_python/recipe/dashboard/constants.html deleted file mode 100644 index d4762020b..000000000 --- a/html/supertokens_python/recipe/dashboard/constants.html +++ /dev/null @@ -1,73 +0,0 @@ - - - - - - -supertokens_python.recipe.dashboard.constants API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.dashboard.constants

-
-
-
- -Expand source code - -
DASHBOARD_API = "/dashboard"
-VALIDATE_KEY_API = "/api/key/validate"
-USERS_LIST_GET_API = "/api/users"
-USERS_COUNT_API = "/api/users/count"
-USER_API = "/api/user"
-USER_EMAIL_VERIFY_API = "/api/user/email/verify"
-USER_METADATA_API = "/api/user/metadata"
-USER_SESSION_API = "/api/user/sessions"
-USER_PASSWORD_API = "/api/user/password"
-USER_EMAIL_VERIFY_TOKEN_API = "/api/user/email/verify/token"
-SIGN_IN_API = "/api/signin"
-SIGN_OUT_API = "/api/signout"
-SEARCH_TAGS_API = "/api/search/tags"
-DASHBOARD_ANALYTICS_API = "/api/analytics"
-TENANTS_LIST_API = "/api/tenants/list"
-
-
-
-
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/dashboard/exceptions.html b/html/supertokens_python/recipe/dashboard/exceptions.html deleted file mode 100644 index 55cfffe76..000000000 --- a/html/supertokens_python/recipe/dashboard/exceptions.html +++ /dev/null @@ -1,125 +0,0 @@ - - - - - - -supertokens_python.recipe.dashboard.exceptions API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.dashboard.exceptions

-
-
-
- -Expand source code - -
from supertokens_python.exceptions import SuperTokensError
-
-
-class SuperTokensDashboardError(SuperTokensError):
-    pass
-
-
-class DashboardOperationNotAllowedError(SuperTokensDashboardError):
-    pass
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class DashboardOperationNotAllowedError -(*args, **kwargs) -
-
-

Common base class for all non-exit exceptions.

-
- -Expand source code - -
class DashboardOperationNotAllowedError(SuperTokensDashboardError):
-    pass
-
-

Ancestors

- -
-
-class SuperTokensDashboardError -(*args, **kwargs) -
-
-

Common base class for all non-exit exceptions.

-
- -Expand source code - -
class SuperTokensDashboardError(SuperTokensError):
-    pass
-
-

Ancestors

- -

Subclasses

- -
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/dashboard/index.html b/html/supertokens_python/recipe/dashboard/index.html deleted file mode 100644 index f5c97daf0..000000000 --- a/html/supertokens_python/recipe/dashboard/index.html +++ /dev/null @@ -1,166 +0,0 @@ - - - - - - -supertokens_python.recipe.dashboard API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.dashboard

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from __future__ import annotations
-
-from typing import Callable, Optional, List
-
-from supertokens_python import AppInfo, RecipeModule
-
-from .recipe import DashboardRecipe
-
-from supertokens_python.recipe.dashboard import utils
-
-InputOverrideConfig = utils.InputOverrideConfig
-
-
-def init(
-    api_key: Optional[str] = None,
-    admins: Optional[List[str]] = None,
-    override: Optional[InputOverrideConfig] = None,
-) -> Callable[[AppInfo], RecipeModule]:
-    return DashboardRecipe.init(
-        api_key,
-        admins,
-        override,
-    )
-
-
-
-

Sub-modules

-
-
supertokens_python.recipe.dashboard.api
-
-
-
-
supertokens_python.recipe.dashboard.constants
-
-
-
-
supertokens_python.recipe.dashboard.exceptions
-
-
-
-
supertokens_python.recipe.dashboard.interfaces
-
-
-
-
supertokens_python.recipe.dashboard.recipe
-
-
-
-
supertokens_python.recipe.dashboard.recipe_implementation
-
-
-
-
supertokens_python.recipe.dashboard.utils
-
-
-
-
-
-
-
-
-

Functions

-
-
-def init(api_key: Optional[str] = None, admins: Optional[List[str]] = None, override: Optional[InputOverrideConfig] = None) ‑> Callable[[AppInfo], RecipeModule] -
-
-
-
- -Expand source code - -
def init(
-    api_key: Optional[str] = None,
-    admins: Optional[List[str]] = None,
-    override: Optional[InputOverrideConfig] = None,
-) -> Callable[[AppInfo], RecipeModule]:
-    return DashboardRecipe.init(
-        api_key,
-        admins,
-        override,
-    )
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/dashboard/interfaces.html b/html/supertokens_python/recipe/dashboard/interfaces.html deleted file mode 100644 index 04238799a..000000000 --- a/html/supertokens_python/recipe/dashboard/interfaces.html +++ /dev/null @@ -1,2084 +0,0 @@ - - - - - - -supertokens_python.recipe.dashboard.interfaces API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.dashboard.interfaces

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from abc import ABC, abstractmethod
-from typing import TYPE_CHECKING, Any, Awaitable, Callable, Dict, List, Optional, Union
-
-from supertokens_python.types import User
-
-from ...types import APIResponse
-
-if TYPE_CHECKING:
-    from ...supertokens import AppInfo
-    from .utils import DashboardConfig, UserWithMetadata
-
-    from supertokens_python.recipe.session.interfaces import SessionInformationResult
-    from supertokens_python.framework import BaseRequest, BaseResponse
-
-    from supertokens_python.recipe.multitenancy.interfaces import (
-        EmailPasswordConfig,
-        PasswordlessConfig,
-        ThirdPartyConfig,
-    )
-
-
-class SessionInfo:
-    def __init__(self, info: SessionInformationResult) -> None:
-        self.session_handle = info.session_handle
-        self.user_id = info.user_id
-        self.session_data_in_database = info.session_data_in_database
-        self.expiry = info.expiry
-        self.access_token_payload = info.custom_claims_in_access_token_payload
-        self.time_created = info.time_created
-        self.tenant_id = info.tenant_id
-
-
-class RecipeInterface(ABC):
-    def __init__(self):
-        pass
-
-    @abstractmethod
-    async def get_dashboard_bundle_location(self, user_context: Dict[str, Any]) -> str:
-        pass
-
-    @abstractmethod
-    async def should_allow_access(
-        self,
-        request: BaseRequest,
-        config: DashboardConfig,
-        user_context: Dict[str, Any],
-    ) -> bool:
-        pass
-
-
-class APIOptions:
-    def __init__(
-        self,
-        request: BaseRequest,
-        response: BaseResponse,
-        recipe_id: str,
-        config: DashboardConfig,
-        recipe_implementation: RecipeInterface,
-        app_info: AppInfo,
-    ):
-        self.request: BaseRequest = request
-        self.response: BaseResponse = response
-        self.recipe_id: str = recipe_id
-        self.config: DashboardConfig = config
-        self.recipe_implementation: RecipeInterface = recipe_implementation
-        self.app_info = app_info
-
-
-class APIInterface:
-    def __init__(self):
-        # undefined should be allowed
-        self.dashboard_get: Optional[
-            Callable[[APIOptions, Dict[str, Any]], Awaitable[str]]
-        ] = None
-
-
-class DashboardUsersGetResponse(APIResponse):
-    status: str = "OK"
-
-    def __init__(
-        self,
-        users: Union[List[User], List[UserWithMetadata]],
-        next_pagination_token: Optional[str],
-    ):
-        self.users = users
-        self.next_pagination_token = next_pagination_token
-
-    def to_json(self) -> Dict[str, Any]:
-        return {
-            "status": self.status,
-            "users": [u.to_json() for u in self.users],
-            "nextPaginationToken": self.next_pagination_token,
-        }
-
-
-class DashboardListTenantItem:
-    def __init__(
-        self,
-        tenant_id: str,
-        emailpassword: EmailPasswordConfig,
-        passwordless: PasswordlessConfig,
-        third_party: ThirdPartyConfig,
-    ):
-        self.tenant_id = tenant_id
-        self.emailpassword = emailpassword
-        self.passwordless = passwordless
-        self.third_party = third_party
-
-    def to_json(self):
-        res = {
-            "tenantId": self.tenant_id,
-            "emailPassword": self.emailpassword.to_json(),
-            "passwordless": self.passwordless.to_json(),
-            "thirdParty": self.third_party.to_json(),
-        }
-
-        return res
-
-
-class DashboardListTenantsGetResponse(APIResponse):
-    status: str = "OK"
-
-    def __init__(self, tenants: List[DashboardListTenantItem]) -> None:
-        self.tenants = tenants
-
-    def to_json(self) -> Dict[str, Any]:
-        return {
-            "status": self.status,
-            "tenants": [t.to_json() for t in self.tenants],
-        }
-
-
-class UserCountGetAPIResponse(APIResponse):
-    status: str = "OK"
-
-    def __init__(self, count: int):
-        self.count = count
-
-    def to_json(self) -> Dict[str, Any]:
-        return {"status": self.status, "count": self.count}
-
-
-class UserGetAPIOkResponse(APIResponse):
-    status: str = "OK"
-
-    def __init__(self, recipe_id: str, user: UserWithMetadata):
-        self.recipe_id = recipe_id
-        self.user = user
-
-    def to_json(self) -> Dict[str, Any]:
-        return {
-            "status": self.status,
-            "recipeId": self.recipe_id,
-            "user": self.user.to_json(),
-        }
-
-
-class UserGetAPINoUserFoundError(APIResponse):
-    status: str = "NO_USER_FOUND_ERROR"
-
-    def to_json(self) -> Dict[str, Any]:
-        return {"status": self.status}
-
-
-class UserGetAPIRecipeNotInitialisedError(APIResponse):
-    status: str = "RECIPE_NOT_INITIALISED"
-
-    def to_json(self) -> Dict[str, Any]:
-        return {"status": self.status}
-
-
-class FeatureNotEnabledError(APIResponse):
-    status: str = "FEATURE_NOT_ENABLED_ERROR"
-
-    def to_json(self) -> Dict[str, Any]:
-        return {"status": self.status}
-
-
-class UserMetadataGetAPIOkResponse(APIResponse):
-    status: str = "OK"
-
-    def __init__(self, data: Dict[str, Any]) -> None:
-        self.data = data
-
-    def to_json(self) -> Dict[str, Any]:
-        return {"status": "OK", "data": self.data}
-
-
-class UserSessionsGetAPIResponse(APIResponse):
-    status: str = "OK"
-
-    def __init__(self, sessions: List[SessionInfo]):
-        self.sessions = [
-            {
-                "accessTokenPayload": s.access_token_payload,
-                "expiry": s.expiry,
-                "sessionDataInDatabase": s.session_data_in_database,
-                "tenantId": s.tenant_id,
-                "timeCreated": s.time_created,
-                "userId": s.user_id,
-                "sessionHandle": s.session_handle,
-            }
-            for s in sessions
-        ]
-
-    def to_json(self) -> Dict[str, Any]:
-        return {"status": self.status, "sessions": self.sessions}
-
-
-class UserEmailVerifyGetAPIResponse(APIResponse):
-    status: str = "OK"
-
-    def __init__(self, is_verified: bool):
-        self.is_verified = is_verified
-
-    def to_json(self) -> Dict[str, Any]:
-        return {"status": self.status, "isVerified": self.is_verified}
-
-
-class UserDeleteAPIResponse(APIResponse):
-    status: str = "OK"
-
-    def to_json(self) -> Dict[str, Any]:
-        return {"status": self.status}
-
-
-class UserEmailVerifyPutAPIResponse(APIResponse):
-    status: str = "OK"
-
-    def to_json(self) -> Dict[str, Any]:
-        return {"status": self.status}
-
-
-class UserPasswordPutAPIResponse(APIResponse):
-    status: str = "OK"
-
-    def to_json(self) -> Dict[str, Any]:
-        return {"status": self.status}
-
-
-class UserPasswordPutAPIInvalidPasswordErrorResponse(APIResponse):
-    status: str = "INVALID_PASSWORD_ERROR"
-
-    def __init__(self, error: str) -> None:
-        self.error = error
-
-    def to_json(self) -> Dict[str, Any]:
-        return {"status": self.status, "error": self.error}
-
-
-class UserSessionsPostAPIResponse(APIResponse):
-    status: str = "OK"
-
-    def to_json(self) -> Dict[str, Any]:
-        return {"status": self.status}
-
-
-class UserEmailVerifyTokenPostAPIOkResponse(APIResponse):
-    status: str = "OK"
-
-    def to_json(self) -> Dict[str, Any]:
-        return {"status": self.status}
-
-
-class UserEmailVerifyTokenPostAPIEmailAlreadyVerifiedErrorResponse(APIResponse):
-    status: str = "EMAIL_ALREADY_VERIFIED_ERROR"
-
-    def to_json(self) -> Dict[str, Any]:
-        return {"status": self.status}
-
-
-class UserMetadataPutAPIResponse(APIResponse):
-    status: str = "OK"
-
-    def to_json(self) -> Dict[str, Any]:
-        return {"status": self.status}
-
-
-class UserPutAPIOkResponse(APIResponse):
-    status: str = "OK"
-
-    def to_json(self) -> Dict[str, Any]:
-        return {"status": self.status}
-
-
-class UserPutAPIInvalidEmailErrorResponse(APIResponse):
-    status: str = "INVALID_EMAIL_ERROR"
-
-    def __init__(self, error: str) -> None:
-        self.error = error
-
-    def to_json(self) -> Dict[str, Any]:
-        return {"status": self.status, "error": self.error}
-
-
-class UserPutAPIEmailAlreadyExistsErrorResponse(APIResponse):
-    status: str = "EMAIL_ALREADY_EXISTS_ERROR"
-
-    def to_json(self) -> Dict[str, Any]:
-        return {"status": self.status}
-
-
-class UserPutPhoneAlreadyExistsAPIResponse(APIResponse):
-    status: str = "PHONE_ALREADY_EXISTS_ERROR"
-
-    def to_json(self) -> Dict[str, Any]:
-        return {"status": self.status}
-
-
-class UserPutAPIInvalidPhoneErrorResponse(APIResponse):
-    status: str = "INVALID_PHONE_ERROR"
-
-    def __init__(self, error: str) -> None:
-        self.error = error
-
-    def to_json(self) -> Dict[str, Any]:
-        return {"status": self.status, "error": self.error}
-
-
-class SignOutOK(APIResponse):
-    status: str = "OK"
-
-    def to_json(self):
-        return {"status": self.status}
-
-
-class SearchTagsOK(APIResponse):
-    status: str = "OK"
-    tags: List[str]
-
-    def __init__(self, tags: List[str]) -> None:
-        self.tags = tags
-
-    def to_json(self):
-        return {"status": self.status, "tags": self.tags}
-
-
-class AnalyticsResponse(APIResponse):
-    status: str = "OK"
-
-    def to_json(self) -> Dict[str, Any]:
-        return {"status": self.status}
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class APIInterface -
-
-
-
- -Expand source code - -
class APIInterface:
-    def __init__(self):
-        # undefined should be allowed
-        self.dashboard_get: Optional[
-            Callable[[APIOptions, Dict[str, Any]], Awaitable[str]]
-        ] = None
-
-

Subclasses

- -
-
-class APIOptions -(request: BaseRequest, response: BaseResponse, recipe_id: str, config: DashboardConfig, recipe_implementation: RecipeInterface, app_info: AppInfo) -
-
-
-
- -Expand source code - -
class APIOptions:
-    def __init__(
-        self,
-        request: BaseRequest,
-        response: BaseResponse,
-        recipe_id: str,
-        config: DashboardConfig,
-        recipe_implementation: RecipeInterface,
-        app_info: AppInfo,
-    ):
-        self.request: BaseRequest = request
-        self.response: BaseResponse = response
-        self.recipe_id: str = recipe_id
-        self.config: DashboardConfig = config
-        self.recipe_implementation: RecipeInterface = recipe_implementation
-        self.app_info = app_info
-
-
-
-class AnalyticsResponse -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class AnalyticsResponse(APIResponse):
-    status: str = "OK"
-
-    def to_json(self) -> Dict[str, Any]:
-        return {"status": self.status}
-
-

Ancestors

- -

Class variables

-
-
var status : str
-
-
-
-
-

Methods

-
-
-def to_json(self) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
def to_json(self) -> Dict[str, Any]:
-    return {"status": self.status}
-
-
-
-
-
-class DashboardListTenantItem -(tenant_id: str, emailpassword: EmailPasswordConfig, passwordless: PasswordlessConfig, third_party: ThirdPartyConfig) -
-
-
-
- -Expand source code - -
class DashboardListTenantItem:
-    def __init__(
-        self,
-        tenant_id: str,
-        emailpassword: EmailPasswordConfig,
-        passwordless: PasswordlessConfig,
-        third_party: ThirdPartyConfig,
-    ):
-        self.tenant_id = tenant_id
-        self.emailpassword = emailpassword
-        self.passwordless = passwordless
-        self.third_party = third_party
-
-    def to_json(self):
-        res = {
-            "tenantId": self.tenant_id,
-            "emailPassword": self.emailpassword.to_json(),
-            "passwordless": self.passwordless.to_json(),
-            "thirdParty": self.third_party.to_json(),
-        }
-
-        return res
-
-

Methods

-
-
-def to_json(self) -
-
-
-
- -Expand source code - -
def to_json(self):
-    res = {
-        "tenantId": self.tenant_id,
-        "emailPassword": self.emailpassword.to_json(),
-        "passwordless": self.passwordless.to_json(),
-        "thirdParty": self.third_party.to_json(),
-    }
-
-    return res
-
-
-
-
-
-class DashboardListTenantsGetResponse -(tenants: List[DashboardListTenantItem]) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class DashboardListTenantsGetResponse(APIResponse):
-    status: str = "OK"
-
-    def __init__(self, tenants: List[DashboardListTenantItem]) -> None:
-        self.tenants = tenants
-
-    def to_json(self) -> Dict[str, Any]:
-        return {
-            "status": self.status,
-            "tenants": [t.to_json() for t in self.tenants],
-        }
-
-

Ancestors

- -

Class variables

-
-
var status : str
-
-
-
-
-

Methods

-
-
-def to_json(self) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
def to_json(self) -> Dict[str, Any]:
-    return {
-        "status": self.status,
-        "tenants": [t.to_json() for t in self.tenants],
-    }
-
-
-
-
-
-class DashboardUsersGetResponse -(users: Union[List[User], List[UserWithMetadata]], next_pagination_token: Optional[str]) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class DashboardUsersGetResponse(APIResponse):
-    status: str = "OK"
-
-    def __init__(
-        self,
-        users: Union[List[User], List[UserWithMetadata]],
-        next_pagination_token: Optional[str],
-    ):
-        self.users = users
-        self.next_pagination_token = next_pagination_token
-
-    def to_json(self) -> Dict[str, Any]:
-        return {
-            "status": self.status,
-            "users": [u.to_json() for u in self.users],
-            "nextPaginationToken": self.next_pagination_token,
-        }
-
-

Ancestors

- -

Class variables

-
-
var status : str
-
-
-
-
-

Methods

-
-
-def to_json(self) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
def to_json(self) -> Dict[str, Any]:
-    return {
-        "status": self.status,
-        "users": [u.to_json() for u in self.users],
-        "nextPaginationToken": self.next_pagination_token,
-    }
-
-
-
-
-
-class FeatureNotEnabledError -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class FeatureNotEnabledError(APIResponse):
-    status: str = "FEATURE_NOT_ENABLED_ERROR"
-
-    def to_json(self) -> Dict[str, Any]:
-        return {"status": self.status}
-
-

Ancestors

- -

Class variables

-
-
var status : str
-
-
-
-
-

Methods

-
-
-def to_json(self) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
def to_json(self) -> Dict[str, Any]:
-    return {"status": self.status}
-
-
-
-
-
-class RecipeInterface -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class RecipeInterface(ABC):
-    def __init__(self):
-        pass
-
-    @abstractmethod
-    async def get_dashboard_bundle_location(self, user_context: Dict[str, Any]) -> str:
-        pass
-
-    @abstractmethod
-    async def should_allow_access(
-        self,
-        request: BaseRequest,
-        config: DashboardConfig,
-        user_context: Dict[str, Any],
-    ) -> bool:
-        pass
-
-

Ancestors

-
    -
  • abc.ABC
  • -
-

Subclasses

- -

Methods

-
-
-async def get_dashboard_bundle_location(self, user_context: Dict[str, Any]) ‑> str -
-
-
-
- -Expand source code - -
@abstractmethod
-async def get_dashboard_bundle_location(self, user_context: Dict[str, Any]) -> str:
-    pass
-
-
-
-async def should_allow_access(self, request: BaseRequest, config: DashboardConfig, user_context: Dict[str, Any]) ‑> bool -
-
-
-
- -Expand source code - -
@abstractmethod
-async def should_allow_access(
-    self,
-    request: BaseRequest,
-    config: DashboardConfig,
-    user_context: Dict[str, Any],
-) -> bool:
-    pass
-
-
-
-
-
-class SearchTagsOK -(tags: List[str]) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class SearchTagsOK(APIResponse):
-    status: str = "OK"
-    tags: List[str]
-
-    def __init__(self, tags: List[str]) -> None:
-        self.tags = tags
-
-    def to_json(self):
-        return {"status": self.status, "tags": self.tags}
-
-

Ancestors

- -

Class variables

-
-
var status : str
-
-
-
-
var tags : List[str]
-
-
-
-
-

Methods

-
-
-def to_json(self) -
-
-
-
- -Expand source code - -
def to_json(self):
-    return {"status": self.status, "tags": self.tags}
-
-
-
-
-
-class SessionInfo -(info: SessionInformationResult) -
-
-
-
- -Expand source code - -
class SessionInfo:
-    def __init__(self, info: SessionInformationResult) -> None:
-        self.session_handle = info.session_handle
-        self.user_id = info.user_id
-        self.session_data_in_database = info.session_data_in_database
-        self.expiry = info.expiry
-        self.access_token_payload = info.custom_claims_in_access_token_payload
-        self.time_created = info.time_created
-        self.tenant_id = info.tenant_id
-
-
-
-class SignOutOK -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class SignOutOK(APIResponse):
-    status: str = "OK"
-
-    def to_json(self):
-        return {"status": self.status}
-
-

Ancestors

- -

Class variables

-
-
var status : str
-
-
-
-
-

Methods

-
-
-def to_json(self) -
-
-
-
- -Expand source code - -
def to_json(self):
-    return {"status": self.status}
-
-
-
-
-
-class UserCountGetAPIResponse -(count: int) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class UserCountGetAPIResponse(APIResponse):
-    status: str = "OK"
-
-    def __init__(self, count: int):
-        self.count = count
-
-    def to_json(self) -> Dict[str, Any]:
-        return {"status": self.status, "count": self.count}
-
-

Ancestors

- -

Class variables

-
-
var status : str
-
-
-
-
-

Methods

-
-
-def to_json(self) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
def to_json(self) -> Dict[str, Any]:
-    return {"status": self.status, "count": self.count}
-
-
-
-
-
-class UserDeleteAPIResponse -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class UserDeleteAPIResponse(APIResponse):
-    status: str = "OK"
-
-    def to_json(self) -> Dict[str, Any]:
-        return {"status": self.status}
-
-

Ancestors

- -

Class variables

-
-
var status : str
-
-
-
-
-

Methods

-
-
-def to_json(self) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
def to_json(self) -> Dict[str, Any]:
-    return {"status": self.status}
-
-
-
-
-
-class UserEmailVerifyGetAPIResponse -(is_verified: bool) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class UserEmailVerifyGetAPIResponse(APIResponse):
-    status: str = "OK"
-
-    def __init__(self, is_verified: bool):
-        self.is_verified = is_verified
-
-    def to_json(self) -> Dict[str, Any]:
-        return {"status": self.status, "isVerified": self.is_verified}
-
-

Ancestors

- -

Class variables

-
-
var status : str
-
-
-
-
-

Methods

-
-
-def to_json(self) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
def to_json(self) -> Dict[str, Any]:
-    return {"status": self.status, "isVerified": self.is_verified}
-
-
-
-
-
-class UserEmailVerifyPutAPIResponse -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class UserEmailVerifyPutAPIResponse(APIResponse):
-    status: str = "OK"
-
-    def to_json(self) -> Dict[str, Any]:
-        return {"status": self.status}
-
-

Ancestors

- -

Class variables

-
-
var status : str
-
-
-
-
-

Methods

-
-
-def to_json(self) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
def to_json(self) -> Dict[str, Any]:
-    return {"status": self.status}
-
-
-
-
-
-class UserEmailVerifyTokenPostAPIEmailAlreadyVerifiedErrorResponse -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class UserEmailVerifyTokenPostAPIEmailAlreadyVerifiedErrorResponse(APIResponse):
-    status: str = "EMAIL_ALREADY_VERIFIED_ERROR"
-
-    def to_json(self) -> Dict[str, Any]:
-        return {"status": self.status}
-
-

Ancestors

- -

Class variables

-
-
var status : str
-
-
-
-
-

Methods

-
-
-def to_json(self) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
def to_json(self) -> Dict[str, Any]:
-    return {"status": self.status}
-
-
-
-
-
-class UserEmailVerifyTokenPostAPIOkResponse -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class UserEmailVerifyTokenPostAPIOkResponse(APIResponse):
-    status: str = "OK"
-
-    def to_json(self) -> Dict[str, Any]:
-        return {"status": self.status}
-
-

Ancestors

- -

Class variables

-
-
var status : str
-
-
-
-
-

Methods

-
-
-def to_json(self) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
def to_json(self) -> Dict[str, Any]:
-    return {"status": self.status}
-
-
-
-
-
-class UserGetAPINoUserFoundError -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class UserGetAPINoUserFoundError(APIResponse):
-    status: str = "NO_USER_FOUND_ERROR"
-
-    def to_json(self) -> Dict[str, Any]:
-        return {"status": self.status}
-
-

Ancestors

- -

Class variables

-
-
var status : str
-
-
-
-
-

Methods

-
-
-def to_json(self) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
def to_json(self) -> Dict[str, Any]:
-    return {"status": self.status}
-
-
-
-
-
-class UserGetAPIOkResponse -(recipe_id: str, user: UserWithMetadata) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class UserGetAPIOkResponse(APIResponse):
-    status: str = "OK"
-
-    def __init__(self, recipe_id: str, user: UserWithMetadata):
-        self.recipe_id = recipe_id
-        self.user = user
-
-    def to_json(self) -> Dict[str, Any]:
-        return {
-            "status": self.status,
-            "recipeId": self.recipe_id,
-            "user": self.user.to_json(),
-        }
-
-

Ancestors

- -

Class variables

-
-
var status : str
-
-
-
-
-

Methods

-
-
-def to_json(self) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
def to_json(self) -> Dict[str, Any]:
-    return {
-        "status": self.status,
-        "recipeId": self.recipe_id,
-        "user": self.user.to_json(),
-    }
-
-
-
-
-
-class UserGetAPIRecipeNotInitialisedError -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class UserGetAPIRecipeNotInitialisedError(APIResponse):
-    status: str = "RECIPE_NOT_INITIALISED"
-
-    def to_json(self) -> Dict[str, Any]:
-        return {"status": self.status}
-
-

Ancestors

- -

Class variables

-
-
var status : str
-
-
-
-
-

Methods

-
-
-def to_json(self) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
def to_json(self) -> Dict[str, Any]:
-    return {"status": self.status}
-
-
-
-
-
-class UserMetadataGetAPIOkResponse -(data: Dict[str, Any]) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class UserMetadataGetAPIOkResponse(APIResponse):
-    status: str = "OK"
-
-    def __init__(self, data: Dict[str, Any]) -> None:
-        self.data = data
-
-    def to_json(self) -> Dict[str, Any]:
-        return {"status": "OK", "data": self.data}
-
-

Ancestors

- -

Class variables

-
-
var status : str
-
-
-
-
-

Methods

-
-
-def to_json(self) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
def to_json(self) -> Dict[str, Any]:
-    return {"status": "OK", "data": self.data}
-
-
-
-
-
-class UserMetadataPutAPIResponse -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class UserMetadataPutAPIResponse(APIResponse):
-    status: str = "OK"
-
-    def to_json(self) -> Dict[str, Any]:
-        return {"status": self.status}
-
-

Ancestors

- -

Class variables

-
-
var status : str
-
-
-
-
-

Methods

-
-
-def to_json(self) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
def to_json(self) -> Dict[str, Any]:
-    return {"status": self.status}
-
-
-
-
-
-class UserPasswordPutAPIInvalidPasswordErrorResponse -(error: str) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class UserPasswordPutAPIInvalidPasswordErrorResponse(APIResponse):
-    status: str = "INVALID_PASSWORD_ERROR"
-
-    def __init__(self, error: str) -> None:
-        self.error = error
-
-    def to_json(self) -> Dict[str, Any]:
-        return {"status": self.status, "error": self.error}
-
-

Ancestors

- -

Class variables

-
-
var status : str
-
-
-
-
-

Methods

-
-
-def to_json(self) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
def to_json(self) -> Dict[str, Any]:
-    return {"status": self.status, "error": self.error}
-
-
-
-
-
-class UserPasswordPutAPIResponse -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class UserPasswordPutAPIResponse(APIResponse):
-    status: str = "OK"
-
-    def to_json(self) -> Dict[str, Any]:
-        return {"status": self.status}
-
-

Ancestors

- -

Class variables

-
-
var status : str
-
-
-
-
-

Methods

-
-
-def to_json(self) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
def to_json(self) -> Dict[str, Any]:
-    return {"status": self.status}
-
-
-
-
-
-class UserPutAPIEmailAlreadyExistsErrorResponse -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class UserPutAPIEmailAlreadyExistsErrorResponse(APIResponse):
-    status: str = "EMAIL_ALREADY_EXISTS_ERROR"
-
-    def to_json(self) -> Dict[str, Any]:
-        return {"status": self.status}
-
-

Ancestors

- -

Class variables

-
-
var status : str
-
-
-
-
-

Methods

-
-
-def to_json(self) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
def to_json(self) -> Dict[str, Any]:
-    return {"status": self.status}
-
-
-
-
-
-class UserPutAPIInvalidEmailErrorResponse -(error: str) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class UserPutAPIInvalidEmailErrorResponse(APIResponse):
-    status: str = "INVALID_EMAIL_ERROR"
-
-    def __init__(self, error: str) -> None:
-        self.error = error
-
-    def to_json(self) -> Dict[str, Any]:
-        return {"status": self.status, "error": self.error}
-
-

Ancestors

- -

Class variables

-
-
var status : str
-
-
-
-
-

Methods

-
-
-def to_json(self) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
def to_json(self) -> Dict[str, Any]:
-    return {"status": self.status, "error": self.error}
-
-
-
-
-
-class UserPutAPIInvalidPhoneErrorResponse -(error: str) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class UserPutAPIInvalidPhoneErrorResponse(APIResponse):
-    status: str = "INVALID_PHONE_ERROR"
-
-    def __init__(self, error: str) -> None:
-        self.error = error
-
-    def to_json(self) -> Dict[str, Any]:
-        return {"status": self.status, "error": self.error}
-
-

Ancestors

- -

Class variables

-
-
var status : str
-
-
-
-
-

Methods

-
-
-def to_json(self) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
def to_json(self) -> Dict[str, Any]:
-    return {"status": self.status, "error": self.error}
-
-
-
-
-
-class UserPutAPIOkResponse -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class UserPutAPIOkResponse(APIResponse):
-    status: str = "OK"
-
-    def to_json(self) -> Dict[str, Any]:
-        return {"status": self.status}
-
-

Ancestors

- -

Class variables

-
-
var status : str
-
-
-
-
-

Methods

-
-
-def to_json(self) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
def to_json(self) -> Dict[str, Any]:
-    return {"status": self.status}
-
-
-
-
-
-class UserPutPhoneAlreadyExistsAPIResponse -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class UserPutPhoneAlreadyExistsAPIResponse(APIResponse):
-    status: str = "PHONE_ALREADY_EXISTS_ERROR"
-
-    def to_json(self) -> Dict[str, Any]:
-        return {"status": self.status}
-
-

Ancestors

- -

Class variables

-
-
var status : str
-
-
-
-
-

Methods

-
-
-def to_json(self) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
def to_json(self) -> Dict[str, Any]:
-    return {"status": self.status}
-
-
-
-
-
-class UserSessionsGetAPIResponse -(sessions: List[SessionInfo]) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class UserSessionsGetAPIResponse(APIResponse):
-    status: str = "OK"
-
-    def __init__(self, sessions: List[SessionInfo]):
-        self.sessions = [
-            {
-                "accessTokenPayload": s.access_token_payload,
-                "expiry": s.expiry,
-                "sessionDataInDatabase": s.session_data_in_database,
-                "tenantId": s.tenant_id,
-                "timeCreated": s.time_created,
-                "userId": s.user_id,
-                "sessionHandle": s.session_handle,
-            }
-            for s in sessions
-        ]
-
-    def to_json(self) -> Dict[str, Any]:
-        return {"status": self.status, "sessions": self.sessions}
-
-

Ancestors

- -

Class variables

-
-
var status : str
-
-
-
-
-

Methods

-
-
-def to_json(self) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
def to_json(self) -> Dict[str, Any]:
-    return {"status": self.status, "sessions": self.sessions}
-
-
-
-
-
-class UserSessionsPostAPIResponse -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class UserSessionsPostAPIResponse(APIResponse):
-    status: str = "OK"
-
-    def to_json(self) -> Dict[str, Any]:
-        return {"status": self.status}
-
-

Ancestors

- -

Class variables

-
-
var status : str
-
-
-
-
-

Methods

-
-
-def to_json(self) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
def to_json(self) -> Dict[str, Any]:
-    return {"status": self.status}
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/dashboard/recipe.html b/html/supertokens_python/recipe/dashboard/recipe.html deleted file mode 100644 index a907b23b8..000000000 --- a/html/supertokens_python/recipe/dashboard/recipe.html +++ /dev/null @@ -1,1187 +0,0 @@ - - - - - - -supertokens_python.recipe.dashboard.recipe API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.dashboard.recipe

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from os import environ
-from typing import TYPE_CHECKING, Awaitable, Callable, List, Optional, Dict, Any
-
-from supertokens_python.normalised_url_path import NormalisedURLPath
-from supertokens_python.recipe_module import APIHandled, RecipeModule
-
-from .api import (
-    api_key_protector,
-    handle_analytics_post,
-    handle_dashboard_api,
-    handle_email_verify_token_post,
-    handle_emailpassword_signin_api,
-    handle_emailpassword_signout_api,
-    handle_get_tags,
-    handle_metadata_get,
-    handle_metadata_put,
-    handle_sessions_get,
-    handle_user_delete,
-    handle_user_email_verify_get,
-    handle_user_email_verify_put,
-    handle_user_get,
-    handle_user_password_put,
-    handle_user_put,
-    handle_user_sessions_post,
-    handle_users_count_get_api,
-    handle_users_get_api,
-    handle_validate_key_api,
-    handle_list_tenants_api,
-)
-from .api.implementation import APIImplementation
-from .exceptions import SuperTokensDashboardError
-from .interfaces import APIInterface, APIOptions
-from .recipe_implementation import RecipeImplementation
-
-if TYPE_CHECKING:
-    from supertokens_python.framework.request import BaseRequest
-    from supertokens_python.framework.response import BaseResponse
-    from supertokens_python.supertokens import AppInfo
-    from supertokens_python.types import APIResponse
-
-from supertokens_python.exceptions import SuperTokensError, raise_general_exception
-from supertokens_python.recipe.dashboard.utils import get_api_path_with_dashboard_base
-
-from .constants import (
-    DASHBOARD_ANALYTICS_API,
-    DASHBOARD_API,
-    SIGN_OUT_API,
-    SIGN_IN_API,
-    SEARCH_TAGS_API,
-    USER_API,
-    USER_EMAIL_VERIFY_API,
-    USER_EMAIL_VERIFY_TOKEN_API,
-    USER_METADATA_API,
-    USER_PASSWORD_API,
-    USER_SESSION_API,
-    USERS_COUNT_API,
-    USERS_LIST_GET_API,
-    VALIDATE_KEY_API,
-    TENANTS_LIST_API,
-)
-from .utils import (
-    InputOverrideConfig,
-    validate_and_normalise_user_input,
-)
-
-
-class DashboardRecipe(RecipeModule):
-    recipe_id = "dashboard"
-    __instance = None
-
-    def __init__(
-        self,
-        recipe_id: str,
-        app_info: AppInfo,
-        api_key: Optional[str],
-        admins: Optional[List[str]],
-        override: Optional[InputOverrideConfig] = None,
-    ):
-        super().__init__(recipe_id, app_info)
-        self.config = validate_and_normalise_user_input(
-            api_key,
-            admins,
-            override,
-        )
-        recipe_implementation = RecipeImplementation()
-        self.recipe_implementation = (
-            recipe_implementation
-            if self.config.override.functions is None
-            else self.config.override.functions(recipe_implementation)
-        )
-
-        api_implementation = APIImplementation()
-        self.api_implementation = (
-            api_implementation
-            if self.config.override.apis is None
-            else self.config.override.apis(api_implementation)
-        )
-
-    def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
-        return isinstance(err, SuperTokensError) and (
-            isinstance(err, SuperTokensDashboardError)
-        )
-
-    def get_apis_handled(self) -> List[APIHandled]:
-        return [
-            APIHandled(
-                NormalisedURLPath(get_api_path_with_dashboard_base("/")),
-                "get",
-                DASHBOARD_API,
-                False,
-            ),
-            APIHandled(
-                NormalisedURLPath(get_api_path_with_dashboard_base(SIGN_IN_API)),
-                "post",
-                SIGN_IN_API,
-                False,
-            ),
-            APIHandled(
-                NormalisedURLPath(get_api_path_with_dashboard_base(VALIDATE_KEY_API)),
-                "post",
-                VALIDATE_KEY_API,
-                False,
-            ),
-            APIHandled(
-                NormalisedURLPath(get_api_path_with_dashboard_base(SIGN_OUT_API)),
-                "post",
-                SIGN_OUT_API,
-                False,
-            ),
-            APIHandled(
-                NormalisedURLPath(get_api_path_with_dashboard_base(USERS_LIST_GET_API)),
-                "get",
-                USERS_LIST_GET_API,
-                False,
-            ),
-            APIHandled(
-                NormalisedURLPath(get_api_path_with_dashboard_base(USERS_COUNT_API)),
-                "get",
-                USERS_COUNT_API,
-                False,
-            ),
-            APIHandled(
-                NormalisedURLPath(get_api_path_with_dashboard_base(USER_API)),
-                "get",
-                USER_API,
-                False,
-            ),
-            APIHandled(
-                NormalisedURLPath(get_api_path_with_dashboard_base(USER_API)),
-                "post",
-                USER_API,
-                False,
-            ),
-            APIHandled(
-                NormalisedURLPath(get_api_path_with_dashboard_base(USER_API)),
-                "put",
-                USER_API,
-                False,
-            ),
-            APIHandled(
-                NormalisedURLPath(get_api_path_with_dashboard_base(USER_API)),
-                "delete",
-                USER_API,
-                False,
-            ),
-            APIHandled(
-                NormalisedURLPath(
-                    get_api_path_with_dashboard_base(USER_EMAIL_VERIFY_API)
-                ),
-                "get",
-                USER_EMAIL_VERIFY_API,
-                False,
-            ),
-            APIHandled(
-                NormalisedURLPath(
-                    get_api_path_with_dashboard_base(USER_EMAIL_VERIFY_API)
-                ),
-                "put",
-                USER_EMAIL_VERIFY_API,
-                False,
-            ),
-            APIHandled(
-                NormalisedURLPath(get_api_path_with_dashboard_base(USER_METADATA_API)),
-                "get",
-                USER_METADATA_API,
-                False,
-            ),
-            APIHandled(
-                NormalisedURLPath(get_api_path_with_dashboard_base(USER_METADATA_API)),
-                "put",
-                USER_METADATA_API,
-                False,
-            ),
-            APIHandled(
-                NormalisedURLPath(get_api_path_with_dashboard_base(USER_SESSION_API)),
-                "get",
-                USER_SESSION_API,
-                False,
-            ),
-            APIHandled(
-                NormalisedURLPath(get_api_path_with_dashboard_base(USER_SESSION_API)),
-                "post",
-                USER_SESSION_API,
-                False,
-            ),
-            APIHandled(
-                NormalisedURLPath(get_api_path_with_dashboard_base(USER_PASSWORD_API)),
-                "put",
-                USER_PASSWORD_API,
-                False,
-            ),
-            APIHandled(
-                NormalisedURLPath(
-                    get_api_path_with_dashboard_base(USER_EMAIL_VERIFY_TOKEN_API)
-                ),
-                "post",
-                USER_EMAIL_VERIFY_TOKEN_API,
-                False,
-            ),
-            APIHandled(
-                NormalisedURLPath(get_api_path_with_dashboard_base(SEARCH_TAGS_API)),
-                "get",
-                SEARCH_TAGS_API,
-                False,
-            ),
-            APIHandled(
-                NormalisedURLPath(
-                    get_api_path_with_dashboard_base(DASHBOARD_ANALYTICS_API)
-                ),
-                "post",
-                DASHBOARD_ANALYTICS_API,
-                False,
-            ),
-            APIHandled(
-                NormalisedURLPath(get_api_path_with_dashboard_base(TENANTS_LIST_API)),
-                "get",
-                TENANTS_LIST_API,
-                False,
-            ),
-        ]
-
-    async def handle_api_request(
-        self,
-        request_id: str,
-        tenant_id: str,
-        request: BaseRequest,
-        path: NormalisedURLPath,
-        method: str,
-        response: BaseResponse,
-        user_context: Dict[str, Any],
-    ) -> Optional[BaseResponse]:
-        api_options = APIOptions(
-            request,
-            response,
-            self.recipe_id,
-            self.config,
-            self.recipe_implementation,
-            self.get_app_info(),
-        )
-        # For these APIs we dont need API key validation
-        if request_id == DASHBOARD_API:
-            return await handle_dashboard_api(
-                self.api_implementation, api_options, user_context
-            )
-        if request_id == VALIDATE_KEY_API:
-            return await handle_validate_key_api(
-                self.api_implementation, api_options, user_context
-            )
-        if request_id == SIGN_IN_API:
-            return await handle_emailpassword_signin_api(
-                self.api_implementation, api_options, user_context
-            )
-
-        # Do API key validation for the remaining APIs
-        api_function: Optional[
-            Callable[
-                [APIInterface, str, APIOptions, Dict[str, Any]], Awaitable[APIResponse]
-            ]
-        ] = None
-        if request_id == USERS_LIST_GET_API:
-            api_function = handle_users_get_api
-        elif request_id == USERS_COUNT_API:
-            api_function = handle_users_count_get_api
-        elif request_id == USER_API:
-            if method == "get":
-                api_function = handle_user_get
-            if method == "delete":
-                api_function = handle_user_delete
-            if method == "put":
-                api_function = handle_user_put
-        elif request_id == USER_EMAIL_VERIFY_API:
-            if method == "get":
-                api_function = handle_user_email_verify_get
-            if method == "put":
-                api_function = handle_user_email_verify_put
-        elif request_id == USER_METADATA_API:
-            if method == "get":
-                api_function = handle_metadata_get
-            if method == "put":
-                api_function = handle_metadata_put
-        elif request_id == USER_SESSION_API:
-            if method == "get":
-                api_function = handle_sessions_get
-            if method == "post":
-                api_function = handle_user_sessions_post
-        elif request_id == USER_PASSWORD_API:
-            api_function = handle_user_password_put
-        elif request_id == USER_EMAIL_VERIFY_TOKEN_API:
-            api_function = handle_email_verify_token_post
-        elif request_id == SIGN_OUT_API:
-            api_function = handle_emailpassword_signout_api
-        elif request_id == SEARCH_TAGS_API:
-            api_function = handle_get_tags
-        elif request_id == DASHBOARD_ANALYTICS_API:
-            if method == "post":
-                api_function = handle_analytics_post
-        elif request_id == TENANTS_LIST_API:
-            api_function = handle_list_tenants_api
-
-        if api_function is not None:
-            return await api_key_protector(
-                self.api_implementation,
-                tenant_id,
-                api_options,
-                api_function,
-                user_context,
-            )
-
-        return None
-
-    async def handle_error(
-        self,
-        request: BaseRequest,
-        err: SuperTokensError,
-        response: BaseResponse,
-        user_context: Dict[str, Any],
-    ) -> BaseResponse:
-        raise err
-
-    def get_all_cors_headers(self) -> List[str]:
-        return []
-
-    @staticmethod
-    def init(
-        api_key: Optional[str],
-        admins: Optional[List[str]] = None,
-        override: Optional[InputOverrideConfig] = None,
-    ):
-        def func(app_info: AppInfo):
-            if DashboardRecipe.__instance is None:
-                DashboardRecipe.__instance = DashboardRecipe(
-                    DashboardRecipe.recipe_id,
-                    app_info,
-                    api_key,
-                    admins,
-                    override,
-                )
-                return DashboardRecipe.__instance
-            raise Exception(
-                None,
-                "Dashboard recipe has already been initialised. Please check your code for bugs.",
-            )
-
-        return func
-
-    @staticmethod
-    def get_instance() -> DashboardRecipe:
-        if DashboardRecipe.__instance is not None:
-            return DashboardRecipe.__instance
-        raise_general_exception(
-            "Initialisation not done. Did you forget to call the SuperTokens.init function?"
-        )
-
-    @staticmethod
-    def reset():
-        if ("SUPERTOKENS_ENV" not in environ) or (
-            environ["SUPERTOKENS_ENV"] != "testing"
-        ):
-            raise_general_exception("calling testing function in non testing env")
-        DashboardRecipe.__instance = None
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class DashboardRecipe -(recipe_id: str, app_info: AppInfo, api_key: Optional[str], admins: Optional[List[str]], override: Optional[InputOverrideConfig] = None) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class DashboardRecipe(RecipeModule):
-    recipe_id = "dashboard"
-    __instance = None
-
-    def __init__(
-        self,
-        recipe_id: str,
-        app_info: AppInfo,
-        api_key: Optional[str],
-        admins: Optional[List[str]],
-        override: Optional[InputOverrideConfig] = None,
-    ):
-        super().__init__(recipe_id, app_info)
-        self.config = validate_and_normalise_user_input(
-            api_key,
-            admins,
-            override,
-        )
-        recipe_implementation = RecipeImplementation()
-        self.recipe_implementation = (
-            recipe_implementation
-            if self.config.override.functions is None
-            else self.config.override.functions(recipe_implementation)
-        )
-
-        api_implementation = APIImplementation()
-        self.api_implementation = (
-            api_implementation
-            if self.config.override.apis is None
-            else self.config.override.apis(api_implementation)
-        )
-
-    def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
-        return isinstance(err, SuperTokensError) and (
-            isinstance(err, SuperTokensDashboardError)
-        )
-
-    def get_apis_handled(self) -> List[APIHandled]:
-        return [
-            APIHandled(
-                NormalisedURLPath(get_api_path_with_dashboard_base("/")),
-                "get",
-                DASHBOARD_API,
-                False,
-            ),
-            APIHandled(
-                NormalisedURLPath(get_api_path_with_dashboard_base(SIGN_IN_API)),
-                "post",
-                SIGN_IN_API,
-                False,
-            ),
-            APIHandled(
-                NormalisedURLPath(get_api_path_with_dashboard_base(VALIDATE_KEY_API)),
-                "post",
-                VALIDATE_KEY_API,
-                False,
-            ),
-            APIHandled(
-                NormalisedURLPath(get_api_path_with_dashboard_base(SIGN_OUT_API)),
-                "post",
-                SIGN_OUT_API,
-                False,
-            ),
-            APIHandled(
-                NormalisedURLPath(get_api_path_with_dashboard_base(USERS_LIST_GET_API)),
-                "get",
-                USERS_LIST_GET_API,
-                False,
-            ),
-            APIHandled(
-                NormalisedURLPath(get_api_path_with_dashboard_base(USERS_COUNT_API)),
-                "get",
-                USERS_COUNT_API,
-                False,
-            ),
-            APIHandled(
-                NormalisedURLPath(get_api_path_with_dashboard_base(USER_API)),
-                "get",
-                USER_API,
-                False,
-            ),
-            APIHandled(
-                NormalisedURLPath(get_api_path_with_dashboard_base(USER_API)),
-                "post",
-                USER_API,
-                False,
-            ),
-            APIHandled(
-                NormalisedURLPath(get_api_path_with_dashboard_base(USER_API)),
-                "put",
-                USER_API,
-                False,
-            ),
-            APIHandled(
-                NormalisedURLPath(get_api_path_with_dashboard_base(USER_API)),
-                "delete",
-                USER_API,
-                False,
-            ),
-            APIHandled(
-                NormalisedURLPath(
-                    get_api_path_with_dashboard_base(USER_EMAIL_VERIFY_API)
-                ),
-                "get",
-                USER_EMAIL_VERIFY_API,
-                False,
-            ),
-            APIHandled(
-                NormalisedURLPath(
-                    get_api_path_with_dashboard_base(USER_EMAIL_VERIFY_API)
-                ),
-                "put",
-                USER_EMAIL_VERIFY_API,
-                False,
-            ),
-            APIHandled(
-                NormalisedURLPath(get_api_path_with_dashboard_base(USER_METADATA_API)),
-                "get",
-                USER_METADATA_API,
-                False,
-            ),
-            APIHandled(
-                NormalisedURLPath(get_api_path_with_dashboard_base(USER_METADATA_API)),
-                "put",
-                USER_METADATA_API,
-                False,
-            ),
-            APIHandled(
-                NormalisedURLPath(get_api_path_with_dashboard_base(USER_SESSION_API)),
-                "get",
-                USER_SESSION_API,
-                False,
-            ),
-            APIHandled(
-                NormalisedURLPath(get_api_path_with_dashboard_base(USER_SESSION_API)),
-                "post",
-                USER_SESSION_API,
-                False,
-            ),
-            APIHandled(
-                NormalisedURLPath(get_api_path_with_dashboard_base(USER_PASSWORD_API)),
-                "put",
-                USER_PASSWORD_API,
-                False,
-            ),
-            APIHandled(
-                NormalisedURLPath(
-                    get_api_path_with_dashboard_base(USER_EMAIL_VERIFY_TOKEN_API)
-                ),
-                "post",
-                USER_EMAIL_VERIFY_TOKEN_API,
-                False,
-            ),
-            APIHandled(
-                NormalisedURLPath(get_api_path_with_dashboard_base(SEARCH_TAGS_API)),
-                "get",
-                SEARCH_TAGS_API,
-                False,
-            ),
-            APIHandled(
-                NormalisedURLPath(
-                    get_api_path_with_dashboard_base(DASHBOARD_ANALYTICS_API)
-                ),
-                "post",
-                DASHBOARD_ANALYTICS_API,
-                False,
-            ),
-            APIHandled(
-                NormalisedURLPath(get_api_path_with_dashboard_base(TENANTS_LIST_API)),
-                "get",
-                TENANTS_LIST_API,
-                False,
-            ),
-        ]
-
-    async def handle_api_request(
-        self,
-        request_id: str,
-        tenant_id: str,
-        request: BaseRequest,
-        path: NormalisedURLPath,
-        method: str,
-        response: BaseResponse,
-        user_context: Dict[str, Any],
-    ) -> Optional[BaseResponse]:
-        api_options = APIOptions(
-            request,
-            response,
-            self.recipe_id,
-            self.config,
-            self.recipe_implementation,
-            self.get_app_info(),
-        )
-        # For these APIs we dont need API key validation
-        if request_id == DASHBOARD_API:
-            return await handle_dashboard_api(
-                self.api_implementation, api_options, user_context
-            )
-        if request_id == VALIDATE_KEY_API:
-            return await handle_validate_key_api(
-                self.api_implementation, api_options, user_context
-            )
-        if request_id == SIGN_IN_API:
-            return await handle_emailpassword_signin_api(
-                self.api_implementation, api_options, user_context
-            )
-
-        # Do API key validation for the remaining APIs
-        api_function: Optional[
-            Callable[
-                [APIInterface, str, APIOptions, Dict[str, Any]], Awaitable[APIResponse]
-            ]
-        ] = None
-        if request_id == USERS_LIST_GET_API:
-            api_function = handle_users_get_api
-        elif request_id == USERS_COUNT_API:
-            api_function = handle_users_count_get_api
-        elif request_id == USER_API:
-            if method == "get":
-                api_function = handle_user_get
-            if method == "delete":
-                api_function = handle_user_delete
-            if method == "put":
-                api_function = handle_user_put
-        elif request_id == USER_EMAIL_VERIFY_API:
-            if method == "get":
-                api_function = handle_user_email_verify_get
-            if method == "put":
-                api_function = handle_user_email_verify_put
-        elif request_id == USER_METADATA_API:
-            if method == "get":
-                api_function = handle_metadata_get
-            if method == "put":
-                api_function = handle_metadata_put
-        elif request_id == USER_SESSION_API:
-            if method == "get":
-                api_function = handle_sessions_get
-            if method == "post":
-                api_function = handle_user_sessions_post
-        elif request_id == USER_PASSWORD_API:
-            api_function = handle_user_password_put
-        elif request_id == USER_EMAIL_VERIFY_TOKEN_API:
-            api_function = handle_email_verify_token_post
-        elif request_id == SIGN_OUT_API:
-            api_function = handle_emailpassword_signout_api
-        elif request_id == SEARCH_TAGS_API:
-            api_function = handle_get_tags
-        elif request_id == DASHBOARD_ANALYTICS_API:
-            if method == "post":
-                api_function = handle_analytics_post
-        elif request_id == TENANTS_LIST_API:
-            api_function = handle_list_tenants_api
-
-        if api_function is not None:
-            return await api_key_protector(
-                self.api_implementation,
-                tenant_id,
-                api_options,
-                api_function,
-                user_context,
-            )
-
-        return None
-
-    async def handle_error(
-        self,
-        request: BaseRequest,
-        err: SuperTokensError,
-        response: BaseResponse,
-        user_context: Dict[str, Any],
-    ) -> BaseResponse:
-        raise err
-
-    def get_all_cors_headers(self) -> List[str]:
-        return []
-
-    @staticmethod
-    def init(
-        api_key: Optional[str],
-        admins: Optional[List[str]] = None,
-        override: Optional[InputOverrideConfig] = None,
-    ):
-        def func(app_info: AppInfo):
-            if DashboardRecipe.__instance is None:
-                DashboardRecipe.__instance = DashboardRecipe(
-                    DashboardRecipe.recipe_id,
-                    app_info,
-                    api_key,
-                    admins,
-                    override,
-                )
-                return DashboardRecipe.__instance
-            raise Exception(
-                None,
-                "Dashboard recipe has already been initialised. Please check your code for bugs.",
-            )
-
-        return func
-
-    @staticmethod
-    def get_instance() -> DashboardRecipe:
-        if DashboardRecipe.__instance is not None:
-            return DashboardRecipe.__instance
-        raise_general_exception(
-            "Initialisation not done. Did you forget to call the SuperTokens.init function?"
-        )
-
-    @staticmethod
-    def reset():
-        if ("SUPERTOKENS_ENV" not in environ) or (
-            environ["SUPERTOKENS_ENV"] != "testing"
-        ):
-            raise_general_exception("calling testing function in non testing env")
-        DashboardRecipe.__instance = None
-
-

Ancestors

- -

Class variables

-
-
var get_tenant_id : Optional[Callable[[str, Dict[str, Any]], Awaitable[str]]]
-
-
-
-
var recipe_id
-
-
-
-
-

Static methods

-
-
-def get_instance() ‑> DashboardRecipe -
-
-
-
- -Expand source code - -
@staticmethod
-def get_instance() -> DashboardRecipe:
-    if DashboardRecipe.__instance is not None:
-        return DashboardRecipe.__instance
-    raise_general_exception(
-        "Initialisation not done. Did you forget to call the SuperTokens.init function?"
-    )
-
-
-
-def init(api_key: Optional[str], admins: Optional[List[str]] = None, override: Optional[InputOverrideConfig] = None) -
-
-
-
- -Expand source code - -
@staticmethod
-def init(
-    api_key: Optional[str],
-    admins: Optional[List[str]] = None,
-    override: Optional[InputOverrideConfig] = None,
-):
-    def func(app_info: AppInfo):
-        if DashboardRecipe.__instance is None:
-            DashboardRecipe.__instance = DashboardRecipe(
-                DashboardRecipe.recipe_id,
-                app_info,
-                api_key,
-                admins,
-                override,
-            )
-            return DashboardRecipe.__instance
-        raise Exception(
-            None,
-            "Dashboard recipe has already been initialised. Please check your code for bugs.",
-        )
-
-    return func
-
-
-
-def reset() -
-
-
-
- -Expand source code - -
@staticmethod
-def reset():
-    if ("SUPERTOKENS_ENV" not in environ) or (
-        environ["SUPERTOKENS_ENV"] != "testing"
-    ):
-        raise_general_exception("calling testing function in non testing env")
-    DashboardRecipe.__instance = None
-
-
-
-

Methods

-
-
-def get_all_cors_headers(self) ‑> List[str] -
-
-
-
- -Expand source code - -
def get_all_cors_headers(self) -> List[str]:
-    return []
-
-
-
-def get_apis_handled(self) ‑> List[APIHandled] -
-
-
-
- -Expand source code - -
def get_apis_handled(self) -> List[APIHandled]:
-    return [
-        APIHandled(
-            NormalisedURLPath(get_api_path_with_dashboard_base("/")),
-            "get",
-            DASHBOARD_API,
-            False,
-        ),
-        APIHandled(
-            NormalisedURLPath(get_api_path_with_dashboard_base(SIGN_IN_API)),
-            "post",
-            SIGN_IN_API,
-            False,
-        ),
-        APIHandled(
-            NormalisedURLPath(get_api_path_with_dashboard_base(VALIDATE_KEY_API)),
-            "post",
-            VALIDATE_KEY_API,
-            False,
-        ),
-        APIHandled(
-            NormalisedURLPath(get_api_path_with_dashboard_base(SIGN_OUT_API)),
-            "post",
-            SIGN_OUT_API,
-            False,
-        ),
-        APIHandled(
-            NormalisedURLPath(get_api_path_with_dashboard_base(USERS_LIST_GET_API)),
-            "get",
-            USERS_LIST_GET_API,
-            False,
-        ),
-        APIHandled(
-            NormalisedURLPath(get_api_path_with_dashboard_base(USERS_COUNT_API)),
-            "get",
-            USERS_COUNT_API,
-            False,
-        ),
-        APIHandled(
-            NormalisedURLPath(get_api_path_with_dashboard_base(USER_API)),
-            "get",
-            USER_API,
-            False,
-        ),
-        APIHandled(
-            NormalisedURLPath(get_api_path_with_dashboard_base(USER_API)),
-            "post",
-            USER_API,
-            False,
-        ),
-        APIHandled(
-            NormalisedURLPath(get_api_path_with_dashboard_base(USER_API)),
-            "put",
-            USER_API,
-            False,
-        ),
-        APIHandled(
-            NormalisedURLPath(get_api_path_with_dashboard_base(USER_API)),
-            "delete",
-            USER_API,
-            False,
-        ),
-        APIHandled(
-            NormalisedURLPath(
-                get_api_path_with_dashboard_base(USER_EMAIL_VERIFY_API)
-            ),
-            "get",
-            USER_EMAIL_VERIFY_API,
-            False,
-        ),
-        APIHandled(
-            NormalisedURLPath(
-                get_api_path_with_dashboard_base(USER_EMAIL_VERIFY_API)
-            ),
-            "put",
-            USER_EMAIL_VERIFY_API,
-            False,
-        ),
-        APIHandled(
-            NormalisedURLPath(get_api_path_with_dashboard_base(USER_METADATA_API)),
-            "get",
-            USER_METADATA_API,
-            False,
-        ),
-        APIHandled(
-            NormalisedURLPath(get_api_path_with_dashboard_base(USER_METADATA_API)),
-            "put",
-            USER_METADATA_API,
-            False,
-        ),
-        APIHandled(
-            NormalisedURLPath(get_api_path_with_dashboard_base(USER_SESSION_API)),
-            "get",
-            USER_SESSION_API,
-            False,
-        ),
-        APIHandled(
-            NormalisedURLPath(get_api_path_with_dashboard_base(USER_SESSION_API)),
-            "post",
-            USER_SESSION_API,
-            False,
-        ),
-        APIHandled(
-            NormalisedURLPath(get_api_path_with_dashboard_base(USER_PASSWORD_API)),
-            "put",
-            USER_PASSWORD_API,
-            False,
-        ),
-        APIHandled(
-            NormalisedURLPath(
-                get_api_path_with_dashboard_base(USER_EMAIL_VERIFY_TOKEN_API)
-            ),
-            "post",
-            USER_EMAIL_VERIFY_TOKEN_API,
-            False,
-        ),
-        APIHandled(
-            NormalisedURLPath(get_api_path_with_dashboard_base(SEARCH_TAGS_API)),
-            "get",
-            SEARCH_TAGS_API,
-            False,
-        ),
-        APIHandled(
-            NormalisedURLPath(
-                get_api_path_with_dashboard_base(DASHBOARD_ANALYTICS_API)
-            ),
-            "post",
-            DASHBOARD_ANALYTICS_API,
-            False,
-        ),
-        APIHandled(
-            NormalisedURLPath(get_api_path_with_dashboard_base(TENANTS_LIST_API)),
-            "get",
-            TENANTS_LIST_API,
-            False,
-        ),
-    ]
-
-
-
-async def handle_api_request(self, request_id: str, tenant_id: str, request: BaseRequest, path: NormalisedURLPath, method: str, response: BaseResponse, user_context: Dict[str, Any]) ‑> Optional[BaseResponse] -
-
-
-
- -Expand source code - -
async def handle_api_request(
-    self,
-    request_id: str,
-    tenant_id: str,
-    request: BaseRequest,
-    path: NormalisedURLPath,
-    method: str,
-    response: BaseResponse,
-    user_context: Dict[str, Any],
-) -> Optional[BaseResponse]:
-    api_options = APIOptions(
-        request,
-        response,
-        self.recipe_id,
-        self.config,
-        self.recipe_implementation,
-        self.get_app_info(),
-    )
-    # For these APIs we dont need API key validation
-    if request_id == DASHBOARD_API:
-        return await handle_dashboard_api(
-            self.api_implementation, api_options, user_context
-        )
-    if request_id == VALIDATE_KEY_API:
-        return await handle_validate_key_api(
-            self.api_implementation, api_options, user_context
-        )
-    if request_id == SIGN_IN_API:
-        return await handle_emailpassword_signin_api(
-            self.api_implementation, api_options, user_context
-        )
-
-    # Do API key validation for the remaining APIs
-    api_function: Optional[
-        Callable[
-            [APIInterface, str, APIOptions, Dict[str, Any]], Awaitable[APIResponse]
-        ]
-    ] = None
-    if request_id == USERS_LIST_GET_API:
-        api_function = handle_users_get_api
-    elif request_id == USERS_COUNT_API:
-        api_function = handle_users_count_get_api
-    elif request_id == USER_API:
-        if method == "get":
-            api_function = handle_user_get
-        if method == "delete":
-            api_function = handle_user_delete
-        if method == "put":
-            api_function = handle_user_put
-    elif request_id == USER_EMAIL_VERIFY_API:
-        if method == "get":
-            api_function = handle_user_email_verify_get
-        if method == "put":
-            api_function = handle_user_email_verify_put
-    elif request_id == USER_METADATA_API:
-        if method == "get":
-            api_function = handle_metadata_get
-        if method == "put":
-            api_function = handle_metadata_put
-    elif request_id == USER_SESSION_API:
-        if method == "get":
-            api_function = handle_sessions_get
-        if method == "post":
-            api_function = handle_user_sessions_post
-    elif request_id == USER_PASSWORD_API:
-        api_function = handle_user_password_put
-    elif request_id == USER_EMAIL_VERIFY_TOKEN_API:
-        api_function = handle_email_verify_token_post
-    elif request_id == SIGN_OUT_API:
-        api_function = handle_emailpassword_signout_api
-    elif request_id == SEARCH_TAGS_API:
-        api_function = handle_get_tags
-    elif request_id == DASHBOARD_ANALYTICS_API:
-        if method == "post":
-            api_function = handle_analytics_post
-    elif request_id == TENANTS_LIST_API:
-        api_function = handle_list_tenants_api
-
-    if api_function is not None:
-        return await api_key_protector(
-            self.api_implementation,
-            tenant_id,
-            api_options,
-            api_function,
-            user_context,
-        )
-
-    return None
-
-
-
-async def handle_error(self, request: BaseRequest, err: SuperTokensError, response: BaseResponse, user_context: Dict[str, Any]) ‑> BaseResponse -
-
-
-
- -Expand source code - -
async def handle_error(
-    self,
-    request: BaseRequest,
-    err: SuperTokensError,
-    response: BaseResponse,
-    user_context: Dict[str, Any],
-) -> BaseResponse:
-    raise err
-
-
-
-def is_error_from_this_recipe_based_on_instance(self, err: Exception) ‑> bool -
-
-
-
- -Expand source code - -
def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
-    return isinstance(err, SuperTokensError) and (
-        isinstance(err, SuperTokensDashboardError)
-    )
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/dashboard/recipe_implementation.html b/html/supertokens_python/recipe/dashboard/recipe_implementation.html deleted file mode 100644 index c44d25aa6..000000000 --- a/html/supertokens_python/recipe/dashboard/recipe_implementation.html +++ /dev/null @@ -1,342 +0,0 @@ - - - - - - -supertokens_python.recipe.dashboard.recipe_implementation API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.dashboard.recipe_implementation

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from typing import Any, Dict
-
-from supertokens_python.constants import DASHBOARD_VERSION
-from supertokens_python.framework import BaseRequest
-from supertokens_python.normalised_url_path import NormalisedURLPath
-from supertokens_python.utils import log_debug_message, normalise_http_method
-from supertokens_python.querier import Querier
-from supertokens_python.recipe.dashboard.constants import (
-    DASHBOARD_ANALYTICS_API,
-    SIGN_OUT_API,
-)
-
-from .interfaces import RecipeInterface
-from .utils import DashboardConfig, validate_api_key
-from .exceptions import DashboardOperationNotAllowedError
-
-
-class RecipeImplementation(RecipeInterface):
-    async def get_dashboard_bundle_location(self, user_context: Dict[str, Any]) -> str:
-        return f"https://cdn.jsdelivr.net/gh/supertokens/dashboard@v{DASHBOARD_VERSION}/build/"
-
-    async def should_allow_access(
-        self,
-        request: BaseRequest,
-        config: DashboardConfig,
-        user_context: Dict[str, Any],
-    ) -> bool:
-        # For cases where we're not using the API key, the JWT is being used; we allow their access by default
-        if config.api_key is None:
-            auth_header_value = request.get_header("authorization")
-            if not auth_header_value:
-                return False
-
-            auth_header_value = auth_header_value.split()[1]
-            session_verification_response = (
-                await Querier.get_instance().send_post_request(
-                    NormalisedURLPath("/recipe/dashboard/session/verify"),
-                    {"sessionId": auth_header_value},
-                    user_context=user_context,
-                )
-            )
-            if session_verification_response.get("status") != "OK":
-                return False
-
-            # For all non GET requests we also want to check if the
-            # user is allowed to perform this operation
-            if normalise_http_method(request.method()) != "get":
-                # We dont want to block the analytics API
-                if request.get_original_url().endswith(DASHBOARD_ANALYTICS_API):
-                    return True
-
-                # We do not want to block the sign out request
-                if request.get_original_url().endswith(SIGN_OUT_API):
-                    return True
-
-                admins = config.admins
-
-                if admins is None:
-                    return True
-
-                if len(admins) == 0:
-                    log_debug_message(
-                        "User Dashboard: Throwing OPERATION_NOT_ALLOWED because user is not an admin"
-                    )
-                    raise DashboardOperationNotAllowedError()
-
-                user_email = session_verification_response.get("email")
-
-                if user_email is None or not isinstance(user_email, str):
-                    log_debug_message(
-                        "User Dashboard: Returning UNAUTHORISED_ERROR because no email was provided in headers"
-                    )
-                    return False
-
-                if user_email not in admins:
-                    log_debug_message(
-                        "User Dashboard: Throwing OPERATION_NOT_ALLOWED because user is not an admin"
-                    )
-                    raise DashboardOperationNotAllowedError()
-
-            return True
-
-        return await validate_api_key(request, config, user_context)
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class RecipeImplementation -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class RecipeImplementation(RecipeInterface):
-    async def get_dashboard_bundle_location(self, user_context: Dict[str, Any]) -> str:
-        return f"https://cdn.jsdelivr.net/gh/supertokens/dashboard@v{DASHBOARD_VERSION}/build/"
-
-    async def should_allow_access(
-        self,
-        request: BaseRequest,
-        config: DashboardConfig,
-        user_context: Dict[str, Any],
-    ) -> bool:
-        # For cases where we're not using the API key, the JWT is being used; we allow their access by default
-        if config.api_key is None:
-            auth_header_value = request.get_header("authorization")
-            if not auth_header_value:
-                return False
-
-            auth_header_value = auth_header_value.split()[1]
-            session_verification_response = (
-                await Querier.get_instance().send_post_request(
-                    NormalisedURLPath("/recipe/dashboard/session/verify"),
-                    {"sessionId": auth_header_value},
-                    user_context=user_context,
-                )
-            )
-            if session_verification_response.get("status") != "OK":
-                return False
-
-            # For all non GET requests we also want to check if the
-            # user is allowed to perform this operation
-            if normalise_http_method(request.method()) != "get":
-                # We dont want to block the analytics API
-                if request.get_original_url().endswith(DASHBOARD_ANALYTICS_API):
-                    return True
-
-                # We do not want to block the sign out request
-                if request.get_original_url().endswith(SIGN_OUT_API):
-                    return True
-
-                admins = config.admins
-
-                if admins is None:
-                    return True
-
-                if len(admins) == 0:
-                    log_debug_message(
-                        "User Dashboard: Throwing OPERATION_NOT_ALLOWED because user is not an admin"
-                    )
-                    raise DashboardOperationNotAllowedError()
-
-                user_email = session_verification_response.get("email")
-
-                if user_email is None or not isinstance(user_email, str):
-                    log_debug_message(
-                        "User Dashboard: Returning UNAUTHORISED_ERROR because no email was provided in headers"
-                    )
-                    return False
-
-                if user_email not in admins:
-                    log_debug_message(
-                        "User Dashboard: Throwing OPERATION_NOT_ALLOWED because user is not an admin"
-                    )
-                    raise DashboardOperationNotAllowedError()
-
-            return True
-
-        return await validate_api_key(request, config, user_context)
-
-

Ancestors

- -

Methods

-
-
-async def get_dashboard_bundle_location(self, user_context: Dict[str, Any]) ‑> str -
-
-
-
- -Expand source code - -
async def get_dashboard_bundle_location(self, user_context: Dict[str, Any]) -> str:
-    return f"https://cdn.jsdelivr.net/gh/supertokens/dashboard@v{DASHBOARD_VERSION}/build/"
-
-
-
-async def should_allow_access(self, request: BaseRequest, config: DashboardConfig, user_context: Dict[str, Any]) ‑> bool -
-
-
-
- -Expand source code - -
async def should_allow_access(
-    self,
-    request: BaseRequest,
-    config: DashboardConfig,
-    user_context: Dict[str, Any],
-) -> bool:
-    # For cases where we're not using the API key, the JWT is being used; we allow their access by default
-    if config.api_key is None:
-        auth_header_value = request.get_header("authorization")
-        if not auth_header_value:
-            return False
-
-        auth_header_value = auth_header_value.split()[1]
-        session_verification_response = (
-            await Querier.get_instance().send_post_request(
-                NormalisedURLPath("/recipe/dashboard/session/verify"),
-                {"sessionId": auth_header_value},
-                user_context=user_context,
-            )
-        )
-        if session_verification_response.get("status") != "OK":
-            return False
-
-        # For all non GET requests we also want to check if the
-        # user is allowed to perform this operation
-        if normalise_http_method(request.method()) != "get":
-            # We dont want to block the analytics API
-            if request.get_original_url().endswith(DASHBOARD_ANALYTICS_API):
-                return True
-
-            # We do not want to block the sign out request
-            if request.get_original_url().endswith(SIGN_OUT_API):
-                return True
-
-            admins = config.admins
-
-            if admins is None:
-                return True
-
-            if len(admins) == 0:
-                log_debug_message(
-                    "User Dashboard: Throwing OPERATION_NOT_ALLOWED because user is not an admin"
-                )
-                raise DashboardOperationNotAllowedError()
-
-            user_email = session_verification_response.get("email")
-
-            if user_email is None or not isinstance(user_email, str):
-                log_debug_message(
-                    "User Dashboard: Returning UNAUTHORISED_ERROR because no email was provided in headers"
-                )
-                return False
-
-            if user_email not in admins:
-                log_debug_message(
-                    "User Dashboard: Throwing OPERATION_NOT_ALLOWED because user is not an admin"
-                )
-                raise DashboardOperationNotAllowedError()
-
-        return True
-
-    return await validate_api_key(request, config, user_context)
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/dashboard/utils.html b/html/supertokens_python/recipe/dashboard/utils.html deleted file mode 100644 index e7f79748c..000000000 --- a/html/supertokens_python/recipe/dashboard/utils.html +++ /dev/null @@ -1,1060 +0,0 @@ - - - - - - -supertokens_python.recipe.dashboard.utils API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.dashboard.utils

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from typing import TYPE_CHECKING, Any, Callable, Dict, Optional, Union, List
-
-if TYPE_CHECKING:
-    from supertokens_python.framework.request import BaseRequest
-
-from supertokens_python.recipe.emailpassword import EmailPasswordRecipe
-from supertokens_python.recipe.emailpassword.asyncio import (
-    get_user_by_id as ep_get_user_by_id,
-)
-from supertokens_python.recipe.passwordless import PasswordlessRecipe
-from supertokens_python.recipe.passwordless.asyncio import (
-    get_user_by_id as pless_get_user_by_id,
-)
-from supertokens_python.recipe.thirdparty import ThirdPartyRecipe
-from supertokens_python.recipe.thirdparty.asyncio import (
-    get_user_by_id as tp_get_user_by_idx,
-)
-from supertokens_python.types import User
-from supertokens_python.utils import Awaitable, log_debug_message, normalise_email
-
-from ...normalised_url_path import NormalisedURLPath
-from .constants import (
-    DASHBOARD_ANALYTICS_API,
-    DASHBOARD_API,
-    SIGN_OUT_API,
-    SIGN_IN_API,
-    SEARCH_TAGS_API,
-    USER_API,
-    USER_EMAIL_VERIFY_API,
-    USER_EMAIL_VERIFY_TOKEN_API,
-    USER_METADATA_API,
-    USER_PASSWORD_API,
-    USER_SESSION_API,
-    USERS_COUNT_API,
-    USERS_LIST_GET_API,
-    VALIDATE_KEY_API,
-)
-
-if TYPE_CHECKING:
-    from .interfaces import APIInterface, RecipeInterface
-
-
-class UserWithMetadata:
-    user_id: str
-    time_joined: int
-    recipe_id: Optional[str] = None
-    email: Optional[str] = None
-    phone_number: Optional[str] = None
-    tp_info: Optional[Dict[str, Any]] = None
-    first_name: Optional[str] = None
-    last_name: Optional[str] = None
-    tenant_ids: List[str]
-
-    def from_user(
-        self,
-        user: User,
-        first_name: Optional[str] = None,
-        last_name: Optional[str] = None,
-    ):
-        self.first_name = first_name
-        self.last_name = last_name
-
-        self.user_id = user.user_id
-        # from_user() is called in /api/users (note extra s)
-        # here we DashboardUsersGetResponse() doesn't maintain
-        # recipe id for each user on its own. That's why we need
-        # to set self.recipe_id here.
-        self.recipe_id = user.recipe_id
-        self.time_joined = user.time_joined
-        self.email = user.email
-        self.phone_number = user.phone_number
-        self.tp_info = (
-            None if user.third_party_info is None else user.third_party_info.__dict__
-        )
-        self.tenant_ids = user.tenant_ids
-
-        return self
-
-    def from_dict(
-        self,
-        user_obj_dict: Dict[str, Any],
-        first_name: Optional[str] = None,
-        last_name: Optional[str] = None,
-    ):
-        self.first_name = first_name
-        self.last_name = last_name
-
-        self.user_id = user_obj_dict["user_id"]
-        # from_dict() is used in `/api/user` where
-        # recipe_id is already passed seperately to
-        # GetUserForRecipeIdResult object
-        # So we set recipe_id to None here
-        self.recipe_id = None
-        self.time_joined = user_obj_dict["time_joined"]
-        self.tenant_ids = user_obj_dict.get("tenant_ids", [])
-
-        self.email = user_obj_dict.get("email")
-        self.phone_number = user_obj_dict.get("phone_number")
-        self.tp_info = (
-            None
-            if user_obj_dict.get("third_party_info") is None
-            else user_obj_dict["third_party_info"].__dict__
-        )
-
-        return self
-
-    def to_json(self) -> Dict[str, Any]:
-        user_json: Dict[str, Any] = {
-            "id": self.user_id,
-            "timeJoined": self.time_joined,
-            "tenantIds": self.tenant_ids,
-        }
-        if self.tp_info is not None:
-            user_json["thirdParty"] = {
-                "id": self.tp_info["id"],
-                "userId": self.tp_info["user_id"],
-            }
-        if self.phone_number is not None:
-            user_json["phoneNumber"] = self.phone_number
-        if self.email is not None:
-            user_json["email"] = self.email
-        if self.first_name is not None:
-            user_json["firstName"] = self.first_name
-        if self.last_name is not None:
-            user_json["lastName"] = self.last_name
-
-        if self.recipe_id is not None:
-            return {
-                "recipeId": self.recipe_id,
-                "user": user_json,
-            }
-        return user_json
-
-
-class InputOverrideConfig:
-    def __init__(
-        self,
-        functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None,
-        apis: Union[Callable[[APIInterface], APIInterface], None] = None,
-    ):
-        self.functions = functions
-        self.apis = apis
-
-
-class OverrideConfig:
-    def __init__(
-        self,
-        functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None,
-        apis: Union[Callable[[APIInterface], APIInterface], None] = None,
-    ):
-        self.functions = functions
-        self.apis = apis
-
-
-class DashboardConfig:
-    def __init__(
-        self,
-        api_key: Optional[str],
-        admins: Optional[List[str]],
-        override: OverrideConfig,
-        auth_mode: str,
-    ):
-        self.api_key = api_key
-        self.admins = admins
-        self.override = override
-        self.auth_mode = auth_mode
-
-
-def validate_and_normalise_user_input(
-    # app_info: AppInfo,
-    api_key: Union[str, None],
-    admins: Optional[List[str]],
-    override: Optional[InputOverrideConfig] = None,
-) -> DashboardConfig:
-
-    if override is None:
-        override = InputOverrideConfig()
-
-    if api_key is not None and admins is not None:
-        log_debug_message(
-            "User Dashboard: Providing 'admins' has no effect when using an api key."
-        )
-
-    admins = [normalise_email(a) for a in admins] if admins is not None else None
-
-    return DashboardConfig(
-        api_key,
-        admins,
-        OverrideConfig(
-            functions=override.functions,
-            apis=override.apis,
-        ),
-        "api-key" if api_key else "email-password",
-    )
-
-
-def is_api_path(path: NormalisedURLPath, base_path: NormalisedURLPath) -> bool:
-    dashboard_recipe_base_path = base_path.append(NormalisedURLPath(DASHBOARD_API))
-
-    if not path.startswith(dashboard_recipe_base_path):
-        return False
-
-    path_without_dashboard_path = path.get_as_string_dangerous().split(DASHBOARD_API)[1]
-
-    if len(path_without_dashboard_path) > 0 and path_without_dashboard_path[0] == "/":
-        path_without_dashboard_path = path_without_dashboard_path[1:]
-
-    if path_without_dashboard_path.split("/")[0] == "api":
-        return True
-
-    return False
-
-
-def get_api_if_matched(path: NormalisedURLPath, method: str) -> Optional[str]:
-    path_str = path.get_as_string_dangerous()
-
-    if path_str.endswith(VALIDATE_KEY_API) and method == "post":
-        return VALIDATE_KEY_API
-    if path_str.endswith(USERS_LIST_GET_API) and method == "get":
-        return USERS_LIST_GET_API
-    if path_str.endswith(USERS_COUNT_API) and method == "get":
-        return USERS_COUNT_API
-    if path_str.endswith(USER_API) and method in ("get", "delete", "put"):
-        return USER_API
-    if path_str.endswith(USER_EMAIL_VERIFY_API) and method in ("get", "put"):
-        return USER_EMAIL_VERIFY_API
-    if path_str.endswith(USER_METADATA_API) and method in ("get", "put"):
-        return USER_METADATA_API
-    if path_str.endswith(USER_SESSION_API) and method in ("get", "post"):
-        return USER_SESSION_API
-    if path_str.endswith(USER_PASSWORD_API) and method == "put":
-        return USER_PASSWORD_API
-    if path_str.endswith(USER_EMAIL_VERIFY_TOKEN_API) and method == "post":
-        return USER_EMAIL_VERIFY_TOKEN_API
-    if path_str.endswith(SIGN_IN_API) and method == "post":
-        return SIGN_IN_API
-    if path_str.endswith(SIGN_OUT_API) and method == "post":
-        return SIGN_OUT_API
-    if path_str.endswith(SEARCH_TAGS_API) and method == "get":
-        return SEARCH_TAGS_API
-    if path_str.endswith(DASHBOARD_ANALYTICS_API) and method == "post":
-        return DASHBOARD_ANALYTICS_API
-
-    return None
-
-
-def is_valid_recipe_id(recipe_id: str) -> bool:
-    return recipe_id in ("emailpassword", "thirdparty", "passwordless")
-
-
-class GetUserForRecipeIdResult:
-    def __init__(self, user: UserWithMetadata, recipe: str):
-        self.user = user
-        self.recipe = recipe
-
-
-if TYPE_CHECKING:
-    from supertokens_python.recipe.emailpassword.types import User as EmailPasswordUser
-    from supertokens_python.recipe.passwordless.types import User as PasswordlessUser
-    from supertokens_python.recipe.thirdparty.types import User as ThirdPartyUser
-
-    GetUserResult = Union[
-        EmailPasswordUser,
-        ThirdPartyUser,
-        PasswordlessUser,
-        None,
-    ]
-
-
-async def get_user_for_recipe_id(
-    user_id: str, recipe_id: str
-) -> Optional[GetUserForRecipeIdResult]:
-    user: Optional[UserWithMetadata] = None
-    recipe: Optional[str] = None
-
-    async def update_user_dict(
-        get_user_funcs: List[Callable[[str], Awaitable[GetUserResult]]],
-        recipes: List[str],
-    ):
-        nonlocal user, user_id, recipe
-
-        for get_user_func, recipe_id in zip(get_user_funcs, recipes):
-            try:
-                recipe_user = await get_user_func(user_id)  # type: ignore
-
-                if recipe_user is not None:
-                    user = UserWithMetadata().from_dict(
-                        recipe_user.__dict__, first_name="", last_name=""
-                    )
-                    recipe = recipe_id
-                    break
-            except Exception:
-                pass
-
-    if recipe_id == EmailPasswordRecipe.recipe_id:
-        await update_user_dict(
-            [ep_get_user_by_id],
-            ["emailpassword"],
-        )
-
-    elif recipe_id == ThirdPartyRecipe.recipe_id:
-        await update_user_dict(
-            [tp_get_user_by_idx],
-            ["thirdparty"],
-        )
-
-    elif recipe_id == PasswordlessRecipe.recipe_id:
-        await update_user_dict(
-            [pless_get_user_by_id],
-            ["passwordless"],
-        )
-
-    if user is not None and recipe is not None:
-        return GetUserForRecipeIdResult(user, recipe)
-
-    return None
-
-
-def is_recipe_initialised(recipeId: str) -> bool:
-    isRecipeInitialised: bool = False
-
-    if recipeId == EmailPasswordRecipe.recipe_id:
-        try:
-            EmailPasswordRecipe.get_instance()
-            isRecipeInitialised = True
-        except Exception:
-            pass
-
-    elif recipeId == PasswordlessRecipe.recipe_id:
-        try:
-            PasswordlessRecipe.get_instance()
-            isRecipeInitialised = True
-        except Exception:
-            pass
-
-    elif recipeId == ThirdPartyRecipe.recipe_id:
-        try:
-            ThirdPartyRecipe.get_instance()
-            isRecipeInitialised = True
-        except Exception:
-            pass
-
-    return isRecipeInitialised
-
-
-async def validate_api_key(
-    req: BaseRequest, config: DashboardConfig, _user_context: Dict[str, Any]
-) -> bool:
-    api_key_header_value = req.get_header("authorization")
-    if not api_key_header_value:
-        return False
-    # We receieve the api key as `Bearer API_KEY`, this retrieves just the key
-    api_key_header_value = api_key_header_value.split(" ")[1]
-    return api_key_header_value == config.api_key
-
-
-def get_api_path_with_dashboard_base(path: str) -> str:
-    return DASHBOARD_API + path
-
-
-
-
-
-
-
-

Functions

-
-
-def get_api_if_matched(path: NormalisedURLPath, method: str) ‑> Optional[str] -
-
-
-
- -Expand source code - -
def get_api_if_matched(path: NormalisedURLPath, method: str) -> Optional[str]:
-    path_str = path.get_as_string_dangerous()
-
-    if path_str.endswith(VALIDATE_KEY_API) and method == "post":
-        return VALIDATE_KEY_API
-    if path_str.endswith(USERS_LIST_GET_API) and method == "get":
-        return USERS_LIST_GET_API
-    if path_str.endswith(USERS_COUNT_API) and method == "get":
-        return USERS_COUNT_API
-    if path_str.endswith(USER_API) and method in ("get", "delete", "put"):
-        return USER_API
-    if path_str.endswith(USER_EMAIL_VERIFY_API) and method in ("get", "put"):
-        return USER_EMAIL_VERIFY_API
-    if path_str.endswith(USER_METADATA_API) and method in ("get", "put"):
-        return USER_METADATA_API
-    if path_str.endswith(USER_SESSION_API) and method in ("get", "post"):
-        return USER_SESSION_API
-    if path_str.endswith(USER_PASSWORD_API) and method == "put":
-        return USER_PASSWORD_API
-    if path_str.endswith(USER_EMAIL_VERIFY_TOKEN_API) and method == "post":
-        return USER_EMAIL_VERIFY_TOKEN_API
-    if path_str.endswith(SIGN_IN_API) and method == "post":
-        return SIGN_IN_API
-    if path_str.endswith(SIGN_OUT_API) and method == "post":
-        return SIGN_OUT_API
-    if path_str.endswith(SEARCH_TAGS_API) and method == "get":
-        return SEARCH_TAGS_API
-    if path_str.endswith(DASHBOARD_ANALYTICS_API) and method == "post":
-        return DASHBOARD_ANALYTICS_API
-
-    return None
-
-
-
-def get_api_path_with_dashboard_base(path: str) ‑> str -
-
-
-
- -Expand source code - -
def get_api_path_with_dashboard_base(path: str) -> str:
-    return DASHBOARD_API + path
-
-
-
-async def get_user_for_recipe_id(user_id: str, recipe_id: str) ‑> Optional[GetUserForRecipeIdResult] -
-
-
-
- -Expand source code - -
async def get_user_for_recipe_id(
-    user_id: str, recipe_id: str
-) -> Optional[GetUserForRecipeIdResult]:
-    user: Optional[UserWithMetadata] = None
-    recipe: Optional[str] = None
-
-    async def update_user_dict(
-        get_user_funcs: List[Callable[[str], Awaitable[GetUserResult]]],
-        recipes: List[str],
-    ):
-        nonlocal user, user_id, recipe
-
-        for get_user_func, recipe_id in zip(get_user_funcs, recipes):
-            try:
-                recipe_user = await get_user_func(user_id)  # type: ignore
-
-                if recipe_user is not None:
-                    user = UserWithMetadata().from_dict(
-                        recipe_user.__dict__, first_name="", last_name=""
-                    )
-                    recipe = recipe_id
-                    break
-            except Exception:
-                pass
-
-    if recipe_id == EmailPasswordRecipe.recipe_id:
-        await update_user_dict(
-            [ep_get_user_by_id],
-            ["emailpassword"],
-        )
-
-    elif recipe_id == ThirdPartyRecipe.recipe_id:
-        await update_user_dict(
-            [tp_get_user_by_idx],
-            ["thirdparty"],
-        )
-
-    elif recipe_id == PasswordlessRecipe.recipe_id:
-        await update_user_dict(
-            [pless_get_user_by_id],
-            ["passwordless"],
-        )
-
-    if user is not None and recipe is not None:
-        return GetUserForRecipeIdResult(user, recipe)
-
-    return None
-
-
-
-def is_api_path(path: NormalisedURLPath, base_path: NormalisedURLPath) ‑> bool -
-
-
-
- -Expand source code - -
def is_api_path(path: NormalisedURLPath, base_path: NormalisedURLPath) -> bool:
-    dashboard_recipe_base_path = base_path.append(NormalisedURLPath(DASHBOARD_API))
-
-    if not path.startswith(dashboard_recipe_base_path):
-        return False
-
-    path_without_dashboard_path = path.get_as_string_dangerous().split(DASHBOARD_API)[1]
-
-    if len(path_without_dashboard_path) > 0 and path_without_dashboard_path[0] == "/":
-        path_without_dashboard_path = path_without_dashboard_path[1:]
-
-    if path_without_dashboard_path.split("/")[0] == "api":
-        return True
-
-    return False
-
-
-
-def is_recipe_initialised(recipeId: str) ‑> bool -
-
-
-
- -Expand source code - -
def is_recipe_initialised(recipeId: str) -> bool:
-    isRecipeInitialised: bool = False
-
-    if recipeId == EmailPasswordRecipe.recipe_id:
-        try:
-            EmailPasswordRecipe.get_instance()
-            isRecipeInitialised = True
-        except Exception:
-            pass
-
-    elif recipeId == PasswordlessRecipe.recipe_id:
-        try:
-            PasswordlessRecipe.get_instance()
-            isRecipeInitialised = True
-        except Exception:
-            pass
-
-    elif recipeId == ThirdPartyRecipe.recipe_id:
-        try:
-            ThirdPartyRecipe.get_instance()
-            isRecipeInitialised = True
-        except Exception:
-            pass
-
-    return isRecipeInitialised
-
-
-
-def is_valid_recipe_id(recipe_id: str) ‑> bool -
-
-
-
- -Expand source code - -
def is_valid_recipe_id(recipe_id: str) -> bool:
-    return recipe_id in ("emailpassword", "thirdparty", "passwordless")
-
-
-
-def validate_and_normalise_user_input(api_key: Union[str, None], admins: Optional[List[str]], override: Optional[InputOverrideConfig] = None) ‑> DashboardConfig -
-
-
-
- -Expand source code - -
def validate_and_normalise_user_input(
-    # app_info: AppInfo,
-    api_key: Union[str, None],
-    admins: Optional[List[str]],
-    override: Optional[InputOverrideConfig] = None,
-) -> DashboardConfig:
-
-    if override is None:
-        override = InputOverrideConfig()
-
-    if api_key is not None and admins is not None:
-        log_debug_message(
-            "User Dashboard: Providing 'admins' has no effect when using an api key."
-        )
-
-    admins = [normalise_email(a) for a in admins] if admins is not None else None
-
-    return DashboardConfig(
-        api_key,
-        admins,
-        OverrideConfig(
-            functions=override.functions,
-            apis=override.apis,
-        ),
-        "api-key" if api_key else "email-password",
-    )
-
-
-
-async def validate_api_key(req: BaseRequest, config: DashboardConfig, _user_context: Dict[str, Any]) ‑> bool -
-
-
-
- -Expand source code - -
async def validate_api_key(
-    req: BaseRequest, config: DashboardConfig, _user_context: Dict[str, Any]
-) -> bool:
-    api_key_header_value = req.get_header("authorization")
-    if not api_key_header_value:
-        return False
-    # We receieve the api key as `Bearer API_KEY`, this retrieves just the key
-    api_key_header_value = api_key_header_value.split(" ")[1]
-    return api_key_header_value == config.api_key
-
-
-
-
-
-

Classes

-
-
-class DashboardConfig -(api_key: Optional[str], admins: Optional[List[str]], override: OverrideConfig, auth_mode: str) -
-
-
-
- -Expand source code - -
class DashboardConfig:
-    def __init__(
-        self,
-        api_key: Optional[str],
-        admins: Optional[List[str]],
-        override: OverrideConfig,
-        auth_mode: str,
-    ):
-        self.api_key = api_key
-        self.admins = admins
-        self.override = override
-        self.auth_mode = auth_mode
-
-
-
-class GetUserForRecipeIdResult -(user: UserWithMetadata, recipe: str) -
-
-
-
- -Expand source code - -
class GetUserForRecipeIdResult:
-    def __init__(self, user: UserWithMetadata, recipe: str):
-        self.user = user
-        self.recipe = recipe
-
-
-
-class InputOverrideConfig -(functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None, apis: Union[Callable[[APIInterface], APIInterface], None] = None) -
-
-
-
- -Expand source code - -
class InputOverrideConfig:
-    def __init__(
-        self,
-        functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None,
-        apis: Union[Callable[[APIInterface], APIInterface], None] = None,
-    ):
-        self.functions = functions
-        self.apis = apis
-
-
-
-class OverrideConfig -(functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None, apis: Union[Callable[[APIInterface], APIInterface], None] = None) -
-
-
-
- -Expand source code - -
class OverrideConfig:
-    def __init__(
-        self,
-        functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None,
-        apis: Union[Callable[[APIInterface], APIInterface], None] = None,
-    ):
-        self.functions = functions
-        self.apis = apis
-
-
-
-class UserWithMetadata -
-
-
-
- -Expand source code - -
class UserWithMetadata:
-    user_id: str
-    time_joined: int
-    recipe_id: Optional[str] = None
-    email: Optional[str] = None
-    phone_number: Optional[str] = None
-    tp_info: Optional[Dict[str, Any]] = None
-    first_name: Optional[str] = None
-    last_name: Optional[str] = None
-    tenant_ids: List[str]
-
-    def from_user(
-        self,
-        user: User,
-        first_name: Optional[str] = None,
-        last_name: Optional[str] = None,
-    ):
-        self.first_name = first_name
-        self.last_name = last_name
-
-        self.user_id = user.user_id
-        # from_user() is called in /api/users (note extra s)
-        # here we DashboardUsersGetResponse() doesn't maintain
-        # recipe id for each user on its own. That's why we need
-        # to set self.recipe_id here.
-        self.recipe_id = user.recipe_id
-        self.time_joined = user.time_joined
-        self.email = user.email
-        self.phone_number = user.phone_number
-        self.tp_info = (
-            None if user.third_party_info is None else user.third_party_info.__dict__
-        )
-        self.tenant_ids = user.tenant_ids
-
-        return self
-
-    def from_dict(
-        self,
-        user_obj_dict: Dict[str, Any],
-        first_name: Optional[str] = None,
-        last_name: Optional[str] = None,
-    ):
-        self.first_name = first_name
-        self.last_name = last_name
-
-        self.user_id = user_obj_dict["user_id"]
-        # from_dict() is used in `/api/user` where
-        # recipe_id is already passed seperately to
-        # GetUserForRecipeIdResult object
-        # So we set recipe_id to None here
-        self.recipe_id = None
-        self.time_joined = user_obj_dict["time_joined"]
-        self.tenant_ids = user_obj_dict.get("tenant_ids", [])
-
-        self.email = user_obj_dict.get("email")
-        self.phone_number = user_obj_dict.get("phone_number")
-        self.tp_info = (
-            None
-            if user_obj_dict.get("third_party_info") is None
-            else user_obj_dict["third_party_info"].__dict__
-        )
-
-        return self
-
-    def to_json(self) -> Dict[str, Any]:
-        user_json: Dict[str, Any] = {
-            "id": self.user_id,
-            "timeJoined": self.time_joined,
-            "tenantIds": self.tenant_ids,
-        }
-        if self.tp_info is not None:
-            user_json["thirdParty"] = {
-                "id": self.tp_info["id"],
-                "userId": self.tp_info["user_id"],
-            }
-        if self.phone_number is not None:
-            user_json["phoneNumber"] = self.phone_number
-        if self.email is not None:
-            user_json["email"] = self.email
-        if self.first_name is not None:
-            user_json["firstName"] = self.first_name
-        if self.last_name is not None:
-            user_json["lastName"] = self.last_name
-
-        if self.recipe_id is not None:
-            return {
-                "recipeId": self.recipe_id,
-                "user": user_json,
-            }
-        return user_json
-
-

Class variables

-
-
var email : Optional[str]
-
-
-
-
var first_name : Optional[str]
-
-
-
-
var last_name : Optional[str]
-
-
-
-
var phone_number : Optional[str]
-
-
-
-
var recipe_id : Optional[str]
-
-
-
-
var tenant_ids : List[str]
-
-
-
-
var time_joined : int
-
-
-
-
var tp_info : Optional[Dict[str, Any]]
-
-
-
-
var user_id : str
-
-
-
-
-

Methods

-
-
-def from_dict(self, user_obj_dict: Dict[str, Any], first_name: Optional[str] = None, last_name: Optional[str] = None) -
-
-
-
- -Expand source code - -
def from_dict(
-    self,
-    user_obj_dict: Dict[str, Any],
-    first_name: Optional[str] = None,
-    last_name: Optional[str] = None,
-):
-    self.first_name = first_name
-    self.last_name = last_name
-
-    self.user_id = user_obj_dict["user_id"]
-    # from_dict() is used in `/api/user` where
-    # recipe_id is already passed seperately to
-    # GetUserForRecipeIdResult object
-    # So we set recipe_id to None here
-    self.recipe_id = None
-    self.time_joined = user_obj_dict["time_joined"]
-    self.tenant_ids = user_obj_dict.get("tenant_ids", [])
-
-    self.email = user_obj_dict.get("email")
-    self.phone_number = user_obj_dict.get("phone_number")
-    self.tp_info = (
-        None
-        if user_obj_dict.get("third_party_info") is None
-        else user_obj_dict["third_party_info"].__dict__
-    )
-
-    return self
-
-
-
-def from_user(self, user: User, first_name: Optional[str] = None, last_name: Optional[str] = None) -
-
-
-
- -Expand source code - -
def from_user(
-    self,
-    user: User,
-    first_name: Optional[str] = None,
-    last_name: Optional[str] = None,
-):
-    self.first_name = first_name
-    self.last_name = last_name
-
-    self.user_id = user.user_id
-    # from_user() is called in /api/users (note extra s)
-    # here we DashboardUsersGetResponse() doesn't maintain
-    # recipe id for each user on its own. That's why we need
-    # to set self.recipe_id here.
-    self.recipe_id = user.recipe_id
-    self.time_joined = user.time_joined
-    self.email = user.email
-    self.phone_number = user.phone_number
-    self.tp_info = (
-        None if user.third_party_info is None else user.third_party_info.__dict__
-    )
-    self.tenant_ids = user.tenant_ids
-
-    return self
-
-
-
-def to_json(self) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
def to_json(self) -> Dict[str, Any]:
-    user_json: Dict[str, Any] = {
-        "id": self.user_id,
-        "timeJoined": self.time_joined,
-        "tenantIds": self.tenant_ids,
-    }
-    if self.tp_info is not None:
-        user_json["thirdParty"] = {
-            "id": self.tp_info["id"],
-            "userId": self.tp_info["user_id"],
-        }
-    if self.phone_number is not None:
-        user_json["phoneNumber"] = self.phone_number
-    if self.email is not None:
-        user_json["email"] = self.email
-    if self.first_name is not None:
-        user_json["firstName"] = self.first_name
-    if self.last_name is not None:
-        user_json["lastName"] = self.last_name
-
-    if self.recipe_id is not None:
-        return {
-            "recipeId": self.recipe_id,
-            "user": user_json,
-        }
-    return user_json
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailpassword/api/email_exists.html b/html/supertokens_python/recipe/emailpassword/api/email_exists.html deleted file mode 100644 index f0809b0e6..000000000 --- a/html/supertokens_python/recipe/emailpassword/api/email_exists.html +++ /dev/null @@ -1,136 +0,0 @@ - - - - - - -supertokens_python.recipe.emailpassword.api.email_exists API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.emailpassword.api.email_exists

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from typing import TYPE_CHECKING, Dict, Any
-
-if TYPE_CHECKING:
-    from supertokens_python.recipe.emailpassword.interfaces import (
-        APIOptions,
-        APIInterface,
-    )
-
-from supertokens_python.exceptions import raise_bad_input_exception
-from supertokens_python.utils import send_200_response
-
-
-async def handle_email_exists_api(
-    tenant_id: str,
-    api_implementation: APIInterface,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-):
-    if api_implementation.disable_email_exists_get:
-        return None
-    email = api_options.request.get_query_param("email")
-    if email is None:
-        raise_bad_input_exception("Please provide the email as a GET param")
-
-    response = await api_implementation.email_exists_get(
-        email, tenant_id, api_options, user_context
-    )
-    return send_200_response(response.to_json(), api_options.response)
-
-
-
-
-
-
-
-

Functions

-
-
-async def handle_email_exists_api(tenant_id: str, api_implementation: APIInterface, api_options: APIOptions, user_context: Dict[str, Any]) -
-
-
-
- -Expand source code - -
async def handle_email_exists_api(
-    tenant_id: str,
-    api_implementation: APIInterface,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-):
-    if api_implementation.disable_email_exists_get:
-        return None
-    email = api_options.request.get_query_param("email")
-    if email is None:
-        raise_bad_input_exception("Please provide the email as a GET param")
-
-    response = await api_implementation.email_exists_get(
-        email, tenant_id, api_options, user_context
-    )
-    return send_200_response(response.to_json(), api_options.response)
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailpassword/api/generate_password_reset_token.html b/html/supertokens_python/recipe/emailpassword/api/generate_password_reset_token.html deleted file mode 100644 index b9453ade5..000000000 --- a/html/supertokens_python/recipe/emailpassword/api/generate_password_reset_token.html +++ /dev/null @@ -1,150 +0,0 @@ - - - - - - -supertokens_python.recipe.emailpassword.api.generate_password_reset_token API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.emailpassword.api.generate_password_reset_token

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from typing import TYPE_CHECKING, Any, Dict
-
-if TYPE_CHECKING:
-    from supertokens_python.recipe.emailpassword.interfaces import (
-        APIOptions,
-        APIInterface,
-    )
-
-from supertokens_python.exceptions import raise_bad_input_exception
-from supertokens_python.utils import send_200_response
-
-from .utils import validate_form_fields_or_throw_error
-
-
-async def handle_generate_password_reset_token_api(
-    tenant_id: str,
-    api_implementation: APIInterface,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-):
-    if api_implementation.disable_generate_password_reset_token_post:
-        return None
-    body = await api_options.request.json()
-    if body is None:
-        raise_bad_input_exception("Please provide a JSON body")
-    form_fields_raw: Any = body["formFields"] if "formFields" in body else []
-    form_fields = await validate_form_fields_or_throw_error(
-        api_options.config.reset_password_using_token_feature.form_fields_for_generate_token_form,
-        form_fields_raw,
-        tenant_id,
-    )
-
-    response = await api_implementation.generate_password_reset_token_post(
-        form_fields, tenant_id, api_options, user_context
-    )
-    return send_200_response(response.to_json(), api_options.response)
-
-
-
-
-
-
-
-

Functions

-
-
-async def handle_generate_password_reset_token_api(tenant_id: str, api_implementation: APIInterface, api_options: APIOptions, user_context: Dict[str, Any]) -
-
-
-
- -Expand source code - -
async def handle_generate_password_reset_token_api(
-    tenant_id: str,
-    api_implementation: APIInterface,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-):
-    if api_implementation.disable_generate_password_reset_token_post:
-        return None
-    body = await api_options.request.json()
-    if body is None:
-        raise_bad_input_exception("Please provide a JSON body")
-    form_fields_raw: Any = body["formFields"] if "formFields" in body else []
-    form_fields = await validate_form_fields_or_throw_error(
-        api_options.config.reset_password_using_token_feature.form_fields_for_generate_token_form,
-        form_fields_raw,
-        tenant_id,
-    )
-
-    response = await api_implementation.generate_password_reset_token_post(
-        form_fields, tenant_id, api_options, user_context
-    )
-    return send_200_response(response.to_json(), api_options.response)
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailpassword/api/implementation.html b/html/supertokens_python/recipe/emailpassword/api/implementation.html deleted file mode 100644 index f8b1081ee..000000000 --- a/html/supertokens_python/recipe/emailpassword/api/implementation.html +++ /dev/null @@ -1,720 +0,0 @@ - - - - - - -supertokens_python.recipe.emailpassword.api.implementation API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.emailpassword.api.implementation

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from typing import TYPE_CHECKING, Any, Dict, List, Union
-
-from supertokens_python.logger import log_debug_message
-from supertokens_python.recipe.emailpassword.constants import (
-    FORM_FIELD_EMAIL_ID,
-    FORM_FIELD_PASSWORD_ID,
-)
-from supertokens_python.recipe.emailpassword.interfaces import (
-    APIInterface,
-    CreateResetPasswordWrongUserIdError,
-    EmailExistsGetOkResult,
-    GeneratePasswordResetTokenPostOkResult,
-    PasswordResetPostInvalidTokenResponse,
-    PasswordResetPostOkResult,
-    ResetPasswordUsingTokenInvalidTokenError,
-    SignInPostOkResult,
-    SignInPostWrongCredentialsError,
-    SignInWrongCredentialsError,
-    SignUpEmailAlreadyExistsError,
-    SignUpPostEmailAlreadyExistsError,
-    SignUpPostOkResult,
-)
-from supertokens_python.recipe.emailpassword.types import (
-    FormField,
-    PasswordResetEmailTemplateVars,
-    PasswordResetEmailTemplateVarsUser,
-)
-from ..utils import get_password_reset_link
-from supertokens_python.recipe.session.asyncio import create_new_session
-from supertokens_python.utils import find_first_occurrence_in_list
-
-if TYPE_CHECKING:
-    from supertokens_python.recipe.emailpassword.interfaces import APIOptions
-
-from supertokens_python.types import GeneralErrorResponse
-
-
-class APIImplementation(APIInterface):
-    async def email_exists_get(
-        self,
-        email: str,
-        tenant_id: str,
-        api_options: APIOptions,
-        user_context: Dict[str, Any],
-    ) -> Union[EmailExistsGetOkResult, GeneralErrorResponse]:
-        user = await api_options.recipe_implementation.get_user_by_email(
-            email, tenant_id, user_context
-        )
-        return EmailExistsGetOkResult(user is not None)
-
-    async def generate_password_reset_token_post(
-        self,
-        form_fields: List[FormField],
-        tenant_id: str,
-        api_options: APIOptions,
-        user_context: Dict[str, Any],
-    ) -> Union[GeneratePasswordResetTokenPostOkResult, GeneralErrorResponse]:
-        emailFormField = find_first_occurrence_in_list(
-            lambda x: x.id == FORM_FIELD_EMAIL_ID, form_fields
-        )
-        if emailFormField is None:
-            raise Exception("Should never come here")
-        email = emailFormField.value
-
-        user = await api_options.recipe_implementation.get_user_by_email(
-            email, tenant_id, user_context
-        )
-
-        if user is None:
-            return GeneratePasswordResetTokenPostOkResult()
-
-        token_result = (
-            await api_options.recipe_implementation.create_reset_password_token(
-                user.user_id, tenant_id, user_context
-            )
-        )
-
-        if isinstance(token_result, CreateResetPasswordWrongUserIdError):
-            log_debug_message(
-                "Password reset email not sent, unknown user id: %s", user.user_id
-            )
-            return GeneratePasswordResetTokenPostOkResult()
-
-        password_reset_link = get_password_reset_link(
-            api_options.app_info,
-            token_result.token,
-            tenant_id,
-            api_options.request,
-            user_context,
-        )
-
-        log_debug_message("Sending password reset email to %s", email)
-        send_email_input = PasswordResetEmailTemplateVars(
-            user=PasswordResetEmailTemplateVarsUser(user.user_id, user.email),
-            password_reset_link=password_reset_link,
-            tenant_id=tenant_id,
-        )
-        await api_options.email_delivery.ingredient_interface_impl.send_email(
-            send_email_input, user_context
-        )
-
-        return GeneratePasswordResetTokenPostOkResult()
-
-    async def password_reset_post(
-        self,
-        form_fields: List[FormField],
-        token: str,
-        tenant_id: str,
-        api_options: APIOptions,
-        user_context: Dict[str, Any],
-    ) -> Union[
-        PasswordResetPostOkResult,
-        PasswordResetPostInvalidTokenResponse,
-        GeneralErrorResponse,
-    ]:
-        new_password_for_field = find_first_occurrence_in_list(
-            lambda x: x.id == FORM_FIELD_PASSWORD_ID, form_fields
-        )
-        if new_password_for_field is None:
-            raise Exception("Should never come here")
-        new_password = new_password_for_field.value
-
-        result = await api_options.recipe_implementation.reset_password_using_token(
-            token, new_password, tenant_id, user_context
-        )
-
-        if isinstance(result, ResetPasswordUsingTokenInvalidTokenError):
-            return PasswordResetPostInvalidTokenResponse()
-
-        return PasswordResetPostOkResult(result.user_id)
-
-    async def sign_in_post(
-        self,
-        form_fields: List[FormField],
-        tenant_id: str,
-        api_options: APIOptions,
-        user_context: Dict[str, Any],
-    ) -> Union[
-        SignInPostOkResult, SignInPostWrongCredentialsError, GeneralErrorResponse
-    ]:
-        password_form_field = find_first_occurrence_in_list(
-            lambda x: x.id == FORM_FIELD_PASSWORD_ID, form_fields
-        )
-        if password_form_field is None:
-            raise Exception("Should never come here")
-        password = password_form_field.value
-
-        email_form_field = find_first_occurrence_in_list(
-            lambda x: x.id == FORM_FIELD_EMAIL_ID, form_fields
-        )
-        if email_form_field is None:
-            raise Exception("Should never come here")
-        email = email_form_field.value
-
-        result = await api_options.recipe_implementation.sign_in(
-            email, password, tenant_id, user_context
-        )
-
-        if isinstance(result, SignInWrongCredentialsError):
-            return SignInPostWrongCredentialsError()
-
-        user = result.user
-        session = await create_new_session(
-            tenant_id=tenant_id,
-            request=api_options.request,
-            user_id=user.user_id,
-            access_token_payload={},
-            session_data_in_database={},
-            user_context=user_context,
-        )
-        return SignInPostOkResult(user, session)
-
-    async def sign_up_post(
-        self,
-        form_fields: List[FormField],
-        tenant_id: str,
-        api_options: APIOptions,
-        user_context: Dict[str, Any],
-    ) -> Union[
-        SignUpPostOkResult, SignUpPostEmailAlreadyExistsError, GeneralErrorResponse
-    ]:
-        password_form_field = find_first_occurrence_in_list(
-            lambda x: x.id == FORM_FIELD_PASSWORD_ID, form_fields
-        )
-        if password_form_field is None:
-            raise Exception("Should never come here")
-        password = password_form_field.value
-
-        email_form_field = find_first_occurrence_in_list(
-            lambda x: x.id == FORM_FIELD_EMAIL_ID, form_fields
-        )
-        if email_form_field is None:
-            raise Exception("Should never come here")
-        email = email_form_field.value
-
-        result = await api_options.recipe_implementation.sign_up(
-            email, password, tenant_id, user_context
-        )
-
-        if isinstance(result, SignUpEmailAlreadyExistsError):
-            return SignUpPostEmailAlreadyExistsError()
-
-        user = result.user
-        session = await create_new_session(
-            tenant_id=tenant_id,
-            request=api_options.request,
-            user_id=user.user_id,
-            access_token_payload={},
-            session_data_in_database={},
-            user_context=user_context,
-        )
-        return SignUpPostOkResult(user, session)
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class APIImplementation -
-
-
-
- -Expand source code - -
class APIImplementation(APIInterface):
-    async def email_exists_get(
-        self,
-        email: str,
-        tenant_id: str,
-        api_options: APIOptions,
-        user_context: Dict[str, Any],
-    ) -> Union[EmailExistsGetOkResult, GeneralErrorResponse]:
-        user = await api_options.recipe_implementation.get_user_by_email(
-            email, tenant_id, user_context
-        )
-        return EmailExistsGetOkResult(user is not None)
-
-    async def generate_password_reset_token_post(
-        self,
-        form_fields: List[FormField],
-        tenant_id: str,
-        api_options: APIOptions,
-        user_context: Dict[str, Any],
-    ) -> Union[GeneratePasswordResetTokenPostOkResult, GeneralErrorResponse]:
-        emailFormField = find_first_occurrence_in_list(
-            lambda x: x.id == FORM_FIELD_EMAIL_ID, form_fields
-        )
-        if emailFormField is None:
-            raise Exception("Should never come here")
-        email = emailFormField.value
-
-        user = await api_options.recipe_implementation.get_user_by_email(
-            email, tenant_id, user_context
-        )
-
-        if user is None:
-            return GeneratePasswordResetTokenPostOkResult()
-
-        token_result = (
-            await api_options.recipe_implementation.create_reset_password_token(
-                user.user_id, tenant_id, user_context
-            )
-        )
-
-        if isinstance(token_result, CreateResetPasswordWrongUserIdError):
-            log_debug_message(
-                "Password reset email not sent, unknown user id: %s", user.user_id
-            )
-            return GeneratePasswordResetTokenPostOkResult()
-
-        password_reset_link = get_password_reset_link(
-            api_options.app_info,
-            token_result.token,
-            tenant_id,
-            api_options.request,
-            user_context,
-        )
-
-        log_debug_message("Sending password reset email to %s", email)
-        send_email_input = PasswordResetEmailTemplateVars(
-            user=PasswordResetEmailTemplateVarsUser(user.user_id, user.email),
-            password_reset_link=password_reset_link,
-            tenant_id=tenant_id,
-        )
-        await api_options.email_delivery.ingredient_interface_impl.send_email(
-            send_email_input, user_context
-        )
-
-        return GeneratePasswordResetTokenPostOkResult()
-
-    async def password_reset_post(
-        self,
-        form_fields: List[FormField],
-        token: str,
-        tenant_id: str,
-        api_options: APIOptions,
-        user_context: Dict[str, Any],
-    ) -> Union[
-        PasswordResetPostOkResult,
-        PasswordResetPostInvalidTokenResponse,
-        GeneralErrorResponse,
-    ]:
-        new_password_for_field = find_first_occurrence_in_list(
-            lambda x: x.id == FORM_FIELD_PASSWORD_ID, form_fields
-        )
-        if new_password_for_field is None:
-            raise Exception("Should never come here")
-        new_password = new_password_for_field.value
-
-        result = await api_options.recipe_implementation.reset_password_using_token(
-            token, new_password, tenant_id, user_context
-        )
-
-        if isinstance(result, ResetPasswordUsingTokenInvalidTokenError):
-            return PasswordResetPostInvalidTokenResponse()
-
-        return PasswordResetPostOkResult(result.user_id)
-
-    async def sign_in_post(
-        self,
-        form_fields: List[FormField],
-        tenant_id: str,
-        api_options: APIOptions,
-        user_context: Dict[str, Any],
-    ) -> Union[
-        SignInPostOkResult, SignInPostWrongCredentialsError, GeneralErrorResponse
-    ]:
-        password_form_field = find_first_occurrence_in_list(
-            lambda x: x.id == FORM_FIELD_PASSWORD_ID, form_fields
-        )
-        if password_form_field is None:
-            raise Exception("Should never come here")
-        password = password_form_field.value
-
-        email_form_field = find_first_occurrence_in_list(
-            lambda x: x.id == FORM_FIELD_EMAIL_ID, form_fields
-        )
-        if email_form_field is None:
-            raise Exception("Should never come here")
-        email = email_form_field.value
-
-        result = await api_options.recipe_implementation.sign_in(
-            email, password, tenant_id, user_context
-        )
-
-        if isinstance(result, SignInWrongCredentialsError):
-            return SignInPostWrongCredentialsError()
-
-        user = result.user
-        session = await create_new_session(
-            tenant_id=tenant_id,
-            request=api_options.request,
-            user_id=user.user_id,
-            access_token_payload={},
-            session_data_in_database={},
-            user_context=user_context,
-        )
-        return SignInPostOkResult(user, session)
-
-    async def sign_up_post(
-        self,
-        form_fields: List[FormField],
-        tenant_id: str,
-        api_options: APIOptions,
-        user_context: Dict[str, Any],
-    ) -> Union[
-        SignUpPostOkResult, SignUpPostEmailAlreadyExistsError, GeneralErrorResponse
-    ]:
-        password_form_field = find_first_occurrence_in_list(
-            lambda x: x.id == FORM_FIELD_PASSWORD_ID, form_fields
-        )
-        if password_form_field is None:
-            raise Exception("Should never come here")
-        password = password_form_field.value
-
-        email_form_field = find_first_occurrence_in_list(
-            lambda x: x.id == FORM_FIELD_EMAIL_ID, form_fields
-        )
-        if email_form_field is None:
-            raise Exception("Should never come here")
-        email = email_form_field.value
-
-        result = await api_options.recipe_implementation.sign_up(
-            email, password, tenant_id, user_context
-        )
-
-        if isinstance(result, SignUpEmailAlreadyExistsError):
-            return SignUpPostEmailAlreadyExistsError()
-
-        user = result.user
-        session = await create_new_session(
-            tenant_id=tenant_id,
-            request=api_options.request,
-            user_id=user.user_id,
-            access_token_payload={},
-            session_data_in_database={},
-            user_context=user_context,
-        )
-        return SignUpPostOkResult(user, session)
-
-

Ancestors

- -

Methods

-
-
-async def email_exists_get(self, email: str, tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[EmailExistsGetOkResult, GeneralErrorResponse] -
-
-
-
- -Expand source code - -
async def email_exists_get(
-    self,
-    email: str,
-    tenant_id: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-) -> Union[EmailExistsGetOkResult, GeneralErrorResponse]:
-    user = await api_options.recipe_implementation.get_user_by_email(
-        email, tenant_id, user_context
-    )
-    return EmailExistsGetOkResult(user is not None)
-
-
-
-async def generate_password_reset_token_post(self, form_fields: List[FormField], tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[GeneratePasswordResetTokenPostOkResult, GeneralErrorResponse] -
-
-
-
- -Expand source code - -
async def generate_password_reset_token_post(
-    self,
-    form_fields: List[FormField],
-    tenant_id: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-) -> Union[GeneratePasswordResetTokenPostOkResult, GeneralErrorResponse]:
-    emailFormField = find_first_occurrence_in_list(
-        lambda x: x.id == FORM_FIELD_EMAIL_ID, form_fields
-    )
-    if emailFormField is None:
-        raise Exception("Should never come here")
-    email = emailFormField.value
-
-    user = await api_options.recipe_implementation.get_user_by_email(
-        email, tenant_id, user_context
-    )
-
-    if user is None:
-        return GeneratePasswordResetTokenPostOkResult()
-
-    token_result = (
-        await api_options.recipe_implementation.create_reset_password_token(
-            user.user_id, tenant_id, user_context
-        )
-    )
-
-    if isinstance(token_result, CreateResetPasswordWrongUserIdError):
-        log_debug_message(
-            "Password reset email not sent, unknown user id: %s", user.user_id
-        )
-        return GeneratePasswordResetTokenPostOkResult()
-
-    password_reset_link = get_password_reset_link(
-        api_options.app_info,
-        token_result.token,
-        tenant_id,
-        api_options.request,
-        user_context,
-    )
-
-    log_debug_message("Sending password reset email to %s", email)
-    send_email_input = PasswordResetEmailTemplateVars(
-        user=PasswordResetEmailTemplateVarsUser(user.user_id, user.email),
-        password_reset_link=password_reset_link,
-        tenant_id=tenant_id,
-    )
-    await api_options.email_delivery.ingredient_interface_impl.send_email(
-        send_email_input, user_context
-    )
-
-    return GeneratePasswordResetTokenPostOkResult()
-
-
-
-async def password_reset_post(self, form_fields: List[FormField], token: str, tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[PasswordResetPostOkResult, PasswordResetPostInvalidTokenResponse, GeneralErrorResponse] -
-
-
-
- -Expand source code - -
async def password_reset_post(
-    self,
-    form_fields: List[FormField],
-    token: str,
-    tenant_id: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-) -> Union[
-    PasswordResetPostOkResult,
-    PasswordResetPostInvalidTokenResponse,
-    GeneralErrorResponse,
-]:
-    new_password_for_field = find_first_occurrence_in_list(
-        lambda x: x.id == FORM_FIELD_PASSWORD_ID, form_fields
-    )
-    if new_password_for_field is None:
-        raise Exception("Should never come here")
-    new_password = new_password_for_field.value
-
-    result = await api_options.recipe_implementation.reset_password_using_token(
-        token, new_password, tenant_id, user_context
-    )
-
-    if isinstance(result, ResetPasswordUsingTokenInvalidTokenError):
-        return PasswordResetPostInvalidTokenResponse()
-
-    return PasswordResetPostOkResult(result.user_id)
-
-
-
-async def sign_in_post(self, form_fields: List[FormField], tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[SignInPostOkResult, SignInPostWrongCredentialsError, GeneralErrorResponse] -
-
-
-
- -Expand source code - -
async def sign_in_post(
-    self,
-    form_fields: List[FormField],
-    tenant_id: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-) -> Union[
-    SignInPostOkResult, SignInPostWrongCredentialsError, GeneralErrorResponse
-]:
-    password_form_field = find_first_occurrence_in_list(
-        lambda x: x.id == FORM_FIELD_PASSWORD_ID, form_fields
-    )
-    if password_form_field is None:
-        raise Exception("Should never come here")
-    password = password_form_field.value
-
-    email_form_field = find_first_occurrence_in_list(
-        lambda x: x.id == FORM_FIELD_EMAIL_ID, form_fields
-    )
-    if email_form_field is None:
-        raise Exception("Should never come here")
-    email = email_form_field.value
-
-    result = await api_options.recipe_implementation.sign_in(
-        email, password, tenant_id, user_context
-    )
-
-    if isinstance(result, SignInWrongCredentialsError):
-        return SignInPostWrongCredentialsError()
-
-    user = result.user
-    session = await create_new_session(
-        tenant_id=tenant_id,
-        request=api_options.request,
-        user_id=user.user_id,
-        access_token_payload={},
-        session_data_in_database={},
-        user_context=user_context,
-    )
-    return SignInPostOkResult(user, session)
-
-
-
-async def sign_up_post(self, form_fields: List[FormField], tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[SignUpPostOkResult, SignUpPostEmailAlreadyExistsError, GeneralErrorResponse] -
-
-
-
- -Expand source code - -
async def sign_up_post(
-    self,
-    form_fields: List[FormField],
-    tenant_id: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-) -> Union[
-    SignUpPostOkResult, SignUpPostEmailAlreadyExistsError, GeneralErrorResponse
-]:
-    password_form_field = find_first_occurrence_in_list(
-        lambda x: x.id == FORM_FIELD_PASSWORD_ID, form_fields
-    )
-    if password_form_field is None:
-        raise Exception("Should never come here")
-    password = password_form_field.value
-
-    email_form_field = find_first_occurrence_in_list(
-        lambda x: x.id == FORM_FIELD_EMAIL_ID, form_fields
-    )
-    if email_form_field is None:
-        raise Exception("Should never come here")
-    email = email_form_field.value
-
-    result = await api_options.recipe_implementation.sign_up(
-        email, password, tenant_id, user_context
-    )
-
-    if isinstance(result, SignUpEmailAlreadyExistsError):
-        return SignUpPostEmailAlreadyExistsError()
-
-    user = result.user
-    session = await create_new_session(
-        tenant_id=tenant_id,
-        request=api_options.request,
-        user_id=user.user_id,
-        access_token_payload={},
-        session_data_in_database={},
-        user_context=user_context,
-    )
-    return SignUpPostOkResult(user, session)
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailpassword/api/index.html b/html/supertokens_python/recipe/emailpassword/api/index.html deleted file mode 100644 index c18ea51c5..000000000 --- a/html/supertokens_python/recipe/emailpassword/api/index.html +++ /dev/null @@ -1,120 +0,0 @@ - - - - - - -supertokens_python.recipe.emailpassword.api API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.emailpassword.api

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from .email_exists import handle_email_exists_api  # type: ignore
-from .generate_password_reset_token import (
-    handle_generate_password_reset_token_api,  # type: ignore
-)
-from .password_reset import handle_password_reset_api  # type: ignore
-from .signin import handle_sign_in_api  # type: ignore
-from .signup import handle_sign_up_api  # type: ignore
-
-
-
-

Sub-modules

-
-
supertokens_python.recipe.emailpassword.api.email_exists
-
-
-
-
supertokens_python.recipe.emailpassword.api.generate_password_reset_token
-
-
-
-
supertokens_python.recipe.emailpassword.api.implementation
-
-
-
-
supertokens_python.recipe.emailpassword.api.password_reset
-
-
-
-
supertokens_python.recipe.emailpassword.api.signin
-
-
-
-
supertokens_python.recipe.emailpassword.api.signup
-
-
-
-
supertokens_python.recipe.emailpassword.api.utils
-
-
-
-
-
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailpassword/api/password_reset.html b/html/supertokens_python/recipe/emailpassword/api/password_reset.html deleted file mode 100644 index 7a9d15af0..000000000 --- a/html/supertokens_python/recipe/emailpassword/api/password_reset.html +++ /dev/null @@ -1,164 +0,0 @@ - - - - - - -supertokens_python.recipe.emailpassword.api.password_reset API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.emailpassword.api.password_reset

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from typing import TYPE_CHECKING, Any, Dict
-
-if TYPE_CHECKING:
-    from supertokens_python.recipe.emailpassword.interfaces import (
-        APIOptions,
-        APIInterface,
-    )
-
-from supertokens_python.exceptions import raise_bad_input_exception
-from supertokens_python.utils import send_200_response
-
-from .utils import validate_form_fields_or_throw_error
-
-
-async def handle_password_reset_api(
-    tenant_id: str,
-    api_implementation: APIInterface,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-):
-    if api_implementation.disable_generate_password_reset_token_post:
-        return None
-    body = await api_options.request.json()
-    if body is None:
-        raise_bad_input_exception("Please provide a JSON body")
-    form_fields_raw: Any = body["formFields"] if "formFields" in body else []
-    form_fields = await validate_form_fields_or_throw_error(
-        api_options.config.reset_password_using_token_feature.form_fields_for_password_reset_form,
-        form_fields_raw,
-        tenant_id,
-    )
-
-    if "token" not in body:
-        raise_bad_input_exception("Please provide the password reset token")
-    if not isinstance(body["token"], str):
-        raise_bad_input_exception("The password reset token must be a string")
-
-    token = body["token"]
-
-    response = await api_implementation.password_reset_post(
-        form_fields, token, tenant_id, api_options, user_context
-    )
-    return send_200_response(response.to_json(), api_options.response)
-
-
-
-
-
-
-
-

Functions

-
-
-async def handle_password_reset_api(tenant_id: str, api_implementation: APIInterface, api_options: APIOptions, user_context: Dict[str, Any]) -
-
-
-
- -Expand source code - -
async def handle_password_reset_api(
-    tenant_id: str,
-    api_implementation: APIInterface,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-):
-    if api_implementation.disable_generate_password_reset_token_post:
-        return None
-    body = await api_options.request.json()
-    if body is None:
-        raise_bad_input_exception("Please provide a JSON body")
-    form_fields_raw: Any = body["formFields"] if "formFields" in body else []
-    form_fields = await validate_form_fields_or_throw_error(
-        api_options.config.reset_password_using_token_feature.form_fields_for_password_reset_form,
-        form_fields_raw,
-        tenant_id,
-    )
-
-    if "token" not in body:
-        raise_bad_input_exception("Please provide the password reset token")
-    if not isinstance(body["token"], str):
-        raise_bad_input_exception("The password reset token must be a string")
-
-    token = body["token"]
-
-    response = await api_implementation.password_reset_post(
-        form_fields, token, tenant_id, api_options, user_context
-    )
-    return send_200_response(response.to_json(), api_options.response)
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailpassword/api/signin.html b/html/supertokens_python/recipe/emailpassword/api/signin.html deleted file mode 100644 index cdb344600..000000000 --- a/html/supertokens_python/recipe/emailpassword/api/signin.html +++ /dev/null @@ -1,148 +0,0 @@ - - - - - - -supertokens_python.recipe.emailpassword.api.signin API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.emailpassword.api.signin

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from typing import TYPE_CHECKING, Any, Dict
-
-if TYPE_CHECKING:
-    from supertokens_python.recipe.emailpassword.interfaces import (
-        APIOptions,
-        APIInterface,
-    )
-
-from supertokens_python.exceptions import raise_bad_input_exception
-from supertokens_python.utils import send_200_response
-
-from .utils import validate_form_fields_or_throw_error
-
-
-async def handle_sign_in_api(
-    tenant_id: str,
-    api_implementation: APIInterface,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-):
-    if api_implementation.disable_sign_in_post:
-        return None
-    body = await api_options.request.json()
-    if body is None:
-        raise_bad_input_exception("Please provide a JSON body")
-    form_fields_raw: Any = body["formFields"] if "formFields" in body else []
-    form_fields = await validate_form_fields_or_throw_error(
-        api_options.config.sign_in_feature.form_fields, form_fields_raw, tenant_id
-    )
-
-    response = await api_implementation.sign_in_post(
-        form_fields, tenant_id, api_options, user_context
-    )
-
-    return send_200_response(response.to_json(), api_options.response)
-
-
-
-
-
-
-
-

Functions

-
-
-async def handle_sign_in_api(tenant_id: str, api_implementation: APIInterface, api_options: APIOptions, user_context: Dict[str, Any]) -
-
-
-
- -Expand source code - -
async def handle_sign_in_api(
-    tenant_id: str,
-    api_implementation: APIInterface,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-):
-    if api_implementation.disable_sign_in_post:
-        return None
-    body = await api_options.request.json()
-    if body is None:
-        raise_bad_input_exception("Please provide a JSON body")
-    form_fields_raw: Any = body["formFields"] if "formFields" in body else []
-    form_fields = await validate_form_fields_or_throw_error(
-        api_options.config.sign_in_feature.form_fields, form_fields_raw, tenant_id
-    )
-
-    response = await api_implementation.sign_in_post(
-        form_fields, tenant_id, api_options, user_context
-    )
-
-    return send_200_response(response.to_json(), api_options.response)
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailpassword/api/signup.html b/html/supertokens_python/recipe/emailpassword/api/signup.html deleted file mode 100644 index 5146f4f59..000000000 --- a/html/supertokens_python/recipe/emailpassword/api/signup.html +++ /dev/null @@ -1,180 +0,0 @@ - - - - - - -supertokens_python.recipe.emailpassword.api.signup API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.emailpassword.api.signup

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from typing import TYPE_CHECKING, Any, Dict
-
-from supertokens_python.recipe.emailpassword.interfaces import SignUpPostOkResult
-from supertokens_python.types import GeneralErrorResponse
-
-from ..exceptions import raise_form_field_exception
-from ..types import ErrorFormField
-
-if TYPE_CHECKING:
-    from supertokens_python.recipe.emailpassword.interfaces import (
-        APIOptions,
-        APIInterface,
-    )
-
-from supertokens_python.exceptions import raise_bad_input_exception
-from supertokens_python.utils import send_200_response
-
-from .utils import validate_form_fields_or_throw_error
-
-
-async def handle_sign_up_api(
-    tenant_id: str,
-    api_implementation: APIInterface,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-):
-    if api_implementation.disable_sign_up_post:
-        return None
-    body = await api_options.request.json()
-    if body is None:
-        raise_bad_input_exception("Please provide a JSON body")
-    form_fields_raw: Any = body["formFields"] if "formFields" in body else []
-    form_fields = await validate_form_fields_or_throw_error(
-        api_options.config.sign_up_feature.form_fields, form_fields_raw, tenant_id
-    )
-
-    response = await api_implementation.sign_up_post(
-        form_fields, tenant_id, api_options, user_context
-    )
-
-    if isinstance(response, SignUpPostOkResult):
-        return send_200_response(response.to_json(), api_options.response)
-    if isinstance(response, GeneralErrorResponse):
-        return send_200_response(response.to_json(), api_options.response)
-
-    return raise_form_field_exception(
-        "EMAIL_ALREADY_EXISTS_ERROR",
-        [
-            ErrorFormField(
-                id="email",
-                error="This email already exists. Please sign in instead.",
-            )
-        ],
-    )
-
-
-
-
-
-
-
-

Functions

-
-
-async def handle_sign_up_api(tenant_id: str, api_implementation: APIInterface, api_options: APIOptions, user_context: Dict[str, Any]) -
-
-
-
- -Expand source code - -
async def handle_sign_up_api(
-    tenant_id: str,
-    api_implementation: APIInterface,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-):
-    if api_implementation.disable_sign_up_post:
-        return None
-    body = await api_options.request.json()
-    if body is None:
-        raise_bad_input_exception("Please provide a JSON body")
-    form_fields_raw: Any = body["formFields"] if "formFields" in body else []
-    form_fields = await validate_form_fields_or_throw_error(
-        api_options.config.sign_up_feature.form_fields, form_fields_raw, tenant_id
-    )
-
-    response = await api_implementation.sign_up_post(
-        form_fields, tenant_id, api_options, user_context
-    )
-
-    if isinstance(response, SignUpPostOkResult):
-        return send_200_response(response.to_json(), api_options.response)
-    if isinstance(response, GeneralErrorResponse):
-        return send_200_response(response.to_json(), api_options.response)
-
-    return raise_form_field_exception(
-        "EMAIL_ALREADY_EXISTS_ERROR",
-        [
-            ErrorFormField(
-                id="email",
-                error="This email already exists. Please sign in instead.",
-            )
-        ],
-    )
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailpassword/api/utils.html b/html/supertokens_python/recipe/emailpassword/api/utils.html deleted file mode 100644 index d3e575a12..000000000 --- a/html/supertokens_python/recipe/emailpassword/api/utils.html +++ /dev/null @@ -1,270 +0,0 @@ - - - - - - -supertokens_python.recipe.emailpassword.api.utils API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.emailpassword.api.utils

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from typing import Any, Dict, List, Union
-
-from supertokens_python.exceptions import raise_bad_input_exception
-from supertokens_python.recipe.emailpassword.constants import (
-    FORM_FIELD_EMAIL_ID,
-    FORM_FIELD_PASSWORD_ID,
-)
-from supertokens_python.recipe.emailpassword.exceptions import (
-    raise_form_field_exception,
-)
-from supertokens_python.recipe.emailpassword.types import (
-    ErrorFormField,
-    FormField,
-    NormalisedFormField,
-)
-from supertokens_python.utils import find_first_occurrence_in_list
-
-
-async def validate_form_or_throw_error(
-    inputs: List[FormField],
-    config_form_fields: List[NormalisedFormField],
-    tenant_id: str,
-):
-    validation_errors: List[ErrorFormField] = []
-    if len(config_form_fields) < len(inputs):
-        raise_bad_input_exception("Are you sending too many formFields?")
-
-    for field in config_form_fields:
-        input_field: Union[None, FormField] = find_first_occurrence_in_list(
-            lambda x: x.id == field.id, inputs
-        )
-        is_invalid_value = input_field is None or (
-            isinstance(input_field.value, str) and input_field.value == ""
-        )
-        if not field.optional and is_invalid_value:
-            validation_errors.append(ErrorFormField(field.id, "Field is not optional"))
-            continue
-
-        # If the field was invalid and not optional, execution won't reach here.
-        # so we need to skip it if  the value is invalid and optional.
-        if is_invalid_value:
-            continue
-
-        assert input_field is not None
-
-        error = await field.validate(input_field.value, tenant_id)
-        if error is not None:
-            validation_errors.append(ErrorFormField(field.id, error))
-
-    if len(validation_errors) != 0:
-        # raise BadInputError(msg="Error in input formFields")
-        raise_form_field_exception("Error in input formFields", validation_errors)
-
-
-async def validate_form_fields_or_throw_error(
-    config_form_fields: List[NormalisedFormField], form_fields_raw: Any, tenant_id: str
-) -> List[FormField]:
-    if form_fields_raw is None:
-        raise_bad_input_exception("Missing input param: formFields")
-
-    if not isinstance(form_fields_raw, List):
-        raise_bad_input_exception("formFields must be an array")
-
-    form_fields: List[FormField] = []
-
-    form_fields_list_raw: List[Dict[str, Any]] = form_fields_raw
-    for current_form_field in form_fields_list_raw:
-        if (
-            "id" not in current_form_field
-            or not isinstance(current_form_field["id"], str)
-            or "value" not in current_form_field
-        ):
-            raise_bad_input_exception(
-                "All elements of formFields must contain an 'id' and 'value' field"
-            )
-
-        value = current_form_field["value"]
-        if current_form_field["id"] in [
-            FORM_FIELD_EMAIL_ID,
-            FORM_FIELD_PASSWORD_ID,
-        ] and not isinstance(value, str):
-            # Ensure that the type is string else we will throw a bad input
-            # error.
-            raise_bad_input_exception(
-                f"{current_form_field['id']} value must be a string"
-            )
-
-        if current_form_field["id"] == FORM_FIELD_EMAIL_ID and isinstance(value, str):
-            value = value.strip()
-        form_fields.append(FormField(current_form_field["id"], value))
-
-    await validate_form_or_throw_error(form_fields, config_form_fields, tenant_id)
-    return form_fields
-
-
-
-
-
-
-
-

Functions

-
-
-async def validate_form_fields_or_throw_error(config_form_fields: List[NormalisedFormField], form_fields_raw: Any, tenant_id: str) ‑> List[FormField] -
-
-
-
- -Expand source code - -
async def validate_form_fields_or_throw_error(
-    config_form_fields: List[NormalisedFormField], form_fields_raw: Any, tenant_id: str
-) -> List[FormField]:
-    if form_fields_raw is None:
-        raise_bad_input_exception("Missing input param: formFields")
-
-    if not isinstance(form_fields_raw, List):
-        raise_bad_input_exception("formFields must be an array")
-
-    form_fields: List[FormField] = []
-
-    form_fields_list_raw: List[Dict[str, Any]] = form_fields_raw
-    for current_form_field in form_fields_list_raw:
-        if (
-            "id" not in current_form_field
-            or not isinstance(current_form_field["id"], str)
-            or "value" not in current_form_field
-        ):
-            raise_bad_input_exception(
-                "All elements of formFields must contain an 'id' and 'value' field"
-            )
-
-        value = current_form_field["value"]
-        if current_form_field["id"] in [
-            FORM_FIELD_EMAIL_ID,
-            FORM_FIELD_PASSWORD_ID,
-        ] and not isinstance(value, str):
-            # Ensure that the type is string else we will throw a bad input
-            # error.
-            raise_bad_input_exception(
-                f"{current_form_field['id']} value must be a string"
-            )
-
-        if current_form_field["id"] == FORM_FIELD_EMAIL_ID and isinstance(value, str):
-            value = value.strip()
-        form_fields.append(FormField(current_form_field["id"], value))
-
-    await validate_form_or_throw_error(form_fields, config_form_fields, tenant_id)
-    return form_fields
-
-
-
-async def validate_form_or_throw_error(inputs: List[FormField], config_form_fields: List[NormalisedFormField], tenant_id: str) -
-
-
-
- -Expand source code - -
async def validate_form_or_throw_error(
-    inputs: List[FormField],
-    config_form_fields: List[NormalisedFormField],
-    tenant_id: str,
-):
-    validation_errors: List[ErrorFormField] = []
-    if len(config_form_fields) < len(inputs):
-        raise_bad_input_exception("Are you sending too many formFields?")
-
-    for field in config_form_fields:
-        input_field: Union[None, FormField] = find_first_occurrence_in_list(
-            lambda x: x.id == field.id, inputs
-        )
-        is_invalid_value = input_field is None or (
-            isinstance(input_field.value, str) and input_field.value == ""
-        )
-        if not field.optional and is_invalid_value:
-            validation_errors.append(ErrorFormField(field.id, "Field is not optional"))
-            continue
-
-        # If the field was invalid and not optional, execution won't reach here.
-        # so we need to skip it if  the value is invalid and optional.
-        if is_invalid_value:
-            continue
-
-        assert input_field is not None
-
-        error = await field.validate(input_field.value, tenant_id)
-        if error is not None:
-            validation_errors.append(ErrorFormField(field.id, error))
-
-    if len(validation_errors) != 0:
-        # raise BadInputError(msg="Error in input formFields")
-        raise_form_field_exception("Error in input formFields", validation_errors)
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailpassword/asyncio/index.html b/html/supertokens_python/recipe/emailpassword/asyncio/index.html deleted file mode 100644 index 7ea32049a..000000000 --- a/html/supertokens_python/recipe/emailpassword/asyncio/index.html +++ /dev/null @@ -1,490 +0,0 @@ - - - - - - -supertokens_python.recipe.emailpassword.asyncio API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.emailpassword.asyncio

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from typing import Any, Dict, Union, Optional
-from supertokens_python import get_request_from_user_context
-
-from supertokens_python.recipe.emailpassword import EmailPasswordRecipe
-
-from ..types import EmailTemplateVars, User
-from ...multitenancy.constants import DEFAULT_TENANT_ID
-
-from supertokens_python.recipe.emailpassword.interfaces import (
-    CreateResetPasswordWrongUserIdError,
-    CreateResetPasswordLinkUnknownUserIdError,
-    CreateResetPasswordLinkOkResult,
-    SendResetPasswordEmailOkResult,
-    SendResetPasswordEmailUnknownUserIdError,
-)
-from supertokens_python.recipe.emailpassword.utils import get_password_reset_link
-from supertokens_python.recipe.emailpassword.types import (
-    PasswordResetEmailTemplateVars,
-    PasswordResetEmailTemplateVarsUser,
-)
-
-
-async def update_email_or_password(
-    user_id: str,
-    email: Union[str, None] = None,
-    password: Union[str, None] = None,
-    apply_password_policy: Union[bool, None] = None,
-    tenant_id_for_password_policy: Optional[str] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-):
-    if user_context is None:
-        user_context = {}
-    return await EmailPasswordRecipe.get_instance().recipe_implementation.update_email_or_password(
-        user_id,
-        email,
-        password,
-        apply_password_policy,
-        tenant_id_for_password_policy or DEFAULT_TENANT_ID,
-        user_context,
-    )
-
-
-async def get_user_by_id(
-    user_id: str, user_context: Union[None, Dict[str, Any]] = None
-) -> Union[None, User]:
-    if user_context is None:
-        user_context = {}
-    return (
-        await EmailPasswordRecipe.get_instance().recipe_implementation.get_user_by_id(
-            user_id, user_context
-        )
-    )
-
-
-async def get_user_by_email(
-    tenant_id: str, email: str, user_context: Union[None, Dict[str, Any]] = None
-) -> Union[User, None]:
-    if user_context is None:
-        user_context = {}
-    return await EmailPasswordRecipe.get_instance().recipe_implementation.get_user_by_email(
-        email, tenant_id, user_context
-    )
-
-
-async def create_reset_password_token(
-    tenant_id: str, user_id: str, user_context: Union[None, Dict[str, Any]] = None
-):
-    if user_context is None:
-        user_context = {}
-    return await EmailPasswordRecipe.get_instance().recipe_implementation.create_reset_password_token(
-        user_id, tenant_id, user_context
-    )
-
-
-async def reset_password_using_token(
-    tenant_id: str,
-    token: str,
-    new_password: str,
-    user_context: Union[None, Dict[str, Any]] = None,
-):
-    if user_context is None:
-        user_context = {}
-    return await EmailPasswordRecipe.get_instance().recipe_implementation.reset_password_using_token(
-        token, new_password, tenant_id, user_context
-    )
-
-
-async def sign_in(
-    tenant_id: str,
-    email: str,
-    password: str,
-    user_context: Union[None, Dict[str, Any]] = None,
-):
-    if user_context is None:
-        user_context = {}
-    return await EmailPasswordRecipe.get_instance().recipe_implementation.sign_in(
-        email, password, tenant_id, user_context
-    )
-
-
-async def sign_up(
-    tenant_id: str,
-    email: str,
-    password: str,
-    user_context: Union[None, Dict[str, Any]] = None,
-):
-    if user_context is None:
-        user_context = {}
-    return await EmailPasswordRecipe.get_instance().recipe_implementation.sign_up(
-        email, password, tenant_id, user_context
-    )
-
-
-async def send_email(
-    input_: EmailTemplateVars,
-    user_context: Union[None, Dict[str, Any]] = None,
-):
-    if user_context is None:
-        user_context = {}
-    return await EmailPasswordRecipe.get_instance().email_delivery.ingredient_interface_impl.send_email(
-        input_, user_context
-    )
-
-
-async def create_reset_password_link(
-    tenant_id: str, user_id: str, user_context: Optional[Dict[str, Any]] = None
-):
-    if user_context is None:
-        user_context = {}
-    token = await create_reset_password_token(tenant_id, user_id, user_context)
-    if isinstance(token, CreateResetPasswordWrongUserIdError):
-        return CreateResetPasswordLinkUnknownUserIdError()
-
-    recipe_instance = EmailPasswordRecipe.get_instance()
-    request = get_request_from_user_context(user_context)
-    return CreateResetPasswordLinkOkResult(
-        link=get_password_reset_link(
-            recipe_instance.get_app_info(),
-            token.token,
-            tenant_id,
-            request,
-            user_context,
-        )
-    )
-
-
-async def send_reset_password_email(
-    tenant_id: str, user_id: str, user_context: Optional[Dict[str, Any]] = None
-):
-    link = await create_reset_password_link(tenant_id, user_id, user_context)
-    if isinstance(link, CreateResetPasswordLinkUnknownUserIdError):
-        return SendResetPasswordEmailUnknownUserIdError()
-
-    user = await get_user_by_id(user_id, user_context)
-    assert user is not None
-
-    await send_email(
-        PasswordResetEmailTemplateVars(
-            PasswordResetEmailTemplateVarsUser(user.user_id, user.email),
-            link.link,
-            tenant_id,
-        ),
-        user_context,
-    )
-
-    return SendResetPasswordEmailOkResult()
-
-
-
-
-
-
-
-

Functions

-
- -
-
-
- -Expand source code - -
async def create_reset_password_link(
-    tenant_id: str, user_id: str, user_context: Optional[Dict[str, Any]] = None
-):
-    if user_context is None:
-        user_context = {}
-    token = await create_reset_password_token(tenant_id, user_id, user_context)
-    if isinstance(token, CreateResetPasswordWrongUserIdError):
-        return CreateResetPasswordLinkUnknownUserIdError()
-
-    recipe_instance = EmailPasswordRecipe.get_instance()
-    request = get_request_from_user_context(user_context)
-    return CreateResetPasswordLinkOkResult(
-        link=get_password_reset_link(
-            recipe_instance.get_app_info(),
-            token.token,
-            tenant_id,
-            request,
-            user_context,
-        )
-    )
-
-
-
-async def create_reset_password_token(tenant_id: str, user_id: str, user_context: Optional[Dict[str, Any]] = None) -
-
-
-
- -Expand source code - -
async def create_reset_password_token(
-    tenant_id: str, user_id: str, user_context: Union[None, Dict[str, Any]] = None
-):
-    if user_context is None:
-        user_context = {}
-    return await EmailPasswordRecipe.get_instance().recipe_implementation.create_reset_password_token(
-        user_id, tenant_id, user_context
-    )
-
-
-
-async def get_user_by_email(tenant_id: str, email: str, user_context: Optional[Dict[str, Any]] = None) ‑> Optional[User] -
-
-
-
- -Expand source code - -
async def get_user_by_email(
-    tenant_id: str, email: str, user_context: Union[None, Dict[str, Any]] = None
-) -> Union[User, None]:
-    if user_context is None:
-        user_context = {}
-    return await EmailPasswordRecipe.get_instance().recipe_implementation.get_user_by_email(
-        email, tenant_id, user_context
-    )
-
-
-
-async def get_user_by_id(user_id: str, user_context: Optional[Dict[str, Any]] = None) ‑> Optional[User] -
-
-
-
- -Expand source code - -
async def get_user_by_id(
-    user_id: str, user_context: Union[None, Dict[str, Any]] = None
-) -> Union[None, User]:
-    if user_context is None:
-        user_context = {}
-    return (
-        await EmailPasswordRecipe.get_instance().recipe_implementation.get_user_by_id(
-            user_id, user_context
-        )
-    )
-
-
-
-async def reset_password_using_token(tenant_id: str, token: str, new_password: str, user_context: Optional[Dict[str, Any]] = None) -
-
-
-
- -Expand source code - -
async def reset_password_using_token(
-    tenant_id: str,
-    token: str,
-    new_password: str,
-    user_context: Union[None, Dict[str, Any]] = None,
-):
-    if user_context is None:
-        user_context = {}
-    return await EmailPasswordRecipe.get_instance().recipe_implementation.reset_password_using_token(
-        token, new_password, tenant_id, user_context
-    )
-
-
-
-async def send_email(input_: PasswordResetEmailTemplateVars, user_context: Optional[Dict[str, Any]] = None) -
-
-
-
- -Expand source code - -
async def send_email(
-    input_: EmailTemplateVars,
-    user_context: Union[None, Dict[str, Any]] = None,
-):
-    if user_context is None:
-        user_context = {}
-    return await EmailPasswordRecipe.get_instance().email_delivery.ingredient_interface_impl.send_email(
-        input_, user_context
-    )
-
-
-
-async def send_reset_password_email(tenant_id: str, user_id: str, user_context: Optional[Dict[str, Any]] = None) -
-
-
-
- -Expand source code - -
async def send_reset_password_email(
-    tenant_id: str, user_id: str, user_context: Optional[Dict[str, Any]] = None
-):
-    link = await create_reset_password_link(tenant_id, user_id, user_context)
-    if isinstance(link, CreateResetPasswordLinkUnknownUserIdError):
-        return SendResetPasswordEmailUnknownUserIdError()
-
-    user = await get_user_by_id(user_id, user_context)
-    assert user is not None
-
-    await send_email(
-        PasswordResetEmailTemplateVars(
-            PasswordResetEmailTemplateVarsUser(user.user_id, user.email),
-            link.link,
-            tenant_id,
-        ),
-        user_context,
-    )
-
-    return SendResetPasswordEmailOkResult()
-
-
-
-async def sign_in(tenant_id: str, email: str, password: str, user_context: Optional[Dict[str, Any]] = None) -
-
-
-
- -Expand source code - -
async def sign_in(
-    tenant_id: str,
-    email: str,
-    password: str,
-    user_context: Union[None, Dict[str, Any]] = None,
-):
-    if user_context is None:
-        user_context = {}
-    return await EmailPasswordRecipe.get_instance().recipe_implementation.sign_in(
-        email, password, tenant_id, user_context
-    )
-
-
-
-async def sign_up(tenant_id: str, email: str, password: str, user_context: Optional[Dict[str, Any]] = None) -
-
-
-
- -Expand source code - -
async def sign_up(
-    tenant_id: str,
-    email: str,
-    password: str,
-    user_context: Union[None, Dict[str, Any]] = None,
-):
-    if user_context is None:
-        user_context = {}
-    return await EmailPasswordRecipe.get_instance().recipe_implementation.sign_up(
-        email, password, tenant_id, user_context
-    )
-
-
-
-async def update_email_or_password(user_id: str, email: Optional[str] = None, password: Optional[str] = None, apply_password_policy: Optional[bool] = None, tenant_id_for_password_policy: Optional[str] = None, user_context: Optional[Dict[str, Any]] = None) -
-
-
-
- -Expand source code - -
async def update_email_or_password(
-    user_id: str,
-    email: Union[str, None] = None,
-    password: Union[str, None] = None,
-    apply_password_policy: Union[bool, None] = None,
-    tenant_id_for_password_policy: Optional[str] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-):
-    if user_context is None:
-        user_context = {}
-    return await EmailPasswordRecipe.get_instance().recipe_implementation.update_email_or_password(
-        user_id,
-        email,
-        password,
-        apply_password_policy,
-        tenant_id_for_password_policy or DEFAULT_TENANT_ID,
-        user_context,
-    )
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailpassword/constants.html b/html/supertokens_python/recipe/emailpassword/constants.html deleted file mode 100644 index 5b30aefb2..000000000 --- a/html/supertokens_python/recipe/emailpassword/constants.html +++ /dev/null @@ -1,80 +0,0 @@ - - - - - - -supertokens_python.recipe.emailpassword.constants API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.emailpassword.constants

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-FORM_FIELD_PASSWORD_ID = "password"
-FORM_FIELD_EMAIL_ID = "email"
-SIGNUP = "/signup"
-SIGNIN = "/signin"
-USER_PASSWORD_RESET_TOKEN = "/user/password/reset/token"
-USER_PASSWORD_RESET = "/user/password/reset"
-SIGNUP_EMAIL_EXISTS_OLD = "/signup/email/exists"
-SIGNUP_EMAIL_EXISTS = "/emailpassword/email/exists"
-RESET_PASSWORD = "/reset-password"
-
-
-
-
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailpassword/emaildelivery/index.html b/html/supertokens_python/recipe/emailpassword/emaildelivery/index.html deleted file mode 100644 index cd73e83cd..000000000 --- a/html/supertokens_python/recipe/emailpassword/emaildelivery/index.html +++ /dev/null @@ -1,83 +0,0 @@ - - - - - - -supertokens_python.recipe.emailpassword.emaildelivery API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.emailpassword.emaildelivery

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-
-
-

Sub-modules

-
-
supertokens_python.recipe.emailpassword.emaildelivery.services
-
-
-
-
-
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailpassword/emaildelivery/services/backward_compatibility/index.html b/html/supertokens_python/recipe/emailpassword/emaildelivery/services/backward_compatibility/index.html deleted file mode 100644 index c05100626..000000000 --- a/html/supertokens_python/recipe/emailpassword/emaildelivery/services/backward_compatibility/index.html +++ /dev/null @@ -1,287 +0,0 @@ - - - - - - -supertokens_python.recipe.emailpassword.emaildelivery.services.backward_compatibility API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.emailpassword.emaildelivery.services.backward_compatibility

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from os import environ
-from typing import Any, Dict
-
-from httpx import AsyncClient
-
-from supertokens_python.ingredients.emaildelivery.types import EmailDeliveryInterface
-from supertokens_python.logger import log_debug_message
-from supertokens_python.recipe.emailpassword.interfaces import (
-    EmailTemplateVars,
-    RecipeInterface,
-)
-from supertokens_python.recipe.emailpassword.types import User
-from supertokens_python.supertokens import AppInfo
-from supertokens_python.utils import handle_httpx_client_exceptions
-
-
-async def create_and_send_email_using_supertokens_service(
-    app_info: AppInfo, user: User, password_reset_url_with_token: str
-) -> None:
-    if ("SUPERTOKENS_ENV" in environ) and (environ["SUPERTOKENS_ENV"] == "testing"):
-        return
-
-    data = {
-        "email": user.email,
-        "appName": app_info.app_name,
-        "passwordResetURL": password_reset_url_with_token,
-    }
-    try:
-        async with AsyncClient(timeout=30.0) as client:
-            resp = await client.post("https://api.supertokens.io/0/st/auth/password/reset", json=data, headers={"api-version": "0"})  # type: ignore
-            resp.raise_for_status()
-            log_debug_message("Password reset email sent to %s", user.email)
-    except Exception as e:
-        log_debug_message("Error sending password reset email")
-        handle_httpx_client_exceptions(e, data)
-
-
-class BackwardCompatibilityService(EmailDeliveryInterface[EmailTemplateVars]):
-    app_info: AppInfo
-
-    def __init__(
-        self,
-        app_info: AppInfo,
-        recipe_interface_impl: RecipeInterface,
-    ) -> None:
-        self.recipe_interface_impl = recipe_interface_impl
-        self.app_info = app_info
-
-    async def send_email(
-        self,
-        template_vars: EmailTemplateVars,
-        user_context: Dict[str, Any],
-    ) -> None:
-        user = await self.recipe_interface_impl.get_user_by_id(
-            user_id=template_vars.user.id, user_context=user_context
-        )
-        if user is None:
-            raise Exception("Should never come here")
-
-        # we add this here cause the user may have overridden the sendEmail function
-        # to change the input email and if we don't do this, the input email
-        # will get reset by the getUserById call above.
-        user.email = template_vars.user.email
-        try:
-            await create_and_send_email_using_supertokens_service(
-                self.app_info, user, template_vars.password_reset_link
-            )
-        except Exception:
-            pass
-
-
-
-
-
-
-
-

Functions

-
-
-async def create_and_send_email_using_supertokens_service(app_info: AppInfo, user: User, password_reset_url_with_token: str) ‑> None -
-
-
-
- -Expand source code - -
async def create_and_send_email_using_supertokens_service(
-    app_info: AppInfo, user: User, password_reset_url_with_token: str
-) -> None:
-    if ("SUPERTOKENS_ENV" in environ) and (environ["SUPERTOKENS_ENV"] == "testing"):
-        return
-
-    data = {
-        "email": user.email,
-        "appName": app_info.app_name,
-        "passwordResetURL": password_reset_url_with_token,
-    }
-    try:
-        async with AsyncClient(timeout=30.0) as client:
-            resp = await client.post("https://api.supertokens.io/0/st/auth/password/reset", json=data, headers={"api-version": "0"})  # type: ignore
-            resp.raise_for_status()
-            log_debug_message("Password reset email sent to %s", user.email)
-    except Exception as e:
-        log_debug_message("Error sending password reset email")
-        handle_httpx_client_exceptions(e, data)
-
-
-
-
-
-

Classes

-
-
-class BackwardCompatibilityService -(app_info: AppInfo, recipe_interface_impl: RecipeInterface) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class BackwardCompatibilityService(EmailDeliveryInterface[EmailTemplateVars]):
-    app_info: AppInfo
-
-    def __init__(
-        self,
-        app_info: AppInfo,
-        recipe_interface_impl: RecipeInterface,
-    ) -> None:
-        self.recipe_interface_impl = recipe_interface_impl
-        self.app_info = app_info
-
-    async def send_email(
-        self,
-        template_vars: EmailTemplateVars,
-        user_context: Dict[str, Any],
-    ) -> None:
-        user = await self.recipe_interface_impl.get_user_by_id(
-            user_id=template_vars.user.id, user_context=user_context
-        )
-        if user is None:
-            raise Exception("Should never come here")
-
-        # we add this here cause the user may have overridden the sendEmail function
-        # to change the input email and if we don't do this, the input email
-        # will get reset by the getUserById call above.
-        user.email = template_vars.user.email
-        try:
-            await create_and_send_email_using_supertokens_service(
-                self.app_info, user, template_vars.password_reset_link
-            )
-        except Exception:
-            pass
-
-

Ancestors

- -

Class variables

-
-
var app_infoAppInfo
-
-
-
-
-

Methods

-
-
-async def send_email(self, template_vars: EmailTemplateVars, user_context: Dict[str, Any]) ‑> None -
-
-
-
- -Expand source code - -
async def send_email(
-    self,
-    template_vars: EmailTemplateVars,
-    user_context: Dict[str, Any],
-) -> None:
-    user = await self.recipe_interface_impl.get_user_by_id(
-        user_id=template_vars.user.id, user_context=user_context
-    )
-    if user is None:
-        raise Exception("Should never come here")
-
-    # we add this here cause the user may have overridden the sendEmail function
-    # to change the input email and if we don't do this, the input email
-    # will get reset by the getUserById call above.
-    user.email = template_vars.user.email
-    try:
-        await create_and_send_email_using_supertokens_service(
-            self.app_info, user, template_vars.password_reset_link
-        )
-    except Exception:
-        pass
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailpassword/emaildelivery/services/index.html b/html/supertokens_python/recipe/emailpassword/emaildelivery/services/index.html deleted file mode 100644 index 41760fca0..000000000 --- a/html/supertokens_python/recipe/emailpassword/emaildelivery/services/index.html +++ /dev/null @@ -1,92 +0,0 @@ - - - - - - -supertokens_python.recipe.emailpassword.emaildelivery.services API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.emailpassword.emaildelivery.services

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from . import smtp
-
-SMTPService = smtp.SMTPService
-
-
-
-

Sub-modules

-
-
supertokens_python.recipe.emailpassword.emaildelivery.services.backward_compatibility
-
-
-
-
supertokens_python.recipe.emailpassword.emaildelivery.services.smtp
-
-
-
-
-
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailpassword/emaildelivery/services/smtp/index.html b/html/supertokens_python/recipe/emailpassword/emaildelivery/services/smtp/index.html deleted file mode 100644 index be9fba3b1..000000000 --- a/html/supertokens_python/recipe/emailpassword/emaildelivery/services/smtp/index.html +++ /dev/null @@ -1,217 +0,0 @@ - - - - - - -supertokens_python.recipe.emailpassword.emaildelivery.services.smtp API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.emailpassword.emaildelivery.services.smtp

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from typing import Any, Dict, Callable, Union
-
-from supertokens_python.ingredients.emaildelivery.services.smtp import Transporter
-from supertokens_python.ingredients.emaildelivery.types import (
-    EmailDeliveryInterface,
-    SMTPServiceInterface,
-    SMTPSettings,
-)
-from supertokens_python.recipe.emailpassword.types import (
-    EmailTemplateVars,
-    SMTPOverrideInput,
-)
-
-from .service_implementation import ServiceImplementation
-
-
-class SMTPService(EmailDeliveryInterface[EmailTemplateVars]):
-    service_implementation: SMTPServiceInterface[EmailTemplateVars]
-
-    def __init__(
-        self,
-        smtp_settings: SMTPSettings,
-        override: Union[Callable[[SMTPOverrideInput], SMTPOverrideInput], None] = None,
-    ) -> None:
-        transporter = Transporter(smtp_settings)
-
-        oi = ServiceImplementation(transporter)
-        self.service_implementation = oi if override is None else override(oi)
-
-    async def send_email(
-        self,
-        template_vars: EmailTemplateVars,
-        user_context: Dict[str, Any],
-    ) -> None:
-        content = await self.service_implementation.get_content(
-            template_vars, user_context
-        )
-        await self.service_implementation.send_raw_email(content, user_context)
-
-
-
-

Sub-modules

-
-
supertokens_python.recipe.emailpassword.emaildelivery.services.smtp.password_reset
-
-
-
-
supertokens_python.recipe.emailpassword.emaildelivery.services.smtp.password_reset_email
-
-
-
-
supertokens_python.recipe.emailpassword.emaildelivery.services.smtp.service_implementation
-
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class SMTPService -(smtp_settings: SMTPSettings, override: Optional[Callable[[SMTPServiceInterface[PasswordResetEmailTemplateVars]], SMTPServiceInterface[PasswordResetEmailTemplateVars]]] = None) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class SMTPService(EmailDeliveryInterface[EmailTemplateVars]):
-    service_implementation: SMTPServiceInterface[EmailTemplateVars]
-
-    def __init__(
-        self,
-        smtp_settings: SMTPSettings,
-        override: Union[Callable[[SMTPOverrideInput], SMTPOverrideInput], None] = None,
-    ) -> None:
-        transporter = Transporter(smtp_settings)
-
-        oi = ServiceImplementation(transporter)
-        self.service_implementation = oi if override is None else override(oi)
-
-    async def send_email(
-        self,
-        template_vars: EmailTemplateVars,
-        user_context: Dict[str, Any],
-    ) -> None:
-        content = await self.service_implementation.get_content(
-            template_vars, user_context
-        )
-        await self.service_implementation.send_raw_email(content, user_context)
-
-

Ancestors

- -

Class variables

-
-
var service_implementationSMTPServiceInterface[PasswordResetEmailTemplateVars]
-
-
-
-
-

Methods

-
-
-async def send_email(self, template_vars: PasswordResetEmailTemplateVars, user_context: Dict[str, Any]) ‑> None -
-
-
-
- -Expand source code - -
async def send_email(
-    self,
-    template_vars: EmailTemplateVars,
-    user_context: Dict[str, Any],
-) -> None:
-    content = await self.service_implementation.get_content(
-        template_vars, user_context
-    )
-    await self.service_implementation.send_raw_email(content, user_context)
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailpassword/emaildelivery/services/smtp/password_reset.html b/html/supertokens_python/recipe/emailpassword/emaildelivery/services/smtp/password_reset.html deleted file mode 100644 index 016ca7c08..000000000 --- a/html/supertokens_python/recipe/emailpassword/emaildelivery/services/smtp/password_reset.html +++ /dev/null @@ -1,146 +0,0 @@ - - - - - - -supertokens_python.recipe.emailpassword.emaildelivery.services.smtp.password_reset API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.emailpassword.emaildelivery.services.smtp.password_reset

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from string import Template
-
-from supertokens_python.ingredients.emaildelivery.types import EmailContent
-from supertokens_python.recipe.emailpassword.types import PasswordResetEmailTemplateVars
-from supertokens_python.supertokens import Supertokens
-
-from .password_reset_email import html_template
-
-
-def get_password_reset_email_content(
-    email_input: PasswordResetEmailTemplateVars,
-) -> EmailContent:
-    supertokens = Supertokens.get_instance()
-    app_name = supertokens.app_info.app_name
-    body = get_password_reset_email_html(
-        app_name, email_input.user.email, email_input.password_reset_link
-    )
-    content_result = EmailContent(
-        body, "Password reset instructions", email_input.user.email, is_html=True
-    )
-    return content_result
-
-
-def get_password_reset_email_html(app_name: str, email: str, reset_link: str):
-    return Template(html_template).substitute(
-        appname=app_name, resetLink=reset_link, toEmail=email
-    )
-
-
-
-
-
-
-
-

Functions

-
-
-def get_password_reset_email_content(email_input: PasswordResetEmailTemplateVars) ‑> EmailContent -
-
-
-
- -Expand source code - -
def get_password_reset_email_content(
-    email_input: PasswordResetEmailTemplateVars,
-) -> EmailContent:
-    supertokens = Supertokens.get_instance()
-    app_name = supertokens.app_info.app_name
-    body = get_password_reset_email_html(
-        app_name, email_input.user.email, email_input.password_reset_link
-    )
-    content_result = EmailContent(
-        body, "Password reset instructions", email_input.user.email, is_html=True
-    )
-    return content_result
-
-
-
-def get_password_reset_email_html(app_name: str, email: str, reset_link: str) -
-
-
-
- -Expand source code - -
def get_password_reset_email_html(app_name: str, email: str, reset_link: str):
-    return Template(html_template).substitute(
-        appname=app_name, resetLink=reset_link, toEmail=email
-    )
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailpassword/emaildelivery/services/smtp/password_reset_email.html b/html/supertokens_python/recipe/emailpassword/emaildelivery/services/smtp/password_reset_email.html deleted file mode 100644 index bcee905c2..000000000 --- a/html/supertokens_python/recipe/emailpassword/emaildelivery/services/smtp/password_reset_email.html +++ /dev/null @@ -1,969 +0,0 @@ - - - - - - -supertokens_python.recipe.emailpassword.emaildelivery.services.smtp.password_reset_email API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.emailpassword.emaildelivery.services.smtp.password_reset_email

-
-
-
- -Expand source code - -
html_template = """
-<!doctype html>
-<html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml"
-        xmlns:o="urn:schemas-microsoft-com:office:office">
-
-<head>
-        <meta charset="UTF-8">
-        <meta http-equiv="X-UA-Compatible" content="IE=edge">
-        <meta name="viewport" content="width=device-width, initial-scale=1">
-        <title>*|MC:SUBJECT|*</title>
-
-        <style type="text/css">
-            body {
-                        max-width: 100vw;
-                        overflow: hidden;
-                }
-                p {
-                        margin: 10px 0;
-                        padding: 0;
-                }
-
-                table {
-                        border-collapse: collapse;
-                }
-
-                h1,
-                h2,
-                h3,
-                h4,
-                h5,
-                h6 {
-                        display: block;
-                        margin: 0;
-                        padding: 0;
-                }
-
-                img,
-                a img {
-                        border: 0;
-                        height: auto;
-                        outline: none;
-                        text-decoration: none;
-                }
-
-                body,
-                #bodyTable,
-                #bodyCell {
-                        height: 100%;
-                        margin: 0;
-                        padding: 0;
-                        width: 100%;
-                }
-
-                .mcnPreviewText {
-                        display: none !important;
-                }
-
-                #outlook a {
-                        padding: 0;
-                }
-
-                img {
-                        -ms-interpolation-mode: bicubic;
-                }
-
-                table {
-                        mso-table-lspace: 0pt;
-                        mso-table-rspace: 0pt;
-                }
-
-                .ReadMsgBody {
-                        width: 100%;
-                }
-
-                .ExternalClass {
-                        width: 100%;
-                }
-
-                p,
-                a,
-                li,
-                td,
-                blockquote {
-                        mso-line-height-rule: exactly;
-                }
-
-                a[href^=tel],
-                a[href^=sms] {
-                        color: inherit;
-                        cursor: default;
-                        text-decoration: none;
-                }
-
-                p,
-                a,
-                li,
-                td,
-                body,
-                table,
-                blockquote {
-                        -ms-text-size-adjust: 100%;
-                        -webkit-text-size-adjust: 100%;
-                }
-
-                .ExternalClass,
-                .ExternalClass p,
-                .ExternalClass td,
-                .ExternalClass div,
-                .ExternalClass span,
-                .ExternalClass font {
-                        line-height: 100%;
-                }
-
-                a[x-apple-data-detectors] {
-                        color: inherit !important;
-                        text-decoration: none !important;
-                        font-size: inherit !important;
-                        font-family: inherit !important;
-                        font-weight: inherit !important;
-                        line-height: inherit !important;
-                }
-
-                .templateContainer {
-                        max-width: 600px !important;
-                }
-
-                a.mcnButton {
-                        display: block;
-                }
-
-                .mcnImage,
-                .mcnRetinaImage {
-                        vertical-align: bottom;
-                }
-
-                .mcnTextContent {
-                        word-break: break-word;
-                }
-
-                .mcnTextContent img {
-                        height: auto !important;
-                }
-
-                .mcnDividerBlock {
-                        table-layout: fixed !important;
-                }
-
-                /*
-        @tab Page
-        @section Heading 1
-        @style heading 1
-        */
-                h1 {
-                        /*@editable*/
-                        color: #222222;
-                        /*@editable*/
-                        font-family: 'Open Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
-                        /*@editable*/
-                        font-size: 40px;
-                        /*@editable*/
-                        font-style: normal;
-                        /*@editable*/
-                        font-weight: bold;
-                        /*@editable*/
-                        line-height: 150%;
-                        /*@editable*/
-                        letter-spacing: normal;
-                        /*@editable*/
-                        text-align: center;
-                }
-
-                /*
-        @tab Page
-        @section Heading 2
-        @style heading 2
-        */
-                h2 {
-                        /*@editable*/
-                        color: #222222;
-                        /*@editable*/
-                        font-family: Helvetica;
-                        /*@editable*/
-                        font-size: 34px;
-                        /*@editable*/
-                        font-style: normal;
-                        /*@editable*/
-                        font-weight: bold;
-                        /*@editable*/
-                        line-height: 150%;
-                        /*@editable*/
-                        letter-spacing: normal;
-                        /*@editable*/
-                        text-align: left;
-                }
-
-                /*
-        @tab Page
-        @section Heading 3
-        @style heading 3
-        */
-                h3 {
-                        /*@editable*/
-                        color: #444444;
-                        /*@editable*/
-                        font-family: Helvetica;
-                        /*@editable*/
-                        font-size: 22px;
-                        /*@editable*/
-                        font-style: normal;
-                        /*@editable*/
-                        font-weight: bold;
-                        /*@editable*/
-                        line-height: 150%;
-                        /*@editable*/
-                        letter-spacing: normal;
-                        /*@editable*/
-                        text-align: left;
-                }
-
-                /*
-        @tab Page
-        @section Heading 4
-        @style heading 4
-        */
-                h4 {
-                        /*@editable*/
-                        color: #949494;
-                        /*@editable*/
-                        font-family: Georgia;
-                        /*@editable*/
-                        font-size: 20px;
-                        /*@editable*/
-                        font-style: italic;
-                        /*@editable*/
-                        font-weight: normal;
-                        /*@editable*/
-                        line-height: 125%;
-                        /*@editable*/
-                        letter-spacing: normal;
-                        /*@editable*/
-                        text-align: left;
-                }
-
-                /*
-        @tab Header
-        @section Header Container Style
-        */
-                #templateHeader {
-                        /*@editable*/
-                        background-color: #f4f4f4;
-                        /*@editable*/
-                        background-image: none;
-                        /*@editable*/
-                        background-repeat: no-repeat;
-                        /*@editable*/
-                        background-position: center;
-                        /*@editable*/
-                        background-size: cover;
-                        /*@editable*/
-                        border-top: 0;
-                        /*@editable*/
-                        border-bottom: 0;
-                        /*@editable*/
-                        padding-top: 0px;
-                        /*@editable*/
-                        padding-bottom: 0px;
-                }
-
-                /*
-        @tab Header
-        @section Header Interior Style
-        */
-                .headerContainer {
-                        /*@editable*/
-                        background-color: #transparent;
-                        /*@editable*/
-                        background-image: none;
-                        /*@editable*/
-                        background-repeat: no-repeat;
-                        /*@editable*/
-                        background-position: center;
-                        /*@editable*/
-                        background-size: cover;
-                        /*@editable*/
-                        border-top: 0;
-                        /*@editable*/
-                        border-bottom: 0;
-                        /*@editable*/
-                        padding-top: 0;
-                        /*@editable*/
-                        padding-bottom: 0;
-                }
-
-                /*
-        @tab Header
-        @section Header Text
-        */
-                .headerContainer .mcnTextContent,
-                .headerContainer .mcnTextContent p {
-                        /*@editable*/
-                        color: #757575;
-                        /*@editable*/
-                        font-family: Helvetica;
-                        /*@editable*/
-                        font-size: 16px;
-                        /*@editable*/
-                        line-height: 150%;
-                        /*@editable*/
-                        text-align: left;
-                }
-
-                /*
-        @tab Header
-        @section Header Link
-        */
-                .headerContainer .mcnTextContent a,
-                .headerContainer .mcnTextContent p a {
-                        /*@editable*/
-                        color: #007C89;
-                        /*@editable*/
-                        font-weight: normal;
-                        /*@editable*/
-                        text-decoration: underline;
-                }
-
-                /*
-        @tab Body
-        @section Body Container Style
-        */
-                #templateBody {
-                        /*@editable*/
-                        background-color: #f4f4f4;
-                        /*@editable*/
-                        background-image: none;
-                        /*@editable*/
-                        background-repeat: no-repeat;
-                        /*@editable*/
-                        background-position: center;
-                        /*@editable*/
-                        background-size: cover;
-                        /*@editable*/
-                        border-top: 0;
-                        /*@editable*/
-                        border-bottom: 0;
-                        /*@editable*/
-                        padding-top: 0px;
-                        /*@editable*/
-                        padding-bottom: 20px;
-                }
-
-                /*
-        @tab Body
-        @section Body Interior Style
-        */
-                .bodyContainer {
-                        /*@editable*/
-                        background-color: #f4f4f4;
-                        /*@editable*/
-                        background-image: none;
-                        /*@editable*/
-                        background-repeat: no-repeat;
-                        /*@editable*/
-                        background-position: center;
-                        /*@editable*/
-                        background-size: cover;
-                        /*@editable*/
-                        border-top: 2px none #ff9933;
-                        /*@editable*/
-                        border-bottom: 2px none #ff9933;
-                        /*@editable*/
-                        padding-top: 10px;
-                        /*@editable*/
-                        padding-bottom: 10px;
-                }
-
-                /*
-        @tab Body
-        @section Body Text
-        */
-                .bodyContainer .mcnTextContent,
-                .bodyContainer .mcnTextContent p {
-                        /*@editable*/
-                        color: #757575;
-                        /*@editable*/
-                        font-family: Helvetica;
-                        /*@editable*/
-                        font-size: 16px;
-                        /*@editable*/
-                        line-height: 150%;
-                        /*@editable*/
-                        text-align: left;
-                }
-
-                /*
-        @tab Body
-        @section Body Link
-        */
-                .bodyContainer .mcnTextContent a,
-                .bodyContainer .mcnTextContent p a {
-                        /*@editable*/
-                        color: #222222;
-                        /*@editable*/
-                        font-weight: normal;
-                        /*@editable*/
-                        text-decoration: underline;
-                }
-
-                /*
-        @tab Footer
-        @section Footer Style
-        */
-                #templateFooter {
-                        /*@editable*/
-                        background-color: #f4f4f4;
-                        /*@editable*/
-                        background-image: none;
-                        /*@editable*/
-                        background-repeat: no-repeat;
-                        /*@editable*/
-                        background-position: center;
-                        /*@editable*/
-                        background-size: cover;
-                        /*@editable*/
-                        border-top: 0;
-                        /*@editable*/
-                        border-bottom: 0;
-                        /*@editable*/
-                        padding-top: 0px;
-                        /*@editable*/
-                        padding-bottom: 20px;
-                }
-
-                /*
-        @tab Footer
-        @section Footer Interior Style
-        */
-                .footerContainer {
-                        /*@editable*/
-                        background-color: #transparent;
-                        /*@editable*/
-                        background-image: none;
-                        /*@editable*/
-                        background-repeat: no-repeat;
-                        /*@editable*/
-                        background-position: center;
-                        /*@editable*/
-                        background-size: cover;
-                        /*@editable*/
-                        border-top: 0;
-                        /*@editable*/
-                        border-bottom: 0;
-                        /*@editable*/
-                        padding-top: 0;
-                        /*@editable*/
-                        padding-bottom: 0;
-                }
-
-                /*
-        @tab Footer
-        @section Footer Text
-        */
-                .footerContainer .mcnTextContent,
-                .footerContainer .mcnTextContent p {
-                        /*@editable*/
-                        color: #FFFFFF;
-                        /*@editable*/
-                        font-family: 'Helvetica Neue', Helvetica, Arial, Verdana, sans-serif;
-                        /*@editable*/
-                        font-size: 12px;
-                        /*@editable*/
-                        line-height: 150%;
-                        /*@editable*/
-                        text-align: center;
-                }
-
-                /*
-        @tab Footer
-        @section Footer Link
-        */
-                .footerContainer .mcnTextContent a,
-                .footerContainer .mcnTextContent p a {
-                        /*@editable*/
-                        color: #FFFFFF;
-                        /*@editable*/
-                        font-weight: normal;
-                        /*@editable*/
-                        text-decoration: underline;
-                }
-
-                @media only screen and (max-width: 480px) {
-
-                        body,
-                        table,
-                        td,
-                        p,
-                        a,
-                        li,
-                        blockquote {
-                                -webkit-text-size-adjust: none !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-                        body {
-                                width: 100% !important;
-                                min-width: 100% !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-                        .mcnRetinaImage {
-                                max-width: 100% !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-                        .mcnImage {
-                                width: 100% !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-
-                        .mcnCartContainer,
-                        .mcnCaptionTopContent,
-                        .mcnRecContentContainer,
-                        .mcnCaptionBottomContent,
-                        .mcnTextContentContainer,
-                        .mcnBoxedTextContentContainer,
-                        .mcnImageGroupContentContainer,
-                        .mcnCaptionLeftTextContentContainer,
-                        .mcnCaptionRightTextContentContainer,
-                        .mcnCaptionLeftImageContentContainer,
-                        .mcnCaptionRightImageContentContainer,
-                        .mcnImageCardLeftTextContentContainer,
-                        .mcnImageCardRightTextContentContainer,
-                        .mcnImageCardLeftImageContentContainer,
-                        .mcnImageCardRightImageContentContainer {
-                                max-width: 100% !important;
-                                width: 100% !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-                        .mcnBoxedTextContentContainer {
-                                min-width: 100% !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-                        .mcnImageGroupContent {
-                                padding: 9px !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-
-                        .mcnCaptionLeftContentOuter .mcnTextContent,
-                        .mcnCaptionRightContentOuter .mcnTextContent {
-                                padding-top: 9px !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-
-                        .mcnImageCardTopImageContent,
-                        .mcnCaptionBottomContent:last-child .mcnCaptionBottomImageContent,
-                        .mcnCaptionBlockInner .mcnCaptionTopContent:last-child .mcnTextContent {
-                                padding-top: 18px !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-                        .mcnImageCardBottomImageContent {
-                                padding-bottom: 9px !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-                        .mcnImageGroupBlockInner {
-                                padding-top: 0 !important;
-                                padding-bottom: 0 !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-                        .mcnImageGroupBlockOuter {
-                                padding-top: 9px !important;
-                                padding-bottom: 9px !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-
-                        .mcnTextContent,
-                        .mcnBoxedTextContentColumn {
-                                padding-right: 18px !important;
-                                padding-left: 18px !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-
-                        .mcnImageCardLeftImageContent,
-                        .mcnImageCardRightImageContent {
-                                padding-right: 18px !important;
-                                padding-bottom: 0 !important;
-                                padding-left: 18px !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-                        .mcpreview-image-uploader {
-                                display: none !important;
-                                width: 100% !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-
-                        /*
-        @tab Mobile Styles
-        @section Heading 1
-        @tip Make the first-level headings larger in size for better readability on small screens.
-        */
-                        h1 {
-                                /*@editable*/
-                                font-size: 30px !important;
-                                /*@editable*/
-                                line-height: 125% !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-
-                        /*
-        @tab Mobile Styles
-        @section Heading 2
-        @tip Make the second-level headings larger in size for better readability on small screens.
-        */
-                        h2 {
-                                /*@editable*/
-                                font-size: 26px !important;
-                                /*@editable*/
-                                line-height: 125% !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-
-                        /*
-        @tab Mobile Styles
-        @section Heading 3
-        @tip Make the third-level headings larger in size for better readability on small screens.
-        */
-                        h3 {
-                                /*@editable*/
-                                font-size: 20px !important;
-                                /*@editable*/
-                                line-height: 150% !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-
-                        /*
-        @tab Mobile Styles
-        @section Heading 4
-        @tip Make the fourth-level headings larger in size for better readability on small screens.
-        */
-                        h4 {
-                                /*@editable*/
-                                font-size: 18px !important;
-                                /*@editable*/
-                                line-height: 150% !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-
-                        /*
-        @tab Mobile Styles
-        @section Boxed Text
-        @tip Make the boxed text larger in size for better readability on small screens. We recommend a font size of at least 16px.
-        */
-                        .mcnBoxedTextContentContainer .mcnTextContent,
-                        .mcnBoxedTextContentContainer .mcnTextContent p {
-                                /*@editable*/
-                                font-size: 14px !important;
-                                /*@editable*/
-                                line-height: 150% !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-
-                        /*
-        @tab Mobile Styles
-        @section Header Text
-        @tip Make the header text larger in size for better readability on small screens.
-        */
-                        .headerContainer .mcnTextContent,
-                        .headerContainer .mcnTextContent p {
-                                /*@editable*/
-                                font-size: 16px !important;
-                                /*@editable*/
-                                line-height: 150% !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-
-                        /*
-        @tab Mobile Styles
-        @section Body Text
-        @tip Make the body text larger in size for better readability on small screens. We recommend a font size of at least 16px.
-        */
-                        .bodyContainer .mcnTextContent,
-                        .bodyContainer .mcnTextContent p {
-                                /*@editable*/
-                                font-size: 16px !important;
-                                /*@editable*/
-                                line-height: 150% !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-
-                        /*
-        @tab Mobile Styles
-        @section Footer Text
-        @tip Make the footer content text larger in size for better readability on small screens.
-        */
-                        .footerContainer .mcnTextContent,
-                        .footerContainer .mcnTextContent p {
-                                /*@editable*/
-                                font-size: 14px !important;
-                                /*@editable*/
-                                line-height: 150% !important;
-                        }
-
-                }
-        </style>
-</head>
-
-<body>
-        <!--*|IF:MC_PREVIEW_TEXT|*-->
-        <!--[if !gte mso 9]><!----><span class="mcnPreviewText"
-                style="display:none; font-size:0px; line-height:0px; max-height:0px; max-width:0px; opacity:0; overflow:hidden; visibility:hidden; mso-hide:all;"></span>
-        <!--<![endif]-->
-        <!--*|END:IF|*-->
-        <center>
-                <table align="center" border="0" cellpadding="0" cellspacing="0" height="100%" width="100%" id="bodyTable">
-                        <tr>
-                                <td align="center" valign="top" id="bodyCell">
-                                        <!-- BEGIN TEMPLATE // -->
-                                        <table border="0" cellpadding="0" cellspacing="0" width="100%">
-                                                <tr>
-                                                        <td align="center" valign="top" id="templateHeader" data-template-container>
-                                                                <!--[if (gte mso 9)|(IE)]>
-                                    <table align="center" border="0" cellspacing="0" cellpadding="0" width="600" style="width:600px;">
-                                    <tr>
-                                    <td align="center" valign="top" width="600" style="width:600px;">
-                                    <![endif]-->
-                                                                <table align="center" border="0" cellpadding="0" cellspacing="0" width="100%"
-                                                                        class="templateContainer">
-                                                                        <tr>
-                                                                                <td valign="top" class="headerContainer"></td>
-                                                                        </tr>
-                                                                </table>
-                                                                <!--[if (gte mso 9)|(IE)]>
-                                    </td>
-                                    </tr>
-                                    </table>
-                                    <![endif]-->
-                                                        </td>
-                                                </tr>
-                                                <tr>
-                                                        <td align="center" valign="top" id="templateBody" data-template-container>
-                                                                <!--[if (gte mso 9)|(IE)]>
-                                    <table align="center" border="0" cellspacing="0" cellpadding="0" width="600" style="width:600px;">
-                                    <tr>
-                                    <td align="center" valign="top" width="600" style="width:600px;">
-                                    <![endif]-->
-                                                                <table align="center" border="0" cellpadding="0" cellspacing="0" width="100%"
-                                                                        class="templateContainer">
-                                                                        <tr>
-                                                                                <td valign="top" class="bodyContainer">
-                                                                                        <table border="0" cellpadding="0" cellspacing="0" width="100%"
-                                                                                                class="mcnCodeBlock">
-                                                                                                <tbody class="mcnTextBlockOuter">
-                                                                                                        <tr>
-                                                                                                                <td valign="top" class="mcnTextBlockInner">
-
-
-                                                                                                                        <div
-                                                                                                                                style="background-color:#fff; margin-left: 3%; margin-right: 3%; border: 1px solid #ddd; margin-top: 40px; border-radius: 6px;">
-                                                                                                                                <div style="padding-left: 15%; padding-right: 15%;">
-
-                                                                                                                                        <p
-                                                                                                                                                style="font-family:'Helvetica'; font-size: 16px; line-height: 26px; font-weight:700; text-align: center; padding-top: 24px; padding-bottom: 24px; padding-left: 8%; padding-right: 8%; ">
-                                                                                                                                                A password reset request for your account on
-                                                                                                                                                ${appname} has been received.
-                                                                                                                                        </p>
-
-                                                                                                                                        <div class="button-td button-td-primary"
-                                                                                                                                                style="border-radius: 6px; margin-bottom: 50px; display: block; text-align: center;">
-                                                                                                                                                <a class="button-a button-a-primary"
-                                                                                                                                                        href="${resetLink}" target="_blank"
-                                                                                                                                                        style="background: #52B56E;font-size: 17px;line-height: 24px;font-weight: 700;font-family: 'Helvetica', sans-serif;text-decoration: none;padding: 9px 25px 9px 25px;color: #ffffff;display: block;border-radius: 6px;width: fit-content;margin: 0 auto;">Reset
-                                                                                                                                                        Password</a>
-                                                                                                                                        </div>
-                                                                                                                                </div>
-                                                                                                                                <div
-                                                                                                                                        style="background-color:#fafafa; border-top: 1px solid #ddd; padding-left: 15%; padding-right: 15%; padding-bottom: 24px; padding-top: 24px">
-                                                                                                                                        <p
-                                                                                                                                                style="font-family: 'Hevetica', sans-serif; font-size: 14px; line-height: 23px; font-weight:400;  text-align: center; color: #808080;">
-                                                                                                                                                Alternatively, you can directly paste this link
-                                                                                                                                                in your browser <br>
-                                                                                                                                                <a style="font-family: 'Helvetica', sans-serif, sans-serif; text-align: center; word-break: break-all; font-weight: 400; font-size: 14px; line-height: 23px; color: #007aff !important;"
-                                                                                                                                                        target="_blank"
-                                                                                                                                                        href="${resetLink}">${resetLink}</a>
-                                                                                                                                        </p>
-                                                                                                                                </div>
-                                                                                                                        </div>
-
-
-
-
-                                                                                                                </td>
-                                                                                                        </tr>
-                                                                                                </tbody>
-                                                                                        </table>
-                                                                                        <table border="0" cellpadding="0" cellspacing="0" width="100%"
-                                                                                                class="mcnCodeBlock">
-                                                                                                <tbody class="mcnTextBlockOuter">
-                                                                                                        <tr>
-                                                                                                                <td valign="top" class="mcnTextBlockInner">
-                                                                                                                        <p
-                                                                                                                                style="font-family: 'Helvetica', sans-serif; font-size: 16px; line-height: 26px; font-weight:400; text-align: center; color: #808080">
-                                                                                                                                This email is meant for <a
-                                                                                                                                        style="font-family: 'Helvetica', sans-serif; text-align: center; word-break: break-all; font-weight: 400; font-size: 16px; line-height: 26px; color: #808080 !important;"
-                                                                                                                                        target="_blank"
-                                                                                                                                        href="mailto:${toEmail}">${toEmail}</a>
-                                                                                                                        </p>
-                                                                                                                </td>
-                                                                                                        </tr>
-                                                                                                </tbody>
-                                                                                        </table>
-                                                                                </td>
-                                                                        </tr>
-                                                                </table>
-                                                                <!--[if (gte mso 9)|(IE)]>
-                                    </td>
-                                    </tr>
-                                    </table>
-                                    <![endif]-->
-                                                        </td>
-                                                </tr>
-                                                <tr>
-                                                        <td align="center" valign="top" id="templateFooter" data-template-container>
-                                                                <!--[if (gte mso 9)|(IE)]>
-                                    <table align="center" border="0" cellspacing="0" cellpadding="0" width="600" style="width:600px;">
-                                    <tr>
-                                    <td align="center" valign="top" width="600" style="width:600px;">
-                                    <![endif]-->
-                                                                <table align="center" border="0" cellpadding="0" cellspacing="0" width="100%"
-                                                                        class="templateContainer">
-                                                                        <tr>
-                                                                                <td valign="top" class="footerContainer"></td>
-                                                                        </tr>
-                                                                </table>
-                                                                <!--[if (gte mso 9)|(IE)]>
-                                    </td>
-                                    </tr>
-                                    </table>
-                                    <![endif]-->
-                                                        </td>
-                                                </tr>
-                                        </table>
-                                        <!-- // END TEMPLATE -->
-                                </td>
-                        </tr>
-                </table>
-        </center>
-</body>
-
-</html>
-"""
-
-
-
-
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailpassword/emaildelivery/services/smtp/service_implementation/index.html b/html/supertokens_python/recipe/emailpassword/emaildelivery/services/smtp/service_implementation/index.html deleted file mode 100644 index 0f67374d2..000000000 --- a/html/supertokens_python/recipe/emailpassword/emaildelivery/services/smtp/service_implementation/index.html +++ /dev/null @@ -1,170 +0,0 @@ - - - - - - -supertokens_python.recipe.emailpassword.emaildelivery.services.smtp.service_implementation API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.emailpassword.emaildelivery.services.smtp.service_implementation

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from typing import Any, Dict
-
-from supertokens_python.ingredients.emaildelivery.types import (
-    EmailContent,
-    SMTPServiceInterface,
-)
-from supertokens_python.recipe.emailpassword.emaildelivery.services.smtp.password_reset import (
-    get_password_reset_email_content,
-)
-from supertokens_python.recipe.emailpassword.types import EmailTemplateVars
-
-
-class ServiceImplementation(SMTPServiceInterface[EmailTemplateVars]):
-    async def send_raw_email(
-        self, content: EmailContent, user_context: Dict[str, Any]
-    ) -> None:
-        await self.transporter.send_email(content, user_context)
-
-    async def get_content(
-        self, template_vars: EmailTemplateVars, user_context: Dict[str, Any]
-    ) -> EmailContent:
-        return get_password_reset_email_content(template_vars)
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class ServiceImplementation -(transporter: Transporter) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class ServiceImplementation(SMTPServiceInterface[EmailTemplateVars]):
-    async def send_raw_email(
-        self, content: EmailContent, user_context: Dict[str, Any]
-    ) -> None:
-        await self.transporter.send_email(content, user_context)
-
-    async def get_content(
-        self, template_vars: EmailTemplateVars, user_context: Dict[str, Any]
-    ) -> EmailContent:
-        return get_password_reset_email_content(template_vars)
-
-

Ancestors

- -

Methods

-
-
-async def get_content(self, template_vars: PasswordResetEmailTemplateVars, user_context: Dict[str, Any]) ‑> EmailContent -
-
-
-
- -Expand source code - -
async def get_content(
-    self, template_vars: EmailTemplateVars, user_context: Dict[str, Any]
-) -> EmailContent:
-    return get_password_reset_email_content(template_vars)
-
-
-
-async def send_raw_email(self, content: EmailContent, user_context: Dict[str, Any]) ‑> None -
-
-
-
- -Expand source code - -
async def send_raw_email(
-    self, content: EmailContent, user_context: Dict[str, Any]
-) -> None:
-    await self.transporter.send_email(content, user_context)
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailpassword/exceptions.html b/html/supertokens_python/recipe/emailpassword/exceptions.html deleted file mode 100644 index cfe6ad462..000000000 --- a/html/supertokens_python/recipe/emailpassword/exceptions.html +++ /dev/null @@ -1,208 +0,0 @@ - - - - - - -supertokens_python.recipe.emailpassword.exceptions API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.emailpassword.exceptions

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from typing import TYPE_CHECKING, Any, Dict, List, NoReturn
-
-from supertokens_python.exceptions import SuperTokensError
-
-if TYPE_CHECKING:
-    from .types import ErrorFormField
-
-
-def raise_form_field_exception(msg: str, form_fields: List[ErrorFormField]) -> NoReturn:
-    raise FieldError(msg, form_fields)
-
-
-class SuperTokensEmailPasswordError(SuperTokensError):
-    pass
-
-
-class FieldError(SuperTokensEmailPasswordError):
-    def __init__(self, msg: str, form_fields: List[ErrorFormField]):
-        super().__init__(msg)
-        self.form_fields = form_fields
-
-    def get_json_form_fields(self) -> List[Dict[str, Any]]:
-        form_fields: List[Dict[str, Any]] = []
-        for form_field in self.form_fields:
-            form_fields.append({"id": form_field.id, "error": form_field.error})
-        return form_fields
-
-
-
-
-
-
-
-

Functions

-
-
-def raise_form_field_exception(msg: str, form_fields: List[ErrorFormField]) ‑> NoReturn -
-
-
-
- -Expand source code - -
def raise_form_field_exception(msg: str, form_fields: List[ErrorFormField]) -> NoReturn:
-    raise FieldError(msg, form_fields)
-
-
-
-
-
-

Classes

-
-
-class FieldError -(msg: str, form_fields: List[ErrorFormField]) -
-
-

Common base class for all non-exit exceptions.

-
- -Expand source code - -
class FieldError(SuperTokensEmailPasswordError):
-    def __init__(self, msg: str, form_fields: List[ErrorFormField]):
-        super().__init__(msg)
-        self.form_fields = form_fields
-
-    def get_json_form_fields(self) -> List[Dict[str, Any]]:
-        form_fields: List[Dict[str, Any]] = []
-        for form_field in self.form_fields:
-            form_fields.append({"id": form_field.id, "error": form_field.error})
-        return form_fields
-
-

Ancestors

- -

Methods

-
-
-def get_json_form_fields(self) ‑> List[Dict[str, Any]] -
-
-
-
- -Expand source code - -
def get_json_form_fields(self) -> List[Dict[str, Any]]:
-    form_fields: List[Dict[str, Any]] = []
-    for form_field in self.form_fields:
-        form_fields.append({"id": form_field.id, "error": form_field.error})
-    return form_fields
-
-
-
-
-
-class SuperTokensEmailPasswordError -(*args, **kwargs) -
-
-

Common base class for all non-exit exceptions.

-
- -Expand source code - -
class SuperTokensEmailPasswordError(SuperTokensError):
-    pass
-
-

Ancestors

- -

Subclasses

- -
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailpassword/index.html b/html/supertokens_python/recipe/emailpassword/index.html deleted file mode 100644 index 0dfb72456..000000000 --- a/html/supertokens_python/recipe/emailpassword/index.html +++ /dev/null @@ -1,198 +0,0 @@ - - - - - - -supertokens_python.recipe.emailpassword API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.emailpassword

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from typing import TYPE_CHECKING, Callable, Union
-
-from supertokens_python.ingredients.emaildelivery.types import EmailDeliveryConfig
-from supertokens_python.recipe.emailpassword.types import EmailTemplateVars
-from supertokens_python.ingredients.emaildelivery import types as emaildelivery_types
-
-from . import exceptions as ex
-from . import utils
-from .emaildelivery import services as emaildelivery_services
-from .recipe import EmailPasswordRecipe
-
-exceptions = ex
-InputOverrideConfig = utils.InputOverrideConfig
-InputSignUpFeature = utils.InputSignUpFeature
-InputFormField = utils.InputFormField
-SMTPService = emaildelivery_services.SMTPService
-EmailDeliveryInterface = emaildelivery_types.EmailDeliveryInterface
-
-if TYPE_CHECKING:
-    from supertokens_python.supertokens import AppInfo
-
-    from ...recipe_module import RecipeModule
-
-
-def init(
-    sign_up_feature: Union[utils.InputSignUpFeature, None] = None,
-    override: Union[utils.InputOverrideConfig, None] = None,
-    email_delivery: Union[EmailDeliveryConfig[EmailTemplateVars], None] = None,
-) -> Callable[[AppInfo], RecipeModule]:
-    return EmailPasswordRecipe.init(
-        sign_up_feature,
-        override,
-        email_delivery,
-    )
-
-
-
-

Sub-modules

-
-
supertokens_python.recipe.emailpassword.api
-
-
-
-
supertokens_python.recipe.emailpassword.asyncio
-
-
-
-
supertokens_python.recipe.emailpassword.constants
-
-
-
-
supertokens_python.recipe.emailpassword.emaildelivery
-
-
-
-
supertokens_python.recipe.emailpassword.exceptions
-
-
-
-
supertokens_python.recipe.emailpassword.interfaces
-
-
-
-
supertokens_python.recipe.emailpassword.recipe
-
-
-
-
supertokens_python.recipe.emailpassword.recipe_implementation
-
-
-
-
supertokens_python.recipe.emailpassword.syncio
-
-
-
-
supertokens_python.recipe.emailpassword.types
-
-
-
-
supertokens_python.recipe.emailpassword.utils
-
-
-
-
-
-
-
-
-

Functions

-
-
-def init(sign_up_feature: Union[InputSignUpFeature, None] = None, override: Union[InputOverrideConfig, None] = None, email_delivery: Union[EmailDeliveryConfig[EmailTemplateVars], None] = None) ‑> Callable[[AppInfo], RecipeModule] -
-
-
-
- -Expand source code - -
def init(
-    sign_up_feature: Union[utils.InputSignUpFeature, None] = None,
-    override: Union[utils.InputOverrideConfig, None] = None,
-    email_delivery: Union[EmailDeliveryConfig[EmailTemplateVars], None] = None,
-) -> Callable[[AppInfo], RecipeModule]:
-    return EmailPasswordRecipe.init(
-        sign_up_feature,
-        override,
-        email_delivery,
-    )
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailpassword/interfaces.html b/html/supertokens_python/recipe/emailpassword/interfaces.html deleted file mode 100644 index a38b8f6f7..000000000 --- a/html/supertokens_python/recipe/emailpassword/interfaces.html +++ /dev/null @@ -1,1607 +0,0 @@ - - - - - - -supertokens_python.recipe.emailpassword.interfaces API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.emailpassword.interfaces

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from abc import ABC, abstractmethod
-from typing import TYPE_CHECKING, Any, Dict, List, Union
-
-from supertokens_python.ingredients.emaildelivery import EmailDeliveryIngredient
-from supertokens_python.recipe.emailpassword.types import EmailTemplateVars
-from ...supertokens import AppInfo
-
-from ...types import APIResponse, GeneralErrorResponse
-
-if TYPE_CHECKING:
-    from supertokens_python.framework import BaseRequest, BaseResponse
-    from supertokens_python.recipe.session import SessionContainer
-
-    from .types import FormField, User
-    from .utils import EmailPasswordConfig
-
-
-class SignUpOkResult:
-    def __init__(self, user: User):
-        self.user = user
-
-
-class SignUpEmailAlreadyExistsError:
-    pass
-
-
-class SignInOkResult:
-    def __init__(self, user: User):
-        self.user = user
-
-
-class SignInWrongCredentialsError:
-    pass
-
-
-class CreateResetPasswordOkResult:
-    def __init__(self, token: str):
-        self.token = token
-
-
-class CreateResetPasswordWrongUserIdError:
-    pass
-
-
-class CreateResetPasswordLinkOkResult:
-    def __init__(self, link: str):
-        self.link = link
-
-
-class CreateResetPasswordLinkUnknownUserIdError:
-    pass
-
-
-class SendResetPasswordEmailOkResult:
-    pass
-
-
-class SendResetPasswordEmailUnknownUserIdError:
-    pass
-
-
-class ResetPasswordUsingTokenOkResult:
-    def __init__(self, user_id: Union[str, None]):
-        self.user_id = user_id
-
-
-class ResetPasswordUsingTokenInvalidTokenError:
-    pass
-
-
-class UpdateEmailOrPasswordOkResult:
-    pass
-
-
-class UpdateEmailOrPasswordEmailAlreadyExistsError:
-    pass
-
-
-class UpdateEmailOrPasswordUnknownUserIdError:
-    pass
-
-
-class UpdateEmailOrPasswordPasswordPolicyViolationError:
-    failure_reason: str
-
-    def __init__(self, failure_reason: str):
-        self.failure_reason = failure_reason
-
-
-class RecipeInterface(ABC):
-    def __init__(self):
-        pass
-
-    @abstractmethod
-    async def get_user_by_id(
-        self, user_id: str, user_context: Dict[str, Any]
-    ) -> Union[User, None]:
-        pass
-
-    @abstractmethod
-    async def get_user_by_email(
-        self, email: str, tenant_id: str, user_context: Dict[str, Any]
-    ) -> Union[User, None]:
-        pass
-
-    @abstractmethod
-    async def create_reset_password_token(
-        self, user_id: str, tenant_id: str, user_context: Dict[str, Any]
-    ) -> Union[CreateResetPasswordOkResult, CreateResetPasswordWrongUserIdError]:
-        pass
-
-    @abstractmethod
-    async def reset_password_using_token(
-        self,
-        token: str,
-        new_password: str,
-        tenant_id: str,
-        user_context: Dict[str, Any],
-    ) -> Union[
-        ResetPasswordUsingTokenOkResult, ResetPasswordUsingTokenInvalidTokenError
-    ]:
-        pass
-
-    @abstractmethod
-    async def sign_in(
-        self, email: str, password: str, tenant_id: str, user_context: Dict[str, Any]
-    ) -> Union[SignInOkResult, SignInWrongCredentialsError]:
-        pass
-
-    @abstractmethod
-    async def sign_up(
-        self, email: str, password: str, tenant_id: str, user_context: Dict[str, Any]
-    ) -> Union[SignUpOkResult, SignUpEmailAlreadyExistsError]:
-        pass
-
-    @abstractmethod
-    async def update_email_or_password(
-        self,
-        user_id: str,
-        email: Union[str, None],
-        password: Union[str, None],
-        apply_password_policy: Union[bool, None],
-        tenant_id_for_password_policy: str,
-        user_context: Dict[str, Any],
-    ) -> Union[
-        UpdateEmailOrPasswordOkResult,
-        UpdateEmailOrPasswordEmailAlreadyExistsError,
-        UpdateEmailOrPasswordUnknownUserIdError,
-        UpdateEmailOrPasswordPasswordPolicyViolationError,
-    ]:
-        pass
-
-
-class APIOptions:
-    def __init__(
-        self,
-        request: BaseRequest,
-        response: BaseResponse,
-        recipe_id: str,
-        config: EmailPasswordConfig,
-        recipe_implementation: RecipeInterface,
-        app_info: AppInfo,
-        email_delivery: EmailDeliveryIngredient[EmailTemplateVars],
-    ):
-        self.request: BaseRequest = request
-        self.response: BaseResponse = response
-        self.recipe_id: str = recipe_id
-        self.config: EmailPasswordConfig = config
-        self.recipe_implementation: RecipeInterface = recipe_implementation
-        self.app_info = app_info
-        self.email_delivery = email_delivery
-
-
-class EmailExistsGetOkResult(APIResponse):
-    status: str = "OK"
-
-    def __init__(self, exists: bool):
-        self.exists = exists
-
-    def to_json(self) -> Dict[str, Any]:
-        return {"status": self.status, "exists": self.exists}
-
-
-class GeneratePasswordResetTokenPostOkResult(APIResponse):
-    status: str = "OK"
-
-    def to_json(self) -> Dict[str, Any]:
-        return {"status": self.status}
-
-
-class PasswordResetPostOkResult(APIResponse):
-    status: str = "OK"
-
-    def __init__(self, user_id: Union[str, None]):
-        self.user_id = user_id
-
-    def to_json(self) -> Dict[str, Any]:
-        return {"status": self.status}
-
-
-class PasswordResetPostInvalidTokenResponse(APIResponse):
-    status: str = "RESET_PASSWORD_INVALID_TOKEN_ERROR"
-
-    def to_json(self) -> Dict[str, Any]:
-        return {"status": self.status}
-
-
-class SignInPostOkResult(APIResponse):
-    status: str = "OK"
-
-    def __init__(self, user: User, session: SessionContainer):
-        self.user = user
-        self.session = session
-
-    def to_json(self) -> Dict[str, Any]:
-        return {
-            "status": self.status,
-            "user": {
-                "id": self.user.user_id,
-                "email": self.user.email,
-                "timeJoined": self.user.time_joined,
-            },
-        }
-
-
-class SignInPostWrongCredentialsError(APIResponse):
-    status: str = "WRONG_CREDENTIALS_ERROR"
-
-    def to_json(self) -> Dict[str, Any]:
-        return {"status": self.status}
-
-
-class SignUpPostOkResult(APIResponse):
-    status: str = "OK"
-
-    def __init__(self, user: User, session: SessionContainer):
-        self.user = user
-        self.session = session
-
-    def to_json(self) -> Dict[str, Any]:
-        return {
-            "status": self.status,
-            "user": {
-                "id": self.user.user_id,
-                "email": self.user.email,
-                "timeJoined": self.user.time_joined,
-            },
-        }
-
-
-class SignUpPostEmailAlreadyExistsError(APIResponse):
-    status: str = "EMAIL_ALREADY_EXISTS_ERROR"
-
-    def to_json(self) -> Dict[str, Any]:
-        return {"status": self.status}
-
-
-class APIInterface:
-    def __init__(self):
-        self.disable_email_exists_get = False
-        self.disable_generate_password_reset_token_post = False
-        self.disable_password_reset_post = False
-        self.disable_sign_in_post = False
-        self.disable_sign_up_post = False
-
-    @abstractmethod
-    async def email_exists_get(
-        self,
-        email: str,
-        tenant_id: str,
-        api_options: APIOptions,
-        user_context: Dict[str, Any],
-    ) -> Union[EmailExistsGetOkResult, GeneralErrorResponse]:
-        pass
-
-    @abstractmethod
-    async def generate_password_reset_token_post(
-        self,
-        form_fields: List[FormField],
-        tenant_id: str,
-        api_options: APIOptions,
-        user_context: Dict[str, Any],
-    ) -> Union[GeneratePasswordResetTokenPostOkResult, GeneralErrorResponse]:
-        pass
-
-    @abstractmethod
-    async def password_reset_post(
-        self,
-        form_fields: List[FormField],
-        token: str,
-        tenant_id: str,
-        api_options: APIOptions,
-        user_context: Dict[str, Any],
-    ) -> Union[
-        PasswordResetPostOkResult,
-        PasswordResetPostInvalidTokenResponse,
-        GeneralErrorResponse,
-    ]:
-        pass
-
-    @abstractmethod
-    async def sign_in_post(
-        self,
-        form_fields: List[FormField],
-        tenant_id: str,
-        api_options: APIOptions,
-        user_context: Dict[str, Any],
-    ) -> Union[
-        SignInPostOkResult, SignInPostWrongCredentialsError, GeneralErrorResponse
-    ]:
-        pass
-
-    @abstractmethod
-    async def sign_up_post(
-        self,
-        form_fields: List[FormField],
-        tenant_id: str,
-        api_options: APIOptions,
-        user_context: Dict[str, Any],
-    ) -> Union[
-        SignUpPostOkResult, SignUpPostEmailAlreadyExistsError, GeneralErrorResponse
-    ]:
-        pass
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class APIInterface -
-
-
-
- -Expand source code - -
class APIInterface:
-    def __init__(self):
-        self.disable_email_exists_get = False
-        self.disable_generate_password_reset_token_post = False
-        self.disable_password_reset_post = False
-        self.disable_sign_in_post = False
-        self.disable_sign_up_post = False
-
-    @abstractmethod
-    async def email_exists_get(
-        self,
-        email: str,
-        tenant_id: str,
-        api_options: APIOptions,
-        user_context: Dict[str, Any],
-    ) -> Union[EmailExistsGetOkResult, GeneralErrorResponse]:
-        pass
-
-    @abstractmethod
-    async def generate_password_reset_token_post(
-        self,
-        form_fields: List[FormField],
-        tenant_id: str,
-        api_options: APIOptions,
-        user_context: Dict[str, Any],
-    ) -> Union[GeneratePasswordResetTokenPostOkResult, GeneralErrorResponse]:
-        pass
-
-    @abstractmethod
-    async def password_reset_post(
-        self,
-        form_fields: List[FormField],
-        token: str,
-        tenant_id: str,
-        api_options: APIOptions,
-        user_context: Dict[str, Any],
-    ) -> Union[
-        PasswordResetPostOkResult,
-        PasswordResetPostInvalidTokenResponse,
-        GeneralErrorResponse,
-    ]:
-        pass
-
-    @abstractmethod
-    async def sign_in_post(
-        self,
-        form_fields: List[FormField],
-        tenant_id: str,
-        api_options: APIOptions,
-        user_context: Dict[str, Any],
-    ) -> Union[
-        SignInPostOkResult, SignInPostWrongCredentialsError, GeneralErrorResponse
-    ]:
-        pass
-
-    @abstractmethod
-    async def sign_up_post(
-        self,
-        form_fields: List[FormField],
-        tenant_id: str,
-        api_options: APIOptions,
-        user_context: Dict[str, Any],
-    ) -> Union[
-        SignUpPostOkResult, SignUpPostEmailAlreadyExistsError, GeneralErrorResponse
-    ]:
-        pass
-
-

Subclasses

- -

Methods

-
-
-async def email_exists_get(self, email: str, tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[EmailExistsGetOkResultGeneralErrorResponse] -
-
-
-
- -Expand source code - -
@abstractmethod
-async def email_exists_get(
-    self,
-    email: str,
-    tenant_id: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-) -> Union[EmailExistsGetOkResult, GeneralErrorResponse]:
-    pass
-
-
-
-async def generate_password_reset_token_post(self, form_fields: List[FormField], tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[GeneratePasswordResetTokenPostOkResult, GeneralErrorResponse] -
-
-
-
- -Expand source code - -
@abstractmethod
-async def generate_password_reset_token_post(
-    self,
-    form_fields: List[FormField],
-    tenant_id: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-) -> Union[GeneratePasswordResetTokenPostOkResult, GeneralErrorResponse]:
-    pass
-
-
-
-async def password_reset_post(self, form_fields: List[FormField], token: str, tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[PasswordResetPostOkResultPasswordResetPostInvalidTokenResponse, GeneralErrorResponse] -
-
-
-
- -Expand source code - -
@abstractmethod
-async def password_reset_post(
-    self,
-    form_fields: List[FormField],
-    token: str,
-    tenant_id: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-) -> Union[
-    PasswordResetPostOkResult,
-    PasswordResetPostInvalidTokenResponse,
-    GeneralErrorResponse,
-]:
-    pass
-
-
-
-async def sign_in_post(self, form_fields: List[FormField], tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[SignInPostOkResultSignInPostWrongCredentialsError, GeneralErrorResponse] -
-
-
-
- -Expand source code - -
@abstractmethod
-async def sign_in_post(
-    self,
-    form_fields: List[FormField],
-    tenant_id: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-) -> Union[
-    SignInPostOkResult, SignInPostWrongCredentialsError, GeneralErrorResponse
-]:
-    pass
-
-
-
-async def sign_up_post(self, form_fields: List[FormField], tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[SignUpPostOkResultSignUpPostEmailAlreadyExistsError, GeneralErrorResponse] -
-
-
-
- -Expand source code - -
@abstractmethod
-async def sign_up_post(
-    self,
-    form_fields: List[FormField],
-    tenant_id: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-) -> Union[
-    SignUpPostOkResult, SignUpPostEmailAlreadyExistsError, GeneralErrorResponse
-]:
-    pass
-
-
-
-
-
-class APIOptions -(request: BaseRequest, response: BaseResponse, recipe_id: str, config: EmailPasswordConfig, recipe_implementation: RecipeInterface, app_info: AppInfo, email_delivery: EmailDeliveryIngredient[EmailTemplateVars]) -
-
-
-
- -Expand source code - -
class APIOptions:
-    def __init__(
-        self,
-        request: BaseRequest,
-        response: BaseResponse,
-        recipe_id: str,
-        config: EmailPasswordConfig,
-        recipe_implementation: RecipeInterface,
-        app_info: AppInfo,
-        email_delivery: EmailDeliveryIngredient[EmailTemplateVars],
-    ):
-        self.request: BaseRequest = request
-        self.response: BaseResponse = response
-        self.recipe_id: str = recipe_id
-        self.config: EmailPasswordConfig = config
-        self.recipe_implementation: RecipeInterface = recipe_implementation
-        self.app_info = app_info
-        self.email_delivery = email_delivery
-
-
-
-class CreateResetPasswordLinkOkResult -(link: str) -
-
-
-
- -Expand source code - -
class CreateResetPasswordLinkOkResult:
-    def __init__(self, link: str):
-        self.link = link
-
-
-
-class CreateResetPasswordLinkUnknownUserIdError -
-
-
-
- -Expand source code - -
class CreateResetPasswordLinkUnknownUserIdError:
-    pass
-
-
-
-class CreateResetPasswordOkResult -(token: str) -
-
-
-
- -Expand source code - -
class CreateResetPasswordOkResult:
-    def __init__(self, token: str):
-        self.token = token
-
-
-
-class CreateResetPasswordWrongUserIdError -
-
-
-
- -Expand source code - -
class CreateResetPasswordWrongUserIdError:
-    pass
-
-
-
-class EmailExistsGetOkResult -(exists: bool) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class EmailExistsGetOkResult(APIResponse):
-    status: str = "OK"
-
-    def __init__(self, exists: bool):
-        self.exists = exists
-
-    def to_json(self) -> Dict[str, Any]:
-        return {"status": self.status, "exists": self.exists}
-
-

Ancestors

- -

Class variables

-
-
var status : str
-
-
-
-
-

Methods

-
-
-def to_json(self) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
def to_json(self) -> Dict[str, Any]:
-    return {"status": self.status, "exists": self.exists}
-
-
-
-
-
-class GeneratePasswordResetTokenPostOkResult -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class GeneratePasswordResetTokenPostOkResult(APIResponse):
-    status: str = "OK"
-
-    def to_json(self) -> Dict[str, Any]:
-        return {"status": self.status}
-
-

Ancestors

- -

Class variables

-
-
var status : str
-
-
-
-
-

Methods

-
-
-def to_json(self) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
def to_json(self) -> Dict[str, Any]:
-    return {"status": self.status}
-
-
-
-
-
-class PasswordResetPostInvalidTokenResponse -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class PasswordResetPostInvalidTokenResponse(APIResponse):
-    status: str = "RESET_PASSWORD_INVALID_TOKEN_ERROR"
-
-    def to_json(self) -> Dict[str, Any]:
-        return {"status": self.status}
-
-

Ancestors

- -

Class variables

-
-
var status : str
-
-
-
-
-

Methods

-
-
-def to_json(self) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
def to_json(self) -> Dict[str, Any]:
-    return {"status": self.status}
-
-
-
-
-
-class PasswordResetPostOkResult -(user_id: Union[str, None]) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class PasswordResetPostOkResult(APIResponse):
-    status: str = "OK"
-
-    def __init__(self, user_id: Union[str, None]):
-        self.user_id = user_id
-
-    def to_json(self) -> Dict[str, Any]:
-        return {"status": self.status}
-
-

Ancestors

- -

Class variables

-
-
var status : str
-
-
-
-
-

Methods

-
-
-def to_json(self) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
def to_json(self) -> Dict[str, Any]:
-    return {"status": self.status}
-
-
-
-
-
-class RecipeInterface -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class RecipeInterface(ABC):
-    def __init__(self):
-        pass
-
-    @abstractmethod
-    async def get_user_by_id(
-        self, user_id: str, user_context: Dict[str, Any]
-    ) -> Union[User, None]:
-        pass
-
-    @abstractmethod
-    async def get_user_by_email(
-        self, email: str, tenant_id: str, user_context: Dict[str, Any]
-    ) -> Union[User, None]:
-        pass
-
-    @abstractmethod
-    async def create_reset_password_token(
-        self, user_id: str, tenant_id: str, user_context: Dict[str, Any]
-    ) -> Union[CreateResetPasswordOkResult, CreateResetPasswordWrongUserIdError]:
-        pass
-
-    @abstractmethod
-    async def reset_password_using_token(
-        self,
-        token: str,
-        new_password: str,
-        tenant_id: str,
-        user_context: Dict[str, Any],
-    ) -> Union[
-        ResetPasswordUsingTokenOkResult, ResetPasswordUsingTokenInvalidTokenError
-    ]:
-        pass
-
-    @abstractmethod
-    async def sign_in(
-        self, email: str, password: str, tenant_id: str, user_context: Dict[str, Any]
-    ) -> Union[SignInOkResult, SignInWrongCredentialsError]:
-        pass
-
-    @abstractmethod
-    async def sign_up(
-        self, email: str, password: str, tenant_id: str, user_context: Dict[str, Any]
-    ) -> Union[SignUpOkResult, SignUpEmailAlreadyExistsError]:
-        pass
-
-    @abstractmethod
-    async def update_email_or_password(
-        self,
-        user_id: str,
-        email: Union[str, None],
-        password: Union[str, None],
-        apply_password_policy: Union[bool, None],
-        tenant_id_for_password_policy: str,
-        user_context: Dict[str, Any],
-    ) -> Union[
-        UpdateEmailOrPasswordOkResult,
-        UpdateEmailOrPasswordEmailAlreadyExistsError,
-        UpdateEmailOrPasswordUnknownUserIdError,
-        UpdateEmailOrPasswordPasswordPolicyViolationError,
-    ]:
-        pass
-
-

Ancestors

-
    -
  • abc.ABC
  • -
-

Subclasses

- -

Methods

-
-
-async def create_reset_password_token(self, user_id: str, tenant_id: str, user_context: Dict[str, Any]) ‑> Union[CreateResetPasswordOkResultCreateResetPasswordWrongUserIdError] -
-
-
-
- -Expand source code - -
@abstractmethod
-async def create_reset_password_token(
-    self, user_id: str, tenant_id: str, user_context: Dict[str, Any]
-) -> Union[CreateResetPasswordOkResult, CreateResetPasswordWrongUserIdError]:
-    pass
-
-
-
-async def get_user_by_email(self, email: str, tenant_id: str, user_context: Dict[str, Any]) ‑> Union[User, None] -
-
-
-
- -Expand source code - -
@abstractmethod
-async def get_user_by_email(
-    self, email: str, tenant_id: str, user_context: Dict[str, Any]
-) -> Union[User, None]:
-    pass
-
-
-
-async def get_user_by_id(self, user_id: str, user_context: Dict[str, Any]) ‑> Union[User, None] -
-
-
-
- -Expand source code - -
@abstractmethod
-async def get_user_by_id(
-    self, user_id: str, user_context: Dict[str, Any]
-) -> Union[User, None]:
-    pass
-
-
-
-async def reset_password_using_token(self, token: str, new_password: str, tenant_id: str, user_context: Dict[str, Any]) ‑> Union[ResetPasswordUsingTokenOkResultResetPasswordUsingTokenInvalidTokenError] -
-
-
-
- -Expand source code - -
@abstractmethod
-async def reset_password_using_token(
-    self,
-    token: str,
-    new_password: str,
-    tenant_id: str,
-    user_context: Dict[str, Any],
-) -> Union[
-    ResetPasswordUsingTokenOkResult, ResetPasswordUsingTokenInvalidTokenError
-]:
-    pass
-
-
-
-async def sign_in(self, email: str, password: str, tenant_id: str, user_context: Dict[str, Any]) ‑> Union[SignInOkResultSignInWrongCredentialsError] -
-
-
-
- -Expand source code - -
@abstractmethod
-async def sign_in(
-    self, email: str, password: str, tenant_id: str, user_context: Dict[str, Any]
-) -> Union[SignInOkResult, SignInWrongCredentialsError]:
-    pass
-
-
-
-async def sign_up(self, email: str, password: str, tenant_id: str, user_context: Dict[str, Any]) ‑> Union[SignUpOkResultSignUpEmailAlreadyExistsError] -
-
-
-
- -Expand source code - -
@abstractmethod
-async def sign_up(
-    self, email: str, password: str, tenant_id: str, user_context: Dict[str, Any]
-) -> Union[SignUpOkResult, SignUpEmailAlreadyExistsError]:
-    pass
-
-
-
-async def update_email_or_password(self, user_id: str, email: Union[str, None], password: Union[str, None], apply_password_policy: Union[bool, None], tenant_id_for_password_policy: str, user_context: Dict[str, Any]) ‑> Union[UpdateEmailOrPasswordOkResultUpdateEmailOrPasswordEmailAlreadyExistsErrorUpdateEmailOrPasswordUnknownUserIdErrorUpdateEmailOrPasswordPasswordPolicyViolationError] -
-
-
-
- -Expand source code - -
@abstractmethod
-async def update_email_or_password(
-    self,
-    user_id: str,
-    email: Union[str, None],
-    password: Union[str, None],
-    apply_password_policy: Union[bool, None],
-    tenant_id_for_password_policy: str,
-    user_context: Dict[str, Any],
-) -> Union[
-    UpdateEmailOrPasswordOkResult,
-    UpdateEmailOrPasswordEmailAlreadyExistsError,
-    UpdateEmailOrPasswordUnknownUserIdError,
-    UpdateEmailOrPasswordPasswordPolicyViolationError,
-]:
-    pass
-
-
-
-
-
-class ResetPasswordUsingTokenInvalidTokenError -
-
-
-
- -Expand source code - -
class ResetPasswordUsingTokenInvalidTokenError:
-    pass
-
-
-
-class ResetPasswordUsingTokenOkResult -(user_id: Union[str, None]) -
-
-
-
- -Expand source code - -
class ResetPasswordUsingTokenOkResult:
-    def __init__(self, user_id: Union[str, None]):
-        self.user_id = user_id
-
-
-
-class SendResetPasswordEmailOkResult -
-
-
-
- -Expand source code - -
class SendResetPasswordEmailOkResult:
-    pass
-
-
-
-class SendResetPasswordEmailUnknownUserIdError -
-
-
-
- -Expand source code - -
class SendResetPasswordEmailUnknownUserIdError:
-    pass
-
-
-
-class SignInOkResult -(user: User) -
-
-
-
- -Expand source code - -
class SignInOkResult:
-    def __init__(self, user: User):
-        self.user = user
-
-
-
-class SignInPostOkResult -(user: User, session: SessionContainer) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class SignInPostOkResult(APIResponse):
-    status: str = "OK"
-
-    def __init__(self, user: User, session: SessionContainer):
-        self.user = user
-        self.session = session
-
-    def to_json(self) -> Dict[str, Any]:
-        return {
-            "status": self.status,
-            "user": {
-                "id": self.user.user_id,
-                "email": self.user.email,
-                "timeJoined": self.user.time_joined,
-            },
-        }
-
-

Ancestors

- -

Class variables

-
-
var status : str
-
-
-
-
-

Methods

-
-
-def to_json(self) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
def to_json(self) -> Dict[str, Any]:
-    return {
-        "status": self.status,
-        "user": {
-            "id": self.user.user_id,
-            "email": self.user.email,
-            "timeJoined": self.user.time_joined,
-        },
-    }
-
-
-
-
-
-class SignInPostWrongCredentialsError -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class SignInPostWrongCredentialsError(APIResponse):
-    status: str = "WRONG_CREDENTIALS_ERROR"
-
-    def to_json(self) -> Dict[str, Any]:
-        return {"status": self.status}
-
-

Ancestors

- -

Class variables

-
-
var status : str
-
-
-
-
-

Methods

-
-
-def to_json(self) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
def to_json(self) -> Dict[str, Any]:
-    return {"status": self.status}
-
-
-
-
-
-class SignInWrongCredentialsError -
-
-
-
- -Expand source code - -
class SignInWrongCredentialsError:
-    pass
-
-
-
-class SignUpEmailAlreadyExistsError -
-
-
-
- -Expand source code - -
class SignUpEmailAlreadyExistsError:
-    pass
-
-
-
-class SignUpOkResult -(user: User) -
-
-
-
- -Expand source code - -
class SignUpOkResult:
-    def __init__(self, user: User):
-        self.user = user
-
-
-
-class SignUpPostEmailAlreadyExistsError -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class SignUpPostEmailAlreadyExistsError(APIResponse):
-    status: str = "EMAIL_ALREADY_EXISTS_ERROR"
-
-    def to_json(self) -> Dict[str, Any]:
-        return {"status": self.status}
-
-

Ancestors

- -

Class variables

-
-
var status : str
-
-
-
-
-

Methods

-
-
-def to_json(self) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
def to_json(self) -> Dict[str, Any]:
-    return {"status": self.status}
-
-
-
-
-
-class SignUpPostOkResult -(user: User, session: SessionContainer) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class SignUpPostOkResult(APIResponse):
-    status: str = "OK"
-
-    def __init__(self, user: User, session: SessionContainer):
-        self.user = user
-        self.session = session
-
-    def to_json(self) -> Dict[str, Any]:
-        return {
-            "status": self.status,
-            "user": {
-                "id": self.user.user_id,
-                "email": self.user.email,
-                "timeJoined": self.user.time_joined,
-            },
-        }
-
-

Ancestors

- -

Class variables

-
-
var status : str
-
-
-
-
-

Methods

-
-
-def to_json(self) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
def to_json(self) -> Dict[str, Any]:
-    return {
-        "status": self.status,
-        "user": {
-            "id": self.user.user_id,
-            "email": self.user.email,
-            "timeJoined": self.user.time_joined,
-        },
-    }
-
-
-
-
-
-class UpdateEmailOrPasswordEmailAlreadyExistsError -
-
-
-
- -Expand source code - -
class UpdateEmailOrPasswordEmailAlreadyExistsError:
-    pass
-
-
-
-class UpdateEmailOrPasswordOkResult -
-
-
-
- -Expand source code - -
class UpdateEmailOrPasswordOkResult:
-    pass
-
-
-
-class UpdateEmailOrPasswordPasswordPolicyViolationError -(failure_reason: str) -
-
-
-
- -Expand source code - -
class UpdateEmailOrPasswordPasswordPolicyViolationError:
-    failure_reason: str
-
-    def __init__(self, failure_reason: str):
-        self.failure_reason = failure_reason
-
-

Class variables

-
-
var failure_reason : str
-
-
-
-
-
-
-class UpdateEmailOrPasswordUnknownUserIdError -
-
-
-
- -Expand source code - -
class UpdateEmailOrPasswordUnknownUserIdError:
-    pass
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailpassword/recipe.html b/html/supertokens_python/recipe/emailpassword/recipe.html deleted file mode 100644 index ad1b2ef1d..000000000 --- a/html/supertokens_python/recipe/emailpassword/recipe.html +++ /dev/null @@ -1,861 +0,0 @@ - - - - - - -supertokens_python.recipe.emailpassword.recipe API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.emailpassword.recipe

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from os import environ
-from typing import TYPE_CHECKING, Any, Dict, List, Union
-
-from supertokens_python.ingredients.emaildelivery import EmailDeliveryIngredient
-from supertokens_python.ingredients.emaildelivery.types import EmailDeliveryConfig
-from supertokens_python.normalised_url_path import NormalisedURLPath
-from supertokens_python.recipe.emailpassword.types import (
-    EmailPasswordIngredients,
-    EmailTemplateVars,
-)
-from supertokens_python.recipe_module import APIHandled, RecipeModule
-from ..emailverification.interfaces import (
-    UnknownUserIdError,
-    GetEmailForUserIdOkResult,
-    EmailDoesNotExistError,
-)
-
-from .api.implementation import APIImplementation
-from .exceptions import FieldError, SuperTokensEmailPasswordError
-from .interfaces import APIOptions
-from .recipe_implementation import RecipeImplementation
-from ...post_init_callbacks import PostSTInitCallbacks
-
-if TYPE_CHECKING:
-    from supertokens_python.framework.request import BaseRequest
-    from supertokens_python.framework.response import BaseResponse
-    from supertokens_python.supertokens import AppInfo
-
-from supertokens_python.exceptions import SuperTokensError, raise_general_exception
-from supertokens_python.querier import Querier
-from supertokens_python.recipe.emailverification import EmailVerificationRecipe
-
-from .api import (
-    handle_email_exists_api,
-    handle_generate_password_reset_token_api,
-    handle_password_reset_api,
-    handle_sign_in_api,
-    handle_sign_up_api,
-)
-from .constants import (
-    SIGNIN,
-    SIGNUP,
-    SIGNUP_EMAIL_EXISTS,
-    USER_PASSWORD_RESET,
-    USER_PASSWORD_RESET_TOKEN,
-    SIGNUP_EMAIL_EXISTS_OLD,
-)
-from .utils import (
-    InputOverrideConfig,
-    InputSignUpFeature,
-    validate_and_normalise_user_input,
-    EmailPasswordConfig,
-)
-
-
-class EmailPasswordRecipe(RecipeModule):
-    recipe_id = "emailpassword"
-    __instance = None
-    email_delivery: EmailDeliveryIngredient[EmailTemplateVars]
-
-    def __init__(
-        self,
-        recipe_id: str,
-        app_info: AppInfo,
-        ingredients: EmailPasswordIngredients,
-        sign_up_feature: Union[InputSignUpFeature, None] = None,
-        override: Union[InputOverrideConfig, None] = None,
-        email_delivery: Union[EmailDeliveryConfig[EmailTemplateVars], None] = None,
-    ):
-        super().__init__(recipe_id, app_info)
-        self.config = validate_and_normalise_user_input(
-            app_info,
-            sign_up_feature,
-            override,
-            email_delivery,
-        )
-
-        def get_emailpassword_config() -> EmailPasswordConfig:
-            return self.config
-
-        recipe_implementation = RecipeImplementation(
-            Querier.get_instance(recipe_id), get_emailpassword_config
-        )
-        self.recipe_implementation = (
-            recipe_implementation
-            if self.config.override.functions is None
-            else self.config.override.functions(recipe_implementation)
-        )
-
-        email_delivery_ingredient = ingredients.email_delivery
-        if email_delivery_ingredient is None:
-            self.email_delivery = EmailDeliveryIngredient(
-                self.config.get_email_delivery_config(self.recipe_implementation)
-            )
-        else:
-            self.email_delivery = email_delivery_ingredient
-
-        api_implementation = APIImplementation()
-        self.api_implementation = (
-            api_implementation
-            if self.config.override.apis is None
-            else self.config.override.apis(api_implementation)
-        )
-
-        def callback():
-            ev_recipe = EmailVerificationRecipe.get_instance_optional()
-            if ev_recipe:
-                ev_recipe.add_get_email_for_user_id_func(self.get_email_for_user_id)
-
-        PostSTInitCallbacks.add_post_init_callback(callback)
-
-    def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
-        return isinstance(err, SuperTokensError) and (
-            isinstance(err, SuperTokensEmailPasswordError)
-        )
-
-    def get_apis_handled(self) -> List[APIHandled]:
-        return [
-            APIHandled(
-                NormalisedURLPath(SIGNUP),
-                "post",
-                SIGNUP,
-                self.api_implementation.disable_sign_up_post,
-            ),
-            APIHandled(
-                NormalisedURLPath(SIGNIN),
-                "post",
-                SIGNIN,
-                self.api_implementation.disable_sign_in_post,
-            ),
-            APIHandled(
-                NormalisedURLPath(USER_PASSWORD_RESET_TOKEN),
-                "post",
-                USER_PASSWORD_RESET_TOKEN,
-                self.api_implementation.disable_generate_password_reset_token_post,
-            ),
-            APIHandled(
-                NormalisedURLPath(USER_PASSWORD_RESET),
-                "post",
-                USER_PASSWORD_RESET,
-                self.api_implementation.disable_password_reset_post,
-            ),
-            APIHandled(
-                NormalisedURLPath(SIGNUP_EMAIL_EXISTS_OLD),
-                "get",
-                SIGNUP_EMAIL_EXISTS_OLD,
-                self.api_implementation.disable_email_exists_get,
-            ),
-            APIHandled(
-                NormalisedURLPath(SIGNUP_EMAIL_EXISTS),
-                "get",
-                SIGNUP_EMAIL_EXISTS,
-                self.api_implementation.disable_email_exists_get,
-            ),
-        ]
-
-    async def handle_api_request(
-        self,
-        request_id: str,
-        tenant_id: str,
-        request: BaseRequest,
-        path: NormalisedURLPath,
-        method: str,
-        response: BaseResponse,
-        user_context: Dict[str, Any],
-    ):
-        api_options = APIOptions(
-            request,
-            response,
-            self.recipe_id,
-            self.config,
-            self.recipe_implementation,
-            self.get_app_info(),
-            self.email_delivery,
-        )
-        if request_id == SIGNUP:
-            return await handle_sign_up_api(
-                tenant_id, self.api_implementation, api_options, user_context
-            )
-        if request_id == SIGNIN:
-            return await handle_sign_in_api(
-                tenant_id, self.api_implementation, api_options, user_context
-            )
-        if request_id in (SIGNUP_EMAIL_EXISTS, SIGNUP_EMAIL_EXISTS_OLD):
-            return await handle_email_exists_api(
-                tenant_id, self.api_implementation, api_options, user_context
-            )
-        if request_id == USER_PASSWORD_RESET_TOKEN:
-            return await handle_generate_password_reset_token_api(
-                tenant_id, self.api_implementation, api_options, user_context
-            )
-        if request_id == USER_PASSWORD_RESET:
-            return await handle_password_reset_api(
-                tenant_id, self.api_implementation, api_options, user_context
-            )
-
-        return None
-
-    async def handle_error(
-        self,
-        request: BaseRequest,
-        err: SuperTokensError,
-        response: BaseResponse,
-        user_context: Dict[str, Any],
-    ) -> BaseResponse:
-        if isinstance(err, SuperTokensEmailPasswordError):
-            if isinstance(err, FieldError):
-                response.set_json_content(
-                    {"status": "FIELD_ERROR", "formFields": err.get_json_form_fields()}
-                )
-                return response
-        raise err
-
-    def get_all_cors_headers(self) -> List[str]:
-        return []
-
-    @staticmethod
-    def init(
-        sign_up_feature: Union[InputSignUpFeature, None] = None,
-        override: Union[InputOverrideConfig, None] = None,
-        email_delivery: Union[EmailDeliveryConfig[EmailTemplateVars], None] = None,
-    ):
-        def func(app_info: AppInfo):
-            if EmailPasswordRecipe.__instance is None:
-                ingredients = EmailPasswordIngredients(None)
-                EmailPasswordRecipe.__instance = EmailPasswordRecipe(
-                    EmailPasswordRecipe.recipe_id,
-                    app_info,
-                    ingredients,
-                    sign_up_feature,
-                    override,
-                    email_delivery=email_delivery,
-                )
-                return EmailPasswordRecipe.__instance
-            raise Exception(
-                None,
-                "Emailpassword recipe has already been initialised. Please check your code for bugs.",
-            )
-
-        return func
-
-    @staticmethod
-    def get_instance() -> EmailPasswordRecipe:
-        if EmailPasswordRecipe.__instance is not None:
-            return EmailPasswordRecipe.__instance
-        raise_general_exception(
-            "Initialisation not done. Did you forget to call the SuperTokens.init function?"
-        )
-
-    @staticmethod
-    def reset():
-        if ("SUPERTOKENS_ENV" not in environ) or (
-            environ["SUPERTOKENS_ENV"] != "testing"
-        ):
-            raise_general_exception("calling testing function in non testing env")
-        EmailPasswordRecipe.__instance = None
-
-    # instance functions below...............
-
-    async def get_email_for_user_id(
-        self, user_id: str, user_context: Dict[str, Any]
-    ) -> Union[UnknownUserIdError, GetEmailForUserIdOkResult, EmailDoesNotExistError]:
-        user_info = await self.recipe_implementation.get_user_by_id(
-            user_id, user_context
-        )
-        if user_info is not None:
-            return GetEmailForUserIdOkResult(user_info.email)
-
-        return UnknownUserIdError()
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class EmailPasswordRecipe -(recipe_id: str, app_info: AppInfo, ingredients: EmailPasswordIngredients, sign_up_feature: Union[InputSignUpFeature, None] = None, override: Union[InputOverrideConfig, None] = None, email_delivery: Union[EmailDeliveryConfig[EmailTemplateVars], None] = None) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class EmailPasswordRecipe(RecipeModule):
-    recipe_id = "emailpassword"
-    __instance = None
-    email_delivery: EmailDeliveryIngredient[EmailTemplateVars]
-
-    def __init__(
-        self,
-        recipe_id: str,
-        app_info: AppInfo,
-        ingredients: EmailPasswordIngredients,
-        sign_up_feature: Union[InputSignUpFeature, None] = None,
-        override: Union[InputOverrideConfig, None] = None,
-        email_delivery: Union[EmailDeliveryConfig[EmailTemplateVars], None] = None,
-    ):
-        super().__init__(recipe_id, app_info)
-        self.config = validate_and_normalise_user_input(
-            app_info,
-            sign_up_feature,
-            override,
-            email_delivery,
-        )
-
-        def get_emailpassword_config() -> EmailPasswordConfig:
-            return self.config
-
-        recipe_implementation = RecipeImplementation(
-            Querier.get_instance(recipe_id), get_emailpassword_config
-        )
-        self.recipe_implementation = (
-            recipe_implementation
-            if self.config.override.functions is None
-            else self.config.override.functions(recipe_implementation)
-        )
-
-        email_delivery_ingredient = ingredients.email_delivery
-        if email_delivery_ingredient is None:
-            self.email_delivery = EmailDeliveryIngredient(
-                self.config.get_email_delivery_config(self.recipe_implementation)
-            )
-        else:
-            self.email_delivery = email_delivery_ingredient
-
-        api_implementation = APIImplementation()
-        self.api_implementation = (
-            api_implementation
-            if self.config.override.apis is None
-            else self.config.override.apis(api_implementation)
-        )
-
-        def callback():
-            ev_recipe = EmailVerificationRecipe.get_instance_optional()
-            if ev_recipe:
-                ev_recipe.add_get_email_for_user_id_func(self.get_email_for_user_id)
-
-        PostSTInitCallbacks.add_post_init_callback(callback)
-
-    def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
-        return isinstance(err, SuperTokensError) and (
-            isinstance(err, SuperTokensEmailPasswordError)
-        )
-
-    def get_apis_handled(self) -> List[APIHandled]:
-        return [
-            APIHandled(
-                NormalisedURLPath(SIGNUP),
-                "post",
-                SIGNUP,
-                self.api_implementation.disable_sign_up_post,
-            ),
-            APIHandled(
-                NormalisedURLPath(SIGNIN),
-                "post",
-                SIGNIN,
-                self.api_implementation.disable_sign_in_post,
-            ),
-            APIHandled(
-                NormalisedURLPath(USER_PASSWORD_RESET_TOKEN),
-                "post",
-                USER_PASSWORD_RESET_TOKEN,
-                self.api_implementation.disable_generate_password_reset_token_post,
-            ),
-            APIHandled(
-                NormalisedURLPath(USER_PASSWORD_RESET),
-                "post",
-                USER_PASSWORD_RESET,
-                self.api_implementation.disable_password_reset_post,
-            ),
-            APIHandled(
-                NormalisedURLPath(SIGNUP_EMAIL_EXISTS_OLD),
-                "get",
-                SIGNUP_EMAIL_EXISTS_OLD,
-                self.api_implementation.disable_email_exists_get,
-            ),
-            APIHandled(
-                NormalisedURLPath(SIGNUP_EMAIL_EXISTS),
-                "get",
-                SIGNUP_EMAIL_EXISTS,
-                self.api_implementation.disable_email_exists_get,
-            ),
-        ]
-
-    async def handle_api_request(
-        self,
-        request_id: str,
-        tenant_id: str,
-        request: BaseRequest,
-        path: NormalisedURLPath,
-        method: str,
-        response: BaseResponse,
-        user_context: Dict[str, Any],
-    ):
-        api_options = APIOptions(
-            request,
-            response,
-            self.recipe_id,
-            self.config,
-            self.recipe_implementation,
-            self.get_app_info(),
-            self.email_delivery,
-        )
-        if request_id == SIGNUP:
-            return await handle_sign_up_api(
-                tenant_id, self.api_implementation, api_options, user_context
-            )
-        if request_id == SIGNIN:
-            return await handle_sign_in_api(
-                tenant_id, self.api_implementation, api_options, user_context
-            )
-        if request_id in (SIGNUP_EMAIL_EXISTS, SIGNUP_EMAIL_EXISTS_OLD):
-            return await handle_email_exists_api(
-                tenant_id, self.api_implementation, api_options, user_context
-            )
-        if request_id == USER_PASSWORD_RESET_TOKEN:
-            return await handle_generate_password_reset_token_api(
-                tenant_id, self.api_implementation, api_options, user_context
-            )
-        if request_id == USER_PASSWORD_RESET:
-            return await handle_password_reset_api(
-                tenant_id, self.api_implementation, api_options, user_context
-            )
-
-        return None
-
-    async def handle_error(
-        self,
-        request: BaseRequest,
-        err: SuperTokensError,
-        response: BaseResponse,
-        user_context: Dict[str, Any],
-    ) -> BaseResponse:
-        if isinstance(err, SuperTokensEmailPasswordError):
-            if isinstance(err, FieldError):
-                response.set_json_content(
-                    {"status": "FIELD_ERROR", "formFields": err.get_json_form_fields()}
-                )
-                return response
-        raise err
-
-    def get_all_cors_headers(self) -> List[str]:
-        return []
-
-    @staticmethod
-    def init(
-        sign_up_feature: Union[InputSignUpFeature, None] = None,
-        override: Union[InputOverrideConfig, None] = None,
-        email_delivery: Union[EmailDeliveryConfig[EmailTemplateVars], None] = None,
-    ):
-        def func(app_info: AppInfo):
-            if EmailPasswordRecipe.__instance is None:
-                ingredients = EmailPasswordIngredients(None)
-                EmailPasswordRecipe.__instance = EmailPasswordRecipe(
-                    EmailPasswordRecipe.recipe_id,
-                    app_info,
-                    ingredients,
-                    sign_up_feature,
-                    override,
-                    email_delivery=email_delivery,
-                )
-                return EmailPasswordRecipe.__instance
-            raise Exception(
-                None,
-                "Emailpassword recipe has already been initialised. Please check your code for bugs.",
-            )
-
-        return func
-
-    @staticmethod
-    def get_instance() -> EmailPasswordRecipe:
-        if EmailPasswordRecipe.__instance is not None:
-            return EmailPasswordRecipe.__instance
-        raise_general_exception(
-            "Initialisation not done. Did you forget to call the SuperTokens.init function?"
-        )
-
-    @staticmethod
-    def reset():
-        if ("SUPERTOKENS_ENV" not in environ) or (
-            environ["SUPERTOKENS_ENV"] != "testing"
-        ):
-            raise_general_exception("calling testing function in non testing env")
-        EmailPasswordRecipe.__instance = None
-
-    # instance functions below...............
-
-    async def get_email_for_user_id(
-        self, user_id: str, user_context: Dict[str, Any]
-    ) -> Union[UnknownUserIdError, GetEmailForUserIdOkResult, EmailDoesNotExistError]:
-        user_info = await self.recipe_implementation.get_user_by_id(
-            user_id, user_context
-        )
-        if user_info is not None:
-            return GetEmailForUserIdOkResult(user_info.email)
-
-        return UnknownUserIdError()
-
-

Ancestors

- -

Class variables

-
-
var email_deliveryEmailDeliveryIngredient[PasswordResetEmailTemplateVars]
-
-
-
-
var recipe_id
-
-
-
-
-

Static methods

-
-
-def get_instance() ‑> EmailPasswordRecipe -
-
-
-
- -Expand source code - -
@staticmethod
-def get_instance() -> EmailPasswordRecipe:
-    if EmailPasswordRecipe.__instance is not None:
-        return EmailPasswordRecipe.__instance
-    raise_general_exception(
-        "Initialisation not done. Did you forget to call the SuperTokens.init function?"
-    )
-
-
-
-def init(sign_up_feature: Union[InputSignUpFeature, None] = None, override: Union[InputOverrideConfig, None] = None, email_delivery: Union[EmailDeliveryConfig[EmailTemplateVars], None] = None) -
-
-
-
- -Expand source code - -
@staticmethod
-def init(
-    sign_up_feature: Union[InputSignUpFeature, None] = None,
-    override: Union[InputOverrideConfig, None] = None,
-    email_delivery: Union[EmailDeliveryConfig[EmailTemplateVars], None] = None,
-):
-    def func(app_info: AppInfo):
-        if EmailPasswordRecipe.__instance is None:
-            ingredients = EmailPasswordIngredients(None)
-            EmailPasswordRecipe.__instance = EmailPasswordRecipe(
-                EmailPasswordRecipe.recipe_id,
-                app_info,
-                ingredients,
-                sign_up_feature,
-                override,
-                email_delivery=email_delivery,
-            )
-            return EmailPasswordRecipe.__instance
-        raise Exception(
-            None,
-            "Emailpassword recipe has already been initialised. Please check your code for bugs.",
-        )
-
-    return func
-
-
-
-def reset() -
-
-
-
- -Expand source code - -
@staticmethod
-def reset():
-    if ("SUPERTOKENS_ENV" not in environ) or (
-        environ["SUPERTOKENS_ENV"] != "testing"
-    ):
-        raise_general_exception("calling testing function in non testing env")
-    EmailPasswordRecipe.__instance = None
-
-
-
-

Methods

-
-
-def get_all_cors_headers(self) ‑> List[str] -
-
-
-
- -Expand source code - -
def get_all_cors_headers(self) -> List[str]:
-    return []
-
-
-
-def get_apis_handled(self) ‑> List[APIHandled] -
-
-
-
- -Expand source code - -
def get_apis_handled(self) -> List[APIHandled]:
-    return [
-        APIHandled(
-            NormalisedURLPath(SIGNUP),
-            "post",
-            SIGNUP,
-            self.api_implementation.disable_sign_up_post,
-        ),
-        APIHandled(
-            NormalisedURLPath(SIGNIN),
-            "post",
-            SIGNIN,
-            self.api_implementation.disable_sign_in_post,
-        ),
-        APIHandled(
-            NormalisedURLPath(USER_PASSWORD_RESET_TOKEN),
-            "post",
-            USER_PASSWORD_RESET_TOKEN,
-            self.api_implementation.disable_generate_password_reset_token_post,
-        ),
-        APIHandled(
-            NormalisedURLPath(USER_PASSWORD_RESET),
-            "post",
-            USER_PASSWORD_RESET,
-            self.api_implementation.disable_password_reset_post,
-        ),
-        APIHandled(
-            NormalisedURLPath(SIGNUP_EMAIL_EXISTS_OLD),
-            "get",
-            SIGNUP_EMAIL_EXISTS_OLD,
-            self.api_implementation.disable_email_exists_get,
-        ),
-        APIHandled(
-            NormalisedURLPath(SIGNUP_EMAIL_EXISTS),
-            "get",
-            SIGNUP_EMAIL_EXISTS,
-            self.api_implementation.disable_email_exists_get,
-        ),
-    ]
-
-
-
-async def get_email_for_user_id(self, user_id: str, user_context: Dict[str, Any]) ‑> Union[UnknownUserIdErrorGetEmailForUserIdOkResultEmailDoesNotExistError] -
-
-
-
- -Expand source code - -
async def get_email_for_user_id(
-    self, user_id: str, user_context: Dict[str, Any]
-) -> Union[UnknownUserIdError, GetEmailForUserIdOkResult, EmailDoesNotExistError]:
-    user_info = await self.recipe_implementation.get_user_by_id(
-        user_id, user_context
-    )
-    if user_info is not None:
-        return GetEmailForUserIdOkResult(user_info.email)
-
-    return UnknownUserIdError()
-
-
-
-async def handle_api_request(self, request_id: str, tenant_id: str, request: BaseRequest, path: NormalisedURLPath, method: str, response: BaseResponse, user_context: Dict[str, Any]) -
-
-
-
- -Expand source code - -
async def handle_api_request(
-    self,
-    request_id: str,
-    tenant_id: str,
-    request: BaseRequest,
-    path: NormalisedURLPath,
-    method: str,
-    response: BaseResponse,
-    user_context: Dict[str, Any],
-):
-    api_options = APIOptions(
-        request,
-        response,
-        self.recipe_id,
-        self.config,
-        self.recipe_implementation,
-        self.get_app_info(),
-        self.email_delivery,
-    )
-    if request_id == SIGNUP:
-        return await handle_sign_up_api(
-            tenant_id, self.api_implementation, api_options, user_context
-        )
-    if request_id == SIGNIN:
-        return await handle_sign_in_api(
-            tenant_id, self.api_implementation, api_options, user_context
-        )
-    if request_id in (SIGNUP_EMAIL_EXISTS, SIGNUP_EMAIL_EXISTS_OLD):
-        return await handle_email_exists_api(
-            tenant_id, self.api_implementation, api_options, user_context
-        )
-    if request_id == USER_PASSWORD_RESET_TOKEN:
-        return await handle_generate_password_reset_token_api(
-            tenant_id, self.api_implementation, api_options, user_context
-        )
-    if request_id == USER_PASSWORD_RESET:
-        return await handle_password_reset_api(
-            tenant_id, self.api_implementation, api_options, user_context
-        )
-
-    return None
-
-
-
-async def handle_error(self, request: BaseRequest, err: SuperTokensError, response: BaseResponse, user_context: Dict[str, Any]) ‑> BaseResponse -
-
-
-
- -Expand source code - -
async def handle_error(
-    self,
-    request: BaseRequest,
-    err: SuperTokensError,
-    response: BaseResponse,
-    user_context: Dict[str, Any],
-) -> BaseResponse:
-    if isinstance(err, SuperTokensEmailPasswordError):
-        if isinstance(err, FieldError):
-            response.set_json_content(
-                {"status": "FIELD_ERROR", "formFields": err.get_json_form_fields()}
-            )
-            return response
-    raise err
-
-
-
-def is_error_from_this_recipe_based_on_instance(self, err: Exception) ‑> bool -
-
-
-
- -Expand source code - -
def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
-    return isinstance(err, SuperTokensError) and (
-        isinstance(err, SuperTokensEmailPasswordError)
-    )
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailpassword/recipe_implementation.html b/html/supertokens_python/recipe/emailpassword/recipe_implementation.html deleted file mode 100644 index 07603572a..000000000 --- a/html/supertokens_python/recipe/emailpassword/recipe_implementation.html +++ /dev/null @@ -1,675 +0,0 @@ - - - - - - -supertokens_python.recipe.emailpassword.recipe_implementation API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.emailpassword.recipe_implementation

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from typing import TYPE_CHECKING, Any, Dict, Union, Callable
-
-from supertokens_python.normalised_url_path import NormalisedURLPath
-
-from .interfaces import (
-    CreateResetPasswordOkResult,
-    CreateResetPasswordWrongUserIdError,
-    RecipeInterface,
-    ResetPasswordUsingTokenOkResult,
-    ResetPasswordUsingTokenInvalidTokenError,
-    SignInOkResult,
-    SignInWrongCredentialsError,
-    SignUpEmailAlreadyExistsError,
-    SignUpOkResult,
-    UpdateEmailOrPasswordEmailAlreadyExistsError,
-    UpdateEmailOrPasswordOkResult,
-    UpdateEmailOrPasswordUnknownUserIdError,
-    UpdateEmailOrPasswordPasswordPolicyViolationError,
-)
-from .types import User
-from .utils import EmailPasswordConfig
-from .constants import FORM_FIELD_PASSWORD_ID
-
-if TYPE_CHECKING:
-    from supertokens_python.querier import Querier
-
-
-class RecipeImplementation(RecipeInterface):
-    def __init__(
-        self,
-        querier: Querier,
-        get_emailpassword_config: Callable[[], EmailPasswordConfig],
-    ):
-        super().__init__()
-        self.querier = querier
-        self.get_emailpassword_config = get_emailpassword_config
-
-    async def get_user_by_id(
-        self, user_id: str, user_context: Dict[str, Any]
-    ) -> Union[User, None]:
-        params = {"userId": user_id}
-        response = await self.querier.send_get_request(
-            NormalisedURLPath("/recipe/user"), params, user_context
-        )
-        if "status" in response and response["status"] == "OK":
-            return User(
-                response["user"]["id"],
-                response["user"]["email"],
-                response["user"]["timeJoined"],
-                response["user"]["tenantIds"],
-            )
-        return None
-
-    async def get_user_by_email(
-        self, email: str, tenant_id: str, user_context: Dict[str, Any]
-    ) -> Union[User, None]:
-        params = {"email": email}
-        response = await self.querier.send_get_request(
-            NormalisedURLPath(f"{tenant_id}/recipe/user"), params, user_context
-        )
-        if "status" in response and response["status"] == "OK":
-            return User(
-                response["user"]["id"],
-                response["user"]["email"],
-                response["user"]["timeJoined"],
-                response["user"]["tenantIds"],
-            )
-        return None
-
-    async def create_reset_password_token(
-        self, user_id: str, tenant_id: str, user_context: Dict[str, Any]
-    ) -> Union[CreateResetPasswordOkResult, CreateResetPasswordWrongUserIdError]:
-        data = {"userId": user_id}
-        response = await self.querier.send_post_request(
-            NormalisedURLPath(f"{tenant_id}/recipe/user/password/reset/token"),
-            data,
-            user_context=user_context,
-        )
-        if "status" in response and response["status"] == "OK":
-            return CreateResetPasswordOkResult(response["token"])
-        return CreateResetPasswordWrongUserIdError()
-
-    async def reset_password_using_token(
-        self,
-        token: str,
-        new_password: str,
-        tenant_id: str,
-        user_context: Dict[str, Any],
-    ) -> Union[
-        ResetPasswordUsingTokenOkResult, ResetPasswordUsingTokenInvalidTokenError
-    ]:
-        data = {"method": "token", "token": token, "newPassword": new_password}
-        response = await self.querier.send_post_request(
-            NormalisedURLPath(f"{tenant_id}/recipe/user/password/reset"),
-            data,
-            user_context=user_context,
-        )
-        if "status" not in response or response["status"] != "OK":
-            return ResetPasswordUsingTokenInvalidTokenError()
-        user_id = None
-        if "userId" in response:
-            user_id = response["userId"]
-        return ResetPasswordUsingTokenOkResult(user_id)
-
-    async def sign_in(
-        self, email: str, password: str, tenant_id: str, user_context: Dict[str, Any]
-    ) -> Union[SignInOkResult, SignInWrongCredentialsError]:
-        data = {"password": password, "email": email}
-        response = await self.querier.send_post_request(
-            NormalisedURLPath(f"{tenant_id}/recipe/signin"),
-            data,
-            user_context=user_context,
-        )
-        if "status" in response and response["status"] == "OK":
-            return SignInOkResult(
-                User(
-                    response["user"]["id"],
-                    response["user"]["email"],
-                    response["user"]["timeJoined"],
-                    response["user"]["tenantIds"],
-                )
-            )
-        return SignInWrongCredentialsError()
-
-    async def sign_up(
-        self, email: str, password: str, tenant_id: str, user_context: Dict[str, Any]
-    ) -> Union[SignUpOkResult, SignUpEmailAlreadyExistsError]:
-        data = {"password": password, "email": email}
-        response = await self.querier.send_post_request(
-            NormalisedURLPath(f"{tenant_id}/recipe/signup"),
-            data,
-            user_context=user_context,
-        )
-        if "status" in response and response["status"] == "OK":
-            return SignUpOkResult(
-                User(
-                    response["user"]["id"],
-                    response["user"]["email"],
-                    response["user"]["timeJoined"],
-                    response["user"]["tenantIds"],
-                )
-            )
-        return SignUpEmailAlreadyExistsError()
-
-    async def update_email_or_password(
-        self,
-        user_id: str,
-        email: Union[str, None],
-        password: Union[str, None],
-        apply_password_policy: Union[bool, None],
-        tenant_id_for_password_policy: str,
-        user_context: Dict[str, Any],
-    ) -> Union[
-        UpdateEmailOrPasswordOkResult,
-        UpdateEmailOrPasswordEmailAlreadyExistsError,
-        UpdateEmailOrPasswordUnknownUserIdError,
-        UpdateEmailOrPasswordPasswordPolicyViolationError,
-    ]:
-        data = {"userId": user_id}
-        if email is not None:
-            data = {"email": email, **data}
-        if password is not None:
-            if apply_password_policy is None or apply_password_policy:
-                form_fields = (
-                    self.get_emailpassword_config().sign_up_feature.form_fields
-                )
-                password_field = list(
-                    filter(lambda x: x.id == FORM_FIELD_PASSWORD_ID, form_fields)
-                )[0]
-                error = await password_field.validate(
-                    password, tenant_id_for_password_policy
-                )
-                if error is not None:
-                    return UpdateEmailOrPasswordPasswordPolicyViolationError(error)
-            data = {"password": password, **data}
-        response = await self.querier.send_put_request(
-            NormalisedURLPath("/recipe/user"),
-            data,
-            user_context=user_context,
-        )
-        if "status" in response and response["status"] == "OK":
-            return UpdateEmailOrPasswordOkResult()
-        if "status" in response and response["status"] == "EMAIL_ALREADY_EXISTS_ERROR":
-            return UpdateEmailOrPasswordEmailAlreadyExistsError()
-        return UpdateEmailOrPasswordUnknownUserIdError()
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class RecipeImplementation -(querier: Querier, get_emailpassword_config: Callable[[], EmailPasswordConfig]) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class RecipeImplementation(RecipeInterface):
-    def __init__(
-        self,
-        querier: Querier,
-        get_emailpassword_config: Callable[[], EmailPasswordConfig],
-    ):
-        super().__init__()
-        self.querier = querier
-        self.get_emailpassword_config = get_emailpassword_config
-
-    async def get_user_by_id(
-        self, user_id: str, user_context: Dict[str, Any]
-    ) -> Union[User, None]:
-        params = {"userId": user_id}
-        response = await self.querier.send_get_request(
-            NormalisedURLPath("/recipe/user"), params, user_context
-        )
-        if "status" in response and response["status"] == "OK":
-            return User(
-                response["user"]["id"],
-                response["user"]["email"],
-                response["user"]["timeJoined"],
-                response["user"]["tenantIds"],
-            )
-        return None
-
-    async def get_user_by_email(
-        self, email: str, tenant_id: str, user_context: Dict[str, Any]
-    ) -> Union[User, None]:
-        params = {"email": email}
-        response = await self.querier.send_get_request(
-            NormalisedURLPath(f"{tenant_id}/recipe/user"), params, user_context
-        )
-        if "status" in response and response["status"] == "OK":
-            return User(
-                response["user"]["id"],
-                response["user"]["email"],
-                response["user"]["timeJoined"],
-                response["user"]["tenantIds"],
-            )
-        return None
-
-    async def create_reset_password_token(
-        self, user_id: str, tenant_id: str, user_context: Dict[str, Any]
-    ) -> Union[CreateResetPasswordOkResult, CreateResetPasswordWrongUserIdError]:
-        data = {"userId": user_id}
-        response = await self.querier.send_post_request(
-            NormalisedURLPath(f"{tenant_id}/recipe/user/password/reset/token"),
-            data,
-            user_context=user_context,
-        )
-        if "status" in response and response["status"] == "OK":
-            return CreateResetPasswordOkResult(response["token"])
-        return CreateResetPasswordWrongUserIdError()
-
-    async def reset_password_using_token(
-        self,
-        token: str,
-        new_password: str,
-        tenant_id: str,
-        user_context: Dict[str, Any],
-    ) -> Union[
-        ResetPasswordUsingTokenOkResult, ResetPasswordUsingTokenInvalidTokenError
-    ]:
-        data = {"method": "token", "token": token, "newPassword": new_password}
-        response = await self.querier.send_post_request(
-            NormalisedURLPath(f"{tenant_id}/recipe/user/password/reset"),
-            data,
-            user_context=user_context,
-        )
-        if "status" not in response or response["status"] != "OK":
-            return ResetPasswordUsingTokenInvalidTokenError()
-        user_id = None
-        if "userId" in response:
-            user_id = response["userId"]
-        return ResetPasswordUsingTokenOkResult(user_id)
-
-    async def sign_in(
-        self, email: str, password: str, tenant_id: str, user_context: Dict[str, Any]
-    ) -> Union[SignInOkResult, SignInWrongCredentialsError]:
-        data = {"password": password, "email": email}
-        response = await self.querier.send_post_request(
-            NormalisedURLPath(f"{tenant_id}/recipe/signin"),
-            data,
-            user_context=user_context,
-        )
-        if "status" in response and response["status"] == "OK":
-            return SignInOkResult(
-                User(
-                    response["user"]["id"],
-                    response["user"]["email"],
-                    response["user"]["timeJoined"],
-                    response["user"]["tenantIds"],
-                )
-            )
-        return SignInWrongCredentialsError()
-
-    async def sign_up(
-        self, email: str, password: str, tenant_id: str, user_context: Dict[str, Any]
-    ) -> Union[SignUpOkResult, SignUpEmailAlreadyExistsError]:
-        data = {"password": password, "email": email}
-        response = await self.querier.send_post_request(
-            NormalisedURLPath(f"{tenant_id}/recipe/signup"),
-            data,
-            user_context=user_context,
-        )
-        if "status" in response and response["status"] == "OK":
-            return SignUpOkResult(
-                User(
-                    response["user"]["id"],
-                    response["user"]["email"],
-                    response["user"]["timeJoined"],
-                    response["user"]["tenantIds"],
-                )
-            )
-        return SignUpEmailAlreadyExistsError()
-
-    async def update_email_or_password(
-        self,
-        user_id: str,
-        email: Union[str, None],
-        password: Union[str, None],
-        apply_password_policy: Union[bool, None],
-        tenant_id_for_password_policy: str,
-        user_context: Dict[str, Any],
-    ) -> Union[
-        UpdateEmailOrPasswordOkResult,
-        UpdateEmailOrPasswordEmailAlreadyExistsError,
-        UpdateEmailOrPasswordUnknownUserIdError,
-        UpdateEmailOrPasswordPasswordPolicyViolationError,
-    ]:
-        data = {"userId": user_id}
-        if email is not None:
-            data = {"email": email, **data}
-        if password is not None:
-            if apply_password_policy is None or apply_password_policy:
-                form_fields = (
-                    self.get_emailpassword_config().sign_up_feature.form_fields
-                )
-                password_field = list(
-                    filter(lambda x: x.id == FORM_FIELD_PASSWORD_ID, form_fields)
-                )[0]
-                error = await password_field.validate(
-                    password, tenant_id_for_password_policy
-                )
-                if error is not None:
-                    return UpdateEmailOrPasswordPasswordPolicyViolationError(error)
-            data = {"password": password, **data}
-        response = await self.querier.send_put_request(
-            NormalisedURLPath("/recipe/user"),
-            data,
-            user_context=user_context,
-        )
-        if "status" in response and response["status"] == "OK":
-            return UpdateEmailOrPasswordOkResult()
-        if "status" in response and response["status"] == "EMAIL_ALREADY_EXISTS_ERROR":
-            return UpdateEmailOrPasswordEmailAlreadyExistsError()
-        return UpdateEmailOrPasswordUnknownUserIdError()
-
-

Ancestors

- -

Methods

-
-
-async def create_reset_password_token(self, user_id: str, tenant_id: str, user_context: Dict[str, Any]) ‑> Union[CreateResetPasswordOkResultCreateResetPasswordWrongUserIdError] -
-
-
-
- -Expand source code - -
async def create_reset_password_token(
-    self, user_id: str, tenant_id: str, user_context: Dict[str, Any]
-) -> Union[CreateResetPasswordOkResult, CreateResetPasswordWrongUserIdError]:
-    data = {"userId": user_id}
-    response = await self.querier.send_post_request(
-        NormalisedURLPath(f"{tenant_id}/recipe/user/password/reset/token"),
-        data,
-        user_context=user_context,
-    )
-    if "status" in response and response["status"] == "OK":
-        return CreateResetPasswordOkResult(response["token"])
-    return CreateResetPasswordWrongUserIdError()
-
-
-
-async def get_user_by_email(self, email: str, tenant_id: str, user_context: Dict[str, Any]) ‑> Optional[User] -
-
-
-
- -Expand source code - -
async def get_user_by_email(
-    self, email: str, tenant_id: str, user_context: Dict[str, Any]
-) -> Union[User, None]:
-    params = {"email": email}
-    response = await self.querier.send_get_request(
-        NormalisedURLPath(f"{tenant_id}/recipe/user"), params, user_context
-    )
-    if "status" in response and response["status"] == "OK":
-        return User(
-            response["user"]["id"],
-            response["user"]["email"],
-            response["user"]["timeJoined"],
-            response["user"]["tenantIds"],
-        )
-    return None
-
-
-
-async def get_user_by_id(self, user_id: str, user_context: Dict[str, Any]) ‑> Optional[User] -
-
-
-
- -Expand source code - -
async def get_user_by_id(
-    self, user_id: str, user_context: Dict[str, Any]
-) -> Union[User, None]:
-    params = {"userId": user_id}
-    response = await self.querier.send_get_request(
-        NormalisedURLPath("/recipe/user"), params, user_context
-    )
-    if "status" in response and response["status"] == "OK":
-        return User(
-            response["user"]["id"],
-            response["user"]["email"],
-            response["user"]["timeJoined"],
-            response["user"]["tenantIds"],
-        )
-    return None
-
-
-
-async def reset_password_using_token(self, token: str, new_password: str, tenant_id: str, user_context: Dict[str, Any]) ‑> Union[ResetPasswordUsingTokenOkResultResetPasswordUsingTokenInvalidTokenError] -
-
-
-
- -Expand source code - -
async def reset_password_using_token(
-    self,
-    token: str,
-    new_password: str,
-    tenant_id: str,
-    user_context: Dict[str, Any],
-) -> Union[
-    ResetPasswordUsingTokenOkResult, ResetPasswordUsingTokenInvalidTokenError
-]:
-    data = {"method": "token", "token": token, "newPassword": new_password}
-    response = await self.querier.send_post_request(
-        NormalisedURLPath(f"{tenant_id}/recipe/user/password/reset"),
-        data,
-        user_context=user_context,
-    )
-    if "status" not in response or response["status"] != "OK":
-        return ResetPasswordUsingTokenInvalidTokenError()
-    user_id = None
-    if "userId" in response:
-        user_id = response["userId"]
-    return ResetPasswordUsingTokenOkResult(user_id)
-
-
-
-async def sign_in(self, email: str, password: str, tenant_id: str, user_context: Dict[str, Any]) ‑> Union[SignInOkResultSignInWrongCredentialsError] -
-
-
-
- -Expand source code - -
async def sign_in(
-    self, email: str, password: str, tenant_id: str, user_context: Dict[str, Any]
-) -> Union[SignInOkResult, SignInWrongCredentialsError]:
-    data = {"password": password, "email": email}
-    response = await self.querier.send_post_request(
-        NormalisedURLPath(f"{tenant_id}/recipe/signin"),
-        data,
-        user_context=user_context,
-    )
-    if "status" in response and response["status"] == "OK":
-        return SignInOkResult(
-            User(
-                response["user"]["id"],
-                response["user"]["email"],
-                response["user"]["timeJoined"],
-                response["user"]["tenantIds"],
-            )
-        )
-    return SignInWrongCredentialsError()
-
-
-
-async def sign_up(self, email: str, password: str, tenant_id: str, user_context: Dict[str, Any]) ‑> Union[SignUpOkResultSignUpEmailAlreadyExistsError] -
-
-
-
- -Expand source code - -
async def sign_up(
-    self, email: str, password: str, tenant_id: str, user_context: Dict[str, Any]
-) -> Union[SignUpOkResult, SignUpEmailAlreadyExistsError]:
-    data = {"password": password, "email": email}
-    response = await self.querier.send_post_request(
-        NormalisedURLPath(f"{tenant_id}/recipe/signup"),
-        data,
-        user_context=user_context,
-    )
-    if "status" in response and response["status"] == "OK":
-        return SignUpOkResult(
-            User(
-                response["user"]["id"],
-                response["user"]["email"],
-                response["user"]["timeJoined"],
-                response["user"]["tenantIds"],
-            )
-        )
-    return SignUpEmailAlreadyExistsError()
-
-
-
-async def update_email_or_password(self, user_id: str, email: Union[str, None], password: Union[str, None], apply_password_policy: Union[bool, None], tenant_id_for_password_policy: str, user_context: Dict[str, Any]) ‑> Union[UpdateEmailOrPasswordOkResultUpdateEmailOrPasswordEmailAlreadyExistsErrorUpdateEmailOrPasswordUnknownUserIdErrorUpdateEmailOrPasswordPasswordPolicyViolationError] -
-
-
-
- -Expand source code - -
async def update_email_or_password(
-    self,
-    user_id: str,
-    email: Union[str, None],
-    password: Union[str, None],
-    apply_password_policy: Union[bool, None],
-    tenant_id_for_password_policy: str,
-    user_context: Dict[str, Any],
-) -> Union[
-    UpdateEmailOrPasswordOkResult,
-    UpdateEmailOrPasswordEmailAlreadyExistsError,
-    UpdateEmailOrPasswordUnknownUserIdError,
-    UpdateEmailOrPasswordPasswordPolicyViolationError,
-]:
-    data = {"userId": user_id}
-    if email is not None:
-        data = {"email": email, **data}
-    if password is not None:
-        if apply_password_policy is None or apply_password_policy:
-            form_fields = (
-                self.get_emailpassword_config().sign_up_feature.form_fields
-            )
-            password_field = list(
-                filter(lambda x: x.id == FORM_FIELD_PASSWORD_ID, form_fields)
-            )[0]
-            error = await password_field.validate(
-                password, tenant_id_for_password_policy
-            )
-            if error is not None:
-                return UpdateEmailOrPasswordPasswordPolicyViolationError(error)
-        data = {"password": password, **data}
-    response = await self.querier.send_put_request(
-        NormalisedURLPath("/recipe/user"),
-        data,
-        user_context=user_context,
-    )
-    if "status" in response and response["status"] == "OK":
-        return UpdateEmailOrPasswordOkResult()
-    if "status" in response and response["status"] == "EMAIL_ALREADY_EXISTS_ERROR":
-        return UpdateEmailOrPasswordEmailAlreadyExistsError()
-    return UpdateEmailOrPasswordUnknownUserIdError()
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailpassword/syncio/index.html b/html/supertokens_python/recipe/emailpassword/syncio/index.html deleted file mode 100644 index e46bdf972..000000000 --- a/html/supertokens_python/recipe/emailpassword/syncio/index.html +++ /dev/null @@ -1,428 +0,0 @@ - - - - - - -supertokens_python.recipe.emailpassword.syncio API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.emailpassword.syncio

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from typing import Any, Dict, Union, Optional
-
-from supertokens_python.async_to_sync_wrapper import sync
-
-from ..interfaces import SignInOkResult, SignInWrongCredentialsError
-from ..types import EmailTemplateVars, User
-
-
-def update_email_or_password(
-    user_id: str,
-    email: Union[str, None] = None,
-    password: Union[str, None] = None,
-    apply_password_policy: Union[bool, None] = None,
-    tenant_id_for_password_policy: Optional[str] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-):
-    from supertokens_python.recipe.emailpassword.asyncio import update_email_or_password
-
-    return sync(
-        update_email_or_password(
-            user_id,
-            email,
-            password,
-            apply_password_policy,
-            tenant_id_for_password_policy,
-            user_context,
-        )
-    )
-
-
-def get_user_by_id(
-    user_id: str, user_context: Union[None, Dict[str, Any]] = None
-) -> Union[None, User]:
-    from supertokens_python.recipe.emailpassword.asyncio import get_user_by_id
-
-    return sync(get_user_by_id(user_id, user_context))
-
-
-def get_user_by_email(
-    tenant_id: str,
-    email: str,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> Union[None, User]:
-    from supertokens_python.recipe.emailpassword.asyncio import get_user_by_email
-
-    return sync(get_user_by_email(tenant_id, email, user_context))
-
-
-def create_reset_password_token(
-    tenant_id: str,
-    user_id: str,
-    user_context: Union[None, Dict[str, Any]] = None,
-):
-    from supertokens_python.recipe.emailpassword.asyncio import (
-        create_reset_password_token,
-    )
-
-    return sync(create_reset_password_token(tenant_id, user_id, user_context))
-
-
-def reset_password_using_token(
-    tenant_id: str,
-    token: str,
-    new_password: str,
-    user_context: Union[None, Dict[str, Any]] = None,
-):
-    from supertokens_python.recipe.emailpassword.asyncio import (
-        reset_password_using_token,
-    )
-
-    return sync(
-        reset_password_using_token(tenant_id, token, new_password, user_context)
-    )
-
-
-def sign_in(
-    tenant_id: str,
-    email: str,
-    password: str,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> Union[SignInOkResult, SignInWrongCredentialsError]:
-    from supertokens_python.recipe.emailpassword.asyncio import sign_in
-
-    return sync(sign_in(tenant_id, email, password, user_context))
-
-
-def sign_up(
-    tenant_id: str,
-    email: str,
-    password: str,
-    user_context: Union[None, Dict[str, Any]] = None,
-):
-    from supertokens_python.recipe.emailpassword.asyncio import sign_up
-
-    return sync(sign_up(tenant_id, email, password, user_context))
-
-
-def send_email(
-    input_: EmailTemplateVars,
-    user_context: Union[None, Dict[str, Any]] = None,
-):
-    from supertokens_python.recipe.emailpassword.asyncio import send_email
-
-    return sync(send_email(input_, user_context))
-
-
-def create_reset_password_link(
-    tenant_id: str,
-    user_id: str,
-    user_context: Optional[Dict[str, Any]] = None,
-):
-    from supertokens_python.recipe.emailpassword.asyncio import (
-        create_reset_password_link,
-    )
-
-    return sync(create_reset_password_link(tenant_id, user_id, user_context))
-
-
-def send_reset_password_email(
-    tenant_id: str,
-    user_id: str,
-    user_context: Optional[Dict[str, Any]] = None,
-):
-    from supertokens_python.recipe.emailpassword.asyncio import (
-        send_reset_password_email,
-    )
-
-    return sync(send_reset_password_email(tenant_id, user_id, user_context))
-
-
-
-
-
-
-
-

Functions

-
- -
-
-
- -Expand source code - -
def create_reset_password_link(
-    tenant_id: str,
-    user_id: str,
-    user_context: Optional[Dict[str, Any]] = None,
-):
-    from supertokens_python.recipe.emailpassword.asyncio import (
-        create_reset_password_link,
-    )
-
-    return sync(create_reset_password_link(tenant_id, user_id, user_context))
-
-
-
-def create_reset_password_token(tenant_id: str, user_id: str, user_context: Optional[Dict[str, Any]] = None) -
-
-
-
- -Expand source code - -
def create_reset_password_token(
-    tenant_id: str,
-    user_id: str,
-    user_context: Union[None, Dict[str, Any]] = None,
-):
-    from supertokens_python.recipe.emailpassword.asyncio import (
-        create_reset_password_token,
-    )
-
-    return sync(create_reset_password_token(tenant_id, user_id, user_context))
-
-
-
-def get_user_by_email(tenant_id: str, email: str, user_context: Optional[Dict[str, Any]] = None) ‑> Optional[User] -
-
-
-
- -Expand source code - -
def get_user_by_email(
-    tenant_id: str,
-    email: str,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> Union[None, User]:
-    from supertokens_python.recipe.emailpassword.asyncio import get_user_by_email
-
-    return sync(get_user_by_email(tenant_id, email, user_context))
-
-
-
-def get_user_by_id(user_id: str, user_context: Optional[Dict[str, Any]] = None) ‑> Optional[User] -
-
-
-
- -Expand source code - -
def get_user_by_id(
-    user_id: str, user_context: Union[None, Dict[str, Any]] = None
-) -> Union[None, User]:
-    from supertokens_python.recipe.emailpassword.asyncio import get_user_by_id
-
-    return sync(get_user_by_id(user_id, user_context))
-
-
-
-def reset_password_using_token(tenant_id: str, token: str, new_password: str, user_context: Optional[Dict[str, Any]] = None) -
-
-
-
- -Expand source code - -
def reset_password_using_token(
-    tenant_id: str,
-    token: str,
-    new_password: str,
-    user_context: Union[None, Dict[str, Any]] = None,
-):
-    from supertokens_python.recipe.emailpassword.asyncio import (
-        reset_password_using_token,
-    )
-
-    return sync(
-        reset_password_using_token(tenant_id, token, new_password, user_context)
-    )
-
-
-
-def send_email(input_: PasswordResetEmailTemplateVars, user_context: Optional[Dict[str, Any]] = None) -
-
-
-
- -Expand source code - -
def send_email(
-    input_: EmailTemplateVars,
-    user_context: Union[None, Dict[str, Any]] = None,
-):
-    from supertokens_python.recipe.emailpassword.asyncio import send_email
-
-    return sync(send_email(input_, user_context))
-
-
-
-def send_reset_password_email(tenant_id: str, user_id: str, user_context: Optional[Dict[str, Any]] = None) -
-
-
-
- -Expand source code - -
def send_reset_password_email(
-    tenant_id: str,
-    user_id: str,
-    user_context: Optional[Dict[str, Any]] = None,
-):
-    from supertokens_python.recipe.emailpassword.asyncio import (
-        send_reset_password_email,
-    )
-
-    return sync(send_reset_password_email(tenant_id, user_id, user_context))
-
-
-
-def sign_in(tenant_id: str, email: str, password: str, user_context: Optional[Dict[str, Any]] = None) ‑> Union[SignInOkResultSignInWrongCredentialsError] -
-
-
-
- -Expand source code - -
def sign_in(
-    tenant_id: str,
-    email: str,
-    password: str,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> Union[SignInOkResult, SignInWrongCredentialsError]:
-    from supertokens_python.recipe.emailpassword.asyncio import sign_in
-
-    return sync(sign_in(tenant_id, email, password, user_context))
-
-
-
-def sign_up(tenant_id: str, email: str, password: str, user_context: Optional[Dict[str, Any]] = None) -
-
-
-
- -Expand source code - -
def sign_up(
-    tenant_id: str,
-    email: str,
-    password: str,
-    user_context: Union[None, Dict[str, Any]] = None,
-):
-    from supertokens_python.recipe.emailpassword.asyncio import sign_up
-
-    return sync(sign_up(tenant_id, email, password, user_context))
-
-
-
-def update_email_or_password(user_id: str, email: Optional[str] = None, password: Optional[str] = None, apply_password_policy: Optional[bool] = None, tenant_id_for_password_policy: Optional[str] = None, user_context: Optional[Dict[str, Any]] = None) -
-
-
-
- -Expand source code - -
def update_email_or_password(
-    user_id: str,
-    email: Union[str, None] = None,
-    password: Union[str, None] = None,
-    apply_password_policy: Union[bool, None] = None,
-    tenant_id_for_password_policy: Optional[str] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-):
-    from supertokens_python.recipe.emailpassword.asyncio import update_email_or_password
-
-    return sync(
-        update_email_or_password(
-            user_id,
-            email,
-            password,
-            apply_password_policy,
-            tenant_id_for_password_policy,
-            user_context,
-        )
-    )
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailpassword/types.html b/html/supertokens_python/recipe/emailpassword/types.html deleted file mode 100644 index c71226e44..000000000 --- a/html/supertokens_python/recipe/emailpassword/types.html +++ /dev/null @@ -1,422 +0,0 @@ - - - - - - -supertokens_python.recipe.emailpassword.types API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.emailpassword.types

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from typing import Any, Awaitable, Callable, List, TypeVar, Union
-
-from supertokens_python.ingredients.emaildelivery import EmailDeliveryIngredient
-from supertokens_python.ingredients.emaildelivery.types import (
-    EmailDeliveryInterface,
-    SMTPServiceInterface,
-)
-
-
-class User:
-    def __init__(
-        self, user_id: str, email: str, time_joined: int, tenant_ids: List[str]
-    ):
-        self.user_id = user_id
-        self.email = email
-        self.time_joined = time_joined
-        self.tenant_ids = tenant_ids
-
-    def __eq__(self, other: object):
-        return (
-            isinstance(other, self.__class__)
-            and self.user_id == other.user_id
-            and self.email == other.email
-            and self.time_joined == other.time_joined
-            and self.tenant_ids == other.tenant_ids
-        )
-
-
-class UsersResponse:
-    def __init__(self, users: List[User], next_pagination_token: Union[str, None]):
-        self.users = users
-        self.next_pagination_token = next_pagination_token
-
-
-class ErrorFormField:
-    def __init__(self, id: str, error: str):  # pylint: disable=redefined-builtin
-        self.id = id
-        self.error = error
-
-
-class FormField:
-    def __init__(self, id: str, value: Any):  # pylint: disable=redefined-builtin
-        self.id: str = id
-        self.value: Any = value
-
-
-class InputFormField:
-    def __init__(
-        self,
-        id: str,  # pylint: disable=redefined-builtin
-        validate: Union[
-            Callable[[str, str], Awaitable[Union[str, None]]],
-            None,
-        ] = None,
-        optional: Union[bool, None] = None,
-    ):
-        self.id = id
-        self.validate = validate
-        self.optional = optional
-
-
-class NormalisedFormField:
-    def __init__(
-        self,
-        id: str,  # pylint: disable=redefined-builtin
-        validate: Callable[[str, str], Awaitable[Union[str, None]]],
-        optional: bool,
-    ):
-        self.id = id
-        self.validate = validate
-        self.optional = optional
-
-
-_T = TypeVar("_T")
-
-
-class PasswordResetEmailTemplateVarsUser:
-    def __init__(self, user_id: str, email: str):
-        self.id = user_id
-        self.email = email
-
-
-class PasswordResetEmailTemplateVars:
-    def __init__(
-        self,
-        user: PasswordResetEmailTemplateVarsUser,
-        password_reset_link: str,
-        tenant_id: str,
-    ) -> None:
-        self.user = user
-        self.password_reset_link = password_reset_link
-        self.tenant_id = tenant_id
-
-
-# Export:
-EmailTemplateVars = PasswordResetEmailTemplateVars
-
-# PasswordResetEmailTemplateVars (Already exported because it's defined in the same)
-
-SMTPOverrideInput = SMTPServiceInterface[EmailTemplateVars]
-
-EmailDeliveryOverrideInput = EmailDeliveryInterface[EmailTemplateVars]
-
-
-class EmailPasswordIngredients:
-    def __init__(
-        self,
-        email_delivery: Union[EmailDeliveryIngredient[EmailTemplateVars], None] = None,
-    ) -> None:
-        self.email_delivery = email_delivery
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class EmailPasswordIngredients -(email_delivery: Union[EmailDeliveryIngredient[PasswordResetEmailTemplateVars], None] = None) -
-
-
-
- -Expand source code - -
class EmailPasswordIngredients:
-    def __init__(
-        self,
-        email_delivery: Union[EmailDeliveryIngredient[EmailTemplateVars], None] = None,
-    ) -> None:
-        self.email_delivery = email_delivery
-
-
-
-class ErrorFormField -(id: str, error: str) -
-
-
-
- -Expand source code - -
class ErrorFormField:
-    def __init__(self, id: str, error: str):  # pylint: disable=redefined-builtin
-        self.id = id
-        self.error = error
-
-
-
-class FormField -(id: str, value: Any) -
-
-
-
- -Expand source code - -
class FormField:
-    def __init__(self, id: str, value: Any):  # pylint: disable=redefined-builtin
-        self.id: str = id
-        self.value: Any = value
-
-
-
-class InputFormField -(id: str, validate: Union[Callable[[str, str], Awaitable[Union[str, None]]], None] = None, optional: Union[bool, None] = None) -
-
-
-
- -Expand source code - -
class InputFormField:
-    def __init__(
-        self,
-        id: str,  # pylint: disable=redefined-builtin
-        validate: Union[
-            Callable[[str, str], Awaitable[Union[str, None]]],
-            None,
-        ] = None,
-        optional: Union[bool, None] = None,
-    ):
-        self.id = id
-        self.validate = validate
-        self.optional = optional
-
-
-
-class NormalisedFormField -(id: str, validate: Callable[[str, str], Awaitable[Union[str, None]]], optional: bool) -
-
-
-
- -Expand source code - -
class NormalisedFormField:
-    def __init__(
-        self,
-        id: str,  # pylint: disable=redefined-builtin
-        validate: Callable[[str, str], Awaitable[Union[str, None]]],
-        optional: bool,
-    ):
-        self.id = id
-        self.validate = validate
-        self.optional = optional
-
-
-
-class PasswordResetEmailTemplateVars -(user: PasswordResetEmailTemplateVarsUser, password_reset_link: str, tenant_id: str) -
-
-
-
- -Expand source code - -
class PasswordResetEmailTemplateVars:
-    def __init__(
-        self,
-        user: PasswordResetEmailTemplateVarsUser,
-        password_reset_link: str,
-        tenant_id: str,
-    ) -> None:
-        self.user = user
-        self.password_reset_link = password_reset_link
-        self.tenant_id = tenant_id
-
-
-
-class EmailTemplateVars -(user: PasswordResetEmailTemplateVarsUser, password_reset_link: str, tenant_id: str) -
-
-
-
- -Expand source code - -
class PasswordResetEmailTemplateVars:
-    def __init__(
-        self,
-        user: PasswordResetEmailTemplateVarsUser,
-        password_reset_link: str,
-        tenant_id: str,
-    ) -> None:
-        self.user = user
-        self.password_reset_link = password_reset_link
-        self.tenant_id = tenant_id
-
-
-
-class PasswordResetEmailTemplateVarsUser -(user_id: str, email: str) -
-
-
-
- -Expand source code - -
class PasswordResetEmailTemplateVarsUser:
-    def __init__(self, user_id: str, email: str):
-        self.id = user_id
-        self.email = email
-
-
-
-class User -(user_id: str, email: str, time_joined: int, tenant_ids: List[str]) -
-
-
-
- -Expand source code - -
class User:
-    def __init__(
-        self, user_id: str, email: str, time_joined: int, tenant_ids: List[str]
-    ):
-        self.user_id = user_id
-        self.email = email
-        self.time_joined = time_joined
-        self.tenant_ids = tenant_ids
-
-    def __eq__(self, other: object):
-        return (
-            isinstance(other, self.__class__)
-            and self.user_id == other.user_id
-            and self.email == other.email
-            and self.time_joined == other.time_joined
-            and self.tenant_ids == other.tenant_ids
-        )
-
-
-
-class UsersResponse -(users: List[User], next_pagination_token: Union[str, None]) -
-
-
-
- -Expand source code - -
class UsersResponse:
-    def __init__(self, users: List[User], next_pagination_token: Union[str, None]):
-        self.users = users
-        self.next_pagination_token = next_pagination_token
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailpassword/utils.html b/html/supertokens_python/recipe/emailpassword/utils.html deleted file mode 100644 index ed2bb11f5..000000000 --- a/html/supertokens_python/recipe/emailpassword/utils.html +++ /dev/null @@ -1,839 +0,0 @@ - - - - - - -supertokens_python.recipe.emailpassword.utils API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.emailpassword.utils

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from re import fullmatch
-from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Union
-
-from supertokens_python.framework import BaseRequest
-from supertokens_python.ingredients.emaildelivery.types import (
-    EmailDeliveryConfig,
-    EmailDeliveryConfigWithService,
-)
-from supertokens_python.recipe.emailpassword.emaildelivery.services.backward_compatibility import (
-    BackwardCompatibilityService,
-)
-
-from .interfaces import APIInterface, RecipeInterface
-from .types import EmailTemplateVars, InputFormField, NormalisedFormField
-
-if TYPE_CHECKING:
-    from supertokens_python.supertokens import AppInfo
-
-from supertokens_python.utils import get_filtered_list
-
-from .constants import FORM_FIELD_EMAIL_ID, FORM_FIELD_PASSWORD_ID
-
-
-async def default_validator(_: str, __: str) -> Union[str, None]:
-    return None
-
-
-async def default_password_validator(value: str, _tenant_id: str) -> Union[str, None]:
-    # length >= 8 && < 100
-    # must have a number and a character
-    # as per
-    # https://github.com/supertokens/supertokens-auth-react/issues/5#issuecomment-709512438
-    if len(value) < 8:
-        return "Password must contain at least 8 characters, including a number"
-
-    if len(value) >= 100:
-        return "Password's length must be lesser than 100 characters"
-
-    if fullmatch(r"^.*[A-Za-z]+.*$", value) is None:
-        return "Password must contain at least one alphabet"
-
-    if fullmatch(r"^.*[0-9]+.*$", value) is None:
-        return "Password must contain at least one number"
-
-    return None
-
-
-async def default_email_validator(value: Any, _tenant_id: str) -> Union[str, None]:
-    # We check if the email syntax is correct
-    # As per https://github.com/supertokens/supertokens-auth-react/issues/5#issuecomment-709512438
-    # Regex from https://stackoverflow.com/a/46181/3867175
-    if (not isinstance(value, str)) or (
-        fullmatch(
-            r'^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,'
-            r"3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$",
-            value,
-        )
-        is None
-    ):
-        return "Email is not valid"
-
-    return None
-
-
-class InputSignUpFeature:
-    def __init__(self, form_fields: Union[List[InputFormField], None] = None):
-        if form_fields is None:
-            form_fields = []
-        self.form_fields = normalise_sign_up_form_fields(form_fields)
-
-
-class SignUpFeature:
-    def __init__(self, form_fields: List[NormalisedFormField]):
-        self.form_fields = form_fields
-
-
-def normalise_sign_up_form_fields(
-    form_fields: List[InputFormField],
-) -> List[NormalisedFormField]:
-    normalised_form_fields: List[NormalisedFormField] = []
-    for field in form_fields:
-        if field.id == FORM_FIELD_PASSWORD_ID:
-            validator = (
-                field.validate
-                if field.validate is not None
-                else default_password_validator
-            )
-            normalised_form_fields.append(
-                NormalisedFormField(field.id, validator, False)
-            )
-        elif field.id == FORM_FIELD_EMAIL_ID:
-            validator = (
-                field.validate
-                if field.validate is not None
-                else default_email_validator
-            )
-            normalised_form_fields.append(
-                NormalisedFormField(field.id, validator, False)
-            )
-        else:
-            validator = (
-                field.validate if field.validate is not None else default_validator
-            )
-            optional = field.optional if field.optional is not None else False
-            normalised_form_fields.append(
-                NormalisedFormField(field.id, validator, optional)
-            )
-    if (
-        len(
-            get_filtered_list(
-                lambda x: x.id == FORM_FIELD_PASSWORD_ID, normalised_form_fields
-            )
-        )
-        == 0
-    ):
-        normalised_form_fields.append(
-            NormalisedFormField(
-                FORM_FIELD_PASSWORD_ID, default_password_validator, False
-            )
-        )
-    if (
-        len(
-            get_filtered_list(
-                lambda x: x.id == FORM_FIELD_EMAIL_ID, normalised_form_fields
-            )
-        )
-        == 0
-    ):
-        normalised_form_fields.append(
-            NormalisedFormField(FORM_FIELD_EMAIL_ID, default_email_validator, False)
-        )
-    return normalised_form_fields
-
-
-class SignInFeature:
-    def __init__(self, form_fields: List[NormalisedFormField]):
-        self.form_fields = form_fields
-
-
-def normalise_sign_in_form_fields(
-    form_fields: List[NormalisedFormField],
-) -> List[NormalisedFormField]:
-    return list(
-        map(
-            lambda y: NormalisedFormField(
-                y.id,
-                y.validate if y.id == FORM_FIELD_EMAIL_ID else default_validator,
-                False,
-            ),
-            get_filtered_list(
-                lambda x: x.id in (FORM_FIELD_PASSWORD_ID, FORM_FIELD_EMAIL_ID),
-                form_fields,
-            ),
-        )
-    )
-
-
-def validate_and_normalise_sign_in_config(
-    sign_up_config: SignUpFeature,
-) -> SignInFeature:
-    form_fields = normalise_sign_in_form_fields(sign_up_config.form_fields)
-    return SignInFeature(form_fields)
-
-
-class ResetPasswordUsingTokenFeature:
-    def __init__(
-        self,
-        form_fields_for_password_reset_form: List[NormalisedFormField],
-        form_fields_for_generate_token_form: List[NormalisedFormField],
-    ):
-        self.form_fields_for_password_reset_form = form_fields_for_password_reset_form
-        self.form_fields_for_generate_token_form = form_fields_for_generate_token_form
-
-
-def validate_and_normalise_reset_password_using_token_config(
-    sign_up_config: InputSignUpFeature,
-) -> ResetPasswordUsingTokenFeature:
-    form_fields_for_password_reset_form = list(
-        map(
-            lambda y: NormalisedFormField(y.id, y.validate, False),
-            get_filtered_list(
-                lambda x: x.id == FORM_FIELD_PASSWORD_ID, sign_up_config.form_fields
-            ),
-        )
-    )
-    form_fields_for_generate_token_form = list(
-        map(
-            lambda y: NormalisedFormField(y.id, y.validate, False),
-            get_filtered_list(
-                lambda x: x.id == FORM_FIELD_EMAIL_ID, sign_up_config.form_fields
-            ),
-        )
-    )
-
-    return ResetPasswordUsingTokenFeature(
-        form_fields_for_password_reset_form,
-        form_fields_for_generate_token_form,
-    )
-
-
-class InputOverrideConfig:
-    def __init__(
-        self,
-        functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None,
-        apis: Union[Callable[[APIInterface], APIInterface], None] = None,
-    ):
-        self.functions = functions
-        self.apis = apis
-
-
-class OverrideConfig:
-    def __init__(
-        self,
-        functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None,
-        apis: Union[Callable[[APIInterface], APIInterface], None] = None,
-    ):
-        self.functions = functions
-        self.apis = apis
-
-
-class EmailPasswordConfig:
-    def __init__(
-        self,
-        sign_up_feature: SignUpFeature,
-        sign_in_feature: SignInFeature,
-        reset_password_using_token_feature: ResetPasswordUsingTokenFeature,
-        override: OverrideConfig,
-        get_email_delivery_config: Callable[
-            [RecipeInterface], EmailDeliveryConfigWithService[EmailTemplateVars]
-        ],
-    ):
-        self.sign_up_feature = sign_up_feature
-        self.sign_in_feature = sign_in_feature
-        self.reset_password_using_token_feature = reset_password_using_token_feature
-        self.override = override
-        self.get_email_delivery_config = get_email_delivery_config
-
-
-def validate_and_normalise_user_input(
-    app_info: AppInfo,
-    sign_up_feature: Union[InputSignUpFeature, None] = None,
-    override: Union[InputOverrideConfig, None] = None,
-    email_delivery: Union[EmailDeliveryConfig[EmailTemplateVars], None] = None,
-) -> EmailPasswordConfig:
-
-    # NOTE: We don't need to check the instance of sign_up_feature and override
-    # as they will always be either None or the specified type.
-
-    if override is None:
-        override = InputOverrideConfig()
-
-    if sign_up_feature is None:
-        sign_up_feature = InputSignUpFeature()
-
-    def get_email_delivery_config(
-        ep_recipe: RecipeInterface,
-    ) -> EmailDeliveryConfigWithService[EmailTemplateVars]:
-        if email_delivery and email_delivery.service:
-            return EmailDeliveryConfigWithService(
-                service=email_delivery.service, override=email_delivery.override
-            )
-
-        email_service = BackwardCompatibilityService(
-            app_info=app_info,
-            recipe_interface_impl=ep_recipe,
-        )
-        if email_delivery is not None and email_delivery.override is not None:
-            override = email_delivery.override
-        else:
-            override = None
-        return EmailDeliveryConfigWithService(email_service, override=override)
-
-    return EmailPasswordConfig(
-        SignUpFeature(sign_up_feature.form_fields),
-        SignInFeature(normalise_sign_in_form_fields(sign_up_feature.form_fields)),
-        validate_and_normalise_reset_password_using_token_config(sign_up_feature),
-        OverrideConfig(functions=override.functions, apis=override.apis),
-        get_email_delivery_config=get_email_delivery_config,
-    )
-
-
-def get_password_reset_link(
-    app_info: AppInfo,
-    token: str,
-    tenant_id: str,
-    request: Optional[BaseRequest],
-    user_context: Dict[str, Any],
-) -> str:
-    return (
-        app_info.get_origin(request, user_context).get_as_string_dangerous()
-        + app_info.website_base_path.get_as_string_dangerous()
-        + "/reset-password?token="
-        + token
-        + "&tenantId="
-        + tenant_id
-    )
-
-
-
-
-
-
-
-

Functions

-
-
-async def default_email_validator(value: Any, _tenant_id: str) ‑> Optional[str] -
-
-
-
- -Expand source code - -
async def default_email_validator(value: Any, _tenant_id: str) -> Union[str, None]:
-    # We check if the email syntax is correct
-    # As per https://github.com/supertokens/supertokens-auth-react/issues/5#issuecomment-709512438
-    # Regex from https://stackoverflow.com/a/46181/3867175
-    if (not isinstance(value, str)) or (
-        fullmatch(
-            r'^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,'
-            r"3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$",
-            value,
-        )
-        is None
-    ):
-        return "Email is not valid"
-
-    return None
-
-
-
-async def default_password_validator(value: str, _tenant_id: str) ‑> Optional[str] -
-
-
-
- -Expand source code - -
async def default_password_validator(value: str, _tenant_id: str) -> Union[str, None]:
-    # length >= 8 && < 100
-    # must have a number and a character
-    # as per
-    # https://github.com/supertokens/supertokens-auth-react/issues/5#issuecomment-709512438
-    if len(value) < 8:
-        return "Password must contain at least 8 characters, including a number"
-
-    if len(value) >= 100:
-        return "Password's length must be lesser than 100 characters"
-
-    if fullmatch(r"^.*[A-Za-z]+.*$", value) is None:
-        return "Password must contain at least one alphabet"
-
-    if fullmatch(r"^.*[0-9]+.*$", value) is None:
-        return "Password must contain at least one number"
-
-    return None
-
-
-
-async def default_validator(_: str, __: str) ‑> Optional[str] -
-
-
-
- -Expand source code - -
async def default_validator(_: str, __: str) -> Union[str, None]:
-    return None
-
-
- -
-
-
- -Expand source code - -
def get_password_reset_link(
-    app_info: AppInfo,
-    token: str,
-    tenant_id: str,
-    request: Optional[BaseRequest],
-    user_context: Dict[str, Any],
-) -> str:
-    return (
-        app_info.get_origin(request, user_context).get_as_string_dangerous()
-        + app_info.website_base_path.get_as_string_dangerous()
-        + "/reset-password?token="
-        + token
-        + "&tenantId="
-        + tenant_id
-    )
-
-
-
-def normalise_sign_in_form_fields(form_fields: List[NormalisedFormField]) ‑> List[NormalisedFormField] -
-
-
-
- -Expand source code - -
def normalise_sign_in_form_fields(
-    form_fields: List[NormalisedFormField],
-) -> List[NormalisedFormField]:
-    return list(
-        map(
-            lambda y: NormalisedFormField(
-                y.id,
-                y.validate if y.id == FORM_FIELD_EMAIL_ID else default_validator,
-                False,
-            ),
-            get_filtered_list(
-                lambda x: x.id in (FORM_FIELD_PASSWORD_ID, FORM_FIELD_EMAIL_ID),
-                form_fields,
-            ),
-        )
-    )
-
-
-
-def normalise_sign_up_form_fields(form_fields: List[InputFormField]) ‑> List[NormalisedFormField] -
-
-
-
- -Expand source code - -
def normalise_sign_up_form_fields(
-    form_fields: List[InputFormField],
-) -> List[NormalisedFormField]:
-    normalised_form_fields: List[NormalisedFormField] = []
-    for field in form_fields:
-        if field.id == FORM_FIELD_PASSWORD_ID:
-            validator = (
-                field.validate
-                if field.validate is not None
-                else default_password_validator
-            )
-            normalised_form_fields.append(
-                NormalisedFormField(field.id, validator, False)
-            )
-        elif field.id == FORM_FIELD_EMAIL_ID:
-            validator = (
-                field.validate
-                if field.validate is not None
-                else default_email_validator
-            )
-            normalised_form_fields.append(
-                NormalisedFormField(field.id, validator, False)
-            )
-        else:
-            validator = (
-                field.validate if field.validate is not None else default_validator
-            )
-            optional = field.optional if field.optional is not None else False
-            normalised_form_fields.append(
-                NormalisedFormField(field.id, validator, optional)
-            )
-    if (
-        len(
-            get_filtered_list(
-                lambda x: x.id == FORM_FIELD_PASSWORD_ID, normalised_form_fields
-            )
-        )
-        == 0
-    ):
-        normalised_form_fields.append(
-            NormalisedFormField(
-                FORM_FIELD_PASSWORD_ID, default_password_validator, False
-            )
-        )
-    if (
-        len(
-            get_filtered_list(
-                lambda x: x.id == FORM_FIELD_EMAIL_ID, normalised_form_fields
-            )
-        )
-        == 0
-    ):
-        normalised_form_fields.append(
-            NormalisedFormField(FORM_FIELD_EMAIL_ID, default_email_validator, False)
-        )
-    return normalised_form_fields
-
-
-
-def validate_and_normalise_reset_password_using_token_config(sign_up_config: InputSignUpFeature) ‑> ResetPasswordUsingTokenFeature -
-
-
-
- -Expand source code - -
def validate_and_normalise_reset_password_using_token_config(
-    sign_up_config: InputSignUpFeature,
-) -> ResetPasswordUsingTokenFeature:
-    form_fields_for_password_reset_form = list(
-        map(
-            lambda y: NormalisedFormField(y.id, y.validate, False),
-            get_filtered_list(
-                lambda x: x.id == FORM_FIELD_PASSWORD_ID, sign_up_config.form_fields
-            ),
-        )
-    )
-    form_fields_for_generate_token_form = list(
-        map(
-            lambda y: NormalisedFormField(y.id, y.validate, False),
-            get_filtered_list(
-                lambda x: x.id == FORM_FIELD_EMAIL_ID, sign_up_config.form_fields
-            ),
-        )
-    )
-
-    return ResetPasswordUsingTokenFeature(
-        form_fields_for_password_reset_form,
-        form_fields_for_generate_token_form,
-    )
-
-
-
-def validate_and_normalise_sign_in_config(sign_up_config: SignUpFeature) ‑> SignInFeature -
-
-
-
- -Expand source code - -
def validate_and_normalise_sign_in_config(
-    sign_up_config: SignUpFeature,
-) -> SignInFeature:
-    form_fields = normalise_sign_in_form_fields(sign_up_config.form_fields)
-    return SignInFeature(form_fields)
-
-
-
-def validate_and_normalise_user_input(app_info: AppInfo, sign_up_feature: Union[InputSignUpFeature, None] = None, override: Union[InputOverrideConfig, None] = None, email_delivery: Union[EmailDeliveryConfig[EmailTemplateVars], None] = None) ‑> EmailPasswordConfig -
-
-
-
- -Expand source code - -
def validate_and_normalise_user_input(
-    app_info: AppInfo,
-    sign_up_feature: Union[InputSignUpFeature, None] = None,
-    override: Union[InputOverrideConfig, None] = None,
-    email_delivery: Union[EmailDeliveryConfig[EmailTemplateVars], None] = None,
-) -> EmailPasswordConfig:
-
-    # NOTE: We don't need to check the instance of sign_up_feature and override
-    # as they will always be either None or the specified type.
-
-    if override is None:
-        override = InputOverrideConfig()
-
-    if sign_up_feature is None:
-        sign_up_feature = InputSignUpFeature()
-
-    def get_email_delivery_config(
-        ep_recipe: RecipeInterface,
-    ) -> EmailDeliveryConfigWithService[EmailTemplateVars]:
-        if email_delivery and email_delivery.service:
-            return EmailDeliveryConfigWithService(
-                service=email_delivery.service, override=email_delivery.override
-            )
-
-        email_service = BackwardCompatibilityService(
-            app_info=app_info,
-            recipe_interface_impl=ep_recipe,
-        )
-        if email_delivery is not None and email_delivery.override is not None:
-            override = email_delivery.override
-        else:
-            override = None
-        return EmailDeliveryConfigWithService(email_service, override=override)
-
-    return EmailPasswordConfig(
-        SignUpFeature(sign_up_feature.form_fields),
-        SignInFeature(normalise_sign_in_form_fields(sign_up_feature.form_fields)),
-        validate_and_normalise_reset_password_using_token_config(sign_up_feature),
-        OverrideConfig(functions=override.functions, apis=override.apis),
-        get_email_delivery_config=get_email_delivery_config,
-    )
-
-
-
-
-
-

Classes

-
-
-class EmailPasswordConfig -(sign_up_feature: SignUpFeature, sign_in_feature: SignInFeature, reset_password_using_token_feature: ResetPasswordUsingTokenFeature, override: OverrideConfig, get_email_delivery_config: Callable[[RecipeInterface], EmailDeliveryConfigWithService[EmailTemplateVars]]) -
-
-
-
- -Expand source code - -
class EmailPasswordConfig:
-    def __init__(
-        self,
-        sign_up_feature: SignUpFeature,
-        sign_in_feature: SignInFeature,
-        reset_password_using_token_feature: ResetPasswordUsingTokenFeature,
-        override: OverrideConfig,
-        get_email_delivery_config: Callable[
-            [RecipeInterface], EmailDeliveryConfigWithService[EmailTemplateVars]
-        ],
-    ):
-        self.sign_up_feature = sign_up_feature
-        self.sign_in_feature = sign_in_feature
-        self.reset_password_using_token_feature = reset_password_using_token_feature
-        self.override = override
-        self.get_email_delivery_config = get_email_delivery_config
-
-
-
-class InputOverrideConfig -(functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None, apis: Union[Callable[[APIInterface], APIInterface], None] = None) -
-
-
-
- -Expand source code - -
class InputOverrideConfig:
-    def __init__(
-        self,
-        functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None,
-        apis: Union[Callable[[APIInterface], APIInterface], None] = None,
-    ):
-        self.functions = functions
-        self.apis = apis
-
-
-
-class InputSignUpFeature -(form_fields: Union[List[InputFormField], None] = None) -
-
-
-
- -Expand source code - -
class InputSignUpFeature:
-    def __init__(self, form_fields: Union[List[InputFormField], None] = None):
-        if form_fields is None:
-            form_fields = []
-        self.form_fields = normalise_sign_up_form_fields(form_fields)
-
-
-
-class OverrideConfig -(functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None, apis: Union[Callable[[APIInterface], APIInterface], None] = None) -
-
-
-
- -Expand source code - -
class OverrideConfig:
-    def __init__(
-        self,
-        functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None,
-        apis: Union[Callable[[APIInterface], APIInterface], None] = None,
-    ):
-        self.functions = functions
-        self.apis = apis
-
-
-
-class ResetPasswordUsingTokenFeature -(form_fields_for_password_reset_form: List[NormalisedFormField], form_fields_for_generate_token_form: List[NormalisedFormField]) -
-
-
-
- -Expand source code - -
class ResetPasswordUsingTokenFeature:
-    def __init__(
-        self,
-        form_fields_for_password_reset_form: List[NormalisedFormField],
-        form_fields_for_generate_token_form: List[NormalisedFormField],
-    ):
-        self.form_fields_for_password_reset_form = form_fields_for_password_reset_form
-        self.form_fields_for_generate_token_form = form_fields_for_generate_token_form
-
-
-
-class SignInFeature -(form_fields: List[NormalisedFormField]) -
-
-
-
- -Expand source code - -
class SignInFeature:
-    def __init__(self, form_fields: List[NormalisedFormField]):
-        self.form_fields = form_fields
-
-
-
-class SignUpFeature -(form_fields: List[NormalisedFormField]) -
-
-
-
- -Expand source code - -
class SignUpFeature:
-    def __init__(self, form_fields: List[NormalisedFormField]):
-        self.form_fields = form_fields
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailverification/api/email_verify.html b/html/supertokens_python/recipe/emailverification/api/email_verify.html deleted file mode 100644 index e29de5f96..000000000 --- a/html/supertokens_python/recipe/emailverification/api/email_verify.html +++ /dev/null @@ -1,193 +0,0 @@ - - - - - - -supertokens_python.recipe.emailverification.api.email_verify API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.emailverification.api.email_verify

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-from typing import Any, Dict
-
-from supertokens_python.exceptions import raise_bad_input_exception
-from supertokens_python.recipe.emailverification.interfaces import (
-    APIInterface,
-    APIOptions,
-)
-from supertokens_python.utils import (
-    normalise_http_method,
-    send_200_response,
-)
-from supertokens_python.recipe.session.asyncio import get_session
-
-
-async def handle_email_verify_api(
-    api_implementation: APIInterface,
-    tenant_id: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-):
-    if normalise_http_method(api_options.request.method()) == "post":
-        if api_implementation.disable_email_verify_post:
-            return None
-        body = await api_options.request.json()
-        if body is None:
-            raise_bad_input_exception("Please pass JSON input body")
-        if "token" not in body:
-            raise_bad_input_exception("Please provide the email verification token")
-        if not isinstance(body["token"], str):
-            raise_bad_input_exception("The email verification token must be a string")
-
-        token = body["token"]
-
-        session = await get_session(
-            api_options.request,
-            session_required=False,
-            override_global_claim_validators=lambda _, __, ___: [],
-            user_context=user_context,
-        )
-
-        result = await api_implementation.email_verify_post(
-            token, session, tenant_id, api_options, user_context
-        )
-    else:
-        if api_implementation.disable_is_email_verified_get:
-            return None
-
-        session = await get_session(
-            api_options.request,
-            override_global_claim_validators=lambda _, __, ___: [],
-            user_context=user_context,
-        )
-        assert session is not None
-        result = await api_implementation.is_email_verified_get(
-            session, api_options, user_context
-        )
-
-    return send_200_response(result.to_json(), api_options.response)
-
-
-
-
-
-
-
-

Functions

-
-
-async def handle_email_verify_api(api_implementation: APIInterface, tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) -
-
-
-
- -Expand source code - -
async def handle_email_verify_api(
-    api_implementation: APIInterface,
-    tenant_id: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-):
-    if normalise_http_method(api_options.request.method()) == "post":
-        if api_implementation.disable_email_verify_post:
-            return None
-        body = await api_options.request.json()
-        if body is None:
-            raise_bad_input_exception("Please pass JSON input body")
-        if "token" not in body:
-            raise_bad_input_exception("Please provide the email verification token")
-        if not isinstance(body["token"], str):
-            raise_bad_input_exception("The email verification token must be a string")
-
-        token = body["token"]
-
-        session = await get_session(
-            api_options.request,
-            session_required=False,
-            override_global_claim_validators=lambda _, __, ___: [],
-            user_context=user_context,
-        )
-
-        result = await api_implementation.email_verify_post(
-            token, session, tenant_id, api_options, user_context
-        )
-    else:
-        if api_implementation.disable_is_email_verified_get:
-            return None
-
-        session = await get_session(
-            api_options.request,
-            override_global_claim_validators=lambda _, __, ___: [],
-            user_context=user_context,
-        )
-        assert session is not None
-        result = await api_implementation.is_email_verified_get(
-            session, api_options, user_context
-        )
-
-    return send_200_response(result.to_json(), api_options.response)
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailverification/api/generate_email_verify_token.html b/html/supertokens_python/recipe/emailverification/api/generate_email_verify_token.html deleted file mode 100644 index f4be85daa..000000000 --- a/html/supertokens_python/recipe/emailverification/api/generate_email_verify_token.html +++ /dev/null @@ -1,137 +0,0 @@ - - - - - - -supertokens_python.recipe.emailverification.api.generate_email_verify_token API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.emailverification.api.generate_email_verify_token

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-from typing import Any, Dict
-
-from supertokens_python.recipe.emailverification.interfaces import (
-    APIInterface,
-    APIOptions,
-)
-from supertokens_python.utils import send_200_response
-from supertokens_python.recipe.session.asyncio import get_session
-
-
-async def handle_generate_email_verify_token_api(
-    api_implementation: APIInterface,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-):
-    if api_implementation.disable_generate_email_verify_token_post:
-        return None
-    session = await get_session(
-        api_options.request,
-        override_global_claim_validators=lambda _, __, ___: [],
-        user_context=user_context,
-    )
-    assert session is not None
-
-    result = await api_implementation.generate_email_verify_token_post(
-        session, api_options, user_context
-    )
-    return send_200_response(result.to_json(), api_options.response)
-
-
-
-
-
-
-
-

Functions

-
-
-async def handle_generate_email_verify_token_api(api_implementation: APIInterface, api_options: APIOptions, user_context: Dict[str, Any]) -
-
-
-
- -Expand source code - -
async def handle_generate_email_verify_token_api(
-    api_implementation: APIInterface,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-):
-    if api_implementation.disable_generate_email_verify_token_post:
-        return None
-    session = await get_session(
-        api_options.request,
-        override_global_claim_validators=lambda _, __, ___: [],
-        user_context=user_context,
-    )
-    assert session is not None
-
-    result = await api_implementation.generate_email_verify_token_post(
-        session, api_options, user_context
-    )
-    return send_200_response(result.to_json(), api_options.response)
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailverification/api/index.html b/html/supertokens_python/recipe/emailverification/api/index.html deleted file mode 100644 index 31309f3a8..000000000 --- a/html/supertokens_python/recipe/emailverification/api/index.html +++ /dev/null @@ -1,92 +0,0 @@ - - - - - - -supertokens_python.recipe.emailverification.api API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.emailverification.api

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from .email_verify import handle_email_verify_api  # type: ignore
-from .generate_email_verify_token import (
-    handle_generate_email_verify_token_api,  # type: ignore
-)
-
-
-
-

Sub-modules

-
-
supertokens_python.recipe.emailverification.api.email_verify
-
-
-
-
supertokens_python.recipe.emailverification.api.generate_email_verify_token
-
-
-
-
-
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailverification/asyncio/index.html b/html/supertokens_python/recipe/emailverification/asyncio/index.html deleted file mode 100644 index 58a325305..000000000 --- a/html/supertokens_python/recipe/emailverification/asyncio/index.html +++ /dev/null @@ -1,577 +0,0 @@ - - - - - - -supertokens_python.recipe.emailverification.asyncio API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.emailverification.asyncio

-
-
-
- -Expand source code - -
#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from typing import Any, Dict, Union, Optional
-
-from supertokens_python import get_request_from_user_context
-
-from supertokens_python.recipe.emailverification.interfaces import (
-    GetEmailForUserIdOkResult,
-    EmailDoesNotExistError,
-    CreateEmailVerificationTokenEmailAlreadyVerifiedError,
-    CreateEmailVerificationLinkOkResult,
-    CreateEmailVerificationLinkEmailAlreadyVerifiedError,
-    SendEmailVerificationEmailOkResult,
-    SendEmailVerificationEmailAlreadyVerifiedError,
-    UnverifyEmailOkResult,
-    CreateEmailVerificationTokenOkResult,
-    RevokeEmailVerificationTokensOkResult,
-)
-from supertokens_python.recipe.emailverification.types import EmailTemplateVars
-from supertokens_python.recipe.emailverification.recipe import EmailVerificationRecipe
-
-from supertokens_python.recipe.emailverification.utils import get_email_verify_link
-from supertokens_python.recipe.emailverification.types import (
-    VerificationEmailTemplateVars,
-    VerificationEmailTemplateVarsUser,
-)
-
-
-async def create_email_verification_token(
-    tenant_id: str,
-    user_id: str,
-    email: Optional[str] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> Union[
-    CreateEmailVerificationTokenOkResult,
-    CreateEmailVerificationTokenEmailAlreadyVerifiedError,
-]:
-    if user_context is None:
-        user_context = {}
-    recipe = EmailVerificationRecipe.get_instance()
-    if email is None:
-        email_info = await recipe.get_email_for_user_id(user_id, user_context)
-        if isinstance(email_info, GetEmailForUserIdOkResult):
-            email = email_info.email
-        elif isinstance(email_info, EmailDoesNotExistError):
-            return CreateEmailVerificationTokenEmailAlreadyVerifiedError()
-        else:
-            raise Exception("Unknown User ID provided without email")
-
-    return await recipe.recipe_implementation.create_email_verification_token(
-        user_id, email, tenant_id, user_context
-    )
-
-
-async def verify_email_using_token(
-    tenant_id: str, token: str, user_context: Union[None, Dict[str, Any]] = None
-):
-    if user_context is None:
-        user_context = {}
-    return await EmailVerificationRecipe.get_instance().recipe_implementation.verify_email_using_token(
-        token, tenant_id, user_context
-    )
-
-
-async def is_email_verified(
-    user_id: str,
-    email: Optional[str] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-):
-    if user_context is None:
-        user_context = {}
-
-    recipe = EmailVerificationRecipe.get_instance()
-    if email is None:
-        email_info = await recipe.get_email_for_user_id(user_id, user_context)
-        if isinstance(email_info, GetEmailForUserIdOkResult):
-            email = email_info.email
-        elif isinstance(email_info, EmailDoesNotExistError):
-            return True
-        else:
-            raise Exception("Unknown User ID provided without email")
-
-    return await recipe.recipe_implementation.is_email_verified(
-        user_id, email, user_context
-    )
-
-
-async def revoke_email_verification_tokens(
-    tenant_id: str,
-    user_id: str,
-    email: Optional[str] = None,
-    user_context: Optional[Dict[str, Any]] = None,
-) -> RevokeEmailVerificationTokensOkResult:
-    if user_context is None:
-        user_context = {}
-
-    recipe = EmailVerificationRecipe.get_instance()
-    if email is None:
-        email_info = await recipe.get_email_for_user_id(user_id, user_context)
-        if isinstance(email_info, GetEmailForUserIdOkResult):
-            email = email_info.email
-        elif isinstance(email_info, EmailDoesNotExistError):
-            return RevokeEmailVerificationTokensOkResult()
-        else:
-            raise Exception("Unknown User ID provided without email")
-
-    return await EmailVerificationRecipe.get_instance().recipe_implementation.revoke_email_verification_tokens(
-        user_id, email, tenant_id, user_context
-    )
-
-
-async def unverify_email(
-    user_id: str,
-    email: Optional[str] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-):
-    if user_context is None:
-        user_context = {}
-
-    recipe = EmailVerificationRecipe.get_instance()
-    if email is None:
-        email_info = await recipe.get_email_for_user_id(user_id, user_context)
-        if isinstance(email_info, GetEmailForUserIdOkResult):
-            email = email_info.email
-        elif isinstance(email_info, EmailDoesNotExistError):
-            # Here we are returning OK since that's how it used to work, but a later call
-            # to is_verified will still return true
-            return UnverifyEmailOkResult
-        else:
-            raise Exception("Unknown User ID provided without email")
-
-    return await EmailVerificationRecipe.get_instance().recipe_implementation.unverify_email(
-        user_id, email, user_context
-    )
-
-
-async def send_email(
-    input_: EmailTemplateVars,
-    user_context: Union[None, Dict[str, Any]] = None,
-):
-    if user_context is None:
-        user_context = {}
-    return await EmailVerificationRecipe.get_instance().email_delivery.ingredient_interface_impl.send_email(
-        input_, user_context
-    )
-
-
-async def create_email_verification_link(
-    tenant_id: str,
-    user_id: str,
-    email: Optional[str],
-    user_context: Optional[Dict[str, Any]] = None,
-) -> Union[
-    CreateEmailVerificationLinkOkResult,
-    CreateEmailVerificationLinkEmailAlreadyVerifiedError,
-]:
-    if user_context is None:
-        user_context = {}
-
-    recipe_instance = EmailVerificationRecipe.get_instance()
-    app_info = recipe_instance.get_app_info()
-
-    email_verification_token = await create_email_verification_token(
-        tenant_id, user_id, email, user_context
-    )
-    if isinstance(
-        email_verification_token, CreateEmailVerificationTokenEmailAlreadyVerifiedError
-    ):
-        return CreateEmailVerificationLinkEmailAlreadyVerifiedError()
-
-    request = get_request_from_user_context(user_context)
-    return CreateEmailVerificationLinkOkResult(
-        link=get_email_verify_link(
-            app_info,
-            email_verification_token.token,
-            tenant_id,
-            request,
-            user_context,
-        )
-    )
-
-
-async def send_email_verification_email(
-    tenant_id: str,
-    user_id: str,
-    email: Optional[str],
-    user_context: Optional[Dict[str, Any]] = None,
-) -> Union[
-    SendEmailVerificationEmailOkResult,
-    SendEmailVerificationEmailAlreadyVerifiedError,
-]:
-    if user_context is None:
-        user_context = {}
-
-    if email is None:
-        recipe_instance = EmailVerificationRecipe.get_instance()
-
-        email_info = await recipe_instance.get_email_for_user_id(user_id, user_context)
-        if isinstance(email_info, GetEmailForUserIdOkResult):
-            email = email_info.email
-        elif isinstance(email_info, EmailDoesNotExistError):
-            return SendEmailVerificationEmailAlreadyVerifiedError()
-        else:
-            raise Exception("Unknown User ID provided without email")
-
-    email_verification_link = await create_email_verification_link(
-        tenant_id, user_id, email, user_context
-    )
-
-    if isinstance(
-        email_verification_link, CreateEmailVerificationLinkEmailAlreadyVerifiedError
-    ):
-        return SendEmailVerificationEmailAlreadyVerifiedError()
-
-    await send_email(
-        VerificationEmailTemplateVars(
-            user=VerificationEmailTemplateVarsUser(user_id, email),
-            email_verify_link=email_verification_link.link,
-            tenant_id=tenant_id,
-        ),
-        user_context,
-    )
-
-    return SendEmailVerificationEmailOkResult()
-
-
-
-
-
-
-
-

Functions

-
- -
-
-
- -Expand source code - -
async def create_email_verification_link(
-    tenant_id: str,
-    user_id: str,
-    email: Optional[str],
-    user_context: Optional[Dict[str, Any]] = None,
-) -> Union[
-    CreateEmailVerificationLinkOkResult,
-    CreateEmailVerificationLinkEmailAlreadyVerifiedError,
-]:
-    if user_context is None:
-        user_context = {}
-
-    recipe_instance = EmailVerificationRecipe.get_instance()
-    app_info = recipe_instance.get_app_info()
-
-    email_verification_token = await create_email_verification_token(
-        tenant_id, user_id, email, user_context
-    )
-    if isinstance(
-        email_verification_token, CreateEmailVerificationTokenEmailAlreadyVerifiedError
-    ):
-        return CreateEmailVerificationLinkEmailAlreadyVerifiedError()
-
-    request = get_request_from_user_context(user_context)
-    return CreateEmailVerificationLinkOkResult(
-        link=get_email_verify_link(
-            app_info,
-            email_verification_token.token,
-            tenant_id,
-            request,
-            user_context,
-        )
-    )
-
-
-
-async def create_email_verification_token(tenant_id: str, user_id: str, email: Optional[str] = None, user_context: Optional[Dict[str, Any]] = None) ‑> Union[CreateEmailVerificationTokenOkResultCreateEmailVerificationTokenEmailAlreadyVerifiedError] -
-
-
-
- -Expand source code - -
async def create_email_verification_token(
-    tenant_id: str,
-    user_id: str,
-    email: Optional[str] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> Union[
-    CreateEmailVerificationTokenOkResult,
-    CreateEmailVerificationTokenEmailAlreadyVerifiedError,
-]:
-    if user_context is None:
-        user_context = {}
-    recipe = EmailVerificationRecipe.get_instance()
-    if email is None:
-        email_info = await recipe.get_email_for_user_id(user_id, user_context)
-        if isinstance(email_info, GetEmailForUserIdOkResult):
-            email = email_info.email
-        elif isinstance(email_info, EmailDoesNotExistError):
-            return CreateEmailVerificationTokenEmailAlreadyVerifiedError()
-        else:
-            raise Exception("Unknown User ID provided without email")
-
-    return await recipe.recipe_implementation.create_email_verification_token(
-        user_id, email, tenant_id, user_context
-    )
-
-
-
-async def is_email_verified(user_id: str, email: Optional[str] = None, user_context: Optional[Dict[str, Any]] = None) -
-
-
-
- -Expand source code - -
async def is_email_verified(
-    user_id: str,
-    email: Optional[str] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-):
-    if user_context is None:
-        user_context = {}
-
-    recipe = EmailVerificationRecipe.get_instance()
-    if email is None:
-        email_info = await recipe.get_email_for_user_id(user_id, user_context)
-        if isinstance(email_info, GetEmailForUserIdOkResult):
-            email = email_info.email
-        elif isinstance(email_info, EmailDoesNotExistError):
-            return True
-        else:
-            raise Exception("Unknown User ID provided without email")
-
-    return await recipe.recipe_implementation.is_email_verified(
-        user_id, email, user_context
-    )
-
-
-
-async def revoke_email_verification_tokens(tenant_id: str, user_id: str, email: Optional[str] = None, user_context: Optional[Dict[str, Any]] = None) ‑> RevokeEmailVerificationTokensOkResult -
-
-
-
- -Expand source code - -
async def revoke_email_verification_tokens(
-    tenant_id: str,
-    user_id: str,
-    email: Optional[str] = None,
-    user_context: Optional[Dict[str, Any]] = None,
-) -> RevokeEmailVerificationTokensOkResult:
-    if user_context is None:
-        user_context = {}
-
-    recipe = EmailVerificationRecipe.get_instance()
-    if email is None:
-        email_info = await recipe.get_email_for_user_id(user_id, user_context)
-        if isinstance(email_info, GetEmailForUserIdOkResult):
-            email = email_info.email
-        elif isinstance(email_info, EmailDoesNotExistError):
-            return RevokeEmailVerificationTokensOkResult()
-        else:
-            raise Exception("Unknown User ID provided without email")
-
-    return await EmailVerificationRecipe.get_instance().recipe_implementation.revoke_email_verification_tokens(
-        user_id, email, tenant_id, user_context
-    )
-
-
-
-async def send_email(input_: VerificationEmailTemplateVars, user_context: Optional[Dict[str, Any]] = None) -
-
-
-
- -Expand source code - -
async def send_email(
-    input_: EmailTemplateVars,
-    user_context: Union[None, Dict[str, Any]] = None,
-):
-    if user_context is None:
-        user_context = {}
-    return await EmailVerificationRecipe.get_instance().email_delivery.ingredient_interface_impl.send_email(
-        input_, user_context
-    )
-
-
-
-async def send_email_verification_email(tenant_id: str, user_id: str, email: Optional[str], user_context: Optional[Dict[str, Any]] = None) ‑> Union[SendEmailVerificationEmailOkResultSendEmailVerificationEmailAlreadyVerifiedError] -
-
-
-
- -Expand source code - -
async def send_email_verification_email(
-    tenant_id: str,
-    user_id: str,
-    email: Optional[str],
-    user_context: Optional[Dict[str, Any]] = None,
-) -> Union[
-    SendEmailVerificationEmailOkResult,
-    SendEmailVerificationEmailAlreadyVerifiedError,
-]:
-    if user_context is None:
-        user_context = {}
-
-    if email is None:
-        recipe_instance = EmailVerificationRecipe.get_instance()
-
-        email_info = await recipe_instance.get_email_for_user_id(user_id, user_context)
-        if isinstance(email_info, GetEmailForUserIdOkResult):
-            email = email_info.email
-        elif isinstance(email_info, EmailDoesNotExistError):
-            return SendEmailVerificationEmailAlreadyVerifiedError()
-        else:
-            raise Exception("Unknown User ID provided without email")
-
-    email_verification_link = await create_email_verification_link(
-        tenant_id, user_id, email, user_context
-    )
-
-    if isinstance(
-        email_verification_link, CreateEmailVerificationLinkEmailAlreadyVerifiedError
-    ):
-        return SendEmailVerificationEmailAlreadyVerifiedError()
-
-    await send_email(
-        VerificationEmailTemplateVars(
-            user=VerificationEmailTemplateVarsUser(user_id, email),
-            email_verify_link=email_verification_link.link,
-            tenant_id=tenant_id,
-        ),
-        user_context,
-    )
-
-    return SendEmailVerificationEmailOkResult()
-
-
-
-async def unverify_email(user_id: str, email: Optional[str] = None, user_context: Optional[Dict[str, Any]] = None) -
-
-
-
- -Expand source code - -
async def unverify_email(
-    user_id: str,
-    email: Optional[str] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-):
-    if user_context is None:
-        user_context = {}
-
-    recipe = EmailVerificationRecipe.get_instance()
-    if email is None:
-        email_info = await recipe.get_email_for_user_id(user_id, user_context)
-        if isinstance(email_info, GetEmailForUserIdOkResult):
-            email = email_info.email
-        elif isinstance(email_info, EmailDoesNotExistError):
-            # Here we are returning OK since that's how it used to work, but a later call
-            # to is_verified will still return true
-            return UnverifyEmailOkResult
-        else:
-            raise Exception("Unknown User ID provided without email")
-
-    return await EmailVerificationRecipe.get_instance().recipe_implementation.unverify_email(
-        user_id, email, user_context
-    )
-
-
-
-async def verify_email_using_token(tenant_id: str, token: str, user_context: Optional[Dict[str, Any]] = None) -
-
-
-
- -Expand source code - -
async def verify_email_using_token(
-    tenant_id: str, token: str, user_context: Union[None, Dict[str, Any]] = None
-):
-    if user_context is None:
-        user_context = {}
-    return await EmailVerificationRecipe.get_instance().recipe_implementation.verify_email_using_token(
-        token, tenant_id, user_context
-    )
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailverification/constants.html b/html/supertokens_python/recipe/emailverification/constants.html deleted file mode 100644 index 78a66a42c..000000000 --- a/html/supertokens_python/recipe/emailverification/constants.html +++ /dev/null @@ -1,73 +0,0 @@ - - - - - - -supertokens_python.recipe.emailverification.constants API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.emailverification.constants

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-USER_EMAIL_VERIFY_TOKEN = "/user/email/verify/token"
-USER_EMAIL_VERIFY = "/user/email/verify"
-
-
-
-
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailverification/emaildelivery/index.html b/html/supertokens_python/recipe/emailverification/emaildelivery/index.html deleted file mode 100644 index af56a4935..000000000 --- a/html/supertokens_python/recipe/emailverification/emaildelivery/index.html +++ /dev/null @@ -1,83 +0,0 @@ - - - - - - -supertokens_python.recipe.emailverification.emaildelivery API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.emailverification.emaildelivery

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-
-
-

Sub-modules

-
-
supertokens_python.recipe.emailverification.emaildelivery.services
-
-
-
-
-
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailverification/emaildelivery/services/backward_compatibility/index.html b/html/supertokens_python/recipe/emailverification/emaildelivery/services/backward_compatibility/index.html deleted file mode 100644 index 7c026ff1c..000000000 --- a/html/supertokens_python/recipe/emailverification/emaildelivery/services/backward_compatibility/index.html +++ /dev/null @@ -1,245 +0,0 @@ - - - - - - -supertokens_python.recipe.emailverification.emaildelivery.services.backward_compatibility API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.emailverification.emaildelivery.services.backward_compatibility

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from os import environ
-from typing import Any, Dict
-
-from httpx import AsyncClient
-from supertokens_python.ingredients.emaildelivery.types import EmailDeliveryInterface
-from supertokens_python.logger import log_debug_message
-from supertokens_python.recipe.emailverification.types import (
-    User,
-    VerificationEmailTemplateVars,
-)
-from supertokens_python.supertokens import AppInfo
-from supertokens_python.utils import handle_httpx_client_exceptions
-
-
-async def create_and_send_email_using_supertokens_service(
-    app_info: AppInfo, user: User, email_verification_url: str
-) -> None:
-    if ("SUPERTOKENS_ENV" in environ) and (environ["SUPERTOKENS_ENV"] == "testing"):
-        return
-
-    data = {
-        "email": user.email,
-        "appName": app_info.app_name,
-        "emailVerifyURL": email_verification_url,
-    }
-    try:
-        async with AsyncClient(timeout=30.0) as client:
-            resp = await client.post("https://api.supertokens.io/0/st/auth/email/verify", json=data, headers={"api-version": "0"})  # type: ignore
-            resp.raise_for_status()
-            log_debug_message("Email verification email sent to %s", user.email)
-    except Exception as e:
-        log_debug_message("Error sending verification email")
-        handle_httpx_client_exceptions(e, data)
-
-
-class BackwardCompatibilityService(
-    EmailDeliveryInterface[VerificationEmailTemplateVars]
-):
-    def __init__(
-        self,
-        app_info: AppInfo,
-    ) -> None:
-        self.app_info = app_info
-
-    async def send_email(
-        self,
-        template_vars: VerificationEmailTemplateVars,
-        user_context: Dict[str, Any],
-    ) -> None:
-        try:
-            email_user = User(template_vars.user.id, template_vars.user.email)
-            await create_and_send_email_using_supertokens_service(
-                self.app_info, email_user, template_vars.email_verify_link
-            )
-        except Exception as _:
-            pass
-
-
-
-
-
-
-
-

Functions

-
-
-async def create_and_send_email_using_supertokens_service(app_info: AppInfo, user: User, email_verification_url: str) ‑> None -
-
-
-
- -Expand source code - -
async def create_and_send_email_using_supertokens_service(
-    app_info: AppInfo, user: User, email_verification_url: str
-) -> None:
-    if ("SUPERTOKENS_ENV" in environ) and (environ["SUPERTOKENS_ENV"] == "testing"):
-        return
-
-    data = {
-        "email": user.email,
-        "appName": app_info.app_name,
-        "emailVerifyURL": email_verification_url,
-    }
-    try:
-        async with AsyncClient(timeout=30.0) as client:
-            resp = await client.post("https://api.supertokens.io/0/st/auth/email/verify", json=data, headers={"api-version": "0"})  # type: ignore
-            resp.raise_for_status()
-            log_debug_message("Email verification email sent to %s", user.email)
-    except Exception as e:
-        log_debug_message("Error sending verification email")
-        handle_httpx_client_exceptions(e, data)
-
-
-
-
-
-

Classes

-
-
-class BackwardCompatibilityService -(app_info: AppInfo) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class BackwardCompatibilityService(
-    EmailDeliveryInterface[VerificationEmailTemplateVars]
-):
-    def __init__(
-        self,
-        app_info: AppInfo,
-    ) -> None:
-        self.app_info = app_info
-
-    async def send_email(
-        self,
-        template_vars: VerificationEmailTemplateVars,
-        user_context: Dict[str, Any],
-    ) -> None:
-        try:
-            email_user = User(template_vars.user.id, template_vars.user.email)
-            await create_and_send_email_using_supertokens_service(
-                self.app_info, email_user, template_vars.email_verify_link
-            )
-        except Exception as _:
-            pass
-
-

Ancestors

- -

Methods

-
-
-async def send_email(self, template_vars: VerificationEmailTemplateVars, user_context: Dict[str, Any]) ‑> None -
-
-
-
- -Expand source code - -
async def send_email(
-    self,
-    template_vars: VerificationEmailTemplateVars,
-    user_context: Dict[str, Any],
-) -> None:
-    try:
-        email_user = User(template_vars.user.id, template_vars.user.email)
-        await create_and_send_email_using_supertokens_service(
-            self.app_info, email_user, template_vars.email_verify_link
-        )
-    except Exception as _:
-        pass
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailverification/emaildelivery/services/index.html b/html/supertokens_python/recipe/emailverification/emaildelivery/services/index.html deleted file mode 100644 index ce7cad4f7..000000000 --- a/html/supertokens_python/recipe/emailverification/emaildelivery/services/index.html +++ /dev/null @@ -1,92 +0,0 @@ - - - - - - -supertokens_python.recipe.emailverification.emaildelivery.services API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.emailverification.emaildelivery.services

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from . import smtp
-
-SMTPService = smtp.SMTPService
-
-
-
-

Sub-modules

-
-
supertokens_python.recipe.emailverification.emaildelivery.services.backward_compatibility
-
-
-
-
supertokens_python.recipe.emailverification.emaildelivery.services.smtp
-
-
-
-
-
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailverification/emaildelivery/services/smtp/email_verify.html b/html/supertokens_python/recipe/emailverification/emaildelivery/services/smtp/email_verify.html deleted file mode 100644 index 662395f46..000000000 --- a/html/supertokens_python/recipe/emailverification/emaildelivery/services/smtp/email_verify.html +++ /dev/null @@ -1,146 +0,0 @@ - - - - - - -supertokens_python.recipe.emailverification.emaildelivery.services.smtp.email_verify API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.emailverification.emaildelivery.services.smtp.email_verify

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from string import Template
-
-from supertokens_python.ingredients.emaildelivery.types import EmailContent
-from supertokens_python.recipe.emailverification.types import (
-    VerificationEmailTemplateVars,
-)
-from supertokens_python.supertokens import Supertokens
-
-from .email_verify_email import html_template
-
-
-def get_email_verify_email_content(
-    email_input: VerificationEmailTemplateVars,
-) -> EmailContent:
-    supertokens = Supertokens.get_instance()
-    app_name = supertokens.app_info.app_name
-    body = get_email_verify_email_html(
-        app_name, email_input.user.email, email_input.email_verify_link
-    )
-    return EmailContent(
-        body, "Email verification instructions", email_input.user.email, is_html=True
-    )
-
-
-def get_email_verify_email_html(app_name: str, email: str, verification_link: str):
-    return Template(html_template).substitute(
-        appname=app_name, verificationLink=verification_link, toEmail=email
-    )
-
-
-
-
-
-
-
-

Functions

-
-
-def get_email_verify_email_content(email_input: VerificationEmailTemplateVars) ‑> EmailContent -
-
-
-
- -Expand source code - -
def get_email_verify_email_content(
-    email_input: VerificationEmailTemplateVars,
-) -> EmailContent:
-    supertokens = Supertokens.get_instance()
-    app_name = supertokens.app_info.app_name
-    body = get_email_verify_email_html(
-        app_name, email_input.user.email, email_input.email_verify_link
-    )
-    return EmailContent(
-        body, "Email verification instructions", email_input.user.email, is_html=True
-    )
-
-
-
-def get_email_verify_email_html(app_name: str, email: str, verification_link: str) -
-
-
-
- -Expand source code - -
def get_email_verify_email_html(app_name: str, email: str, verification_link: str):
-    return Template(html_template).substitute(
-        appname=app_name, verificationLink=verification_link, toEmail=email
-    )
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailverification/emaildelivery/services/smtp/email_verify_email.html b/html/supertokens_python/recipe/emailverification/emaildelivery/services/smtp/email_verify_email.html deleted file mode 100644 index 7cfd874d9..000000000 --- a/html/supertokens_python/recipe/emailverification/emaildelivery/services/smtp/email_verify_email.html +++ /dev/null @@ -1,977 +0,0 @@ - - - - - - -supertokens_python.recipe.emailverification.emaildelivery.services.smtp.email_verify_email API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.emailverification.emaildelivery.services.smtp.email_verify_email

-
-
-
- -Expand source code - -
html_template = """
-<!doctype html>
-<html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml"
-        xmlns:o="urn:schemas-microsoft-com:office:office">
-
-<head>
-        <meta charset="UTF-8">
-        <meta http-equiv="X-UA-Compatible" content="IE=edge">
-        <meta name="viewport" content="width=device-width, initial-scale=1">
-        <title>*|MC:SUBJECT|*</title>
-
-        <style type="text/css">
-                body {
-                        max-width: 100vw;
-                        overflow: hidden;
-                }
-                p {
-                        margin: 10px 0;
-                        padding: 0;
-                }
-
-                table {
-                        border-collapse: collapse;
-                }
-
-                h1,
-                h2,
-                h3,
-                h4,
-                h5,
-                h6 {
-                        display: block;
-                        margin: 0;
-                        padding: 0;
-                }
-
-                img,
-                a img {
-                        border: 0;
-                        height: auto;
-                        outline: none;
-                        text-decoration: none;
-                }
-
-                body,
-                #bodyTable,
-                #bodyCell {
-                        height: 100%;
-                        margin: 0;
-                        padding: 0;
-                        width: 100%;
-                }
-
-                .mcnPreviewText {
-                        display: none !important;
-                }
-
-                #outlook a {
-                        padding: 0;
-                }
-
-                img {
-                        -ms-interpolation-mode: bicubic;
-                }
-
-                table {
-                        mso-table-lspace: 0pt;
-                        mso-table-rspace: 0pt;
-                }
-
-                .ReadMsgBody {
-                        width: 100%;
-                }
-
-                .ExternalClass {
-                        width: 100%;
-                }
-
-                p,
-                a,
-                li,
-                td,
-                blockquote {
-                        mso-line-height-rule: exactly;
-                }
-
-                a[href^=tel],
-                a[href^=sms] {
-                        color: inherit;
-                        cursor: default;
-                        text-decoration: none;
-                }
-
-                p,
-                a,
-                li,
-                td,
-                body,
-                table,
-                blockquote {
-                        -ms-text-size-adjust: 100%;
-                        -webkit-text-size-adjust: 100%;
-                }
-
-                .ExternalClass,
-                .ExternalClass p,
-                .ExternalClass td,
-                .ExternalClass div,
-                .ExternalClass span,
-                .ExternalClass font {
-                        line-height: 100%;
-                }
-
-                a[x-apple-data-detectors] {
-                        color: inherit !important;
-                        text-decoration: none !important;
-                        font-size: inherit !important;
-                        font-family: inherit !important;
-                        font-weight: inherit !important;
-                        line-height: inherit !important;
-                }
-
-                .templateContainer {
-                        max-width: 600px !important;
-                }
-
-                a.mcnButton {
-                        display: block;
-                }
-
-                .mcnImage,
-                .mcnRetinaImage {
-                        vertical-align: bottom;
-                }
-
-                .mcnTextContent {
-                        word-break: break-word;
-                }
-
-                .mcnTextContent img {
-                        height: auto !important;
-                }
-
-                .mcnDividerBlock {
-                        table-layout: fixed !important;
-                }
-
-                /*
-        @tab Page
-        @section Heading 1
-        @style heading 1
-        */
-                h1 {
-                        /*@editable*/
-                        color: #222222;
-                        /*@editable*/
-                        font-family: 'Open Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
-                        /*@editable*/
-                        font-size: 40px;
-                        /*@editable*/
-                        font-style: normal;
-                        /*@editable*/
-                        font-weight: bold;
-                        /*@editable*/
-                        line-height: 150%;
-                        /*@editable*/
-                        letter-spacing: normal;
-                        /*@editable*/
-                        text-align: center;
-                }
-
-                /*
-        @tab Page
-        @section Heading 2
-        @style heading 2
-        */
-                h2 {
-                        /*@editable*/
-                        color: #222222;
-                        /*@editable*/
-                        font-family: Helvetica;
-                        /*@editable*/
-                        font-size: 34px;
-                        /*@editable*/
-                        font-style: normal;
-                        /*@editable*/
-                        font-weight: bold;
-                        /*@editable*/
-                        line-height: 150%;
-                        /*@editable*/
-                        letter-spacing: normal;
-                        /*@editable*/
-                        text-align: left;
-                }
-
-                /*
-        @tab Page
-        @section Heading 3
-        @style heading 3
-        */
-                h3 {
-                        /*@editable*/
-                        color: #444444;
-                        /*@editable*/
-                        font-family: Helvetica;
-                        /*@editable*/
-                        font-size: 22px;
-                        /*@editable*/
-                        font-style: normal;
-                        /*@editable*/
-                        font-weight: bold;
-                        /*@editable*/
-                        line-height: 150%;
-                        /*@editable*/
-                        letter-spacing: normal;
-                        /*@editable*/
-                        text-align: left;
-                }
-
-                /*
-        @tab Page
-        @section Heading 4
-        @style heading 4
-        */
-                h4 {
-                        /*@editable*/
-                        color: #949494;
-                        /*@editable*/
-                        font-family: Georgia;
-                        /*@editable*/
-                        font-size: 20px;
-                        /*@editable*/
-                        font-style: italic;
-                        /*@editable*/
-                        font-weight: normal;
-                        /*@editable*/
-                        line-height: 125%;
-                        /*@editable*/
-                        letter-spacing: normal;
-                        /*@editable*/
-                        text-align: left;
-                }
-
-                /*
-        @tab Header
-        @section Header Container Style
-        */
-                #templateHeader {
-                        /*@editable*/
-                        background-color: #f4f4f4;
-                        /*@editable*/
-                        background-image: none;
-                        /*@editable*/
-                        background-repeat: no-repeat;
-                        /*@editable*/
-                        background-position: center;
-                        /*@editable*/
-                        background-size: cover;
-                        /*@editable*/
-                        border-top: 0;
-                        /*@editable*/
-                        border-bottom: 0;
-                        /*@editable*/
-                        padding-top: 0px;
-                        /*@editable*/
-                        padding-bottom: 0px;
-                }
-
-                /*
-        @tab Header
-        @section Header Interior Style
-        */
-                .headerContainer {
-                        /*@editable*/
-                        background-color: #transparent;
-                        /*@editable*/
-                        background-image: none;
-                        /*@editable*/
-                        background-repeat: no-repeat;
-                        /*@editable*/
-                        background-position: center;
-                        /*@editable*/
-                        background-size: cover;
-                        /*@editable*/
-                        border-top: 0;
-                        /*@editable*/
-                        border-bottom: 0;
-                        /*@editable*/
-                        padding-top: 0;
-                        /*@editable*/
-                        padding-bottom: 0;
-                }
-
-                /*
-        @tab Header
-        @section Header Text
-        */
-                .headerContainer .mcnTextContent,
-                .headerContainer .mcnTextContent p {
-                        /*@editable*/
-                        color: #757575;
-                        /*@editable*/
-                        font-family: Helvetica;
-                        /*@editable*/
-                        font-size: 16px;
-                        /*@editable*/
-                        line-height: 150%;
-                        /*@editable*/
-                        text-align: left;
-                }
-
-                /*
-        @tab Header
-        @section Header Link
-        */
-                .headerContainer .mcnTextContent a,
-                .headerContainer .mcnTextContent p a {
-                        /*@editable*/
-                        color: #007C89;
-                        /*@editable*/
-                        font-weight: normal;
-                        /*@editable*/
-                        text-decoration: underline;
-                }
-
-                /*
-        @tab Body
-        @section Body Container Style
-        */
-                #templateBody {
-                        /*@editable*/
-                        background-color: #f4f4f4;
-                        /*@editable*/
-                        background-image: none;
-                        /*@editable*/
-                        background-repeat: no-repeat;
-                        /*@editable*/
-                        background-position: center;
-                        /*@editable*/
-                        background-size: cover;
-                        /*@editable*/
-                        border-top: 0;
-                        /*@editable*/
-                        border-bottom: 0;
-                        /*@editable*/
-                        padding-top: 0px;
-                        /*@editable*/
-                        padding-bottom: 20px;
-                }
-
-                /*
-        @tab Body
-        @section Body Interior Style
-        */
-                .bodyContainer {
-                        /*@editable*/
-                        background-color: #f4f4f4;
-                        /*@editable*/
-                        background-image: none;
-                        /*@editable*/
-                        background-repeat: no-repeat;
-                        /*@editable*/
-                        background-position: center;
-                        /*@editable*/
-                        background-size: cover;
-                        /*@editable*/
-                        border-top: 2px none #ff9933;
-                        /*@editable*/
-                        border-bottom: 2px none #ff9933;
-                        /*@editable*/
-                        padding-top: 10px;
-                        /*@editable*/
-                        padding-bottom: 10px;
-                }
-
-                /*
-        @tab Body
-        @section Body Text
-        */
-                .bodyContainer .mcnTextContent,
-                .bodyContainer .mcnTextContent p {
-                        /*@editable*/
-                        color: #757575;
-                        /*@editable*/
-                        font-family: Helvetica;
-                        /*@editable*/
-                        font-size: 16px;
-                        /*@editable*/
-                        line-height: 150%;
-                        /*@editable*/
-                        text-align: left;
-                }
-
-                /*
-        @tab Body
-        @section Body Link
-        */
-                .bodyContainer .mcnTextContent a,
-                .bodyContainer .mcnTextContent p a {
-                        /*@editable*/
-                        color: #222222;
-                        /*@editable*/
-                        font-weight: normal;
-                        /*@editable*/
-                        text-decoration: underline;
-                }
-
-                /*
-        @tab Footer
-        @section Footer Style
-        */
-                #templateFooter {
-                        /*@editable*/
-                        background-color: #f4f4f4;
-                        /*@editable*/
-                        background-image: none;
-                        /*@editable*/
-                        background-repeat: no-repeat;
-                        /*@editable*/
-                        background-position: center;
-                        /*@editable*/
-                        background-size: cover;
-                        /*@editable*/
-                        border-top: 0;
-                        /*@editable*/
-                        border-bottom: 0;
-                        /*@editable*/
-                        padding-top: 0px;
-                        /*@editable*/
-                        padding-bottom: 20px;
-                }
-
-                /*
-        @tab Footer
-        @section Footer Interior Style
-        */
-                .footerContainer {
-                        /*@editable*/
-                        background-color: #transparent;
-                        /*@editable*/
-                        background-image: none;
-                        /*@editable*/
-                        background-repeat: no-repeat;
-                        /*@editable*/
-                        background-position: center;
-                        /*@editable*/
-                        background-size: cover;
-                        /*@editable*/
-                        border-top: 0;
-                        /*@editable*/
-                        border-bottom: 0;
-                        /*@editable*/
-                        padding-top: 0;
-                        /*@editable*/
-                        padding-bottom: 0;
-                }
-
-                /*
-        @tab Footer
-        @section Footer Text
-        */
-                .footerContainer .mcnTextContent,
-                .footerContainer .mcnTextContent p {
-                        /*@editable*/
-                        color: #FFFFFF;
-                        /*@editable*/
-                        font-family: 'Helvetica Neue', Helvetica, Arial, Verdana, sans-serif;
-                        /*@editable*/
-                        font-size: 12px;
-                        /*@editable*/
-                        line-height: 150%;
-                        /*@editable*/
-                        text-align: center;
-                }
-
-                /*
-        @tab Footer
-        @section Footer Link
-        */
-                .footerContainer .mcnTextContent a,
-                .footerContainer .mcnTextContent p a {
-                        /*@editable*/
-                        color: #FFFFFF;
-                        /*@editable*/
-                        font-weight: normal;
-                        /*@editable*/
-                        text-decoration: underline;
-                }
-
-                @media only screen and (max-width: 480px) {
-
-                        body,
-                        table,
-                        td,
-                        p,
-                        a,
-                        li,
-                        blockquote {
-                                -webkit-text-size-adjust: none !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-                        body {
-                                width: 100% !important;
-                                min-width: 100% !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-                        .mcnRetinaImage {
-                                max-width: 100% !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-                        .mcnImage {
-                                width: 100% !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-
-                        .mcnCartContainer,
-                        .mcnCaptionTopContent,
-                        .mcnRecContentContainer,
-                        .mcnCaptionBottomContent,
-                        .mcnTextContentContainer,
-                        .mcnBoxedTextContentContainer,
-                        .mcnImageGroupContentContainer,
-                        .mcnCaptionLeftTextContentContainer,
-                        .mcnCaptionRightTextContentContainer,
-                        .mcnCaptionLeftImageContentContainer,
-                        .mcnCaptionRightImageContentContainer,
-                        .mcnImageCardLeftTextContentContainer,
-                        .mcnImageCardRightTextContentContainer,
-                        .mcnImageCardLeftImageContentContainer,
-                        .mcnImageCardRightImageContentContainer {
-                                max-width: 100% !important;
-                                width: 100% !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-                        .mcnBoxedTextContentContainer {
-                                min-width: 100% !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-                        .mcnImageGroupContent {
-                                padding: 9px !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-
-                        .mcnCaptionLeftContentOuter .mcnTextContent,
-                        .mcnCaptionRightContentOuter .mcnTextContent {
-                                padding-top: 9px !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-
-                        .mcnImageCardTopImageContent,
-                        .mcnCaptionBottomContent:last-child .mcnCaptionBottomImageContent,
-                        .mcnCaptionBlockInner .mcnCaptionTopContent:last-child .mcnTextContent {
-                                padding-top: 18px !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-                        .mcnImageCardBottomImageContent {
-                                padding-bottom: 9px !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-                        .mcnImageGroupBlockInner {
-                                padding-top: 0 !important;
-                                padding-bottom: 0 !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-                        .mcnImageGroupBlockOuter {
-                                padding-top: 9px !important;
-                                padding-bottom: 9px !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-
-                        .mcnTextContent,
-                        .mcnBoxedTextContentColumn {
-                                padding-right: 18px !important;
-                                padding-left: 18px !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-
-                        .mcnImageCardLeftImageContent,
-                        .mcnImageCardRightImageContent {
-                                padding-right: 18px !important;
-                                padding-bottom: 0 !important;
-                                padding-left: 18px !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-                        .mcpreview-image-uploader {
-                                display: none !important;
-                                width: 100% !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-
-                        /*
-        @tab Mobile Styles
-        @section Heading 1
-        @tip Make the first-level headings larger in size for better readability on small screens.
-        */
-                        h1 {
-                                /*@editable*/
-                                font-size: 30px !important;
-                                /*@editable*/
-                                line-height: 125% !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-
-                        /*
-        @tab Mobile Styles
-        @section Heading 2
-        @tip Make the second-level headings larger in size for better readability on small screens.
-        */
-                        h2 {
-                                /*@editable*/
-                                font-size: 26px !important;
-                                /*@editable*/
-                                line-height: 125% !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-
-                        /*
-        @tab Mobile Styles
-        @section Heading 3
-        @tip Make the third-level headings larger in size for better readability on small screens.
-        */
-                        h3 {
-                                /*@editable*/
-                                font-size: 20px !important;
-                                /*@editable*/
-                                line-height: 150% !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-
-                        /*
-        @tab Mobile Styles
-        @section Heading 4
-        @tip Make the fourth-level headings larger in size for better readability on small screens.
-        */
-                        h4 {
-                                /*@editable*/
-                                font-size: 18px !important;
-                                /*@editable*/
-                                line-height: 150% !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-
-                        /*
-        @tab Mobile Styles
-        @section Boxed Text
-        @tip Make the boxed text larger in size for better readability on small screens. We recommend a font size of at least 16px.
-        */
-                        .mcnBoxedTextContentContainer .mcnTextContent,
-                        .mcnBoxedTextContentContainer .mcnTextContent p {
-                                /*@editable*/
-                                font-size: 14px !important;
-                                /*@editable*/
-                                line-height: 150% !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-
-                        /*
-        @tab Mobile Styles
-        @section Header Text
-        @tip Make the header text larger in size for better readability on small screens.
-        */
-                        .headerContainer .mcnTextContent,
-                        .headerContainer .mcnTextContent p {
-                                /*@editable*/
-                                font-size: 16px !important;
-                                /*@editable*/
-                                line-height: 150% !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-
-                        /*
-        @tab Mobile Styles
-        @section Body Text
-        @tip Make the body text larger in size for better readability on small screens. We recommend a font size of at least 16px.
-        */
-                        .bodyContainer .mcnTextContent,
-                        .bodyContainer .mcnTextContent p {
-                                /*@editable*/
-                                font-size: 16px !important;
-                                /*@editable*/
-                                line-height: 150% !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-
-                        /*
-        @tab Mobile Styles
-        @section Footer Text
-        @tip Make the footer content text larger in size for better readability on small screens.
-        */
-                        .footerContainer .mcnTextContent,
-                        .footerContainer .mcnTextContent p {
-                                /*@editable*/
-                                font-size: 14px !important;
-                                /*@editable*/
-                                line-height: 150% !important;
-                        }
-
-                }
-                @media only screen and (max-width: 480px) {
-                        #meant-for {
-                                padding: 20px;
-                        }
-                }
-        </style>
-</head>
-
-<body>
-        <!--*|IF:MC_PREVIEW_TEXT|*-->
-        <!--[if !gte mso 9]><!----><span class="mcnPreviewText"
-                style="display:none; font-size:0px; line-height:0px; max-height:0px; max-width:0px; opacity:0; overflow:hidden; visibility:hidden; mso-hide:all;"></span>
-        <!--<![endif]-->
-        <!--*|END:IF|*-->
-        <center>
-                <table align="center" border="0" cellpadding="0" cellspacing="0" height="100%" width="100%" id="bodyTable">
-                        <tr>
-                                <td align="center" valign="top" id="bodyCell">
-                                        <!-- BEGIN TEMPLATE // -->
-                                        <table border="0" cellpadding="0" cellspacing="0" width="100%">
-                                                <tr>
-                                                        <td align="center" valign="top" id="templateHeader" data-template-container>
-                                                                <!--[if (gte mso 9)|(IE)]>
-                                    <table align="center" border="0" cellspacing="0" cellpadding="0" width="600" style="width:600px;">
-                                    <tr>
-                                    <td align="center" valign="top" width="600" style="width:600px;">
-                                    <![endif]-->
-                                                                <table align="center" border="0" cellpadding="0" cellspacing="0" width="100%"
-                                                                        class="templateContainer">
-                                                                        <tr>
-                                                                                <td valign="top" class="headerContainer"></td>
-                                                                        </tr>
-                                                                </table>
-                                                                <!--[if (gte mso 9)|(IE)]>
-                                    </td>
-                                    </tr>
-                                    </table>
-                                    <![endif]-->
-                                                        </td>
-                                                </tr>
-                                                <tr>
-                                                        <td align="center" valign="top" id="templateBody" data-template-container>
-                                                                <!--[if (gte mso 9)|(IE)]>
-                                    <table align="center" border="0" cellspacing="0" cellpadding="0" width="600" style="width:600px;">
-                                    <tr>
-                                    <td align="center" valign="top" width="600" style="width:600px;">
-                                    <![endif]-->
-                                                                <table align="center" border="0" cellpadding="0" cellspacing="0" width="100%"
-                                                                        class="templateContainer">
-                                                                        <tr>
-                                                                                <td valign="top" class="bodyContainer">
-                                                                                        <table border="0" cellpadding="0" cellspacing="0" width="100%"
-                                                                                                class="mcnCodeBlock">
-                                                                                                <tbody class="mcnTextBlockOuter">
-                                                                                                        <tr>
-                                                                                                                <td valign="top" class="mcnTextBlockInner">
-
-
-                                                                                                                        <div
-                                                                                                                                style="background-color:#fff; margin-left: 3%;  margin-top: 48px; margin-right: 3%; border: 1px solid #ddd;  border-radius: 6px;">
-                                                                                                                                <div style="padding-left: 15%; padding-right: 15%;">
-
-                                                                                                                                        <p
-                                                                                                                                                style="font-family:'Helvetica'; font-size: 16px; line-height: 26px; font-weight:700; text-align: center; padding-top: 24px; padding-bottom: 24px; padding-left: 8%; padding-right: 8%; ">
-                                                                                                                                                Please verify your email address for ${appname}
-                                                                                                                                                by clicking the button below.</p>
-
-                                                                                                                                        <div class="button-td button-td-primary"
-                                                                                                                                                style="border-radius: 6px; margin-bottom: 50px; display: block; text-align: center;">
-                                                                                                                                                <a class="button-a button-a-primary"
-                                                                                                                                                        href="${verificationLink}" target="_blank"
-                                                                                                                                                        style="background: #52B56E;font-size: 17px;line-height: 24px;font-weight: 700;font-family: 'Helvetica', sans-serif;text-decoration: none;padding: 9px 25px 9px 25px;color: #ffffff;display: block;border-radius: 6px;width: fit-content;margin: 0 auto;">Verify
-                                                                                                                                                        My Email</a>
-                                                                                                                                        </div>
-                                                                                                                                </div>
-                                                                                                                                <div
-                                                                                                                                        style="background-color:#fafafa; border-top: 1px solid #ddd; padding-left: 15%; padding-right: 15%; padding-bottom: 24px; padding-top: 24px">
-                                                                                                                                        <p
-                                                                                                                                                style="max-width: 600px !important; margin: auto; font-family: 'Hevetica', sans-serif; font-size: 14px; line-height: 23px; font-weight:400;  text-align: center; color: #808080;">
-                                                                                                                                                Alternatively, you can directly paste this link
-                                                                                                                                                in your browser <br>
-                                                                                                                                                <a style="font-family: 'Helvetica', sans-serif, sans-serif; text-align: center; word-break: break-all; font-weight: 400; font-size: 14px; line-height: 23px; color: #007aff !important;"
-                                                                                                                                                        target="_blank"
-                                                                                                                                                        href="${verificationLink}">${verificationLink}</a>
-                                                                                                                                        </p>
-                                                                                                                                </div>
-                                                                                                                        </div>
-
-
-
-
-                                                                                                                </td>
-                                                                                                        </tr>
-                                                                                                </tbody>
-                                                                                        </table>
-                                                                                </td>
-                                                                        </tr>
-                                                                </table>
-                                                                <!--[if (gte mso 9)|(IE)]>
-                                    </td>
-                                    </tr>
-                                    </table>
-                                    <![endif]-->
-                                                        </td>
-                                                </tr>
-                                                <tr>
-                                                        <td align="center" valign="top" id="templateFooter" data-template-container>
-                                                                <!--[if (gte mso 9)|(IE)]>
-                                    <table align="center" border="0" cellspacing="0" cellpadding="0" width="600" style="width:600px;">
-                                    <tr>
-                                    <td align="center" valign="top" width="600" style="width:600px;">
-                                    <![endif]-->
-                                                                <table align="center" border="0" cellpadding="0" cellspacing="0" width="100%"
-                                                                        class="templateContainer">
-                                                                        <tr>
-                                                                                <td valign="top" class="footerContainer">
-                                                                                        <table border="0" cellpadding="0" cellspacing="0" width="100%"
-                                                                                                class="mcnCodeBlock">
-                                                                                                <tbody class="mcnTextBlockOuter">
-                                                                                                        <tr>
-                                                                                                                <td valign="top" class="mcnTextBlockInner">
-
-
-                                                                                                                        <p
-                                                                                                                            id="meant-for"
-                                                                                                                                style="font-family: 'Helvetica', sans-serif; font-size: 16px; line-height: 26px; font-weight:400; text-align: center; color: #808080">
-                                                                                                                                This email is meant for <a
-                                                                                                                                        style="font-family: 'Helvetica', sans-serif; text-align: center; word-break: break-all; font-weight: 400; font-size: 16px; line-height: 26px; color: #808080 !important;"
-                                                                                                                                        target="_blank"
-                                                                                                                                        href="mailto:${toEmail}">${toEmail}</a>
-                                                                                                                        </p>
-                                                                                                                </td>
-                                                                                                        </tr>
-                                                                                                </tbody>
-                                                                                        </table>
-                                                                                </td>
-                                                                        </tr>
-                                                                </table>
-                                                                <!--[if (gte mso 9)|(IE)]>
-                                    </td>
-                                    </tr>
-                                    </table>
-                                    <![endif]-->
-                                                        </td>
-                                                </tr>
-                                        </table>
-                                        <!-- // END TEMPLATE -->
-                                </td>
-                        </tr>
-                </table>
-        </center>
-</body>
-
-</html>
-"""
-
-
-
-
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailverification/emaildelivery/services/smtp/index.html b/html/supertokens_python/recipe/emailverification/emaildelivery/services/smtp/index.html deleted file mode 100644 index 0bade6d5a..000000000 --- a/html/supertokens_python/recipe/emailverification/emaildelivery/services/smtp/index.html +++ /dev/null @@ -1,215 +0,0 @@ - - - - - - -supertokens_python.recipe.emailverification.emaildelivery.services.smtp API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.emailverification.emaildelivery.services.smtp

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from typing import Any, Dict, Callable, Union
-
-from supertokens_python.ingredients.emaildelivery.services.smtp import Transporter
-from supertokens_python.ingredients.emaildelivery.types import (
-    EmailDeliveryInterface,
-    SMTPServiceInterface,
-    SMTPSettings,
-)
-from supertokens_python.recipe.emailverification.types import (
-    VerificationEmailTemplateVars,
-    SMTPOverrideInput,
-)
-
-from .service_implementation import ServiceImplementation
-
-
-class SMTPService(EmailDeliveryInterface[VerificationEmailTemplateVars]):
-    service_implementation: SMTPServiceInterface[VerificationEmailTemplateVars]
-
-    def __init__(
-        self,
-        smtp_settings: SMTPSettings,
-        override: Union[Callable[[SMTPOverrideInput], SMTPOverrideInput], None] = None,
-    ) -> None:
-        transporter = Transporter(smtp_settings)
-        oi = ServiceImplementation(transporter)
-        self.service_implementation = oi if override is None else override(oi)
-
-    async def send_email(
-        self,
-        template_vars: VerificationEmailTemplateVars,
-        user_context: Dict[str, Any],
-    ) -> None:
-        content = await self.service_implementation.get_content(
-            template_vars, user_context
-        )
-        await self.service_implementation.send_raw_email(content, user_context)
-
-
-
-

Sub-modules

-
-
supertokens_python.recipe.emailverification.emaildelivery.services.smtp.email_verify
-
-
-
-
supertokens_python.recipe.emailverification.emaildelivery.services.smtp.email_verify_email
-
-
-
-
supertokens_python.recipe.emailverification.emaildelivery.services.smtp.service_implementation
-
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class SMTPService -(smtp_settings: SMTPSettings, override: Optional[Callable[[SMTPServiceInterface[VerificationEmailTemplateVars]], SMTPServiceInterface[VerificationEmailTemplateVars]]] = None) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class SMTPService(EmailDeliveryInterface[VerificationEmailTemplateVars]):
-    service_implementation: SMTPServiceInterface[VerificationEmailTemplateVars]
-
-    def __init__(
-        self,
-        smtp_settings: SMTPSettings,
-        override: Union[Callable[[SMTPOverrideInput], SMTPOverrideInput], None] = None,
-    ) -> None:
-        transporter = Transporter(smtp_settings)
-        oi = ServiceImplementation(transporter)
-        self.service_implementation = oi if override is None else override(oi)
-
-    async def send_email(
-        self,
-        template_vars: VerificationEmailTemplateVars,
-        user_context: Dict[str, Any],
-    ) -> None:
-        content = await self.service_implementation.get_content(
-            template_vars, user_context
-        )
-        await self.service_implementation.send_raw_email(content, user_context)
-
-

Ancestors

- -

Class variables

-
-
var service_implementationSMTPServiceInterface[VerificationEmailTemplateVars]
-
-
-
-
-

Methods

-
-
-async def send_email(self, template_vars: VerificationEmailTemplateVars, user_context: Dict[str, Any]) ‑> None -
-
-
-
- -Expand source code - -
async def send_email(
-    self,
-    template_vars: VerificationEmailTemplateVars,
-    user_context: Dict[str, Any],
-) -> None:
-    content = await self.service_implementation.get_content(
-        template_vars, user_context
-    )
-    await self.service_implementation.send_raw_email(content, user_context)
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailverification/emaildelivery/services/smtp/service_implementation.html b/html/supertokens_python/recipe/emailverification/emaildelivery/services/smtp/service_implementation.html deleted file mode 100644 index 1c270e459..000000000 --- a/html/supertokens_python/recipe/emailverification/emaildelivery/services/smtp/service_implementation.html +++ /dev/null @@ -1,175 +0,0 @@ - - - - - - -supertokens_python.recipe.emailverification.emaildelivery.services.smtp.service_implementation API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.emailverification.emaildelivery.services.smtp.service_implementation

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from typing import Any, Dict
-
-from supertokens_python.ingredients.emaildelivery.types import (
-    EmailContent,
-    SMTPServiceInterface,
-)
-from supertokens_python.recipe.emailverification.emaildelivery.services.smtp.email_verify import (
-    get_email_verify_email_content,
-)
-from supertokens_python.recipe.emailverification.types import (
-    VerificationEmailTemplateVars,
-)
-
-
-class ServiceImplementation(SMTPServiceInterface[VerificationEmailTemplateVars]):
-    async def send_raw_email(
-        self, content: EmailContent, user_context: Dict[str, Any]
-    ) -> None:
-        await self.transporter.send_email(content, user_context)
-
-    async def get_content(
-        self, template_vars: VerificationEmailTemplateVars, user_context: Dict[str, Any]
-    ) -> EmailContent:
-        _ = user_context
-        return get_email_verify_email_content(template_vars)
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class ServiceImplementation -(transporter: Transporter) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class ServiceImplementation(SMTPServiceInterface[VerificationEmailTemplateVars]):
-    async def send_raw_email(
-        self, content: EmailContent, user_context: Dict[str, Any]
-    ) -> None:
-        await self.transporter.send_email(content, user_context)
-
-    async def get_content(
-        self, template_vars: VerificationEmailTemplateVars, user_context: Dict[str, Any]
-    ) -> EmailContent:
-        _ = user_context
-        return get_email_verify_email_content(template_vars)
-
-

Ancestors

- -

Methods

-
-
-async def get_content(self, template_vars: VerificationEmailTemplateVars, user_context: Dict[str, Any]) ‑> EmailContent -
-
-
-
- -Expand source code - -
async def get_content(
-    self, template_vars: VerificationEmailTemplateVars, user_context: Dict[str, Any]
-) -> EmailContent:
-    _ = user_context
-    return get_email_verify_email_content(template_vars)
-
-
-
-async def send_raw_email(self, content: EmailContent, user_context: Dict[str, Any]) ‑> None -
-
-
-
- -Expand source code - -
async def send_raw_email(
-    self, content: EmailContent, user_context: Dict[str, Any]
-) -> None:
-    await self.transporter.send_email(content, user_context)
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailverification/ev_claim_validators.html b/html/supertokens_python/recipe/emailverification/ev_claim_validators.html deleted file mode 100644 index 2a04f93c7..000000000 --- a/html/supertokens_python/recipe/emailverification/ev_claim_validators.html +++ /dev/null @@ -1,72 +0,0 @@ - - - - - - -supertokens_python.recipe.emailverification.ev_claim_validators API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.emailverification.ev_claim_validators

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-
-
-
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailverification/exceptions.html b/html/supertokens_python/recipe/emailverification/exceptions.html deleted file mode 100644 index 26b6b5879..000000000 --- a/html/supertokens_python/recipe/emailverification/exceptions.html +++ /dev/null @@ -1,140 +0,0 @@ - - - - - - -supertokens_python.recipe.emailverification.exceptions API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.emailverification.exceptions

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from supertokens_python.exceptions import SuperTokensError
-
-
-class SuperTokensEmailVerificationError(SuperTokensError):
-    pass
-
-
-class EmailVerificationInvalidTokenError(SuperTokensEmailVerificationError):
-    pass
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class EmailVerificationInvalidTokenError -(*args, **kwargs) -
-
-

Common base class for all non-exit exceptions.

-
- -Expand source code - -
class EmailVerificationInvalidTokenError(SuperTokensEmailVerificationError):
-    pass
-
-

Ancestors

- -
-
-class SuperTokensEmailVerificationError -(*args, **kwargs) -
-
-

Common base class for all non-exit exceptions.

-
- -Expand source code - -
class SuperTokensEmailVerificationError(SuperTokensError):
-    pass
-
-

Ancestors

- -

Subclasses

- -
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailverification/index.html b/html/supertokens_python/recipe/emailverification/index.html deleted file mode 100644 index 62d303262..000000000 --- a/html/supertokens_python/recipe/emailverification/index.html +++ /dev/null @@ -1,210 +0,0 @@ - - - - - - -supertokens_python.recipe.emailverification API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.emailverification

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from typing import TYPE_CHECKING, Callable, Union, Optional
-
-from . import exceptions as ex
-from . import utils
-from .emaildelivery import services as emaildelivery_services
-from . import recipe
-from . import types
-from .interfaces import TypeGetEmailForUserIdFunction
-from .recipe import EmailVerificationRecipe
-from .types import EmailTemplateVars
-from ...ingredients.emaildelivery.types import EmailDeliveryConfig
-
-InputOverrideConfig = utils.OverrideConfig
-exception = ex
-SMTPService = emaildelivery_services.SMTPService
-EmailVerificationClaim = recipe.EmailVerificationClaim
-EmailDeliveryInterface = types.EmailDeliveryInterface
-
-
-if TYPE_CHECKING:
-    from supertokens_python.supertokens import AppInfo
-
-    from ...recipe_module import RecipeModule
-
-from .utils import MODE_TYPE, OverrideConfig
-
-
-def init(
-    mode: MODE_TYPE,
-    email_delivery: Union[EmailDeliveryConfig[EmailTemplateVars], None] = None,
-    get_email_for_user_id: Optional[TypeGetEmailForUserIdFunction] = None,
-    override: Union[OverrideConfig, None] = None,
-) -> Callable[[AppInfo], RecipeModule]:
-    return EmailVerificationRecipe.init(
-        mode,
-        email_delivery,
-        get_email_for_user_id,
-        override,
-    )
-
-
-
-

Sub-modules

-
-
supertokens_python.recipe.emailverification.api
-
-
-
-
supertokens_python.recipe.emailverification.asyncio
-
-
-
-
supertokens_python.recipe.emailverification.constants
-
-
-
-
supertokens_python.recipe.emailverification.emaildelivery
-
-
-
-
supertokens_python.recipe.emailverification.ev_claim_validators
-
-
-
-
supertokens_python.recipe.emailverification.exceptions
-
-
-
-
supertokens_python.recipe.emailverification.interfaces
-
-
-
-
supertokens_python.recipe.emailverification.recipe
-
-
-
-
supertokens_python.recipe.emailverification.recipe_implementation
-
-
-
-
supertokens_python.recipe.emailverification.syncio
-
-
-
-
supertokens_python.recipe.emailverification.types
-
-
-
-
supertokens_python.recipe.emailverification.utils
-
-
-
-
-
-
-
-
-

Functions

-
-
-def init(mode: MODE_TYPE, email_delivery: Union[EmailDeliveryConfig[EmailTemplateVars], None] = None, get_email_for_user_id: Optional[TypeGetEmailForUserIdFunction] = None, override: Union[OverrideConfig, None] = None) ‑> Callable[[AppInfo], RecipeModule] -
-
-
-
- -Expand source code - -
def init(
-    mode: MODE_TYPE,
-    email_delivery: Union[EmailDeliveryConfig[EmailTemplateVars], None] = None,
-    get_email_for_user_id: Optional[TypeGetEmailForUserIdFunction] = None,
-    override: Union[OverrideConfig, None] = None,
-) -> Callable[[AppInfo], RecipeModule]:
-    return EmailVerificationRecipe.init(
-        mode,
-        email_delivery,
-        get_email_for_user_id,
-        override,
-    )
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailverification/interfaces.html b/html/supertokens_python/recipe/emailverification/interfaces.html deleted file mode 100644 index bdd594d4e..000000000 --- a/html/supertokens_python/recipe/emailverification/interfaces.html +++ /dev/null @@ -1,1169 +0,0 @@ - - - - - - -supertokens_python.recipe.emailverification.interfaces API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.emailverification.interfaces

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from abc import ABC, abstractmethod
-from typing import TYPE_CHECKING, Any, Awaitable, Callable, Dict, Optional, Union
-
-from supertokens_python.ingredients.emaildelivery import EmailDeliveryIngredient
-from supertokens_python.types import APIResponse, GeneralErrorResponse
-
-from ...supertokens import AppInfo
-from ..session.interfaces import SessionContainer
-
-if TYPE_CHECKING:
-    from supertokens_python.framework import BaseRequest, BaseResponse
-
-    from .types import User, VerificationEmailTemplateVars
-    from .utils import EmailVerificationConfig
-
-
-class CreateEmailVerificationTokenOkResult:
-    status = "OK"
-
-    def __init__(self, token: str):
-        self.token = token
-
-
-class CreateEmailVerificationTokenEmailAlreadyVerifiedError:
-    status = "EMAIL_ALREADY_VERIFIED_ERROR"
-
-
-class CreateEmailVerificationLinkEmailAlreadyVerifiedError:
-    status = "EMAIL_ALREADY_VERIFIED_ERROR"
-
-
-class CreateEmailVerificationLinkOkResult:
-    status = "OK"
-
-    def __init__(self, link: str):
-        self.link = link
-
-
-class SendEmailVerificationEmailAlreadyVerifiedError:
-    status = "EMAIL_ALREADY_VERIFIED_ERROR"
-
-
-class SendEmailVerificationEmailOkResult:
-    status = "OK"
-
-
-class VerifyEmailUsingTokenOkResult:
-    status = "OK"
-
-    def __init__(self, user: User):
-        self.user = user
-
-
-class VerifyEmailUsingTokenInvalidTokenError:
-    pass
-
-
-class RevokeEmailVerificationTokensOkResult:
-    pass
-
-
-class UnverifyEmailOkResult:
-    pass
-
-
-class RecipeInterface(ABC):
-    def __init__(self):
-        pass
-
-    @abstractmethod
-    async def create_email_verification_token(
-        self, user_id: str, email: str, tenant_id: str, user_context: Dict[str, Any]
-    ) -> Union[
-        CreateEmailVerificationTokenOkResult,
-        CreateEmailVerificationTokenEmailAlreadyVerifiedError,
-    ]:
-        pass
-
-    @abstractmethod
-    async def verify_email_using_token(
-        self, token: str, tenant_id: str, user_context: Dict[str, Any]
-    ) -> Union[VerifyEmailUsingTokenOkResult, VerifyEmailUsingTokenInvalidTokenError]:
-        pass
-
-    @abstractmethod
-    async def is_email_verified(
-        self, user_id: str, email: str, user_context: Dict[str, Any]
-    ) -> bool:
-        pass
-
-    @abstractmethod
-    async def revoke_email_verification_tokens(
-        self, user_id: str, email: str, tenant_id: str, user_context: Dict[str, Any]
-    ) -> RevokeEmailVerificationTokensOkResult:
-        pass
-
-    @abstractmethod
-    async def unverify_email(
-        self, user_id: str, email: str, user_context: Dict[str, Any]
-    ) -> UnverifyEmailOkResult:
-        pass
-
-
-class APIOptions:
-    def __init__(
-        self,
-        request: BaseRequest,
-        response: BaseResponse,
-        recipe_id: str,
-        config: EmailVerificationConfig,
-        recipe_implementation: RecipeInterface,
-        app_info: AppInfo,
-        email_delivery: EmailDeliveryIngredient[VerificationEmailTemplateVars],
-    ):
-        self.request = request
-        self.response = response
-        self.recipe_id = recipe_id
-        self.config = config
-        self.recipe_implementation = recipe_implementation
-        self.app_info = app_info
-        self.email_delivery = email_delivery
-
-
-class EmailVerifyPostOkResult(APIResponse):
-    def __init__(self, user: User):
-        self.user = user
-        self.status = "OK"
-
-    def to_json(self) -> Dict[str, Any]:
-        return {
-            "status": self.status,
-            "user": {"id": self.user.user_id, "email": self.user.email},
-        }
-
-
-class EmailVerifyPostInvalidTokenError(APIResponse):
-    def __init__(self):
-        self.status = "EMAIL_VERIFICATION_INVALID_TOKEN_ERROR"
-
-    def to_json(self) -> Dict[str, Any]:
-        return {"status": self.status}
-
-
-class IsEmailVerifiedGetOkResult(APIResponse):
-    def __init__(self, is_verified: bool):
-        self.status = "OK"
-        self.is_verified = is_verified
-
-    def to_json(self) -> Dict[str, Any]:
-        return {"status": self.status, "isVerified": self.is_verified}
-
-
-class GenerateEmailVerifyTokenPostOkResult(APIResponse):
-    def __init__(self):
-        self.status = "OK"
-
-    def to_json(self) -> Dict[str, Any]:
-        return {"status": self.status}
-
-
-class GenerateEmailVerifyTokenPostEmailAlreadyVerifiedError(APIResponse):
-    def __init__(self):
-        self.status = "EMAIL_ALREADY_VERIFIED_ERROR"
-
-    def to_json(self) -> Dict[str, Any]:
-        return {"status": self.status}
-
-
-class APIInterface(ABC):
-    def __init__(self):
-        self.disable_email_verify_post = False
-        self.disable_is_email_verified_get = False
-        self.disable_generate_email_verify_token_post = False
-
-    @abstractmethod
-    async def email_verify_post(
-        self,
-        token: str,
-        session: Optional[SessionContainer],
-        tenant_id: str,
-        api_options: APIOptions,
-        user_context: Dict[str, Any],
-    ) -> Union[
-        EmailVerifyPostOkResult, EmailVerifyPostInvalidTokenError, GeneralErrorResponse
-    ]:
-        pass
-
-    @abstractmethod
-    async def is_email_verified_get(
-        self,
-        session: SessionContainer,
-        api_options: APIOptions,
-        user_context: Dict[str, Any],
-    ) -> Union[IsEmailVerifiedGetOkResult, GeneralErrorResponse]:
-        pass
-
-    @abstractmethod
-    async def generate_email_verify_token_post(
-        self,
-        session: SessionContainer,
-        api_options: APIOptions,
-        user_context: Dict[str, Any],
-    ) -> Union[
-        GenerateEmailVerifyTokenPostOkResult,
-        GenerateEmailVerifyTokenPostEmailAlreadyVerifiedError,
-        GeneralErrorResponse,
-    ]:
-        pass
-
-
-class GetEmailForUserIdOkResult:
-    def __init__(self, email: str):
-        self.email = email
-
-
-class EmailDoesNotExistError(Exception):
-    pass
-
-
-class UnknownUserIdError(Exception):
-    pass
-
-
-TypeGetEmailForUserIdFunction = Callable[
-    [str, Dict[str, Any]],
-    Awaitable[
-        Union[GetEmailForUserIdOkResult, EmailDoesNotExistError, UnknownUserIdError]
-    ],
-]
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class APIInterface -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class APIInterface(ABC):
-    def __init__(self):
-        self.disable_email_verify_post = False
-        self.disable_is_email_verified_get = False
-        self.disable_generate_email_verify_token_post = False
-
-    @abstractmethod
-    async def email_verify_post(
-        self,
-        token: str,
-        session: Optional[SessionContainer],
-        tenant_id: str,
-        api_options: APIOptions,
-        user_context: Dict[str, Any],
-    ) -> Union[
-        EmailVerifyPostOkResult, EmailVerifyPostInvalidTokenError, GeneralErrorResponse
-    ]:
-        pass
-
-    @abstractmethod
-    async def is_email_verified_get(
-        self,
-        session: SessionContainer,
-        api_options: APIOptions,
-        user_context: Dict[str, Any],
-    ) -> Union[IsEmailVerifiedGetOkResult, GeneralErrorResponse]:
-        pass
-
-    @abstractmethod
-    async def generate_email_verify_token_post(
-        self,
-        session: SessionContainer,
-        api_options: APIOptions,
-        user_context: Dict[str, Any],
-    ) -> Union[
-        GenerateEmailVerifyTokenPostOkResult,
-        GenerateEmailVerifyTokenPostEmailAlreadyVerifiedError,
-        GeneralErrorResponse,
-    ]:
-        pass
-
-

Ancestors

-
    -
  • abc.ABC
  • -
-

Subclasses

- -

Methods

-
-
-async def email_verify_post(self, token: str, session: Optional[SessionContainer], tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[EmailVerifyPostOkResultEmailVerifyPostInvalidTokenErrorGeneralErrorResponse] -
-
-
-
- -Expand source code - -
@abstractmethod
-async def email_verify_post(
-    self,
-    token: str,
-    session: Optional[SessionContainer],
-    tenant_id: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-) -> Union[
-    EmailVerifyPostOkResult, EmailVerifyPostInvalidTokenError, GeneralErrorResponse
-]:
-    pass
-
-
-
-async def generate_email_verify_token_post(self, session: SessionContainer, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[GenerateEmailVerifyTokenPostOkResultGenerateEmailVerifyTokenPostEmailAlreadyVerifiedErrorGeneralErrorResponse] -
-
-
-
- -Expand source code - -
@abstractmethod
-async def generate_email_verify_token_post(
-    self,
-    session: SessionContainer,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-) -> Union[
-    GenerateEmailVerifyTokenPostOkResult,
-    GenerateEmailVerifyTokenPostEmailAlreadyVerifiedError,
-    GeneralErrorResponse,
-]:
-    pass
-
-
-
-async def is_email_verified_get(self, session: SessionContainer, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[IsEmailVerifiedGetOkResultGeneralErrorResponse] -
-
-
-
- -Expand source code - -
@abstractmethod
-async def is_email_verified_get(
-    self,
-    session: SessionContainer,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-) -> Union[IsEmailVerifiedGetOkResult, GeneralErrorResponse]:
-    pass
-
-
-
-
-
-class APIOptions -(request: BaseRequest, response: BaseResponse, recipe_id: str, config: EmailVerificationConfig, recipe_implementation: RecipeInterface, app_info: AppInfo, email_delivery: EmailDeliveryIngredient[VerificationEmailTemplateVars]) -
-
-
-
- -Expand source code - -
class APIOptions:
-    def __init__(
-        self,
-        request: BaseRequest,
-        response: BaseResponse,
-        recipe_id: str,
-        config: EmailVerificationConfig,
-        recipe_implementation: RecipeInterface,
-        app_info: AppInfo,
-        email_delivery: EmailDeliveryIngredient[VerificationEmailTemplateVars],
-    ):
-        self.request = request
-        self.response = response
-        self.recipe_id = recipe_id
-        self.config = config
-        self.recipe_implementation = recipe_implementation
-        self.app_info = app_info
-        self.email_delivery = email_delivery
-
-
-
-class CreateEmailVerificationLinkEmailAlreadyVerifiedError -
-
-
-
- -Expand source code - -
class CreateEmailVerificationLinkEmailAlreadyVerifiedError:
-    status = "EMAIL_ALREADY_VERIFIED_ERROR"
-
-

Class variables

-
-
var status
-
-
-
-
-
-
-class CreateEmailVerificationLinkOkResult -(link: str) -
-
-
-
- -Expand source code - -
class CreateEmailVerificationLinkOkResult:
-    status = "OK"
-
-    def __init__(self, link: str):
-        self.link = link
-
-

Class variables

-
-
var status
-
-
-
-
-
-
-class CreateEmailVerificationTokenEmailAlreadyVerifiedError -
-
-
-
- -Expand source code - -
class CreateEmailVerificationTokenEmailAlreadyVerifiedError:
-    status = "EMAIL_ALREADY_VERIFIED_ERROR"
-
-

Class variables

-
-
var status
-
-
-
-
-
-
-class CreateEmailVerificationTokenOkResult -(token: str) -
-
-
-
- -Expand source code - -
class CreateEmailVerificationTokenOkResult:
-    status = "OK"
-
-    def __init__(self, token: str):
-        self.token = token
-
-

Class variables

-
-
var status
-
-
-
-
-
-
-class EmailDoesNotExistError -(*args, **kwargs) -
-
-

Common base class for all non-exit exceptions.

-
- -Expand source code - -
class EmailDoesNotExistError(Exception):
-    pass
-
-

Ancestors

-
    -
  • builtins.Exception
  • -
  • builtins.BaseException
  • -
-
-
-class EmailVerifyPostInvalidTokenError -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class EmailVerifyPostInvalidTokenError(APIResponse):
-    def __init__(self):
-        self.status = "EMAIL_VERIFICATION_INVALID_TOKEN_ERROR"
-
-    def to_json(self) -> Dict[str, Any]:
-        return {"status": self.status}
-
-

Ancestors

- -

Methods

-
-
-def to_json(self) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
def to_json(self) -> Dict[str, Any]:
-    return {"status": self.status}
-
-
-
-
-
-class EmailVerifyPostOkResult -(user: User) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class EmailVerifyPostOkResult(APIResponse):
-    def __init__(self, user: User):
-        self.user = user
-        self.status = "OK"
-
-    def to_json(self) -> Dict[str, Any]:
-        return {
-            "status": self.status,
-            "user": {"id": self.user.user_id, "email": self.user.email},
-        }
-
-

Ancestors

- -

Methods

-
-
-def to_json(self) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
def to_json(self) -> Dict[str, Any]:
-    return {
-        "status": self.status,
-        "user": {"id": self.user.user_id, "email": self.user.email},
-    }
-
-
-
-
-
-class GenerateEmailVerifyTokenPostEmailAlreadyVerifiedError -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class GenerateEmailVerifyTokenPostEmailAlreadyVerifiedError(APIResponse):
-    def __init__(self):
-        self.status = "EMAIL_ALREADY_VERIFIED_ERROR"
-
-    def to_json(self) -> Dict[str, Any]:
-        return {"status": self.status}
-
-

Ancestors

- -

Methods

-
-
-def to_json(self) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
def to_json(self) -> Dict[str, Any]:
-    return {"status": self.status}
-
-
-
-
-
-class GenerateEmailVerifyTokenPostOkResult -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class GenerateEmailVerifyTokenPostOkResult(APIResponse):
-    def __init__(self):
-        self.status = "OK"
-
-    def to_json(self) -> Dict[str, Any]:
-        return {"status": self.status}
-
-

Ancestors

- -

Methods

-
-
-def to_json(self) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
def to_json(self) -> Dict[str, Any]:
-    return {"status": self.status}
-
-
-
-
-
-class GetEmailForUserIdOkResult -(email: str) -
-
-
-
- -Expand source code - -
class GetEmailForUserIdOkResult:
-    def __init__(self, email: str):
-        self.email = email
-
-
-
-class IsEmailVerifiedGetOkResult -(is_verified: bool) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class IsEmailVerifiedGetOkResult(APIResponse):
-    def __init__(self, is_verified: bool):
-        self.status = "OK"
-        self.is_verified = is_verified
-
-    def to_json(self) -> Dict[str, Any]:
-        return {"status": self.status, "isVerified": self.is_verified}
-
-

Ancestors

- -

Methods

-
-
-def to_json(self) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
def to_json(self) -> Dict[str, Any]:
-    return {"status": self.status, "isVerified": self.is_verified}
-
-
-
-
-
-class RecipeInterface -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class RecipeInterface(ABC):
-    def __init__(self):
-        pass
-
-    @abstractmethod
-    async def create_email_verification_token(
-        self, user_id: str, email: str, tenant_id: str, user_context: Dict[str, Any]
-    ) -> Union[
-        CreateEmailVerificationTokenOkResult,
-        CreateEmailVerificationTokenEmailAlreadyVerifiedError,
-    ]:
-        pass
-
-    @abstractmethod
-    async def verify_email_using_token(
-        self, token: str, tenant_id: str, user_context: Dict[str, Any]
-    ) -> Union[VerifyEmailUsingTokenOkResult, VerifyEmailUsingTokenInvalidTokenError]:
-        pass
-
-    @abstractmethod
-    async def is_email_verified(
-        self, user_id: str, email: str, user_context: Dict[str, Any]
-    ) -> bool:
-        pass
-
-    @abstractmethod
-    async def revoke_email_verification_tokens(
-        self, user_id: str, email: str, tenant_id: str, user_context: Dict[str, Any]
-    ) -> RevokeEmailVerificationTokensOkResult:
-        pass
-
-    @abstractmethod
-    async def unverify_email(
-        self, user_id: str, email: str, user_context: Dict[str, Any]
-    ) -> UnverifyEmailOkResult:
-        pass
-
-

Ancestors

-
    -
  • abc.ABC
  • -
-

Subclasses

- -

Methods

-
-
-async def create_email_verification_token(self, user_id: str, email: str, tenant_id: str, user_context: Dict[str, Any]) ‑> Union[CreateEmailVerificationTokenOkResultCreateEmailVerificationTokenEmailAlreadyVerifiedError] -
-
-
-
- -Expand source code - -
@abstractmethod
-async def create_email_verification_token(
-    self, user_id: str, email: str, tenant_id: str, user_context: Dict[str, Any]
-) -> Union[
-    CreateEmailVerificationTokenOkResult,
-    CreateEmailVerificationTokenEmailAlreadyVerifiedError,
-]:
-    pass
-
-
-
-async def is_email_verified(self, user_id: str, email: str, user_context: Dict[str, Any]) ‑> bool -
-
-
-
- -Expand source code - -
@abstractmethod
-async def is_email_verified(
-    self, user_id: str, email: str, user_context: Dict[str, Any]
-) -> bool:
-    pass
-
-
-
-async def revoke_email_verification_tokens(self, user_id: str, email: str, tenant_id: str, user_context: Dict[str, Any]) ‑> RevokeEmailVerificationTokensOkResult -
-
-
-
- -Expand source code - -
@abstractmethod
-async def revoke_email_verification_tokens(
-    self, user_id: str, email: str, tenant_id: str, user_context: Dict[str, Any]
-) -> RevokeEmailVerificationTokensOkResult:
-    pass
-
-
-
-async def unverify_email(self, user_id: str, email: str, user_context: Dict[str, Any]) ‑> UnverifyEmailOkResult -
-
-
-
- -Expand source code - -
@abstractmethod
-async def unverify_email(
-    self, user_id: str, email: str, user_context: Dict[str, Any]
-) -> UnverifyEmailOkResult:
-    pass
-
-
-
-async def verify_email_using_token(self, token: str, tenant_id: str, user_context: Dict[str, Any]) ‑> Union[VerifyEmailUsingTokenOkResultVerifyEmailUsingTokenInvalidTokenError] -
-
-
-
- -Expand source code - -
@abstractmethod
-async def verify_email_using_token(
-    self, token: str, tenant_id: str, user_context: Dict[str, Any]
-) -> Union[VerifyEmailUsingTokenOkResult, VerifyEmailUsingTokenInvalidTokenError]:
-    pass
-
-
-
-
-
-class RevokeEmailVerificationTokensOkResult -
-
-
-
- -Expand source code - -
class RevokeEmailVerificationTokensOkResult:
-    pass
-
-
-
-class SendEmailVerificationEmailAlreadyVerifiedError -
-
-
-
- -Expand source code - -
class SendEmailVerificationEmailAlreadyVerifiedError:
-    status = "EMAIL_ALREADY_VERIFIED_ERROR"
-
-

Class variables

-
-
var status
-
-
-
-
-
-
-class SendEmailVerificationEmailOkResult -
-
-
-
- -Expand source code - -
class SendEmailVerificationEmailOkResult:
-    status = "OK"
-
-

Class variables

-
-
var status
-
-
-
-
-
-
-class UnknownUserIdError -(*args, **kwargs) -
-
-

Common base class for all non-exit exceptions.

-
- -Expand source code - -
class UnknownUserIdError(Exception):
-    pass
-
-

Ancestors

-
    -
  • builtins.Exception
  • -
  • builtins.BaseException
  • -
-
-
-class UnverifyEmailOkResult -
-
-
-
- -Expand source code - -
class UnverifyEmailOkResult:
-    pass
-
-
-
-class VerifyEmailUsingTokenInvalidTokenError -
-
-
-
- -Expand source code - -
class VerifyEmailUsingTokenInvalidTokenError:
-    pass
-
-
-
-class VerifyEmailUsingTokenOkResult -(user: User) -
-
-
-
- -Expand source code - -
class VerifyEmailUsingTokenOkResult:
-    status = "OK"
-
-    def __init__(self, user: User):
-        self.user = user
-
-

Class variables

-
-
var status
-
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailverification/recipe.html b/html/supertokens_python/recipe/emailverification/recipe.html deleted file mode 100644 index 38d7073ba..000000000 --- a/html/supertokens_python/recipe/emailverification/recipe.html +++ /dev/null @@ -1,1679 +0,0 @@ - - - - - - -supertokens_python.recipe.emailverification.recipe API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.emailverification.recipe

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from os import environ
-from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union
-
-from supertokens_python.exceptions import SuperTokensError, raise_general_exception
-from supertokens_python.ingredients.emaildelivery import EmailDeliveryIngredient
-from supertokens_python.recipe.emailverification.exceptions import (
-    EmailVerificationInvalidTokenError,
-)
-from supertokens_python.recipe.emailverification.types import (
-    EmailTemplateVars,
-    EmailVerificationIngredients,
-    VerificationEmailTemplateVars,
-    VerificationEmailTemplateVarsUser,
-)
-from supertokens_python.recipe_module import APIHandled, RecipeModule
-
-from ...ingredients.emaildelivery.types import EmailDeliveryConfig
-from ...logger import log_debug_message
-from ...post_init_callbacks import PostSTInitCallbacks
-from ...types import MaybeAwaitable
-from ...utils import get_timestamp_ms
-from ..session import SessionRecipe
-from ..session.claim_base_classes.boolean_claim import (
-    BooleanClaim,
-    BooleanClaimValidators,
-)
-from ..session.exceptions import raise_unauthorised_exception
-from ..session.interfaces import (
-    ClaimValidationResult,
-    JSONObject,
-    SessionClaimValidator,
-    SessionContainer,
-)
-from .interfaces import (
-    APIInterface,
-    APIOptions,
-    CreateEmailVerificationTokenEmailAlreadyVerifiedError,
-    EmailDoesNotExistError,
-    EmailVerifyPostInvalidTokenError,
-    EmailVerifyPostOkResult,
-    GenerateEmailVerifyTokenPostEmailAlreadyVerifiedError,
-    GenerateEmailVerifyTokenPostOkResult,
-    GetEmailForUserIdOkResult,
-    IsEmailVerifiedGetOkResult,
-    TypeGetEmailForUserIdFunction,
-    UnknownUserIdError,
-    VerifyEmailUsingTokenOkResult,
-)
-from .recipe_implementation import RecipeImplementation
-
-if TYPE_CHECKING:
-    from supertokens_python.framework.request import BaseRequest
-    from supertokens_python.framework.response import BaseResponse
-    from supertokens_python.supertokens import AppInfo
-
-from supertokens_python.normalised_url_path import NormalisedURLPath
-from supertokens_python.querier import Querier
-from supertokens_python.recipe.emailverification.utils import get_email_verify_link
-
-from .api import handle_email_verify_api, handle_generate_email_verify_token_api
-from .constants import USER_EMAIL_VERIFY, USER_EMAIL_VERIFY_TOKEN
-from .exceptions import SuperTokensEmailVerificationError
-from .utils import MODE_TYPE, OverrideConfig, validate_and_normalise_user_input
-
-
-class EmailVerificationRecipe(RecipeModule):
-    recipe_id = "emailverification"
-    __instance = None
-    email_delivery: EmailDeliveryIngredient[VerificationEmailTemplateVars]
-
-    def __init__(
-        self,
-        recipe_id: str,
-        app_info: AppInfo,
-        ingredients: EmailVerificationIngredients,
-        mode: MODE_TYPE,
-        email_delivery: Union[EmailDeliveryConfig[EmailTemplateVars], None] = None,
-        get_email_for_user_id: Optional[TypeGetEmailForUserIdFunction] = None,
-        override: Union[OverrideConfig, None] = None,
-    ) -> None:
-        super().__init__(recipe_id, app_info)
-        self.config = validate_and_normalise_user_input(
-            app_info,
-            mode,
-            email_delivery,
-            get_email_for_user_id,
-            override,
-        )
-
-        recipe_implementation = RecipeImplementation(
-            Querier.get_instance(recipe_id), self.config
-        )
-        self.recipe_implementation = (
-            recipe_implementation
-            if self.config.override.functions is None
-            else self.config.override.functions(recipe_implementation)
-        )
-
-        api_implementation = APIImplementation()
-        self.api_implementation = (
-            api_implementation
-            if self.config.override.apis is None
-            else self.config.override.apis(api_implementation)
-        )
-
-        email_delivery_ingredient = ingredients.email_delivery
-        if email_delivery_ingredient is None:
-            self.email_delivery = EmailDeliveryIngredient(
-                self.config.get_email_delivery_config()
-            )
-        else:
-            self.email_delivery = email_delivery_ingredient
-
-        self.get_email_for_user_id_funcs_from_other_recipes: List[
-            TypeGetEmailForUserIdFunction
-        ] = []
-
-    def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
-        return isinstance(err, SuperTokensError) and isinstance(
-            err, SuperTokensEmailVerificationError
-        )
-
-    def get_apis_handled(self) -> List[APIHandled]:
-        return [
-            APIHandled(
-                NormalisedURLPath(USER_EMAIL_VERIFY_TOKEN),
-                "post",
-                USER_EMAIL_VERIFY_TOKEN,
-                self.api_implementation.disable_generate_email_verify_token_post,
-            ),
-            APIHandled(
-                NormalisedURLPath(USER_EMAIL_VERIFY),
-                "post",
-                USER_EMAIL_VERIFY,
-                self.api_implementation.disable_email_verify_post,
-            ),
-            APIHandled(
-                NormalisedURLPath(USER_EMAIL_VERIFY),
-                "get",
-                USER_EMAIL_VERIFY,
-                self.api_implementation.disable_is_email_verified_get,
-            ),
-        ]
-
-    async def handle_api_request(
-        self,
-        request_id: str,
-        tenant_id: str,
-        request: BaseRequest,
-        path: NormalisedURLPath,
-        method: str,
-        response: BaseResponse,
-        user_context: Dict[str, Any],
-    ) -> Union[BaseResponse, None]:
-        api_options = APIOptions(
-            request,
-            response,
-            self.recipe_id,
-            self.config,
-            self.recipe_implementation,
-            self.get_app_info(),
-            self.email_delivery,
-        )
-        if request_id == USER_EMAIL_VERIFY_TOKEN:
-            return await handle_generate_email_verify_token_api(
-                self.api_implementation, api_options, user_context
-            )
-        return await handle_email_verify_api(
-            self.api_implementation, tenant_id, api_options, user_context
-        )
-
-    async def handle_error(
-        self,
-        request: BaseRequest,
-        err: SuperTokensError,
-        response: BaseResponse,
-        user_context: Dict[str, Any],
-    ) -> BaseResponse:
-        if isinstance(err, EmailVerificationInvalidTokenError):
-            response.set_json_content(
-                {"status": "EMAIL_VERIFICATION_INVALID_TOKEN_ERROR"}
-            )
-            return response
-        response.set_json_content({"status": "EMAIL_ALREADY_VERIFIED_ERROR"})
-        return response
-
-    def get_all_cors_headers(self) -> List[str]:
-        return []
-
-    @staticmethod
-    def init(
-        mode: MODE_TYPE,
-        email_delivery: Union[EmailDeliveryConfig[EmailTemplateVars], None] = None,
-        get_email_for_user_id: Optional[TypeGetEmailForUserIdFunction] = None,
-        override: Union[OverrideConfig, None] = None,
-    ):
-        def func(app_info: AppInfo) -> EmailVerificationRecipe:
-            if EmailVerificationRecipe.__instance is None:
-                ingredients = EmailVerificationIngredients(email_delivery=None)
-                EmailVerificationRecipe.__instance = EmailVerificationRecipe(
-                    EmailVerificationRecipe.recipe_id,
-                    app_info,
-                    ingredients,
-                    mode,
-                    email_delivery,
-                    get_email_for_user_id,
-                    override,
-                )
-
-                def callback():
-                    SessionRecipe.get_instance().add_claim_from_other_recipe(
-                        EmailVerificationClaim
-                    )
-                    if mode == "REQUIRED":
-                        SessionRecipe.get_instance().add_claim_validator_from_other_recipe(
-                            EmailVerificationClaim.validators.is_verified()
-                        )
-
-                PostSTInitCallbacks.add_post_init_callback(callback)
-
-                return EmailVerificationRecipe.__instance
-            raise_general_exception(
-                "Emailverification recipe has already been initialised. Please check your code for bugs."
-            )
-
-        return func
-
-    @staticmethod
-    def get_instance() -> EmailVerificationRecipe:
-        if EmailVerificationRecipe.__instance is not None:
-            return EmailVerificationRecipe.__instance
-        raise_general_exception(
-            "Initialisation not done. Did you forget to call the SuperTokens.init function?"
-        )
-
-    @staticmethod
-    def get_instance_optional() -> Optional[EmailVerificationRecipe]:
-        return EmailVerificationRecipe.__instance
-
-    @staticmethod
-    def reset():
-        if ("SUPERTOKENS_ENV" not in environ) or (
-            environ["SUPERTOKENS_ENV"] != "testing"
-        ):
-            raise_general_exception("calling testing function in non testing env")
-        EmailVerificationRecipe.__instance = None
-
-    async def get_email_for_user_id(
-        self, user_id: str, user_context: Dict[str, Any]
-    ) -> Union[GetEmailForUserIdOkResult, EmailDoesNotExistError, UnknownUserIdError]:
-        if self.config.get_email_for_user_id is not None:
-            res = await self.config.get_email_for_user_id(user_id, user_context)
-            if not isinstance(res, UnknownUserIdError):
-                return res
-
-        for f in self.get_email_for_user_id_funcs_from_other_recipes:
-            res = await f(user_id, user_context)
-            if not isinstance(res, UnknownUserIdError):
-                return res
-
-        return UnknownUserIdError()
-
-    def add_get_email_for_user_id_func(self, f: TypeGetEmailForUserIdFunction):
-        self.get_email_for_user_id_funcs_from_other_recipes.append(f)
-
-
-class EmailVerificationClaimValidators(BooleanClaimValidators):
-    def __init__(self, claim: EmailVerificationClaimClass):
-        super().__init__(claim, None)
-
-    def is_verified(
-        self,
-        refetch_time_on_false_in_seconds: int = 10,
-        max_age_in_seconds: Optional[int] = None,
-        id_: Optional[str] = None,
-    ) -> SessionClaimValidator:
-
-        assert isinstance(self.claim, EmailVerificationClaimClass)
-        return IsVerifiedSCV(
-            (id_ or self.claim.key),
-            self.claim,
-            self,
-            refetch_time_on_false_in_seconds,
-            max_age_in_seconds,
-        )
-
-
-class EmailVerificationClaimClass(BooleanClaim):
-    def __init__(self):
-        async def fetch_value(
-            user_id: str, _tenant_id: str, user_context: Dict[str, Any]
-        ) -> bool:
-            recipe = EmailVerificationRecipe.get_instance()
-            email_info = await recipe.get_email_for_user_id(user_id, user_context)
-
-            if isinstance(email_info, GetEmailForUserIdOkResult):
-                return await recipe.recipe_implementation.is_email_verified(
-                    user_id, email_info.email, user_context
-                )
-            if isinstance(email_info, EmailDoesNotExistError):
-                # we consider people without email addresses as validated
-                return True
-            raise Exception("UNKNOWN_USER_ID")
-
-        super().__init__("st-ev", fetch_value, None)
-
-        self.validators = EmailVerificationClaimValidators(claim=self)
-
-
-EmailVerificationClaim = EmailVerificationClaimClass()
-
-
-class APIImplementation(APIInterface):
-    async def email_verify_post(
-        self,
-        token: str,
-        session: Optional[SessionContainer],
-        tenant_id: str,
-        api_options: APIOptions,
-        user_context: Dict[str, Any],
-    ) -> Union[EmailVerifyPostOkResult, EmailVerifyPostInvalidTokenError]:
-
-        response = await api_options.recipe_implementation.verify_email_using_token(
-            token, tenant_id, user_context
-        )
-        if isinstance(response, VerifyEmailUsingTokenOkResult):
-            if session is not None:
-                try:
-                    await session.fetch_and_set_claim(
-                        EmailVerificationClaim, user_context
-                    )
-                except Exception as e:
-                    # This should never happen since we have just set the status above
-                    if str(e) == "UNKNOWN_USER_ID":
-                        log_debug_message(
-                            "verifyEmailPOST: Returning UNAUTHORISED because the user id provided is unknown"
-                        )
-                        raise_unauthorised_exception("Unknown User ID provided")
-                    else:
-                        raise e
-
-            return EmailVerifyPostOkResult(response.user)
-        return EmailVerifyPostInvalidTokenError()
-
-    async def is_email_verified_get(
-        self,
-        session: SessionContainer,
-        api_options: APIOptions,
-        user_context: Dict[str, Any],
-    ) -> IsEmailVerifiedGetOkResult:
-        try:
-            await session.fetch_and_set_claim(EmailVerificationClaim, user_context)
-        except Exception as e:
-            if str(e) == "UNKNOWN_USER_ID":
-                log_debug_message(
-                    "isEmailVerifiedGET: Returning UNAUTHORISED because the user id provided is unknown"
-                )
-                raise_unauthorised_exception("Unknown User ID provided")
-            else:
-                raise e
-
-        is_verified = await session.get_claim_value(
-            EmailVerificationClaim, user_context
-        )
-
-        if is_verified is None:
-            raise Exception(
-                "Should never come here: EmailVerificationClaim failed to set value"
-            )
-
-        return IsEmailVerifiedGetOkResult(is_verified)
-
-    async def generate_email_verify_token_post(
-        self,
-        session: SessionContainer,
-        api_options: APIOptions,
-        user_context: Dict[str, Any],
-    ) -> Union[
-        GenerateEmailVerifyTokenPostOkResult,
-        GenerateEmailVerifyTokenPostEmailAlreadyVerifiedError,
-    ]:
-        user_id = session.get_user_id(user_context)
-        email_info = await EmailVerificationRecipe.get_instance().get_email_for_user_id(
-            user_id, user_context
-        )
-        tenant_id = session.get_tenant_id()
-
-        if isinstance(email_info, EmailDoesNotExistError):
-            log_debug_message(
-                "Email verification email not sent to user %s because it doesn't have an email address.",
-                user_id,
-            )
-            return GenerateEmailVerifyTokenPostEmailAlreadyVerifiedError()
-        if isinstance(email_info, GetEmailForUserIdOkResult):
-            response = (
-                await api_options.recipe_implementation.create_email_verification_token(
-                    user_id,
-                    email_info.email,
-                    tenant_id,
-                    user_context,
-                )
-            )
-
-            if isinstance(
-                response, CreateEmailVerificationTokenEmailAlreadyVerifiedError
-            ):
-                if await session.get_claim_value(EmailVerificationClaim) is not True:
-                    # this can happen if the email was "verified" in another browser
-                    # and this session is still outdated - and the user has not
-                    # called the get email verification API yet.
-                    await session.fetch_and_set_claim(
-                        EmailVerificationClaim, user_context
-                    )
-                log_debug_message(
-                    "Email verification email not sent to %s because it is already verified.",
-                    email_info.email,
-                )
-                return GenerateEmailVerifyTokenPostEmailAlreadyVerifiedError()
-
-            if await session.get_claim_value(EmailVerificationClaim) is not False:
-                # this can happen if the email was "unverified" in another browser
-                # and this session is still outdated - and the user has not
-                # called the get email verification API yet.
-                await session.fetch_and_set_claim(EmailVerificationClaim, user_context)
-
-            email_verify_link = get_email_verify_link(
-                api_options.app_info,
-                response.token,
-                tenant_id,
-                api_options.request,
-                user_context,
-            )
-
-            log_debug_message("Sending email verification email to %s", email_info)
-            email_verification_email_delivery_input = VerificationEmailTemplateVars(
-                user=VerificationEmailTemplateVarsUser(user_id, email_info.email),
-                email_verify_link=email_verify_link,
-                tenant_id=tenant_id,
-            )
-            await api_options.email_delivery.ingredient_interface_impl.send_email(
-                email_verification_email_delivery_input, user_context
-            )
-            return GenerateEmailVerifyTokenPostOkResult()
-
-        log_debug_message(
-            "generateEmailVerifyTokenPOST: Returning UNAUTHORISED because the user id provided is unknown"
-        )
-        raise_unauthorised_exception("Unknown User ID provided")
-
-
-class IsVerifiedSCV(SessionClaimValidator):
-    def __init__(
-        self,
-        id_: str,
-        claim: EmailVerificationClaimClass,
-        ev_claim_validators: EmailVerificationClaimValidators,
-        refetch_time_on_false_in_seconds: int,
-        max_age_in_seconds: Optional[int],
-    ):
-        super().__init__(id_)
-        self.claim: EmailVerificationClaimClass = claim
-        self.ev_claim_validators = ev_claim_validators
-        self.refetch_time_on_false_in_ms = refetch_time_on_false_in_seconds * 1000
-        self.max_age_in_sec = max_age_in_seconds
-
-    async def validate(
-        self, payload: JSONObject, user_context: Dict[str, Any]
-    ) -> ClaimValidationResult:
-        return await self.ev_claim_validators.has_value(
-            True, self.max_age_in_sec
-        ).validate(payload, user_context)
-
-    def should_refetch(
-        self, payload: JSONObject, user_context: Dict[str, Any]
-    ) -> MaybeAwaitable[bool]:
-        value = self.claim.get_value_from_payload(payload, user_context)
-        if value is None:
-            return True
-
-        current_time = get_timestamp_ms()
-        last_refetch_time = self.claim.get_last_refetch_time(payload, user_context)
-        assert last_refetch_time is not None
-
-        if self.max_age_in_sec is not None:
-            if last_refetch_time < current_time - self.max_age_in_sec * 1000:
-                return True
-
-        if value is False:
-            if last_refetch_time < current_time - self.refetch_time_on_false_in_ms:
-                return True
-
-        return False
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class APIImplementation -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class APIImplementation(APIInterface):
-    async def email_verify_post(
-        self,
-        token: str,
-        session: Optional[SessionContainer],
-        tenant_id: str,
-        api_options: APIOptions,
-        user_context: Dict[str, Any],
-    ) -> Union[EmailVerifyPostOkResult, EmailVerifyPostInvalidTokenError]:
-
-        response = await api_options.recipe_implementation.verify_email_using_token(
-            token, tenant_id, user_context
-        )
-        if isinstance(response, VerifyEmailUsingTokenOkResult):
-            if session is not None:
-                try:
-                    await session.fetch_and_set_claim(
-                        EmailVerificationClaim, user_context
-                    )
-                except Exception as e:
-                    # This should never happen since we have just set the status above
-                    if str(e) == "UNKNOWN_USER_ID":
-                        log_debug_message(
-                            "verifyEmailPOST: Returning UNAUTHORISED because the user id provided is unknown"
-                        )
-                        raise_unauthorised_exception("Unknown User ID provided")
-                    else:
-                        raise e
-
-            return EmailVerifyPostOkResult(response.user)
-        return EmailVerifyPostInvalidTokenError()
-
-    async def is_email_verified_get(
-        self,
-        session: SessionContainer,
-        api_options: APIOptions,
-        user_context: Dict[str, Any],
-    ) -> IsEmailVerifiedGetOkResult:
-        try:
-            await session.fetch_and_set_claim(EmailVerificationClaim, user_context)
-        except Exception as e:
-            if str(e) == "UNKNOWN_USER_ID":
-                log_debug_message(
-                    "isEmailVerifiedGET: Returning UNAUTHORISED because the user id provided is unknown"
-                )
-                raise_unauthorised_exception("Unknown User ID provided")
-            else:
-                raise e
-
-        is_verified = await session.get_claim_value(
-            EmailVerificationClaim, user_context
-        )
-
-        if is_verified is None:
-            raise Exception(
-                "Should never come here: EmailVerificationClaim failed to set value"
-            )
-
-        return IsEmailVerifiedGetOkResult(is_verified)
-
-    async def generate_email_verify_token_post(
-        self,
-        session: SessionContainer,
-        api_options: APIOptions,
-        user_context: Dict[str, Any],
-    ) -> Union[
-        GenerateEmailVerifyTokenPostOkResult,
-        GenerateEmailVerifyTokenPostEmailAlreadyVerifiedError,
-    ]:
-        user_id = session.get_user_id(user_context)
-        email_info = await EmailVerificationRecipe.get_instance().get_email_for_user_id(
-            user_id, user_context
-        )
-        tenant_id = session.get_tenant_id()
-
-        if isinstance(email_info, EmailDoesNotExistError):
-            log_debug_message(
-                "Email verification email not sent to user %s because it doesn't have an email address.",
-                user_id,
-            )
-            return GenerateEmailVerifyTokenPostEmailAlreadyVerifiedError()
-        if isinstance(email_info, GetEmailForUserIdOkResult):
-            response = (
-                await api_options.recipe_implementation.create_email_verification_token(
-                    user_id,
-                    email_info.email,
-                    tenant_id,
-                    user_context,
-                )
-            )
-
-            if isinstance(
-                response, CreateEmailVerificationTokenEmailAlreadyVerifiedError
-            ):
-                if await session.get_claim_value(EmailVerificationClaim) is not True:
-                    # this can happen if the email was "verified" in another browser
-                    # and this session is still outdated - and the user has not
-                    # called the get email verification API yet.
-                    await session.fetch_and_set_claim(
-                        EmailVerificationClaim, user_context
-                    )
-                log_debug_message(
-                    "Email verification email not sent to %s because it is already verified.",
-                    email_info.email,
-                )
-                return GenerateEmailVerifyTokenPostEmailAlreadyVerifiedError()
-
-            if await session.get_claim_value(EmailVerificationClaim) is not False:
-                # this can happen if the email was "unverified" in another browser
-                # and this session is still outdated - and the user has not
-                # called the get email verification API yet.
-                await session.fetch_and_set_claim(EmailVerificationClaim, user_context)
-
-            email_verify_link = get_email_verify_link(
-                api_options.app_info,
-                response.token,
-                tenant_id,
-                api_options.request,
-                user_context,
-            )
-
-            log_debug_message("Sending email verification email to %s", email_info)
-            email_verification_email_delivery_input = VerificationEmailTemplateVars(
-                user=VerificationEmailTemplateVarsUser(user_id, email_info.email),
-                email_verify_link=email_verify_link,
-                tenant_id=tenant_id,
-            )
-            await api_options.email_delivery.ingredient_interface_impl.send_email(
-                email_verification_email_delivery_input, user_context
-            )
-            return GenerateEmailVerifyTokenPostOkResult()
-
-        log_debug_message(
-            "generateEmailVerifyTokenPOST: Returning UNAUTHORISED because the user id provided is unknown"
-        )
-        raise_unauthorised_exception("Unknown User ID provided")
-
-

Ancestors

- -

Methods

-
-
-async def email_verify_post(self, token: str, session: Optional[SessionContainer], tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[EmailVerifyPostOkResultEmailVerifyPostInvalidTokenError] -
-
-
-
- -Expand source code - -
async def email_verify_post(
-    self,
-    token: str,
-    session: Optional[SessionContainer],
-    tenant_id: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-) -> Union[EmailVerifyPostOkResult, EmailVerifyPostInvalidTokenError]:
-
-    response = await api_options.recipe_implementation.verify_email_using_token(
-        token, tenant_id, user_context
-    )
-    if isinstance(response, VerifyEmailUsingTokenOkResult):
-        if session is not None:
-            try:
-                await session.fetch_and_set_claim(
-                    EmailVerificationClaim, user_context
-                )
-            except Exception as e:
-                # This should never happen since we have just set the status above
-                if str(e) == "UNKNOWN_USER_ID":
-                    log_debug_message(
-                        "verifyEmailPOST: Returning UNAUTHORISED because the user id provided is unknown"
-                    )
-                    raise_unauthorised_exception("Unknown User ID provided")
-                else:
-                    raise e
-
-        return EmailVerifyPostOkResult(response.user)
-    return EmailVerifyPostInvalidTokenError()
-
-
-
-async def generate_email_verify_token_post(self, session: SessionContainer, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[GenerateEmailVerifyTokenPostOkResultGenerateEmailVerifyTokenPostEmailAlreadyVerifiedError] -
-
-
-
- -Expand source code - -
async def generate_email_verify_token_post(
-    self,
-    session: SessionContainer,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-) -> Union[
-    GenerateEmailVerifyTokenPostOkResult,
-    GenerateEmailVerifyTokenPostEmailAlreadyVerifiedError,
-]:
-    user_id = session.get_user_id(user_context)
-    email_info = await EmailVerificationRecipe.get_instance().get_email_for_user_id(
-        user_id, user_context
-    )
-    tenant_id = session.get_tenant_id()
-
-    if isinstance(email_info, EmailDoesNotExistError):
-        log_debug_message(
-            "Email verification email not sent to user %s because it doesn't have an email address.",
-            user_id,
-        )
-        return GenerateEmailVerifyTokenPostEmailAlreadyVerifiedError()
-    if isinstance(email_info, GetEmailForUserIdOkResult):
-        response = (
-            await api_options.recipe_implementation.create_email_verification_token(
-                user_id,
-                email_info.email,
-                tenant_id,
-                user_context,
-            )
-        )
-
-        if isinstance(
-            response, CreateEmailVerificationTokenEmailAlreadyVerifiedError
-        ):
-            if await session.get_claim_value(EmailVerificationClaim) is not True:
-                # this can happen if the email was "verified" in another browser
-                # and this session is still outdated - and the user has not
-                # called the get email verification API yet.
-                await session.fetch_and_set_claim(
-                    EmailVerificationClaim, user_context
-                )
-            log_debug_message(
-                "Email verification email not sent to %s because it is already verified.",
-                email_info.email,
-            )
-            return GenerateEmailVerifyTokenPostEmailAlreadyVerifiedError()
-
-        if await session.get_claim_value(EmailVerificationClaim) is not False:
-            # this can happen if the email was "unverified" in another browser
-            # and this session is still outdated - and the user has not
-            # called the get email verification API yet.
-            await session.fetch_and_set_claim(EmailVerificationClaim, user_context)
-
-        email_verify_link = get_email_verify_link(
-            api_options.app_info,
-            response.token,
-            tenant_id,
-            api_options.request,
-            user_context,
-        )
-
-        log_debug_message("Sending email verification email to %s", email_info)
-        email_verification_email_delivery_input = VerificationEmailTemplateVars(
-            user=VerificationEmailTemplateVarsUser(user_id, email_info.email),
-            email_verify_link=email_verify_link,
-            tenant_id=tenant_id,
-        )
-        await api_options.email_delivery.ingredient_interface_impl.send_email(
-            email_verification_email_delivery_input, user_context
-        )
-        return GenerateEmailVerifyTokenPostOkResult()
-
-    log_debug_message(
-        "generateEmailVerifyTokenPOST: Returning UNAUTHORISED because the user id provided is unknown"
-    )
-    raise_unauthorised_exception("Unknown User ID provided")
-
-
-
-async def is_email_verified_get(self, session: SessionContainer, api_options: APIOptions, user_context: Dict[str, Any]) ‑> IsEmailVerifiedGetOkResult -
-
-
-
- -Expand source code - -
async def is_email_verified_get(
-    self,
-    session: SessionContainer,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-) -> IsEmailVerifiedGetOkResult:
-    try:
-        await session.fetch_and_set_claim(EmailVerificationClaim, user_context)
-    except Exception as e:
-        if str(e) == "UNKNOWN_USER_ID":
-            log_debug_message(
-                "isEmailVerifiedGET: Returning UNAUTHORISED because the user id provided is unknown"
-            )
-            raise_unauthorised_exception("Unknown User ID provided")
-        else:
-            raise e
-
-    is_verified = await session.get_claim_value(
-        EmailVerificationClaim, user_context
-    )
-
-    if is_verified is None:
-        raise Exception(
-            "Should never come here: EmailVerificationClaim failed to set value"
-        )
-
-    return IsEmailVerifiedGetOkResult(is_verified)
-
-
-
-
-
-class EmailVerificationClaimClass -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-

Args

-
-
key
-
The key to use when storing the claim in the payload.
-
fetch_value
-
a method that fetches the current value of this claim for the user. -A None return value signifies that we don't want to update the claim payload and or the claim value is -not present in the database. For example, this can happen with a second factor auth claim, where we -don't want to add the claim to the session automatically
-
-
- -Expand source code - -
class EmailVerificationClaimClass(BooleanClaim):
-    def __init__(self):
-        async def fetch_value(
-            user_id: str, _tenant_id: str, user_context: Dict[str, Any]
-        ) -> bool:
-            recipe = EmailVerificationRecipe.get_instance()
-            email_info = await recipe.get_email_for_user_id(user_id, user_context)
-
-            if isinstance(email_info, GetEmailForUserIdOkResult):
-                return await recipe.recipe_implementation.is_email_verified(
-                    user_id, email_info.email, user_context
-                )
-            if isinstance(email_info, EmailDoesNotExistError):
-                # we consider people without email addresses as validated
-                return True
-            raise Exception("UNKNOWN_USER_ID")
-
-        super().__init__("st-ev", fetch_value, None)
-
-        self.validators = EmailVerificationClaimValidators(claim=self)
-
-

Ancestors

- -

Inherited members

- -
-
-class EmailVerificationClaimValidators -(claim: EmailVerificationClaimClass) -
-
-

Abstract base class for generic types.

-

A generic type is typically declared by inheriting from -this class parameterized with one or more type variables. -For example, a generic mapping type might be defined as::

-

class Mapping(Generic[KT, VT]): -def getitem(self, key: KT) -> VT: -… -# Etc.

-

This class can then be used as follows::

-

def lookup_name(mapping: Mapping[KT, VT], key: KT, default: VT) -> VT: -try: -return mapping[key] -except KeyError: -return default

-
- -Expand source code - -
class EmailVerificationClaimValidators(BooleanClaimValidators):
-    def __init__(self, claim: EmailVerificationClaimClass):
-        super().__init__(claim, None)
-
-    def is_verified(
-        self,
-        refetch_time_on_false_in_seconds: int = 10,
-        max_age_in_seconds: Optional[int] = None,
-        id_: Optional[str] = None,
-    ) -> SessionClaimValidator:
-
-        assert isinstance(self.claim, EmailVerificationClaimClass)
-        return IsVerifiedSCV(
-            (id_ or self.claim.key),
-            self.claim,
-            self,
-            refetch_time_on_false_in_seconds,
-            max_age_in_seconds,
-        )
-
-

Ancestors

- -

Methods

-
-
-def is_verified(self, refetch_time_on_false_in_seconds: int = 10, max_age_in_seconds: Optional[int] = None, id_: Optional[str] = None) ‑> SessionClaimValidator -
-
-
-
- -Expand source code - -
def is_verified(
-    self,
-    refetch_time_on_false_in_seconds: int = 10,
-    max_age_in_seconds: Optional[int] = None,
-    id_: Optional[str] = None,
-) -> SessionClaimValidator:
-
-    assert isinstance(self.claim, EmailVerificationClaimClass)
-    return IsVerifiedSCV(
-        (id_ or self.claim.key),
-        self.claim,
-        self,
-        refetch_time_on_false_in_seconds,
-        max_age_in_seconds,
-    )
-
-
-
-
-
-class EmailVerificationRecipe -(recipe_id: str, app_info: AppInfo, ingredients: EmailVerificationIngredients, mode: MODE_TYPE, email_delivery: Union[EmailDeliveryConfig[EmailTemplateVars], None] = None, get_email_for_user_id: Optional[TypeGetEmailForUserIdFunction] = None, override: Union[OverrideConfig, None] = None) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class EmailVerificationRecipe(RecipeModule):
-    recipe_id = "emailverification"
-    __instance = None
-    email_delivery: EmailDeliveryIngredient[VerificationEmailTemplateVars]
-
-    def __init__(
-        self,
-        recipe_id: str,
-        app_info: AppInfo,
-        ingredients: EmailVerificationIngredients,
-        mode: MODE_TYPE,
-        email_delivery: Union[EmailDeliveryConfig[EmailTemplateVars], None] = None,
-        get_email_for_user_id: Optional[TypeGetEmailForUserIdFunction] = None,
-        override: Union[OverrideConfig, None] = None,
-    ) -> None:
-        super().__init__(recipe_id, app_info)
-        self.config = validate_and_normalise_user_input(
-            app_info,
-            mode,
-            email_delivery,
-            get_email_for_user_id,
-            override,
-        )
-
-        recipe_implementation = RecipeImplementation(
-            Querier.get_instance(recipe_id), self.config
-        )
-        self.recipe_implementation = (
-            recipe_implementation
-            if self.config.override.functions is None
-            else self.config.override.functions(recipe_implementation)
-        )
-
-        api_implementation = APIImplementation()
-        self.api_implementation = (
-            api_implementation
-            if self.config.override.apis is None
-            else self.config.override.apis(api_implementation)
-        )
-
-        email_delivery_ingredient = ingredients.email_delivery
-        if email_delivery_ingredient is None:
-            self.email_delivery = EmailDeliveryIngredient(
-                self.config.get_email_delivery_config()
-            )
-        else:
-            self.email_delivery = email_delivery_ingredient
-
-        self.get_email_for_user_id_funcs_from_other_recipes: List[
-            TypeGetEmailForUserIdFunction
-        ] = []
-
-    def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
-        return isinstance(err, SuperTokensError) and isinstance(
-            err, SuperTokensEmailVerificationError
-        )
-
-    def get_apis_handled(self) -> List[APIHandled]:
-        return [
-            APIHandled(
-                NormalisedURLPath(USER_EMAIL_VERIFY_TOKEN),
-                "post",
-                USER_EMAIL_VERIFY_TOKEN,
-                self.api_implementation.disable_generate_email_verify_token_post,
-            ),
-            APIHandled(
-                NormalisedURLPath(USER_EMAIL_VERIFY),
-                "post",
-                USER_EMAIL_VERIFY,
-                self.api_implementation.disable_email_verify_post,
-            ),
-            APIHandled(
-                NormalisedURLPath(USER_EMAIL_VERIFY),
-                "get",
-                USER_EMAIL_VERIFY,
-                self.api_implementation.disable_is_email_verified_get,
-            ),
-        ]
-
-    async def handle_api_request(
-        self,
-        request_id: str,
-        tenant_id: str,
-        request: BaseRequest,
-        path: NormalisedURLPath,
-        method: str,
-        response: BaseResponse,
-        user_context: Dict[str, Any],
-    ) -> Union[BaseResponse, None]:
-        api_options = APIOptions(
-            request,
-            response,
-            self.recipe_id,
-            self.config,
-            self.recipe_implementation,
-            self.get_app_info(),
-            self.email_delivery,
-        )
-        if request_id == USER_EMAIL_VERIFY_TOKEN:
-            return await handle_generate_email_verify_token_api(
-                self.api_implementation, api_options, user_context
-            )
-        return await handle_email_verify_api(
-            self.api_implementation, tenant_id, api_options, user_context
-        )
-
-    async def handle_error(
-        self,
-        request: BaseRequest,
-        err: SuperTokensError,
-        response: BaseResponse,
-        user_context: Dict[str, Any],
-    ) -> BaseResponse:
-        if isinstance(err, EmailVerificationInvalidTokenError):
-            response.set_json_content(
-                {"status": "EMAIL_VERIFICATION_INVALID_TOKEN_ERROR"}
-            )
-            return response
-        response.set_json_content({"status": "EMAIL_ALREADY_VERIFIED_ERROR"})
-        return response
-
-    def get_all_cors_headers(self) -> List[str]:
-        return []
-
-    @staticmethod
-    def init(
-        mode: MODE_TYPE,
-        email_delivery: Union[EmailDeliveryConfig[EmailTemplateVars], None] = None,
-        get_email_for_user_id: Optional[TypeGetEmailForUserIdFunction] = None,
-        override: Union[OverrideConfig, None] = None,
-    ):
-        def func(app_info: AppInfo) -> EmailVerificationRecipe:
-            if EmailVerificationRecipe.__instance is None:
-                ingredients = EmailVerificationIngredients(email_delivery=None)
-                EmailVerificationRecipe.__instance = EmailVerificationRecipe(
-                    EmailVerificationRecipe.recipe_id,
-                    app_info,
-                    ingredients,
-                    mode,
-                    email_delivery,
-                    get_email_for_user_id,
-                    override,
-                )
-
-                def callback():
-                    SessionRecipe.get_instance().add_claim_from_other_recipe(
-                        EmailVerificationClaim
-                    )
-                    if mode == "REQUIRED":
-                        SessionRecipe.get_instance().add_claim_validator_from_other_recipe(
-                            EmailVerificationClaim.validators.is_verified()
-                        )
-
-                PostSTInitCallbacks.add_post_init_callback(callback)
-
-                return EmailVerificationRecipe.__instance
-            raise_general_exception(
-                "Emailverification recipe has already been initialised. Please check your code for bugs."
-            )
-
-        return func
-
-    @staticmethod
-    def get_instance() -> EmailVerificationRecipe:
-        if EmailVerificationRecipe.__instance is not None:
-            return EmailVerificationRecipe.__instance
-        raise_general_exception(
-            "Initialisation not done. Did you forget to call the SuperTokens.init function?"
-        )
-
-    @staticmethod
-    def get_instance_optional() -> Optional[EmailVerificationRecipe]:
-        return EmailVerificationRecipe.__instance
-
-    @staticmethod
-    def reset():
-        if ("SUPERTOKENS_ENV" not in environ) or (
-            environ["SUPERTOKENS_ENV"] != "testing"
-        ):
-            raise_general_exception("calling testing function in non testing env")
-        EmailVerificationRecipe.__instance = None
-
-    async def get_email_for_user_id(
-        self, user_id: str, user_context: Dict[str, Any]
-    ) -> Union[GetEmailForUserIdOkResult, EmailDoesNotExistError, UnknownUserIdError]:
-        if self.config.get_email_for_user_id is not None:
-            res = await self.config.get_email_for_user_id(user_id, user_context)
-            if not isinstance(res, UnknownUserIdError):
-                return res
-
-        for f in self.get_email_for_user_id_funcs_from_other_recipes:
-            res = await f(user_id, user_context)
-            if not isinstance(res, UnknownUserIdError):
-                return res
-
-        return UnknownUserIdError()
-
-    def add_get_email_for_user_id_func(self, f: TypeGetEmailForUserIdFunction):
-        self.get_email_for_user_id_funcs_from_other_recipes.append(f)
-
-

Ancestors

- -

Class variables

-
-
var email_deliveryEmailDeliveryIngredient[VerificationEmailTemplateVars]
-
-
-
-
var recipe_id
-
-
-
-
-

Static methods

-
-
-def get_instance() ‑> EmailVerificationRecipe -
-
-
-
- -Expand source code - -
@staticmethod
-def get_instance() -> EmailVerificationRecipe:
-    if EmailVerificationRecipe.__instance is not None:
-        return EmailVerificationRecipe.__instance
-    raise_general_exception(
-        "Initialisation not done. Did you forget to call the SuperTokens.init function?"
-    )
-
-
-
-def get_instance_optional() ‑> Optional[EmailVerificationRecipe] -
-
-
-
- -Expand source code - -
@staticmethod
-def get_instance_optional() -> Optional[EmailVerificationRecipe]:
-    return EmailVerificationRecipe.__instance
-
-
-
-def init(mode: MODE_TYPE, email_delivery: Union[EmailDeliveryConfig[EmailTemplateVars], None] = None, get_email_for_user_id: Optional[TypeGetEmailForUserIdFunction] = None, override: Union[OverrideConfig, None] = None) -
-
-
-
- -Expand source code - -
@staticmethod
-def init(
-    mode: MODE_TYPE,
-    email_delivery: Union[EmailDeliveryConfig[EmailTemplateVars], None] = None,
-    get_email_for_user_id: Optional[TypeGetEmailForUserIdFunction] = None,
-    override: Union[OverrideConfig, None] = None,
-):
-    def func(app_info: AppInfo) -> EmailVerificationRecipe:
-        if EmailVerificationRecipe.__instance is None:
-            ingredients = EmailVerificationIngredients(email_delivery=None)
-            EmailVerificationRecipe.__instance = EmailVerificationRecipe(
-                EmailVerificationRecipe.recipe_id,
-                app_info,
-                ingredients,
-                mode,
-                email_delivery,
-                get_email_for_user_id,
-                override,
-            )
-
-            def callback():
-                SessionRecipe.get_instance().add_claim_from_other_recipe(
-                    EmailVerificationClaim
-                )
-                if mode == "REQUIRED":
-                    SessionRecipe.get_instance().add_claim_validator_from_other_recipe(
-                        EmailVerificationClaim.validators.is_verified()
-                    )
-
-            PostSTInitCallbacks.add_post_init_callback(callback)
-
-            return EmailVerificationRecipe.__instance
-        raise_general_exception(
-            "Emailverification recipe has already been initialised. Please check your code for bugs."
-        )
-
-    return func
-
-
-
-def reset() -
-
-
-
- -Expand source code - -
@staticmethod
-def reset():
-    if ("SUPERTOKENS_ENV" not in environ) or (
-        environ["SUPERTOKENS_ENV"] != "testing"
-    ):
-        raise_general_exception("calling testing function in non testing env")
-    EmailVerificationRecipe.__instance = None
-
-
-
-

Methods

-
-
-def add_get_email_for_user_id_func(self, f: TypeGetEmailForUserIdFunction) -
-
-
-
- -Expand source code - -
def add_get_email_for_user_id_func(self, f: TypeGetEmailForUserIdFunction):
-    self.get_email_for_user_id_funcs_from_other_recipes.append(f)
-
-
-
-def get_all_cors_headers(self) ‑> List[str] -
-
-
-
- -Expand source code - -
def get_all_cors_headers(self) -> List[str]:
-    return []
-
-
-
-def get_apis_handled(self) ‑> List[APIHandled] -
-
-
-
- -Expand source code - -
def get_apis_handled(self) -> List[APIHandled]:
-    return [
-        APIHandled(
-            NormalisedURLPath(USER_EMAIL_VERIFY_TOKEN),
-            "post",
-            USER_EMAIL_VERIFY_TOKEN,
-            self.api_implementation.disable_generate_email_verify_token_post,
-        ),
-        APIHandled(
-            NormalisedURLPath(USER_EMAIL_VERIFY),
-            "post",
-            USER_EMAIL_VERIFY,
-            self.api_implementation.disable_email_verify_post,
-        ),
-        APIHandled(
-            NormalisedURLPath(USER_EMAIL_VERIFY),
-            "get",
-            USER_EMAIL_VERIFY,
-            self.api_implementation.disable_is_email_verified_get,
-        ),
-    ]
-
-
-
-async def get_email_for_user_id(self, user_id: str, user_context: Dict[str, Any]) ‑> Union[UnknownUserIdErrorGetEmailForUserIdOkResultEmailDoesNotExistError] -
-
-
-
- -Expand source code - -
async def get_email_for_user_id(
-    self, user_id: str, user_context: Dict[str, Any]
-) -> Union[GetEmailForUserIdOkResult, EmailDoesNotExistError, UnknownUserIdError]:
-    if self.config.get_email_for_user_id is not None:
-        res = await self.config.get_email_for_user_id(user_id, user_context)
-        if not isinstance(res, UnknownUserIdError):
-            return res
-
-    for f in self.get_email_for_user_id_funcs_from_other_recipes:
-        res = await f(user_id, user_context)
-        if not isinstance(res, UnknownUserIdError):
-            return res
-
-    return UnknownUserIdError()
-
-
-
-async def handle_api_request(self, request_id: str, tenant_id: str, request: BaseRequest, path: NormalisedURLPath, method: str, response: BaseResponse, user_context: Dict[str, Any]) ‑> Union[BaseResponse, None] -
-
-
-
- -Expand source code - -
async def handle_api_request(
-    self,
-    request_id: str,
-    tenant_id: str,
-    request: BaseRequest,
-    path: NormalisedURLPath,
-    method: str,
-    response: BaseResponse,
-    user_context: Dict[str, Any],
-) -> Union[BaseResponse, None]:
-    api_options = APIOptions(
-        request,
-        response,
-        self.recipe_id,
-        self.config,
-        self.recipe_implementation,
-        self.get_app_info(),
-        self.email_delivery,
-    )
-    if request_id == USER_EMAIL_VERIFY_TOKEN:
-        return await handle_generate_email_verify_token_api(
-            self.api_implementation, api_options, user_context
-        )
-    return await handle_email_verify_api(
-        self.api_implementation, tenant_id, api_options, user_context
-    )
-
-
-
-async def handle_error(self, request: BaseRequest, err: SuperTokensError, response: BaseResponse, user_context: Dict[str, Any]) ‑> BaseResponse -
-
-
-
- -Expand source code - -
async def handle_error(
-    self,
-    request: BaseRequest,
-    err: SuperTokensError,
-    response: BaseResponse,
-    user_context: Dict[str, Any],
-) -> BaseResponse:
-    if isinstance(err, EmailVerificationInvalidTokenError):
-        response.set_json_content(
-            {"status": "EMAIL_VERIFICATION_INVALID_TOKEN_ERROR"}
-        )
-        return response
-    response.set_json_content({"status": "EMAIL_ALREADY_VERIFIED_ERROR"})
-    return response
-
-
-
-def is_error_from_this_recipe_based_on_instance(self, err: Exception) ‑> bool -
-
-
-
- -Expand source code - -
def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
-    return isinstance(err, SuperTokensError) and isinstance(
-        err, SuperTokensEmailVerificationError
-    )
-
-
-
-
-
-class IsVerifiedSCV -(id_: str, claim: EmailVerificationClaimClass, ev_claim_validators: EmailVerificationClaimValidators, refetch_time_on_false_in_seconds: int, max_age_in_seconds: Optional[int]) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class IsVerifiedSCV(SessionClaimValidator):
-    def __init__(
-        self,
-        id_: str,
-        claim: EmailVerificationClaimClass,
-        ev_claim_validators: EmailVerificationClaimValidators,
-        refetch_time_on_false_in_seconds: int,
-        max_age_in_seconds: Optional[int],
-    ):
-        super().__init__(id_)
-        self.claim: EmailVerificationClaimClass = claim
-        self.ev_claim_validators = ev_claim_validators
-        self.refetch_time_on_false_in_ms = refetch_time_on_false_in_seconds * 1000
-        self.max_age_in_sec = max_age_in_seconds
-
-    async def validate(
-        self, payload: JSONObject, user_context: Dict[str, Any]
-    ) -> ClaimValidationResult:
-        return await self.ev_claim_validators.has_value(
-            True, self.max_age_in_sec
-        ).validate(payload, user_context)
-
-    def should_refetch(
-        self, payload: JSONObject, user_context: Dict[str, Any]
-    ) -> MaybeAwaitable[bool]:
-        value = self.claim.get_value_from_payload(payload, user_context)
-        if value is None:
-            return True
-
-        current_time = get_timestamp_ms()
-        last_refetch_time = self.claim.get_last_refetch_time(payload, user_context)
-        assert last_refetch_time is not None
-
-        if self.max_age_in_sec is not None:
-            if last_refetch_time < current_time - self.max_age_in_sec * 1000:
-                return True
-
-        if value is False:
-            if last_refetch_time < current_time - self.refetch_time_on_false_in_ms:
-                return True
-
-        return False
-
-

Ancestors

- -

Methods

-
-
-def should_refetch(self, payload: JSONObject, user_context: Dict[str, Any]) ‑> Union[Awaitable[bool], bool] -
-
-
-
- -Expand source code - -
def should_refetch(
-    self, payload: JSONObject, user_context: Dict[str, Any]
-) -> MaybeAwaitable[bool]:
-    value = self.claim.get_value_from_payload(payload, user_context)
-    if value is None:
-        return True
-
-    current_time = get_timestamp_ms()
-    last_refetch_time = self.claim.get_last_refetch_time(payload, user_context)
-    assert last_refetch_time is not None
-
-    if self.max_age_in_sec is not None:
-        if last_refetch_time < current_time - self.max_age_in_sec * 1000:
-            return True
-
-    if value is False:
-        if last_refetch_time < current_time - self.refetch_time_on_false_in_ms:
-            return True
-
-    return False
-
-
-
-async def validate(self, payload: JSONObject, user_context: Dict[str, Any]) ‑> ClaimValidationResult -
-
-
-
- -Expand source code - -
async def validate(
-    self, payload: JSONObject, user_context: Dict[str, Any]
-) -> ClaimValidationResult:
-    return await self.ev_claim_validators.has_value(
-        True, self.max_age_in_sec
-    ).validate(payload, user_context)
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailverification/recipe_implementation.html b/html/supertokens_python/recipe/emailverification/recipe_implementation.html deleted file mode 100644 index b5aba3c97..000000000 --- a/html/supertokens_python/recipe/emailverification/recipe_implementation.html +++ /dev/null @@ -1,372 +0,0 @@ - - - - - - -supertokens_python.recipe.emailverification.recipe_implementation API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.emailverification.recipe_implementation

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from typing import TYPE_CHECKING, Any, Dict, Union
-
-from supertokens_python.normalised_url_path import NormalisedURLPath
-
-from .interfaces import (
-    CreateEmailVerificationTokenEmailAlreadyVerifiedError,
-    CreateEmailVerificationTokenOkResult,
-    RecipeInterface,
-    RevokeEmailVerificationTokensOkResult,
-    UnverifyEmailOkResult,
-    VerifyEmailUsingTokenInvalidTokenError,
-    VerifyEmailUsingTokenOkResult,
-)
-from .types import User
-
-if TYPE_CHECKING:
-    from supertokens_python.querier import Querier
-
-    from .utils import EmailVerificationConfig
-
-
-class RecipeImplementation(RecipeInterface):
-    def __init__(self, querier: Querier, config: EmailVerificationConfig):
-        super().__init__()
-        self.querier = querier
-        self.config = config
-
-    async def create_email_verification_token(
-        self, user_id: str, email: str, tenant_id: str, user_context: Dict[str, Any]
-    ) -> Union[
-        CreateEmailVerificationTokenOkResult,
-        CreateEmailVerificationTokenEmailAlreadyVerifiedError,
-    ]:
-        data = {"userId": user_id, "email": email}
-        response = await self.querier.send_post_request(
-            NormalisedURLPath(f"{tenant_id}/recipe/user/email/verify/token"),
-            data,
-            user_context,
-        )
-        if "status" in response and response["status"] == "OK":
-            return CreateEmailVerificationTokenOkResult(response["token"])
-        return CreateEmailVerificationTokenEmailAlreadyVerifiedError()
-
-    async def verify_email_using_token(
-        self, token: str, tenant_id: str, user_context: Dict[str, Any]
-    ) -> Union[VerifyEmailUsingTokenOkResult, VerifyEmailUsingTokenInvalidTokenError]:
-        data = {"method": "token", "token": token}
-        response = await self.querier.send_post_request(
-            NormalisedURLPath(f"{tenant_id}/recipe/user/email/verify"),
-            data,
-            user_context,
-        )
-        if "status" in response and response["status"] == "OK":
-            return VerifyEmailUsingTokenOkResult(
-                User(response["userId"], response["email"])
-            )
-        return VerifyEmailUsingTokenInvalidTokenError()
-
-    async def is_email_verified(
-        self, user_id: str, email: str, user_context: Dict[str, Any]
-    ) -> bool:
-        params = {"userId": user_id, "email": email}
-        response = await self.querier.send_get_request(
-            NormalisedURLPath("/recipe/user/email/verify"), params, user_context
-        )
-        return response["isVerified"]
-
-    async def revoke_email_verification_tokens(
-        self, user_id: str, email: str, tenant_id: str, user_context: Dict[str, Any]
-    ) -> RevokeEmailVerificationTokensOkResult:
-        data = {"userId": user_id, "email": email}
-        await self.querier.send_post_request(
-            NormalisedURLPath(f"{tenant_id}/recipe/user/email/verify/token/remove"),
-            data,
-            user_context,
-        )
-        return RevokeEmailVerificationTokensOkResult()
-
-    async def unverify_email(
-        self, user_id: str, email: str, user_context: Dict[str, Any]
-    ) -> UnverifyEmailOkResult:
-        data = {"userId": user_id, "email": email}
-        await self.querier.send_post_request(
-            NormalisedURLPath("/recipe/user/email/verify/remove"), data, user_context
-        )
-        return UnverifyEmailOkResult()
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class RecipeImplementation -(querier: Querier, config: EmailVerificationConfig) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class RecipeImplementation(RecipeInterface):
-    def __init__(self, querier: Querier, config: EmailVerificationConfig):
-        super().__init__()
-        self.querier = querier
-        self.config = config
-
-    async def create_email_verification_token(
-        self, user_id: str, email: str, tenant_id: str, user_context: Dict[str, Any]
-    ) -> Union[
-        CreateEmailVerificationTokenOkResult,
-        CreateEmailVerificationTokenEmailAlreadyVerifiedError,
-    ]:
-        data = {"userId": user_id, "email": email}
-        response = await self.querier.send_post_request(
-            NormalisedURLPath(f"{tenant_id}/recipe/user/email/verify/token"),
-            data,
-            user_context,
-        )
-        if "status" in response and response["status"] == "OK":
-            return CreateEmailVerificationTokenOkResult(response["token"])
-        return CreateEmailVerificationTokenEmailAlreadyVerifiedError()
-
-    async def verify_email_using_token(
-        self, token: str, tenant_id: str, user_context: Dict[str, Any]
-    ) -> Union[VerifyEmailUsingTokenOkResult, VerifyEmailUsingTokenInvalidTokenError]:
-        data = {"method": "token", "token": token}
-        response = await self.querier.send_post_request(
-            NormalisedURLPath(f"{tenant_id}/recipe/user/email/verify"),
-            data,
-            user_context,
-        )
-        if "status" in response and response["status"] == "OK":
-            return VerifyEmailUsingTokenOkResult(
-                User(response["userId"], response["email"])
-            )
-        return VerifyEmailUsingTokenInvalidTokenError()
-
-    async def is_email_verified(
-        self, user_id: str, email: str, user_context: Dict[str, Any]
-    ) -> bool:
-        params = {"userId": user_id, "email": email}
-        response = await self.querier.send_get_request(
-            NormalisedURLPath("/recipe/user/email/verify"), params, user_context
-        )
-        return response["isVerified"]
-
-    async def revoke_email_verification_tokens(
-        self, user_id: str, email: str, tenant_id: str, user_context: Dict[str, Any]
-    ) -> RevokeEmailVerificationTokensOkResult:
-        data = {"userId": user_id, "email": email}
-        await self.querier.send_post_request(
-            NormalisedURLPath(f"{tenant_id}/recipe/user/email/verify/token/remove"),
-            data,
-            user_context,
-        )
-        return RevokeEmailVerificationTokensOkResult()
-
-    async def unverify_email(
-        self, user_id: str, email: str, user_context: Dict[str, Any]
-    ) -> UnverifyEmailOkResult:
-        data = {"userId": user_id, "email": email}
-        await self.querier.send_post_request(
-            NormalisedURLPath("/recipe/user/email/verify/remove"), data, user_context
-        )
-        return UnverifyEmailOkResult()
-
-

Ancestors

- -

Methods

-
-
-async def create_email_verification_token(self, user_id: str, email: str, tenant_id: str, user_context: Dict[str, Any]) ‑> Union[CreateEmailVerificationTokenOkResultCreateEmailVerificationTokenEmailAlreadyVerifiedError] -
-
-
-
- -Expand source code - -
async def create_email_verification_token(
-    self, user_id: str, email: str, tenant_id: str, user_context: Dict[str, Any]
-) -> Union[
-    CreateEmailVerificationTokenOkResult,
-    CreateEmailVerificationTokenEmailAlreadyVerifiedError,
-]:
-    data = {"userId": user_id, "email": email}
-    response = await self.querier.send_post_request(
-        NormalisedURLPath(f"{tenant_id}/recipe/user/email/verify/token"),
-        data,
-        user_context,
-    )
-    if "status" in response and response["status"] == "OK":
-        return CreateEmailVerificationTokenOkResult(response["token"])
-    return CreateEmailVerificationTokenEmailAlreadyVerifiedError()
-
-
-
-async def is_email_verified(self, user_id: str, email: str, user_context: Dict[str, Any]) ‑> bool -
-
-
-
- -Expand source code - -
async def is_email_verified(
-    self, user_id: str, email: str, user_context: Dict[str, Any]
-) -> bool:
-    params = {"userId": user_id, "email": email}
-    response = await self.querier.send_get_request(
-        NormalisedURLPath("/recipe/user/email/verify"), params, user_context
-    )
-    return response["isVerified"]
-
-
-
-async def revoke_email_verification_tokens(self, user_id: str, email: str, tenant_id: str, user_context: Dict[str, Any]) ‑> RevokeEmailVerificationTokensOkResult -
-
-
-
- -Expand source code - -
async def revoke_email_verification_tokens(
-    self, user_id: str, email: str, tenant_id: str, user_context: Dict[str, Any]
-) -> RevokeEmailVerificationTokensOkResult:
-    data = {"userId": user_id, "email": email}
-    await self.querier.send_post_request(
-        NormalisedURLPath(f"{tenant_id}/recipe/user/email/verify/token/remove"),
-        data,
-        user_context,
-    )
-    return RevokeEmailVerificationTokensOkResult()
-
-
-
-async def unverify_email(self, user_id: str, email: str, user_context: Dict[str, Any]) ‑> UnverifyEmailOkResult -
-
-
-
- -Expand source code - -
async def unverify_email(
-    self, user_id: str, email: str, user_context: Dict[str, Any]
-) -> UnverifyEmailOkResult:
-    data = {"userId": user_id, "email": email}
-    await self.querier.send_post_request(
-        NormalisedURLPath("/recipe/user/email/verify/remove"), data, user_context
-    )
-    return UnverifyEmailOkResult()
-
-
-
-async def verify_email_using_token(self, token: str, tenant_id: str, user_context: Dict[str, Any]) ‑> Union[VerifyEmailUsingTokenOkResultVerifyEmailUsingTokenInvalidTokenError] -
-
-
-
- -Expand source code - -
async def verify_email_using_token(
-    self, token: str, tenant_id: str, user_context: Dict[str, Any]
-) -> Union[VerifyEmailUsingTokenOkResult, VerifyEmailUsingTokenInvalidTokenError]:
-    data = {"method": "token", "token": token}
-    response = await self.querier.send_post_request(
-        NormalisedURLPath(f"{tenant_id}/recipe/user/email/verify"),
-        data,
-        user_context,
-    )
-    if "status" in response and response["status"] == "OK":
-        return VerifyEmailUsingTokenOkResult(
-            User(response["userId"], response["email"])
-        )
-    return VerifyEmailUsingTokenInvalidTokenError()
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailverification/syncio/index.html b/html/supertokens_python/recipe/emailverification/syncio/index.html deleted file mode 100644 index fa4c97e09..000000000 --- a/html/supertokens_python/recipe/emailverification/syncio/index.html +++ /dev/null @@ -1,357 +0,0 @@ - - - - - - -supertokens_python.recipe.emailverification.syncio API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.emailverification.syncio

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from typing import Any, Dict, Optional, Union
-
-from supertokens_python.async_to_sync_wrapper import sync
-from supertokens_python.recipe.emailverification.types import EmailTemplateVars
-
-
-def create_email_verification_token(
-    tenant_id: str,
-    user_id: str,
-    email: Optional[str] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-):
-    from supertokens_python.recipe.emailverification.asyncio import (
-        create_email_verification_token,
-    )
-
-    return sync(
-        create_email_verification_token(tenant_id, user_id, email, user_context)
-    )
-
-
-def verify_email_using_token(
-    tenant_id: str,
-    token: str,
-    user_context: Union[None, Dict[str, Any]] = None,
-):
-    from supertokens_python.recipe.emailverification.asyncio import (
-        verify_email_using_token,
-    )
-
-    return sync(verify_email_using_token(tenant_id, token, user_context))
-
-
-def is_email_verified(
-    user_id: str,
-    email: Optional[str] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-):
-    from supertokens_python.recipe.emailverification.asyncio import is_email_verified
-
-    return sync(is_email_verified(user_id, email, user_context))
-
-
-def revoke_email_verification_tokens(
-    tenant_id: str,
-    user_id: str,
-    email: Optional[str] = None,
-    user_context: Optional[Dict[str, Any]] = None,
-):
-    from supertokens_python.recipe.emailverification.asyncio import (
-        revoke_email_verification_tokens,
-    )
-
-    return sync(
-        revoke_email_verification_tokens(tenant_id, user_id, email, user_context)
-    )
-
-
-def unverify_email(
-    user_id: str,
-    email: Optional[str] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-):
-    from supertokens_python.recipe.emailverification.asyncio import unverify_email
-
-    return sync(unverify_email(user_id, email, user_context))
-
-
-def send_email(
-    input_: EmailTemplateVars,
-    user_context: Union[None, Dict[str, Any]] = None,
-):
-    from supertokens_python.recipe.emailverification.asyncio import send_email
-
-    return sync(send_email(input_, user_context))
-
-
-def create_email_verification_link(
-    tenant_id: str,
-    user_id: str,
-    email: Optional[str],
-    user_context: Optional[Dict[str, Any]] = None,
-):
-    from supertokens_python.recipe.emailverification.asyncio import (
-        create_email_verification_link,
-    )
-
-    return sync(create_email_verification_link(tenant_id, user_id, email, user_context))
-
-
-def send_email_verification_email(
-    tenant_id: str,
-    user_id: str,
-    email: Optional[str],
-    user_context: Optional[Dict[str, Any]] = None,
-):
-    from supertokens_python.recipe.emailverification.asyncio import (
-        send_email_verification_email,
-    )
-
-    return sync(send_email_verification_email(tenant_id, user_id, email, user_context))
-
-
-
-
-
-
-
-

Functions

-
- -
-
-
- -Expand source code - -
def create_email_verification_link(
-    tenant_id: str,
-    user_id: str,
-    email: Optional[str],
-    user_context: Optional[Dict[str, Any]] = None,
-):
-    from supertokens_python.recipe.emailverification.asyncio import (
-        create_email_verification_link,
-    )
-
-    return sync(create_email_verification_link(tenant_id, user_id, email, user_context))
-
-
-
-def create_email_verification_token(tenant_id: str, user_id: str, email: Optional[str] = None, user_context: Optional[Dict[str, Any]] = None) -
-
-
-
- -Expand source code - -
def create_email_verification_token(
-    tenant_id: str,
-    user_id: str,
-    email: Optional[str] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-):
-    from supertokens_python.recipe.emailverification.asyncio import (
-        create_email_verification_token,
-    )
-
-    return sync(
-        create_email_verification_token(tenant_id, user_id, email, user_context)
-    )
-
-
-
-def is_email_verified(user_id: str, email: Optional[str] = None, user_context: Optional[Dict[str, Any]] = None) -
-
-
-
- -Expand source code - -
def is_email_verified(
-    user_id: str,
-    email: Optional[str] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-):
-    from supertokens_python.recipe.emailverification.asyncio import is_email_verified
-
-    return sync(is_email_verified(user_id, email, user_context))
-
-
-
-def revoke_email_verification_tokens(tenant_id: str, user_id: str, email: Optional[str] = None, user_context: Optional[Dict[str, Any]] = None) -
-
-
-
- -Expand source code - -
def revoke_email_verification_tokens(
-    tenant_id: str,
-    user_id: str,
-    email: Optional[str] = None,
-    user_context: Optional[Dict[str, Any]] = None,
-):
-    from supertokens_python.recipe.emailverification.asyncio import (
-        revoke_email_verification_tokens,
-    )
-
-    return sync(
-        revoke_email_verification_tokens(tenant_id, user_id, email, user_context)
-    )
-
-
-
-def send_email(input_: VerificationEmailTemplateVars, user_context: Optional[Dict[str, Any]] = None) -
-
-
-
- -Expand source code - -
def send_email(
-    input_: EmailTemplateVars,
-    user_context: Union[None, Dict[str, Any]] = None,
-):
-    from supertokens_python.recipe.emailverification.asyncio import send_email
-
-    return sync(send_email(input_, user_context))
-
-
-
-def send_email_verification_email(tenant_id: str, user_id: str, email: Optional[str], user_context: Optional[Dict[str, Any]] = None) -
-
-
-
- -Expand source code - -
def send_email_verification_email(
-    tenant_id: str,
-    user_id: str,
-    email: Optional[str],
-    user_context: Optional[Dict[str, Any]] = None,
-):
-    from supertokens_python.recipe.emailverification.asyncio import (
-        send_email_verification_email,
-    )
-
-    return sync(send_email_verification_email(tenant_id, user_id, email, user_context))
-
-
-
-def unverify_email(user_id: str, email: Optional[str] = None, user_context: Optional[Dict[str, Any]] = None) -
-
-
-
- -Expand source code - -
def unverify_email(
-    user_id: str,
-    email: Optional[str] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-):
-    from supertokens_python.recipe.emailverification.asyncio import unverify_email
-
-    return sync(unverify_email(user_id, email, user_context))
-
-
-
-def verify_email_using_token(tenant_id: str, token: str, user_context: Optional[Dict[str, Any]] = None) -
-
-
-
- -Expand source code - -
def verify_email_using_token(
-    tenant_id: str,
-    token: str,
-    user_context: Union[None, Dict[str, Any]] = None,
-):
-    from supertokens_python.recipe.emailverification.asyncio import (
-        verify_email_using_token,
-    )
-
-    return sync(verify_email_using_token(tenant_id, token, user_context))
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailverification/types.html b/html/supertokens_python/recipe/emailverification/types.html deleted file mode 100644 index b605b650a..000000000 --- a/html/supertokens_python/recipe/emailverification/types.html +++ /dev/null @@ -1,236 +0,0 @@ - - - - - - -supertokens_python.recipe.emailverification.types API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.emailverification.types

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from typing import Union
-
-from supertokens_python.ingredients.emaildelivery import EmailDeliveryIngredient
-from supertokens_python.ingredients.emaildelivery.types import (
-    SMTPServiceInterface,
-    EmailDeliveryInterface,
-)
-
-
-class User:
-    def __init__(self, user_id: str, email: str):
-        self.user_id = user_id
-        self.email = email
-
-
-class VerificationEmailTemplateVarsUser:
-    def __init__(self, user_id: str, email: str):
-        self.id = user_id
-        self.email = email
-
-
-class VerificationEmailTemplateVars:
-    def __init__(
-        self,
-        user: VerificationEmailTemplateVarsUser,
-        email_verify_link: str,
-        tenant_id: str,
-    ) -> None:
-        self.user = user
-        self.email_verify_link = email_verify_link
-        self.tenant_id = tenant_id
-
-
-# Export:
-EmailTemplateVars = VerificationEmailTemplateVars
-
-SMTPOverrideInput = SMTPServiceInterface[EmailTemplateVars]
-
-EmailDeliveryOverrideInput = EmailDeliveryInterface[EmailTemplateVars]
-
-
-class EmailVerificationIngredients:
-    def __init__(
-        self,
-        email_delivery: Union[EmailDeliveryIngredient[EmailTemplateVars], None] = None,
-    ):
-        self.email_delivery = email_delivery
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class EmailVerificationIngredients -(email_delivery: Union[EmailDeliveryIngredient[VerificationEmailTemplateVars], None] = None) -
-
-
-
- -Expand source code - -
class EmailVerificationIngredients:
-    def __init__(
-        self,
-        email_delivery: Union[EmailDeliveryIngredient[EmailTemplateVars], None] = None,
-    ):
-        self.email_delivery = email_delivery
-
-
-
-class User -(user_id: str, email: str) -
-
-
-
- -Expand source code - -
class User:
-    def __init__(self, user_id: str, email: str):
-        self.user_id = user_id
-        self.email = email
-
-
-
-class VerificationEmailTemplateVars -(user: VerificationEmailTemplateVarsUser, email_verify_link: str, tenant_id: str) -
-
-
-
- -Expand source code - -
class VerificationEmailTemplateVars:
-    def __init__(
-        self,
-        user: VerificationEmailTemplateVarsUser,
-        email_verify_link: str,
-        tenant_id: str,
-    ) -> None:
-        self.user = user
-        self.email_verify_link = email_verify_link
-        self.tenant_id = tenant_id
-
-
-
-class EmailTemplateVars -(user: VerificationEmailTemplateVarsUser, email_verify_link: str, tenant_id: str) -
-
-
-
- -Expand source code - -
class VerificationEmailTemplateVars:
-    def __init__(
-        self,
-        user: VerificationEmailTemplateVarsUser,
-        email_verify_link: str,
-        tenant_id: str,
-    ) -> None:
-        self.user = user
-        self.email_verify_link = email_verify_link
-        self.tenant_id = tenant_id
-
-
-
-class VerificationEmailTemplateVarsUser -(user_id: str, email: str) -
-
-
-
- -Expand source code - -
class VerificationEmailTemplateVarsUser:
-    def __init__(self, user_id: str, email: str):
-        self.id = user_id
-        self.email = email
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailverification/utils.html b/html/supertokens_python/recipe/emailverification/utils.html deleted file mode 100644 index d51278ddf..000000000 --- a/html/supertokens_python/recipe/emailverification/utils.html +++ /dev/null @@ -1,322 +0,0 @@ - - - - - - -supertokens_python.recipe.emailverification.utils API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.emailverification.utils

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from __future__ import annotations
-
-from typing import TYPE_CHECKING, Any, Dict, Optional
-from supertokens_python.framework import BaseRequest
-
-from supertokens_python.ingredients.emaildelivery.types import (
-    EmailDeliveryConfig,
-    EmailDeliveryConfigWithService,
-)
-from supertokens_python.recipe.emailverification.emaildelivery.services.backward_compatibility import (
-    BackwardCompatibilityService,
-)
-from typing_extensions import Literal
-
-if TYPE_CHECKING:
-    from typing import Callable, Union
-
-    from supertokens_python.supertokens import AppInfo
-
-    from .interfaces import APIInterface, RecipeInterface, TypeGetEmailForUserIdFunction
-    from .types import EmailTemplateVars, VerificationEmailTemplateVars
-
-
-class OverrideConfig:
-    def __init__(
-        self,
-        functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None,
-        apis: Union[Callable[[APIInterface], APIInterface], None] = None,
-    ):
-        self.functions = functions
-        self.apis = apis
-
-
-MODE_TYPE = Literal["REQUIRED", "OPTIONAL"]
-
-
-class EmailVerificationConfig:
-    def __init__(
-        self,
-        mode: MODE_TYPE,
-        get_email_delivery_config: Callable[
-            [], EmailDeliveryConfigWithService[VerificationEmailTemplateVars]
-        ],
-        get_email_for_user_id: Optional[TypeGetEmailForUserIdFunction],
-        override: OverrideConfig,
-    ):
-        self.mode = mode
-        self.override = override
-        self.get_email_delivery_config = get_email_delivery_config
-        self.get_email_for_user_id = get_email_for_user_id
-
-
-def validate_and_normalise_user_input(
-    app_info: AppInfo,
-    mode: MODE_TYPE,
-    email_delivery: Union[EmailDeliveryConfig[EmailTemplateVars], None] = None,
-    get_email_for_user_id: Optional[TypeGetEmailForUserIdFunction] = None,
-    override: Union[OverrideConfig, None] = None,
-) -> EmailVerificationConfig:
-    if mode not in ["REQUIRED", "OPTIONAL"]:
-        raise ValueError(
-            "Email Verification recipe mode must be one of 'REQUIRED' or 'OPTIONAL'"
-        )
-
-    def get_email_delivery_config() -> (
-        EmailDeliveryConfigWithService[VerificationEmailTemplateVars]
-    ):
-        email_service = email_delivery.service if email_delivery is not None else None
-        if email_service is None:
-            email_service = BackwardCompatibilityService(app_info)
-
-        if email_delivery is not None and email_delivery.override is not None:
-            override = email_delivery.override
-        else:
-            override = None
-        return EmailDeliveryConfigWithService(email_service, override=override)
-
-    if override is not None and not isinstance(override, OverrideConfig):  # type: ignore
-        raise ValueError("override must be of type OverrideConfig or None")
-
-    if override is None:
-        override = OverrideConfig()
-
-    return EmailVerificationConfig(
-        mode,
-        get_email_delivery_config,
-        get_email_for_user_id,
-        override,
-    )
-
-
-def get_email_verify_link(
-    app_info: AppInfo,
-    token: str,
-    tenant_id: str,
-    request: Optional[BaseRequest],
-    user_context: Dict[str, Any],
-) -> str:
-    return (
-        app_info.get_origin(request, user_context).get_as_string_dangerous()
-        + app_info.website_base_path.get_as_string_dangerous()
-        + "/verify-email"
-        + "?token="
-        + token
-        + "&tenantId="
-        + tenant_id
-    )
-
-
-
-
-
-
-
-

Functions

-
- -
-
-
- -Expand source code - -
def get_email_verify_link(
-    app_info: AppInfo,
-    token: str,
-    tenant_id: str,
-    request: Optional[BaseRequest],
-    user_context: Dict[str, Any],
-) -> str:
-    return (
-        app_info.get_origin(request, user_context).get_as_string_dangerous()
-        + app_info.website_base_path.get_as_string_dangerous()
-        + "/verify-email"
-        + "?token="
-        + token
-        + "&tenantId="
-        + tenant_id
-    )
-
-
-
-def validate_and_normalise_user_input(app_info: AppInfo, mode: MODE_TYPE, email_delivery: Union[EmailDeliveryConfig[EmailTemplateVars], None] = None, get_email_for_user_id: Optional[TypeGetEmailForUserIdFunction] = None, override: Union[OverrideConfig, None] = None) ‑> EmailVerificationConfig -
-
-
-
- -Expand source code - -
def validate_and_normalise_user_input(
-    app_info: AppInfo,
-    mode: MODE_TYPE,
-    email_delivery: Union[EmailDeliveryConfig[EmailTemplateVars], None] = None,
-    get_email_for_user_id: Optional[TypeGetEmailForUserIdFunction] = None,
-    override: Union[OverrideConfig, None] = None,
-) -> EmailVerificationConfig:
-    if mode not in ["REQUIRED", "OPTIONAL"]:
-        raise ValueError(
-            "Email Verification recipe mode must be one of 'REQUIRED' or 'OPTIONAL'"
-        )
-
-    def get_email_delivery_config() -> (
-        EmailDeliveryConfigWithService[VerificationEmailTemplateVars]
-    ):
-        email_service = email_delivery.service if email_delivery is not None else None
-        if email_service is None:
-            email_service = BackwardCompatibilityService(app_info)
-
-        if email_delivery is not None and email_delivery.override is not None:
-            override = email_delivery.override
-        else:
-            override = None
-        return EmailDeliveryConfigWithService(email_service, override=override)
-
-    if override is not None and not isinstance(override, OverrideConfig):  # type: ignore
-        raise ValueError("override must be of type OverrideConfig or None")
-
-    if override is None:
-        override = OverrideConfig()
-
-    return EmailVerificationConfig(
-        mode,
-        get_email_delivery_config,
-        get_email_for_user_id,
-        override,
-    )
-
-
-
-
-
-

Classes

-
-
-class EmailVerificationConfig -(mode: MODE_TYPE, get_email_delivery_config: Callable[[], EmailDeliveryConfigWithService[VerificationEmailTemplateVars]], get_email_for_user_id: Optional[TypeGetEmailForUserIdFunction], override: OverrideConfig) -
-
-
-
- -Expand source code - -
class EmailVerificationConfig:
-    def __init__(
-        self,
-        mode: MODE_TYPE,
-        get_email_delivery_config: Callable[
-            [], EmailDeliveryConfigWithService[VerificationEmailTemplateVars]
-        ],
-        get_email_for_user_id: Optional[TypeGetEmailForUserIdFunction],
-        override: OverrideConfig,
-    ):
-        self.mode = mode
-        self.override = override
-        self.get_email_delivery_config = get_email_delivery_config
-        self.get_email_for_user_id = get_email_for_user_id
-
-
-
-class OverrideConfig -(functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None, apis: Union[Callable[[APIInterface], APIInterface], None] = None) -
-
-
-
- -Expand source code - -
class OverrideConfig:
-    def __init__(
-        self,
-        functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None,
-        apis: Union[Callable[[APIInterface], APIInterface], None] = None,
-    ):
-        self.functions = functions
-        self.apis = apis
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/index.html b/html/supertokens_python/recipe/index.html deleted file mode 100644 index 969701a6a..000000000 --- a/html/supertokens_python/recipe/index.html +++ /dev/null @@ -1,115 +0,0 @@ - - - - - - -supertokens_python.recipe API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe

-
-
-
-
-

Sub-modules

-
-
supertokens_python.recipe.dashboard
-
-
-
-
supertokens_python.recipe.emailpassword
-
-
-
-
supertokens_python.recipe.emailverification
-
-
-
-
supertokens_python.recipe.jwt
-
-
-
-
supertokens_python.recipe.multitenancy
-
-
-
-
supertokens_python.recipe.openid
-
-
-
-
supertokens_python.recipe.passwordless
-
-
-
-
supertokens_python.recipe.session
-
-
-
-
supertokens_python.recipe.thirdparty
-
-
-
-
supertokens_python.recipe.usermetadata
-
-
-
-
supertokens_python.recipe.userroles
-
-
-
-
-
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/jwt/api/implementation.html b/html/supertokens_python/recipe/jwt/api/implementation.html deleted file mode 100644 index 854bab900..000000000 --- a/html/supertokens_python/recipe/jwt/api/implementation.html +++ /dev/null @@ -1,157 +0,0 @@ - - - - - - -supertokens_python.recipe.jwt.api.implementation API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.jwt.api.implementation

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from typing import Any, Dict
-
-from supertokens_python.recipe.jwt.interfaces import (
-    APIInterface,
-    APIOptions,
-    JWKSGetResponse,
-)
-
-
-class APIImplementation(APIInterface):
-    async def jwks_get(
-        self, api_options: APIOptions, user_context: Dict[str, Any]
-    ) -> JWKSGetResponse:
-        response = await api_options.recipe_implementation.get_jwks(user_context)
-
-        if response.validity_in_secs is not None:
-            api_options.response.set_header(
-                "Cache-Control", f"max-age={response.validity_in_secs}, must-revalidate"
-            )
-
-        return JWKSGetResponse(response.keys)
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class APIImplementation -
-
-
-
- -Expand source code - -
class APIImplementation(APIInterface):
-    async def jwks_get(
-        self, api_options: APIOptions, user_context: Dict[str, Any]
-    ) -> JWKSGetResponse:
-        response = await api_options.recipe_implementation.get_jwks(user_context)
-
-        if response.validity_in_secs is not None:
-            api_options.response.set_header(
-                "Cache-Control", f"max-age={response.validity_in_secs}, must-revalidate"
-            )
-
-        return JWKSGetResponse(response.keys)
-
-

Ancestors

- -

Methods

-
-
-async def jwks_get(self, api_options: APIOptions, user_context: Dict[str, Any]) ‑> JWKSGetResponse -
-
-
-
- -Expand source code - -
async def jwks_get(
-    self, api_options: APIOptions, user_context: Dict[str, Any]
-) -> JWKSGetResponse:
-    response = await api_options.recipe_implementation.get_jwks(user_context)
-
-    if response.validity_in_secs is not None:
-        api_options.response.set_header(
-            "Cache-Control", f"max-age={response.validity_in_secs}, must-revalidate"
-        )
-
-    return JWKSGetResponse(response.keys)
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/jwt/api/index.html b/html/supertokens_python/recipe/jwt/api/index.html deleted file mode 100644 index 6bbcaa708..000000000 --- a/html/supertokens_python/recipe/jwt/api/index.html +++ /dev/null @@ -1,70 +0,0 @@ - - - - - - -supertokens_python.recipe.jwt.api API documentation - - - - - - - - - - - -
- - -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/jwt/api/jwks_get.html b/html/supertokens_python/recipe/jwt/api/jwks_get.html deleted file mode 100644 index 0cde8f98a..000000000 --- a/html/supertokens_python/recipe/jwt/api/jwks_get.html +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - -supertokens_python.recipe.jwt.api.jwks_get API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.jwt.api.jwks_get

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-from typing import Any, Dict
-from supertokens_python.recipe.jwt.interfaces import APIInterface, APIOptions
-from supertokens_python.utils import send_200_response
-
-from ..interfaces import JWKSGetResponse
-
-
-async def jwks_get(
-    api_implementation: APIInterface,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-):
-    if api_implementation.disable_jwks_get:
-        return None
-
-    result = await api_implementation.jwks_get(api_options, user_context)
-
-    if isinstance(result, JWKSGetResponse):
-        api_options.response.set_header("Access-Control-Allow-Origin", "*")
-
-    return send_200_response(result.to_json(), api_options.response)
-
-
-
-
-
-
-
-

Functions

-
-
-async def jwks_get(api_implementation: APIInterface, api_options: APIOptions, user_context: Dict[str, Any]) -
-
-
-
- -Expand source code - -
async def jwks_get(
-    api_implementation: APIInterface,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-):
-    if api_implementation.disable_jwks_get:
-        return None
-
-    result = await api_implementation.jwks_get(api_options, user_context)
-
-    if isinstance(result, JWKSGetResponse):
-        api_options.response.set_header("Access-Control-Allow-Origin", "*")
-
-    return send_200_response(result.to_json(), api_options.response)
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/jwt/asyncio/index.html b/html/supertokens_python/recipe/jwt/asyncio/index.html deleted file mode 100644 index c4cfb3c50..000000000 --- a/html/supertokens_python/recipe/jwt/asyncio/index.html +++ /dev/null @@ -1,150 +0,0 @@ - - - - - - -supertokens_python.recipe.jwt.asyncio API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.jwt.asyncio

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from typing import Any, Dict, Union, Optional
-
-from supertokens_python.recipe.jwt.interfaces import (
-    CreateJwtOkResult,
-    CreateJwtResultUnsupportedAlgorithm,
-    GetJWKSResult,
-)
-from supertokens_python.recipe.jwt.recipe import JWTRecipe
-
-
-async def create_jwt(
-    payload: Optional[Dict[str, Any]] = None,
-    validity_seconds: Optional[int] = None,
-    use_static_signing_key: Optional[bool] = None,
-    user_context: Optional[Dict[str, Any]] = None,
-) -> Union[CreateJwtOkResult, CreateJwtResultUnsupportedAlgorithm]:
-    if user_context is None:
-        user_context = {}
-    if payload is None:
-        payload = {}
-
-    return await JWTRecipe.get_instance().recipe_implementation.create_jwt(
-        payload, validity_seconds, use_static_signing_key, user_context
-    )
-
-
-async def get_jwks(user_context: Optional[Dict[str, Any]] = None) -> GetJWKSResult:
-    if user_context is None:
-        user_context = {}
-    return await JWTRecipe.get_instance().recipe_implementation.get_jwks(user_context)
-
-
-
-
-
-
-
-

Functions

-
-
-async def create_jwt(payload: Optional[Dict[str, Any]] = None, validity_seconds: Optional[int] = None, use_static_signing_key: Optional[bool] = None, user_context: Optional[Dict[str, Any]] = None) ‑> Union[CreateJwtOkResultCreateJwtResultUnsupportedAlgorithm] -
-
-
-
- -Expand source code - -
async def create_jwt(
-    payload: Optional[Dict[str, Any]] = None,
-    validity_seconds: Optional[int] = None,
-    use_static_signing_key: Optional[bool] = None,
-    user_context: Optional[Dict[str, Any]] = None,
-) -> Union[CreateJwtOkResult, CreateJwtResultUnsupportedAlgorithm]:
-    if user_context is None:
-        user_context = {}
-    if payload is None:
-        payload = {}
-
-    return await JWTRecipe.get_instance().recipe_implementation.create_jwt(
-        payload, validity_seconds, use_static_signing_key, user_context
-    )
-
-
-
-async def get_jwks(user_context: Optional[Dict[str, Any]] = None) ‑> GetJWKSResult -
-
-
-
- -Expand source code - -
async def get_jwks(user_context: Optional[Dict[str, Any]] = None) -> GetJWKSResult:
-    if user_context is None:
-        user_context = {}
-    return await JWTRecipe.get_instance().recipe_implementation.get_jwks(user_context)
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/jwt/constants.html b/html/supertokens_python/recipe/jwt/constants.html deleted file mode 100644 index e6d5d47ca..000000000 --- a/html/supertokens_python/recipe/jwt/constants.html +++ /dev/null @@ -1,73 +0,0 @@ - - - - - - -supertokens_python.recipe.jwt.constants API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.jwt.constants

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-GET_JWKS_API = "/jwt/jwks.json"
-
-
-
-
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/jwt/exceptions.html b/html/supertokens_python/recipe/jwt/exceptions.html deleted file mode 100644 index 2376b6833..000000000 --- a/html/supertokens_python/recipe/jwt/exceptions.html +++ /dev/null @@ -1,108 +0,0 @@ - - - - - - -supertokens_python.recipe.jwt.exceptions API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.jwt.exceptions

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from supertokens_python.exceptions import SuperTokensError
-
-
-class SuperTokensJWTError(SuperTokensError):
-    pass
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class SuperTokensJWTError -(*args, **kwargs) -
-
-

Common base class for all non-exit exceptions.

-
- -Expand source code - -
class SuperTokensJWTError(SuperTokensError):
-    pass
-
-

Ancestors

- -
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/jwt/index.html b/html/supertokens_python/recipe/jwt/index.html deleted file mode 100644 index 8601620d4..000000000 --- a/html/supertokens_python/recipe/jwt/index.html +++ /dev/null @@ -1,165 +0,0 @@ - - - - - - -supertokens_python.recipe.jwt API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.jwt

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from typing import TYPE_CHECKING, Callable, Union
-
-from .recipe import JWTRecipe
-from .utils import OverrideConfig
-
-if TYPE_CHECKING:
-    from supertokens_python.supertokens import AppInfo
-
-    from ...recipe_module import RecipeModule
-
-
-def init(
-    jwt_validity_seconds: Union[int, None] = None,
-    override: Union[OverrideConfig, None] = None,
-) -> Callable[[AppInfo], RecipeModule]:
-    return JWTRecipe.init(jwt_validity_seconds, override)
-
-
-
-

Sub-modules

-
-
supertokens_python.recipe.jwt.api
-
-
-
-
supertokens_python.recipe.jwt.asyncio
-
-
-
-
supertokens_python.recipe.jwt.constants
-
-
-
-
supertokens_python.recipe.jwt.exceptions
-
-
-
-
supertokens_python.recipe.jwt.interfaces
-
-
-
-
supertokens_python.recipe.jwt.recipe
-
-
-
-
supertokens_python.recipe.jwt.recipe_implementation
-
-
-
-
supertokens_python.recipe.jwt.syncio
-
-
-
-
supertokens_python.recipe.jwt.utils
-
-
-
-
-
-
-
-
-

Functions

-
-
-def init(jwt_validity_seconds: Union[int, None] = None, override: Union[OverrideConfig, None] = None) ‑> Callable[[AppInfo], RecipeModule] -
-
-
-
- -Expand source code - -
def init(
-    jwt_validity_seconds: Union[int, None] = None,
-    override: Union[OverrideConfig, None] = None,
-) -> Callable[[AppInfo], RecipeModule]:
-    return JWTRecipe.init(jwt_validity_seconds, override)
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/jwt/interfaces.html b/html/supertokens_python/recipe/jwt/interfaces.html deleted file mode 100644 index d883348e7..000000000 --- a/html/supertokens_python/recipe/jwt/interfaces.html +++ /dev/null @@ -1,484 +0,0 @@ - - - - - - -supertokens_python.recipe.jwt.interfaces API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.jwt.interfaces

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from abc import ABC, abstractmethod
-from typing import Any, Dict, List, Union, Optional
-
-from supertokens_python.framework import BaseRequest, BaseResponse
-from supertokens_python.types import APIResponse, GeneralErrorResponse
-
-from .utils import JWTConfig
-
-
-class JsonWebKey:
-    def __init__(self, kty: str, kid: str, n: str, e: str, alg: str, use: str):
-        self.kty = kty
-        self.kid = kid
-        self.n = n
-        self.e = e
-        self.alg = alg
-        self.use = use
-
-
-class CreateJwtOkResult:
-    def __init__(self, jwt: str):
-        self.jwt = jwt
-
-
-class CreateJwtResultUnsupportedAlgorithm:
-    pass
-
-
-class GetJWKSResult:
-    def __init__(self, keys: List[JsonWebKey], validity_in_secs: Optional[int]):
-        self.keys = keys
-        self.validity_in_secs = validity_in_secs
-
-
-class RecipeInterface(ABC):
-    def __init__(self):
-        pass
-
-    @abstractmethod
-    async def create_jwt(
-        self,
-        payload: Dict[str, Any],
-        validity_seconds: Optional[int],
-        use_static_signing_key: Optional[bool],
-        user_context: Dict[str, Any],
-    ) -> Union[CreateJwtOkResult, CreateJwtResultUnsupportedAlgorithm]:
-        pass
-
-    @abstractmethod
-    async def get_jwks(self, user_context: Dict[str, Any]) -> GetJWKSResult:
-        pass
-
-
-class APIOptions:
-    def __init__(
-        self,
-        request: BaseRequest,
-        response: BaseResponse,
-        recipe_id: str,
-        config: JWTConfig,
-        recipe_implementation: RecipeInterface,
-    ):
-        self.request = request
-        self.response = response
-        self.recipe_id = recipe_id
-        self.config = config
-        self.recipe_implementation = recipe_implementation
-
-
-class JWKSGetResponse(APIResponse):
-    def __init__(self, keys: List[JsonWebKey]):
-        self.keys = keys
-
-    def to_json(self) -> Dict[str, Any]:
-        keys: List[Dict[str, Any]] = []
-        for key in self.keys:
-            keys.append(
-                {
-                    "kty": key.kty,
-                    "kid": key.kid,
-                    "n": key.n,
-                    "e": key.e,
-                    "alg": key.alg,
-                    "use": key.use,
-                }
-            )
-
-        return {"keys": keys}
-
-
-class APIInterface:
-    def __init__(self):
-        self.disable_jwks_get = False
-
-    @abstractmethod
-    async def jwks_get(
-        self, api_options: APIOptions, user_context: Dict[str, Any]
-    ) -> Union[JWKSGetResponse, GeneralErrorResponse]:
-        pass
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class APIInterface -
-
-
-
- -Expand source code - -
class APIInterface:
-    def __init__(self):
-        self.disable_jwks_get = False
-
-    @abstractmethod
-    async def jwks_get(
-        self, api_options: APIOptions, user_context: Dict[str, Any]
-    ) -> Union[JWKSGetResponse, GeneralErrorResponse]:
-        pass
-
-

Subclasses

- -

Methods

-
-
-async def jwks_get(self, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[JWKSGetResponseGeneralErrorResponse] -
-
-
-
- -Expand source code - -
@abstractmethod
-async def jwks_get(
-    self, api_options: APIOptions, user_context: Dict[str, Any]
-) -> Union[JWKSGetResponse, GeneralErrorResponse]:
-    pass
-
-
-
-
-
-class APIOptions -(request: BaseRequest, response: BaseResponse, recipe_id: str, config: JWTConfig, recipe_implementation: RecipeInterface) -
-
-
-
- -Expand source code - -
class APIOptions:
-    def __init__(
-        self,
-        request: BaseRequest,
-        response: BaseResponse,
-        recipe_id: str,
-        config: JWTConfig,
-        recipe_implementation: RecipeInterface,
-    ):
-        self.request = request
-        self.response = response
-        self.recipe_id = recipe_id
-        self.config = config
-        self.recipe_implementation = recipe_implementation
-
-
-
-class CreateJwtOkResult -(jwt: str) -
-
-
-
- -Expand source code - -
class CreateJwtOkResult:
-    def __init__(self, jwt: str):
-        self.jwt = jwt
-
-
-
-class CreateJwtResultUnsupportedAlgorithm -
-
-
-
- -Expand source code - -
class CreateJwtResultUnsupportedAlgorithm:
-    pass
-
-
-
-class GetJWKSResult -(keys: List[JsonWebKey], validity_in_secs: Optional[int]) -
-
-
-
- -Expand source code - -
class GetJWKSResult:
-    def __init__(self, keys: List[JsonWebKey], validity_in_secs: Optional[int]):
-        self.keys = keys
-        self.validity_in_secs = validity_in_secs
-
-
-
-class JWKSGetResponse -(keys: List[JsonWebKey]) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class JWKSGetResponse(APIResponse):
-    def __init__(self, keys: List[JsonWebKey]):
-        self.keys = keys
-
-    def to_json(self) -> Dict[str, Any]:
-        keys: List[Dict[str, Any]] = []
-        for key in self.keys:
-            keys.append(
-                {
-                    "kty": key.kty,
-                    "kid": key.kid,
-                    "n": key.n,
-                    "e": key.e,
-                    "alg": key.alg,
-                    "use": key.use,
-                }
-            )
-
-        return {"keys": keys}
-
-

Ancestors

- -

Methods

-
-
-def to_json(self) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
def to_json(self) -> Dict[str, Any]:
-    keys: List[Dict[str, Any]] = []
-    for key in self.keys:
-        keys.append(
-            {
-                "kty": key.kty,
-                "kid": key.kid,
-                "n": key.n,
-                "e": key.e,
-                "alg": key.alg,
-                "use": key.use,
-            }
-        )
-
-    return {"keys": keys}
-
-
-
-
-
-class JsonWebKey -(kty: str, kid: str, n: str, e: str, alg: str, use: str) -
-
-
-
- -Expand source code - -
class JsonWebKey:
-    def __init__(self, kty: str, kid: str, n: str, e: str, alg: str, use: str):
-        self.kty = kty
-        self.kid = kid
-        self.n = n
-        self.e = e
-        self.alg = alg
-        self.use = use
-
-
-
-class RecipeInterface -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class RecipeInterface(ABC):
-    def __init__(self):
-        pass
-
-    @abstractmethod
-    async def create_jwt(
-        self,
-        payload: Dict[str, Any],
-        validity_seconds: Optional[int],
-        use_static_signing_key: Optional[bool],
-        user_context: Dict[str, Any],
-    ) -> Union[CreateJwtOkResult, CreateJwtResultUnsupportedAlgorithm]:
-        pass
-
-    @abstractmethod
-    async def get_jwks(self, user_context: Dict[str, Any]) -> GetJWKSResult:
-        pass
-
-

Ancestors

-
    -
  • abc.ABC
  • -
-

Subclasses

- -

Methods

-
-
-async def create_jwt(self, payload: Dict[str, Any], validity_seconds: Optional[int], use_static_signing_key: Optional[bool], user_context: Dict[str, Any]) ‑> Union[CreateJwtOkResultCreateJwtResultUnsupportedAlgorithm] -
-
-
-
- -Expand source code - -
@abstractmethod
-async def create_jwt(
-    self,
-    payload: Dict[str, Any],
-    validity_seconds: Optional[int],
-    use_static_signing_key: Optional[bool],
-    user_context: Dict[str, Any],
-) -> Union[CreateJwtOkResult, CreateJwtResultUnsupportedAlgorithm]:
-    pass
-
-
-
-async def get_jwks(self, user_context: Dict[str, Any]) ‑> GetJWKSResult -
-
-
-
- -Expand source code - -
@abstractmethod
-async def get_jwks(self, user_context: Dict[str, Any]) -> GetJWKSResult:
-    pass
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/jwt/recipe.html b/html/supertokens_python/recipe/jwt/recipe.html deleted file mode 100644 index ffa3b64a6..000000000 --- a/html/supertokens_python/recipe/jwt/recipe.html +++ /dev/null @@ -1,543 +0,0 @@ - - - - - - -supertokens_python.recipe.jwt.recipe API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.jwt.recipe

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from os import environ
-from typing import TYPE_CHECKING, List, Union, Optional, Dict, Any
-
-from supertokens_python.querier import Querier
-from supertokens_python.recipe.jwt.api.implementation import APIImplementation
-from supertokens_python.recipe.jwt.api.jwks_get import jwks_get
-from supertokens_python.recipe.jwt.constants import GET_JWKS_API
-from supertokens_python.recipe.jwt.exceptions import SuperTokensJWTError
-from supertokens_python.recipe.jwt.interfaces import APIOptions
-from supertokens_python.recipe.jwt.recipe_implementation import RecipeImplementation
-from supertokens_python.recipe.jwt.utils import (
-    OverrideConfig,
-    validate_and_normalise_user_input,
-)
-
-if TYPE_CHECKING:
-    from supertokens_python.framework.request import BaseRequest
-    from supertokens_python.framework.response import BaseResponse
-    from supertokens_python.supertokens import AppInfo
-
-from supertokens_python.exceptions import SuperTokensError, raise_general_exception
-from supertokens_python.normalised_url_path import NormalisedURLPath
-from supertokens_python.recipe_module import APIHandled, RecipeModule
-
-
-class JWTRecipe(RecipeModule):
-    recipe_id = "jwt"
-    __instance = None
-
-    def __init__(
-        self,
-        recipe_id: str,
-        app_info: AppInfo,
-        jwt_validity_seconds: Union[int, None] = None,
-        override: Union[OverrideConfig, None] = None,
-    ):
-        super().__init__(recipe_id, app_info)
-        self.config = validate_and_normalise_user_input(jwt_validity_seconds, override)
-
-        recipe_implementation = RecipeImplementation(
-            Querier.get_instance(recipe_id), self.config, app_info
-        )
-        self.recipe_implementation = (
-            recipe_implementation
-            if self.config.override.functions is None
-            else self.config.override.functions(recipe_implementation)
-        )
-        api_implementation = APIImplementation()
-        self.api_implementation = (
-            api_implementation
-            if self.config.override.apis is None
-            else self.config.override.apis(api_implementation)
-        )
-
-    def get_apis_handled(self) -> List[APIHandled]:
-        return [
-            APIHandled(
-                method="get",
-                path_without_api_base_path=NormalisedURLPath(GET_JWKS_API),
-                request_id=GET_JWKS_API,
-                disabled=self.api_implementation.disable_jwks_get,
-            )
-        ]
-
-    async def handle_api_request(
-        self,
-        request_id: str,
-        tenant_id: Optional[str],
-        request: BaseRequest,
-        path: NormalisedURLPath,
-        method: str,
-        response: BaseResponse,
-        user_context: Dict[str, Any],
-    ):
-        options = APIOptions(
-            request,
-            response,
-            self.get_recipe_id(),
-            self.config,
-            self.recipe_implementation,
-        )
-
-        if request_id == GET_JWKS_API:
-            return await jwks_get(self.api_implementation, options, user_context)
-
-        return None
-
-    async def handle_error(
-        self,
-        request: BaseRequest,
-        err: SuperTokensError,
-        response: BaseResponse,
-        user_context: Dict[str, Any],
-    ):
-        raise err
-
-    def get_all_cors_headers(self) -> List[str]:
-        return []
-
-    def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
-        return isinstance(err, SuperTokensError) and isinstance(
-            err, SuperTokensJWTError
-        )
-
-    @staticmethod
-    def init(
-        jwt_validity_seconds: Union[int, None] = None,
-        override: Union[OverrideConfig, None] = None,
-    ):
-        def func(app_info: AppInfo):
-            if JWTRecipe.__instance is None:
-                JWTRecipe.__instance = JWTRecipe(
-                    JWTRecipe.recipe_id, app_info, jwt_validity_seconds, override
-                )
-                return JWTRecipe.__instance
-            raise_general_exception(
-                "JWT recipe has already been initialised. Please check "
-                "your code for bugs."
-            )
-
-        return func
-
-    @staticmethod
-    def get_instance() -> JWTRecipe:
-        if JWTRecipe.__instance is not None:
-            return JWTRecipe.__instance
-        raise_general_exception(
-            "Initialisation not done. Did you forget to call the SuperTokens.init function?"
-        )
-
-    @staticmethod
-    def reset():
-        if ("SUPERTOKENS_ENV" not in environ) or (
-            environ["SUPERTOKENS_ENV"] != "testing"
-        ):
-            raise_general_exception("calling testing function in non testing env")
-        JWTRecipe.__instance = None
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class JWTRecipe -(recipe_id: str, app_info: AppInfo, jwt_validity_seconds: Union[int, None] = None, override: Union[OverrideConfig, None] = None) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class JWTRecipe(RecipeModule):
-    recipe_id = "jwt"
-    __instance = None
-
-    def __init__(
-        self,
-        recipe_id: str,
-        app_info: AppInfo,
-        jwt_validity_seconds: Union[int, None] = None,
-        override: Union[OverrideConfig, None] = None,
-    ):
-        super().__init__(recipe_id, app_info)
-        self.config = validate_and_normalise_user_input(jwt_validity_seconds, override)
-
-        recipe_implementation = RecipeImplementation(
-            Querier.get_instance(recipe_id), self.config, app_info
-        )
-        self.recipe_implementation = (
-            recipe_implementation
-            if self.config.override.functions is None
-            else self.config.override.functions(recipe_implementation)
-        )
-        api_implementation = APIImplementation()
-        self.api_implementation = (
-            api_implementation
-            if self.config.override.apis is None
-            else self.config.override.apis(api_implementation)
-        )
-
-    def get_apis_handled(self) -> List[APIHandled]:
-        return [
-            APIHandled(
-                method="get",
-                path_without_api_base_path=NormalisedURLPath(GET_JWKS_API),
-                request_id=GET_JWKS_API,
-                disabled=self.api_implementation.disable_jwks_get,
-            )
-        ]
-
-    async def handle_api_request(
-        self,
-        request_id: str,
-        tenant_id: Optional[str],
-        request: BaseRequest,
-        path: NormalisedURLPath,
-        method: str,
-        response: BaseResponse,
-        user_context: Dict[str, Any],
-    ):
-        options = APIOptions(
-            request,
-            response,
-            self.get_recipe_id(),
-            self.config,
-            self.recipe_implementation,
-        )
-
-        if request_id == GET_JWKS_API:
-            return await jwks_get(self.api_implementation, options, user_context)
-
-        return None
-
-    async def handle_error(
-        self,
-        request: BaseRequest,
-        err: SuperTokensError,
-        response: BaseResponse,
-        user_context: Dict[str, Any],
-    ):
-        raise err
-
-    def get_all_cors_headers(self) -> List[str]:
-        return []
-
-    def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
-        return isinstance(err, SuperTokensError) and isinstance(
-            err, SuperTokensJWTError
-        )
-
-    @staticmethod
-    def init(
-        jwt_validity_seconds: Union[int, None] = None,
-        override: Union[OverrideConfig, None] = None,
-    ):
-        def func(app_info: AppInfo):
-            if JWTRecipe.__instance is None:
-                JWTRecipe.__instance = JWTRecipe(
-                    JWTRecipe.recipe_id, app_info, jwt_validity_seconds, override
-                )
-                return JWTRecipe.__instance
-            raise_general_exception(
-                "JWT recipe has already been initialised. Please check "
-                "your code for bugs."
-            )
-
-        return func
-
-    @staticmethod
-    def get_instance() -> JWTRecipe:
-        if JWTRecipe.__instance is not None:
-            return JWTRecipe.__instance
-        raise_general_exception(
-            "Initialisation not done. Did you forget to call the SuperTokens.init function?"
-        )
-
-    @staticmethod
-    def reset():
-        if ("SUPERTOKENS_ENV" not in environ) or (
-            environ["SUPERTOKENS_ENV"] != "testing"
-        ):
-            raise_general_exception("calling testing function in non testing env")
-        JWTRecipe.__instance = None
-
-

Ancestors

- -

Class variables

-
-
var get_tenant_id : Optional[Callable[[str, Dict[str, Any]], Awaitable[str]]]
-
-
-
-
var recipe_id
-
-
-
-
-

Static methods

-
-
-def get_instance() ‑> JWTRecipe -
-
-
-
- -Expand source code - -
@staticmethod
-def get_instance() -> JWTRecipe:
-    if JWTRecipe.__instance is not None:
-        return JWTRecipe.__instance
-    raise_general_exception(
-        "Initialisation not done. Did you forget to call the SuperTokens.init function?"
-    )
-
-
-
-def init(jwt_validity_seconds: Union[int, None] = None, override: Union[OverrideConfig, None] = None) -
-
-
-
- -Expand source code - -
@staticmethod
-def init(
-    jwt_validity_seconds: Union[int, None] = None,
-    override: Union[OverrideConfig, None] = None,
-):
-    def func(app_info: AppInfo):
-        if JWTRecipe.__instance is None:
-            JWTRecipe.__instance = JWTRecipe(
-                JWTRecipe.recipe_id, app_info, jwt_validity_seconds, override
-            )
-            return JWTRecipe.__instance
-        raise_general_exception(
-            "JWT recipe has already been initialised. Please check "
-            "your code for bugs."
-        )
-
-    return func
-
-
-
-def reset() -
-
-
-
- -Expand source code - -
@staticmethod
-def reset():
-    if ("SUPERTOKENS_ENV" not in environ) or (
-        environ["SUPERTOKENS_ENV"] != "testing"
-    ):
-        raise_general_exception("calling testing function in non testing env")
-    JWTRecipe.__instance = None
-
-
-
-

Methods

-
-
-def get_all_cors_headers(self) ‑> List[str] -
-
-
-
- -Expand source code - -
def get_all_cors_headers(self) -> List[str]:
-    return []
-
-
-
-def get_apis_handled(self) ‑> List[APIHandled] -
-
-
-
- -Expand source code - -
def get_apis_handled(self) -> List[APIHandled]:
-    return [
-        APIHandled(
-            method="get",
-            path_without_api_base_path=NormalisedURLPath(GET_JWKS_API),
-            request_id=GET_JWKS_API,
-            disabled=self.api_implementation.disable_jwks_get,
-        )
-    ]
-
-
-
-async def handle_api_request(self, request_id: str, tenant_id: Optional[str], request: BaseRequest, path: NormalisedURLPath, method: str, response: BaseResponse, user_context: Dict[str, Any]) -
-
-
-
- -Expand source code - -
async def handle_api_request(
-    self,
-    request_id: str,
-    tenant_id: Optional[str],
-    request: BaseRequest,
-    path: NormalisedURLPath,
-    method: str,
-    response: BaseResponse,
-    user_context: Dict[str, Any],
-):
-    options = APIOptions(
-        request,
-        response,
-        self.get_recipe_id(),
-        self.config,
-        self.recipe_implementation,
-    )
-
-    if request_id == GET_JWKS_API:
-        return await jwks_get(self.api_implementation, options, user_context)
-
-    return None
-
-
-
-async def handle_error(self, request: BaseRequest, err: SuperTokensError, response: BaseResponse, user_context: Dict[str, Any]) -
-
-
-
- -Expand source code - -
async def handle_error(
-    self,
-    request: BaseRequest,
-    err: SuperTokensError,
-    response: BaseResponse,
-    user_context: Dict[str, Any],
-):
-    raise err
-
-
-
-def is_error_from_this_recipe_based_on_instance(self, err: Exception) ‑> bool -
-
-
-
- -Expand source code - -
def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
-    return isinstance(err, SuperTokensError) and isinstance(
-        err, SuperTokensJWTError
-    )
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/jwt/recipe_implementation.html b/html/supertokens_python/recipe/jwt/recipe_implementation.html deleted file mode 100644 index 891b90ba4..000000000 --- a/html/supertokens_python/recipe/jwt/recipe_implementation.html +++ /dev/null @@ -1,332 +0,0 @@ - - - - - - -supertokens_python.recipe.jwt.recipe_implementation API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.jwt.recipe_implementation

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from typing import TYPE_CHECKING, Any, Dict, List, Union, Optional
-
-from supertokens_python.normalised_url_path import NormalisedURLPath
-from supertokens_python.querier import Querier
-import re
-
-if TYPE_CHECKING:
-    from .utils import JWTConfig
-    from supertokens_python.supertokens import AppInfo
-
-from supertokens_python.recipe.jwt.interfaces import (
-    CreateJwtOkResult,
-    CreateJwtResultUnsupportedAlgorithm,
-    GetJWKSResult,
-    RecipeInterface,
-)
-
-from .interfaces import JsonWebKey
-
-
-# This corresponds to the dynamicSigningKeyOverlapMS in the core
-DEFAULT_JWKS_MAX_AGE = 60
-
-
-class RecipeImplementation(RecipeInterface):
-    def __init__(self, querier: Querier, config: JWTConfig, app_info: AppInfo):
-        super().__init__()
-        self.querier = querier
-        self.config = config
-        self.app_info = app_info
-
-    async def create_jwt(
-        self,
-        payload: Dict[str, Any],
-        validity_seconds: Optional[int],
-        use_static_signing_key: Optional[bool],
-        user_context: Dict[str, Any],
-    ) -> Union[CreateJwtOkResult, CreateJwtResultUnsupportedAlgorithm]:
-        if validity_seconds is None:
-            validity_seconds = self.config.jwt_validity_seconds
-
-        data = {
-            "payload": payload,
-            "validity": validity_seconds,
-            "useStaticSigningKey": use_static_signing_key is not False,
-            "algorithm": "RS256",
-            "jwksDomain": self.app_info.api_domain.get_as_string_dangerous(),
-        }
-        response = await self.querier.send_post_request(
-            NormalisedURLPath("/recipe/jwt"),
-            data,
-            user_context=user_context,
-        )
-
-        if response["status"] == "OK":
-            return CreateJwtOkResult(response["jwt"])
-        return CreateJwtResultUnsupportedAlgorithm()
-
-    async def get_jwks(self, user_context: Dict[str, Any]) -> GetJWKSResult:
-        response = await self.querier.send_get_request(
-            NormalisedURLPath("/.well-known/jwks.json"),
-            {},
-            user_context=user_context,
-        )
-
-        validity_in_secs = DEFAULT_JWKS_MAX_AGE
-        cache_control = response["_headers"].get("Cache-Control")
-
-        if cache_control is not None:
-            pattern = r",?\s*max-age=(\d+)(?:,|$)"
-            max_age_header = re.match(pattern, cache_control)
-            if max_age_header is not None:
-                try:
-                    validity_in_secs = int(max_age_header.group(1))
-                except Exception:
-                    validity_in_secs = DEFAULT_JWKS_MAX_AGE
-
-        keys: List[JsonWebKey] = []
-        for key in response["keys"]:
-            keys.append(
-                JsonWebKey(
-                    key["kty"], key["kid"], key["n"], key["e"], key["alg"], key["use"]
-                )
-            )
-
-        return GetJWKSResult(keys, validity_in_secs)
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class RecipeImplementation -(querier: Querier, config: JWTConfig, app_info: AppInfo) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class RecipeImplementation(RecipeInterface):
-    def __init__(self, querier: Querier, config: JWTConfig, app_info: AppInfo):
-        super().__init__()
-        self.querier = querier
-        self.config = config
-        self.app_info = app_info
-
-    async def create_jwt(
-        self,
-        payload: Dict[str, Any],
-        validity_seconds: Optional[int],
-        use_static_signing_key: Optional[bool],
-        user_context: Dict[str, Any],
-    ) -> Union[CreateJwtOkResult, CreateJwtResultUnsupportedAlgorithm]:
-        if validity_seconds is None:
-            validity_seconds = self.config.jwt_validity_seconds
-
-        data = {
-            "payload": payload,
-            "validity": validity_seconds,
-            "useStaticSigningKey": use_static_signing_key is not False,
-            "algorithm": "RS256",
-            "jwksDomain": self.app_info.api_domain.get_as_string_dangerous(),
-        }
-        response = await self.querier.send_post_request(
-            NormalisedURLPath("/recipe/jwt"),
-            data,
-            user_context=user_context,
-        )
-
-        if response["status"] == "OK":
-            return CreateJwtOkResult(response["jwt"])
-        return CreateJwtResultUnsupportedAlgorithm()
-
-    async def get_jwks(self, user_context: Dict[str, Any]) -> GetJWKSResult:
-        response = await self.querier.send_get_request(
-            NormalisedURLPath("/.well-known/jwks.json"),
-            {},
-            user_context=user_context,
-        )
-
-        validity_in_secs = DEFAULT_JWKS_MAX_AGE
-        cache_control = response["_headers"].get("Cache-Control")
-
-        if cache_control is not None:
-            pattern = r",?\s*max-age=(\d+)(?:,|$)"
-            max_age_header = re.match(pattern, cache_control)
-            if max_age_header is not None:
-                try:
-                    validity_in_secs = int(max_age_header.group(1))
-                except Exception:
-                    validity_in_secs = DEFAULT_JWKS_MAX_AGE
-
-        keys: List[JsonWebKey] = []
-        for key in response["keys"]:
-            keys.append(
-                JsonWebKey(
-                    key["kty"], key["kid"], key["n"], key["e"], key["alg"], key["use"]
-                )
-            )
-
-        return GetJWKSResult(keys, validity_in_secs)
-
-

Ancestors

- -

Methods

-
-
-async def create_jwt(self, payload: Dict[str, Any], validity_seconds: Optional[int], use_static_signing_key: Optional[bool], user_context: Dict[str, Any]) ‑> Union[CreateJwtOkResultCreateJwtResultUnsupportedAlgorithm] -
-
-
-
- -Expand source code - -
async def create_jwt(
-    self,
-    payload: Dict[str, Any],
-    validity_seconds: Optional[int],
-    use_static_signing_key: Optional[bool],
-    user_context: Dict[str, Any],
-) -> Union[CreateJwtOkResult, CreateJwtResultUnsupportedAlgorithm]:
-    if validity_seconds is None:
-        validity_seconds = self.config.jwt_validity_seconds
-
-    data = {
-        "payload": payload,
-        "validity": validity_seconds,
-        "useStaticSigningKey": use_static_signing_key is not False,
-        "algorithm": "RS256",
-        "jwksDomain": self.app_info.api_domain.get_as_string_dangerous(),
-    }
-    response = await self.querier.send_post_request(
-        NormalisedURLPath("/recipe/jwt"),
-        data,
-        user_context=user_context,
-    )
-
-    if response["status"] == "OK":
-        return CreateJwtOkResult(response["jwt"])
-    return CreateJwtResultUnsupportedAlgorithm()
-
-
-
-async def get_jwks(self, user_context: Dict[str, Any]) ‑> GetJWKSResult -
-
-
-
- -Expand source code - -
async def get_jwks(self, user_context: Dict[str, Any]) -> GetJWKSResult:
-    response = await self.querier.send_get_request(
-        NormalisedURLPath("/.well-known/jwks.json"),
-        {},
-        user_context=user_context,
-    )
-
-    validity_in_secs = DEFAULT_JWKS_MAX_AGE
-    cache_control = response["_headers"].get("Cache-Control")
-
-    if cache_control is not None:
-        pattern = r",?\s*max-age=(\d+)(?:,|$)"
-        max_age_header = re.match(pattern, cache_control)
-        if max_age_header is not None:
-            try:
-                validity_in_secs = int(max_age_header.group(1))
-            except Exception:
-                validity_in_secs = DEFAULT_JWKS_MAX_AGE
-
-    keys: List[JsonWebKey] = []
-    for key in response["keys"]:
-        keys.append(
-            JsonWebKey(
-                key["kty"], key["kid"], key["n"], key["e"], key["alg"], key["use"]
-            )
-        )
-
-    return GetJWKSResult(keys, validity_in_secs)
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/jwt/syncio/index.html b/html/supertokens_python/recipe/jwt/syncio/index.html deleted file mode 100644 index d759d6af4..000000000 --- a/html/supertokens_python/recipe/jwt/syncio/index.html +++ /dev/null @@ -1,141 +0,0 @@ - - - - - - -supertokens_python.recipe.jwt.syncio API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.jwt.syncio

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from typing import Any, Dict, Union, Optional
-
-from supertokens_python.async_to_sync_wrapper import sync
-from supertokens_python.recipe.jwt import asyncio
-from supertokens_python.recipe.jwt.interfaces import (
-    CreateJwtOkResult,
-    CreateJwtResultUnsupportedAlgorithm,
-    GetJWKSResult,
-)
-
-
-def create_jwt(
-    payload: Optional[Dict[str, Any]] = None,
-    validity_seconds: Optional[int] = None,
-    use_static_signing_key: Optional[bool] = None,
-    user_context: Optional[Dict[str, Any]] = None,
-) -> Union[CreateJwtOkResult, CreateJwtResultUnsupportedAlgorithm]:
-    return sync(
-        asyncio.create_jwt(
-            payload, validity_seconds, use_static_signing_key, user_context
-        )
-    )
-
-
-def get_jwks(user_context: Optional[Dict[str, Any]] = None) -> GetJWKSResult:
-    return sync(asyncio.get_jwks(user_context))
-
-
-
-
-
-
-
-

Functions

-
-
-def create_jwt(payload: Optional[Dict[str, Any]] = None, validity_seconds: Optional[int] = None, use_static_signing_key: Optional[bool] = None, user_context: Optional[Dict[str, Any]] = None) ‑> Union[CreateJwtOkResultCreateJwtResultUnsupportedAlgorithm] -
-
-
-
- -Expand source code - -
def create_jwt(
-    payload: Optional[Dict[str, Any]] = None,
-    validity_seconds: Optional[int] = None,
-    use_static_signing_key: Optional[bool] = None,
-    user_context: Optional[Dict[str, Any]] = None,
-) -> Union[CreateJwtOkResult, CreateJwtResultUnsupportedAlgorithm]:
-    return sync(
-        asyncio.create_jwt(
-            payload, validity_seconds, use_static_signing_key, user_context
-        )
-    )
-
-
-
-def get_jwks(user_context: Optional[Dict[str, Any]] = None) ‑> GetJWKSResult -
-
-
-
- -Expand source code - -
def get_jwks(user_context: Optional[Dict[str, Any]] = None) -> GetJWKSResult:
-    return sync(asyncio.get_jwks(user_context))
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/jwt/utils.html b/html/supertokens_python/recipe/jwt/utils.html deleted file mode 100644 index 5620fd9ce..000000000 --- a/html/supertokens_python/recipe/jwt/utils.html +++ /dev/null @@ -1,195 +0,0 @@ - - - - - - -supertokens_python.recipe.jwt.utils API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.jwt.utils

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from typing import TYPE_CHECKING, Callable, Union
-
-if TYPE_CHECKING:
-    from .interfaces import APIInterface, RecipeInterface
-
-
-class OverrideConfig:
-    def __init__(
-        self,
-        functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None,
-        apis: Union[Callable[[APIInterface], APIInterface], None] = None,
-    ):
-        self.functions = functions
-        self.apis = apis
-
-
-class JWTConfig:
-    def __init__(self, override: OverrideConfig, jwt_validity_seconds: int):
-        self.override = override
-        self.jwt_validity_seconds = jwt_validity_seconds
-
-
-def validate_and_normalise_user_input(
-    jwt_validity_seconds: Union[int, None] = None,
-    override: Union[OverrideConfig, None] = None,
-):
-    if jwt_validity_seconds is not None and not isinstance(jwt_validity_seconds, int):  # type: ignore
-        raise ValueError("jwt_validity_seconds must be an integer or None")
-
-    if override is not None and not isinstance(override, OverrideConfig):  # type: ignore
-        raise ValueError("override must be an instance of OverrideConfig or None")
-
-    if override is None:
-        override = OverrideConfig()
-    if jwt_validity_seconds is None:
-        jwt_validity_seconds = 3153600000
-
-    return JWTConfig(override, jwt_validity_seconds)
-
-
-
-
-
-
-
-

Functions

-
-
-def validate_and_normalise_user_input(jwt_validity_seconds: Union[int, None] = None, override: Union[OverrideConfig, None] = None) -
-
-
-
- -Expand source code - -
def validate_and_normalise_user_input(
-    jwt_validity_seconds: Union[int, None] = None,
-    override: Union[OverrideConfig, None] = None,
-):
-    if jwt_validity_seconds is not None and not isinstance(jwt_validity_seconds, int):  # type: ignore
-        raise ValueError("jwt_validity_seconds must be an integer or None")
-
-    if override is not None and not isinstance(override, OverrideConfig):  # type: ignore
-        raise ValueError("override must be an instance of OverrideConfig or None")
-
-    if override is None:
-        override = OverrideConfig()
-    if jwt_validity_seconds is None:
-        jwt_validity_seconds = 3153600000
-
-    return JWTConfig(override, jwt_validity_seconds)
-
-
-
-
-
-

Classes

-
-
-class JWTConfig -(override: OverrideConfig, jwt_validity_seconds: int) -
-
-
-
- -Expand source code - -
class JWTConfig:
-    def __init__(self, override: OverrideConfig, jwt_validity_seconds: int):
-        self.override = override
-        self.jwt_validity_seconds = jwt_validity_seconds
-
-
-
-class OverrideConfig -(functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None, apis: Union[Callable[[APIInterface], APIInterface], None] = None) -
-
-
-
- -Expand source code - -
class OverrideConfig:
-    def __init__(
-        self,
-        functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None,
-        apis: Union[Callable[[APIInterface], APIInterface], None] = None,
-    ):
-        self.functions = functions
-        self.apis = apis
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/multitenancy/api/implementation.html b/html/supertokens_python/recipe/multitenancy/api/implementation.html deleted file mode 100644 index 99f3112d4..000000000 --- a/html/supertokens_python/recipe/multitenancy/api/implementation.html +++ /dev/null @@ -1,316 +0,0 @@ - - - - - - -supertokens_python.recipe.multitenancy.api.implementation API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.multitenancy.api.implementation

-
-
-
- -Expand source code - -
# Copyright (c) 2023, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from typing import Any, Dict, Optional, Union, List
-from ..constants import DEFAULT_TENANT_ID
-
-from supertokens_python.recipe.multitenancy.interfaces import (
-    APIOptions,
-    LoginMethodsGetOkResult,
-    LoginMethodEmailPassword,
-    LoginMethodPasswordless,
-    LoginMethodThirdParty,
-)
-from supertokens_python.types import GeneralErrorResponse
-
-from ..interfaces import APIInterface, ThirdPartyProvider
-
-
-class APIImplementation(APIInterface):
-    async def login_methods_get(
-        self,
-        tenant_id: str,
-        client_type: Optional[str],
-        api_options: APIOptions,
-        user_context: Dict[str, Any],
-    ) -> Union[LoginMethodsGetOkResult, GeneralErrorResponse]:
-        from supertokens_python.recipe.thirdparty.providers.config_utils import (
-            merge_providers_from_core_and_static,
-            find_and_create_provider_instance,
-        )
-
-        from supertokens_python.recipe.thirdparty.exceptions import (
-            ClientTypeNotFoundError,
-        )
-
-        tenant_config = await api_options.recipe_implementation.get_tenant(
-            tenant_id, user_context
-        )
-
-        if tenant_config is None:
-            raise Exception("Tenant not found")
-
-        provider_inputs_from_static = api_options.static_third_party_providers
-        provider_configs_from_core = tenant_config.third_party.providers
-
-        merged_providers = merge_providers_from_core_and_static(
-            provider_configs_from_core,
-            provider_inputs_from_static,
-            tenant_id == DEFAULT_TENANT_ID,
-        )
-
-        final_provider_list: List[ThirdPartyProvider] = []
-
-        for provider_input in merged_providers:
-            try:
-                provider_instance = await find_and_create_provider_instance(
-                    merged_providers,
-                    provider_input.config.third_party_id,
-                    client_type,
-                    user_context,
-                )
-
-                if provider_instance is None:
-                    raise Exception("Should never come here")
-
-            except ClientTypeNotFoundError:
-                continue
-            final_provider_list.append(
-                ThirdPartyProvider(provider_instance.id, provider_instance.config.name)
-            )
-
-        return LoginMethodsGetOkResult(
-            email_password=LoginMethodEmailPassword(
-                tenant_config.emailpassword.enabled
-            ),
-            passwordless=LoginMethodPasswordless(tenant_config.passwordless.enabled),
-            third_party=LoginMethodThirdParty(
-                tenant_config.third_party.enabled, final_provider_list
-            ),
-        )
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class APIImplementation -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class APIImplementation(APIInterface):
-    async def login_methods_get(
-        self,
-        tenant_id: str,
-        client_type: Optional[str],
-        api_options: APIOptions,
-        user_context: Dict[str, Any],
-    ) -> Union[LoginMethodsGetOkResult, GeneralErrorResponse]:
-        from supertokens_python.recipe.thirdparty.providers.config_utils import (
-            merge_providers_from_core_and_static,
-            find_and_create_provider_instance,
-        )
-
-        from supertokens_python.recipe.thirdparty.exceptions import (
-            ClientTypeNotFoundError,
-        )
-
-        tenant_config = await api_options.recipe_implementation.get_tenant(
-            tenant_id, user_context
-        )
-
-        if tenant_config is None:
-            raise Exception("Tenant not found")
-
-        provider_inputs_from_static = api_options.static_third_party_providers
-        provider_configs_from_core = tenant_config.third_party.providers
-
-        merged_providers = merge_providers_from_core_and_static(
-            provider_configs_from_core,
-            provider_inputs_from_static,
-            tenant_id == DEFAULT_TENANT_ID,
-        )
-
-        final_provider_list: List[ThirdPartyProvider] = []
-
-        for provider_input in merged_providers:
-            try:
-                provider_instance = await find_and_create_provider_instance(
-                    merged_providers,
-                    provider_input.config.third_party_id,
-                    client_type,
-                    user_context,
-                )
-
-                if provider_instance is None:
-                    raise Exception("Should never come here")
-
-            except ClientTypeNotFoundError:
-                continue
-            final_provider_list.append(
-                ThirdPartyProvider(provider_instance.id, provider_instance.config.name)
-            )
-
-        return LoginMethodsGetOkResult(
-            email_password=LoginMethodEmailPassword(
-                tenant_config.emailpassword.enabled
-            ),
-            passwordless=LoginMethodPasswordless(tenant_config.passwordless.enabled),
-            third_party=LoginMethodThirdParty(
-                tenant_config.third_party.enabled, final_provider_list
-            ),
-        )
-
-

Ancestors

- -

Methods

-
-
-async def login_methods_get(self, tenant_id: str, client_type: Optional[str], api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[LoginMethodsGetOkResultGeneralErrorResponse] -
-
-
-
- -Expand source code - -
async def login_methods_get(
-    self,
-    tenant_id: str,
-    client_type: Optional[str],
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-) -> Union[LoginMethodsGetOkResult, GeneralErrorResponse]:
-    from supertokens_python.recipe.thirdparty.providers.config_utils import (
-        merge_providers_from_core_and_static,
-        find_and_create_provider_instance,
-    )
-
-    from supertokens_python.recipe.thirdparty.exceptions import (
-        ClientTypeNotFoundError,
-    )
-
-    tenant_config = await api_options.recipe_implementation.get_tenant(
-        tenant_id, user_context
-    )
-
-    if tenant_config is None:
-        raise Exception("Tenant not found")
-
-    provider_inputs_from_static = api_options.static_third_party_providers
-    provider_configs_from_core = tenant_config.third_party.providers
-
-    merged_providers = merge_providers_from_core_and_static(
-        provider_configs_from_core,
-        provider_inputs_from_static,
-        tenant_id == DEFAULT_TENANT_ID,
-    )
-
-    final_provider_list: List[ThirdPartyProvider] = []
-
-    for provider_input in merged_providers:
-        try:
-            provider_instance = await find_and_create_provider_instance(
-                merged_providers,
-                provider_input.config.third_party_id,
-                client_type,
-                user_context,
-            )
-
-            if provider_instance is None:
-                raise Exception("Should never come here")
-
-        except ClientTypeNotFoundError:
-            continue
-        final_provider_list.append(
-            ThirdPartyProvider(provider_instance.id, provider_instance.config.name)
-        )
-
-    return LoginMethodsGetOkResult(
-        email_password=LoginMethodEmailPassword(
-            tenant_config.emailpassword.enabled
-        ),
-        passwordless=LoginMethodPasswordless(tenant_config.passwordless.enabled),
-        third_party=LoginMethodThirdParty(
-            tenant_config.third_party.enabled, final_provider_list
-        ),
-    )
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/multitenancy/api/index.html b/html/supertokens_python/recipe/multitenancy/api/index.html deleted file mode 100644 index b75fca94d..000000000 --- a/html/supertokens_python/recipe/multitenancy/api/index.html +++ /dev/null @@ -1,89 +0,0 @@ - - - - - - -supertokens_python.recipe.multitenancy.api API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.multitenancy.api

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from .login_methods import handle_login_methods_api  # type: ignore
-
-
-
-

Sub-modules

-
-
supertokens_python.recipe.multitenancy.api.implementation
-
-
-
-
supertokens_python.recipe.multitenancy.api.login_methods
-
-
-
-
-
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/multitenancy/api/login_methods.html b/html/supertokens_python/recipe/multitenancy/api/login_methods.html deleted file mode 100644 index 5c631e14f..000000000 --- a/html/supertokens_python/recipe/multitenancy/api/login_methods.html +++ /dev/null @@ -1,132 +0,0 @@ - - - - - - -supertokens_python.recipe.multitenancy.api.login_methods API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.multitenancy.api.login_methods

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-from typing import Any, Dict
-
-from supertokens_python.recipe.multitenancy.interfaces import (
-    APIInterface,
-    APIOptions,
-)
-from supertokens_python.utils import send_200_response
-
-
-async def handle_login_methods_api(
-    api_implementation: APIInterface,
-    tenant_id: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-):
-    if api_implementation.disable_login_methods_get:
-        return None
-
-    client_type = api_options.request.get_query_param("clientType")
-
-    result = await api_implementation.login_methods_get(
-        tenant_id, client_type, api_options, user_context
-    )
-
-    return send_200_response(result.to_json(), api_options.response)
-
-
-
-
-
-
-
-

Functions

-
-
-async def handle_login_methods_api(api_implementation: APIInterface, tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) -
-
-
-
- -Expand source code - -
async def handle_login_methods_api(
-    api_implementation: APIInterface,
-    tenant_id: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-):
-    if api_implementation.disable_login_methods_get:
-        return None
-
-    client_type = api_options.request.get_query_param("clientType")
-
-    result = await api_implementation.login_methods_get(
-        tenant_id, client_type, api_options, user_context
-    )
-
-    return send_200_response(result.to_json(), api_options.response)
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/multitenancy/asyncio/index.html b/html/supertokens_python/recipe/multitenancy/asyncio/index.html deleted file mode 100644 index ae58c9239..000000000 --- a/html/supertokens_python/recipe/multitenancy/asyncio/index.html +++ /dev/null @@ -1,404 +0,0 @@ - - - - - - -supertokens_python.recipe.multitenancy.asyncio API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.multitenancy.asyncio

-
-
-
- -Expand source code - -
#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from typing import Any, Dict, Union, Optional, TYPE_CHECKING
-
-from ..interfaces import (
-    TenantConfig,
-    CreateOrUpdateTenantOkResult,
-    DeleteTenantOkResult,
-    GetTenantOkResult,
-    ListAllTenantsOkResult,
-    CreateOrUpdateThirdPartyConfigOkResult,
-    DeleteThirdPartyConfigOkResult,
-    AssociateUserToTenantOkResult,
-    AssociateUserToTenantUnknownUserIdError,
-    AssociateUserToTenantEmailAlreadyExistsError,
-    AssociateUserToTenantPhoneNumberAlreadyExistsError,
-    AssociateUserToTenantThirdPartyUserAlreadyExistsError,
-    DisassociateUserFromTenantOkResult,
-)
-from ..recipe import MultitenancyRecipe
-
-if TYPE_CHECKING:
-    from ..interfaces import ProviderConfig
-
-
-async def create_or_update_tenant(
-    tenant_id: str,
-    config: Optional[TenantConfig],
-    user_context: Optional[Dict[str, Any]] = None,
-) -> CreateOrUpdateTenantOkResult:
-    if user_context is None:
-        user_context = {}
-    recipe = MultitenancyRecipe.get_instance()
-
-    return await recipe.recipe_implementation.create_or_update_tenant(
-        tenant_id, config, user_context
-    )
-
-
-async def delete_tenant(
-    tenant_id: str, user_context: Optional[Dict[str, Any]] = None
-) -> DeleteTenantOkResult:
-    if user_context is None:
-        user_context = {}
-    recipe = MultitenancyRecipe.get_instance()
-
-    return await recipe.recipe_implementation.delete_tenant(tenant_id, user_context)
-
-
-async def get_tenant(
-    tenant_id: str, user_context: Optional[Dict[str, Any]] = None
-) -> Optional[GetTenantOkResult]:
-    if user_context is None:
-        user_context = {}
-    recipe = MultitenancyRecipe.get_instance()
-
-    return await recipe.recipe_implementation.get_tenant(tenant_id, user_context)
-
-
-async def list_all_tenants(
-    user_context: Optional[Dict[str, Any]] = None
-) -> ListAllTenantsOkResult:
-    if user_context is None:
-        user_context = {}
-
-    recipe = MultitenancyRecipe.get_instance()
-
-    return await recipe.recipe_implementation.list_all_tenants(user_context)
-
-
-async def create_or_update_third_party_config(
-    tenant_id: str,
-    config: ProviderConfig,
-    skip_validation: Optional[bool] = None,
-    user_context: Optional[Dict[str, Any]] = None,
-) -> CreateOrUpdateThirdPartyConfigOkResult:
-    if user_context is None:
-        user_context = {}
-
-    recipe = MultitenancyRecipe.get_instance()
-
-    return await recipe.recipe_implementation.create_or_update_third_party_config(
-        tenant_id, config, skip_validation, user_context
-    )
-
-
-async def delete_third_party_config(
-    tenant_id: str,
-    third_party_id: str,
-    user_context: Optional[Dict[str, Any]] = None,
-) -> DeleteThirdPartyConfigOkResult:
-    if user_context is None:
-        user_context = {}
-
-    recipe = MultitenancyRecipe.get_instance()
-
-    return await recipe.recipe_implementation.delete_third_party_config(
-        tenant_id, third_party_id, user_context
-    )
-
-
-async def associate_user_to_tenant(
-    tenant_id: str,
-    user_id: str,
-    user_context: Optional[Dict[str, Any]] = None,
-) -> Union[
-    AssociateUserToTenantOkResult,
-    AssociateUserToTenantUnknownUserIdError,
-    AssociateUserToTenantEmailAlreadyExistsError,
-    AssociateUserToTenantPhoneNumberAlreadyExistsError,
-    AssociateUserToTenantThirdPartyUserAlreadyExistsError,
-]:
-    if user_context is None:
-        user_context = {}
-
-    recipe = MultitenancyRecipe.get_instance()
-
-    return await recipe.recipe_implementation.associate_user_to_tenant(
-        tenant_id, user_id, user_context
-    )
-
-
-async def dissociate_user_from_tenant(
-    tenant_id: str,
-    user_id: str,
-    user_context: Optional[Dict[str, Any]] = None,
-) -> DisassociateUserFromTenantOkResult:
-    if user_context is None:
-        user_context = {}
-
-    recipe = MultitenancyRecipe.get_instance()
-
-    return await recipe.recipe_implementation.dissociate_user_from_tenant(
-        tenant_id, user_id, user_context
-    )
-
-
-
-
-
-
-
-

Functions

-
-
-async def associate_user_to_tenant(tenant_id: str, user_id: str, user_context: Optional[Dict[str, Any]] = None) ‑> Union[AssociateUserToTenantOkResultAssociateUserToTenantUnknownUserIdErrorAssociateUserToTenantEmailAlreadyExistsErrorAssociateUserToTenantPhoneNumberAlreadyExistsErrorAssociateUserToTenantThirdPartyUserAlreadyExistsError] -
-
-
-
- -Expand source code - -
async def associate_user_to_tenant(
-    tenant_id: str,
-    user_id: str,
-    user_context: Optional[Dict[str, Any]] = None,
-) -> Union[
-    AssociateUserToTenantOkResult,
-    AssociateUserToTenantUnknownUserIdError,
-    AssociateUserToTenantEmailAlreadyExistsError,
-    AssociateUserToTenantPhoneNumberAlreadyExistsError,
-    AssociateUserToTenantThirdPartyUserAlreadyExistsError,
-]:
-    if user_context is None:
-        user_context = {}
-
-    recipe = MultitenancyRecipe.get_instance()
-
-    return await recipe.recipe_implementation.associate_user_to_tenant(
-        tenant_id, user_id, user_context
-    )
-
-
-
-async def create_or_update_tenant(tenant_id: str, config: Optional[TenantConfig], user_context: Optional[Dict[str, Any]] = None) ‑> CreateOrUpdateTenantOkResult -
-
-
-
- -Expand source code - -
async def create_or_update_tenant(
-    tenant_id: str,
-    config: Optional[TenantConfig],
-    user_context: Optional[Dict[str, Any]] = None,
-) -> CreateOrUpdateTenantOkResult:
-    if user_context is None:
-        user_context = {}
-    recipe = MultitenancyRecipe.get_instance()
-
-    return await recipe.recipe_implementation.create_or_update_tenant(
-        tenant_id, config, user_context
-    )
-
-
-
-async def create_or_update_third_party_config(tenant_id: str, config: ProviderConfig, skip_validation: Optional[bool] = None, user_context: Optional[Dict[str, Any]] = None) ‑> CreateOrUpdateThirdPartyConfigOkResult -
-
-
-
- -Expand source code - -
async def create_or_update_third_party_config(
-    tenant_id: str,
-    config: ProviderConfig,
-    skip_validation: Optional[bool] = None,
-    user_context: Optional[Dict[str, Any]] = None,
-) -> CreateOrUpdateThirdPartyConfigOkResult:
-    if user_context is None:
-        user_context = {}
-
-    recipe = MultitenancyRecipe.get_instance()
-
-    return await recipe.recipe_implementation.create_or_update_third_party_config(
-        tenant_id, config, skip_validation, user_context
-    )
-
-
-
-async def delete_tenant(tenant_id: str, user_context: Optional[Dict[str, Any]] = None) ‑> DeleteTenantOkResult -
-
-
-
- -Expand source code - -
async def delete_tenant(
-    tenant_id: str, user_context: Optional[Dict[str, Any]] = None
-) -> DeleteTenantOkResult:
-    if user_context is None:
-        user_context = {}
-    recipe = MultitenancyRecipe.get_instance()
-
-    return await recipe.recipe_implementation.delete_tenant(tenant_id, user_context)
-
-
-
-async def delete_third_party_config(tenant_id: str, third_party_id: str, user_context: Optional[Dict[str, Any]] = None) ‑> DeleteThirdPartyConfigOkResult -
-
-
-
- -Expand source code - -
async def delete_third_party_config(
-    tenant_id: str,
-    third_party_id: str,
-    user_context: Optional[Dict[str, Any]] = None,
-) -> DeleteThirdPartyConfigOkResult:
-    if user_context is None:
-        user_context = {}
-
-    recipe = MultitenancyRecipe.get_instance()
-
-    return await recipe.recipe_implementation.delete_third_party_config(
-        tenant_id, third_party_id, user_context
-    )
-
-
-
-async def dissociate_user_from_tenant(tenant_id: str, user_id: str, user_context: Optional[Dict[str, Any]] = None) ‑> DisassociateUserFromTenantOkResult -
-
-
-
- -Expand source code - -
async def dissociate_user_from_tenant(
-    tenant_id: str,
-    user_id: str,
-    user_context: Optional[Dict[str, Any]] = None,
-) -> DisassociateUserFromTenantOkResult:
-    if user_context is None:
-        user_context = {}
-
-    recipe = MultitenancyRecipe.get_instance()
-
-    return await recipe.recipe_implementation.dissociate_user_from_tenant(
-        tenant_id, user_id, user_context
-    )
-
-
-
-async def get_tenant(tenant_id: str, user_context: Optional[Dict[str, Any]] = None) ‑> Optional[GetTenantOkResult] -
-
-
-
- -Expand source code - -
async def get_tenant(
-    tenant_id: str, user_context: Optional[Dict[str, Any]] = None
-) -> Optional[GetTenantOkResult]:
-    if user_context is None:
-        user_context = {}
-    recipe = MultitenancyRecipe.get_instance()
-
-    return await recipe.recipe_implementation.get_tenant(tenant_id, user_context)
-
-
-
-async def list_all_tenants(user_context: Optional[Dict[str, Any]] = None) ‑> ListAllTenantsOkResult -
-
-
-
- -Expand source code - -
async def list_all_tenants(
-    user_context: Optional[Dict[str, Any]] = None
-) -> ListAllTenantsOkResult:
-    if user_context is None:
-        user_context = {}
-
-    recipe = MultitenancyRecipe.get_instance()
-
-    return await recipe.recipe_implementation.list_all_tenants(user_context)
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/multitenancy/constants.html b/html/supertokens_python/recipe/multitenancy/constants.html deleted file mode 100644 index 80375303f..000000000 --- a/html/supertokens_python/recipe/multitenancy/constants.html +++ /dev/null @@ -1,73 +0,0 @@ - - - - - - -supertokens_python.recipe.multitenancy.constants API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.multitenancy.constants

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-LOGIN_METHODS = "/loginmethods"
-DEFAULT_TENANT_ID = "public"
-
-
-
-
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/multitenancy/exceptions.html b/html/supertokens_python/recipe/multitenancy/exceptions.html deleted file mode 100644 index 029f9ca74..000000000 --- a/html/supertokens_python/recipe/multitenancy/exceptions.html +++ /dev/null @@ -1,108 +0,0 @@ - - - - - - -supertokens_python.recipe.multitenancy.exceptions API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.multitenancy.exceptions

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from supertokens_python.exceptions import SuperTokensError
-
-
-class MultitenancyError(SuperTokensError):
-    pass
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class MultitenancyError -(*args, **kwargs) -
-
-

Common base class for all non-exit exceptions.

-
- -Expand source code - -
class MultitenancyError(SuperTokensError):
-    pass
-
-

Ancestors

- -
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/multitenancy/index.html b/html/supertokens_python/recipe/multitenancy/index.html deleted file mode 100644 index f7ed66737..000000000 --- a/html/supertokens_python/recipe/multitenancy/index.html +++ /dev/null @@ -1,180 +0,0 @@ - - - - - - -supertokens_python.recipe.multitenancy API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.multitenancy

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from typing import TYPE_CHECKING, Callable, Union
-
-from . import exceptions as ex
-from . import recipe
-
-AllowedDomainsClaim = recipe.AllowedDomainsClaim
-exceptions = ex
-
-if TYPE_CHECKING:
-    from supertokens_python.supertokens import AppInfo
-
-    from ...recipe_module import RecipeModule
-    from .interfaces import TypeGetAllowedDomainsForTenantId
-    from .utils import InputOverrideConfig
-
-
-def init(
-    get_allowed_domains_for_tenant_id: Union[
-        TypeGetAllowedDomainsForTenantId, None
-    ] = None,
-    override: Union[InputOverrideConfig, None] = None,
-) -> Callable[[AppInfo], RecipeModule]:
-    return recipe.MultitenancyRecipe.init(
-        get_allowed_domains_for_tenant_id,
-        override,
-    )
-
-
-
-

Sub-modules

-
-
supertokens_python.recipe.multitenancy.api
-
-
-
-
supertokens_python.recipe.multitenancy.asyncio
-
-
-
-
supertokens_python.recipe.multitenancy.constants
-
-
-
-
supertokens_python.recipe.multitenancy.exceptions
-
-
-
-
supertokens_python.recipe.multitenancy.interfaces
-
-
-
-
supertokens_python.recipe.multitenancy.recipe
-
-
-
-
supertokens_python.recipe.multitenancy.recipe_implementation
-
-
-
-
supertokens_python.recipe.multitenancy.syncio
-
-
-
-
supertokens_python.recipe.multitenancy.utils
-
-
-
-
-
-
-
-
-

Functions

-
-
-def init(get_allowed_domains_for_tenant_id: Union[TypeGetAllowedDomainsForTenantId, None] = None, override: Union[InputOverrideConfig, None] = None) ‑> Callable[[AppInfo], RecipeModule] -
-
-
-
- -Expand source code - -
def init(
-    get_allowed_domains_for_tenant_id: Union[
-        TypeGetAllowedDomainsForTenantId, None
-    ] = None,
-    override: Union[InputOverrideConfig, None] = None,
-) -> Callable[[AppInfo], RecipeModule]:
-    return recipe.MultitenancyRecipe.init(
-        get_allowed_domains_for_tenant_id,
-        override,
-    )
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/multitenancy/interfaces.html b/html/supertokens_python/recipe/multitenancy/interfaces.html deleted file mode 100644 index b57c7b58f..000000000 --- a/html/supertokens_python/recipe/multitenancy/interfaces.html +++ /dev/null @@ -1,1705 +0,0 @@ - - - - - - -supertokens_python.recipe.multitenancy.interfaces API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.multitenancy.interfaces

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from abc import ABC, abstractmethod
-from typing import TYPE_CHECKING, Any, Dict, Union, Callable, Awaitable, Optional, List
-
-from supertokens_python.types import APIResponse, GeneralErrorResponse
-
-if TYPE_CHECKING:
-    from supertokens_python.framework import BaseRequest, BaseResponse
-    from supertokens_python.recipe.thirdparty.provider import (
-        ProviderConfig,
-        ProviderInput,
-    )
-    from .utils import MultitenancyConfig
-
-
-class TenantConfig:
-    def __init__(
-        self,
-        email_password_enabled: Union[bool, None] = None,
-        passwordless_enabled: Union[bool, None] = None,
-        third_party_enabled: Union[bool, None] = None,
-        core_config: Union[Dict[str, Any], None] = None,
-    ):
-        self.email_password_enabled = email_password_enabled
-        self.passwordless_enabled = passwordless_enabled
-        self.third_party_enabled = third_party_enabled
-        self.core_config = core_config
-
-    def to_json(self) -> Dict[str, Any]:
-        res: Dict[str, Any] = {}
-        if self.email_password_enabled is not None:
-            res["emailPasswordEnabled"] = self.email_password_enabled
-        if self.passwordless_enabled is not None:
-            res["passwordlessEnabled"] = self.passwordless_enabled
-        if self.third_party_enabled is not None:
-            res["thirdPartyEnabled"] = self.third_party_enabled
-        if self.core_config is not None:
-            res["coreConfig"] = self.core_config
-        return res
-
-
-class CreateOrUpdateTenantOkResult:
-    status = "OK"
-
-    def __init__(self, created_new: bool):
-        self.created_new = created_new
-
-
-class DeleteTenantOkResult:
-    status = "OK"
-
-    def __init__(self, did_exist: bool):
-        self.did_exist = did_exist
-
-
-class EmailPasswordConfig:
-    def __init__(self, enabled: bool):
-        self.enabled = enabled
-
-    def to_json(self):
-        return {"enabled": self.enabled}
-
-
-class PasswordlessConfig:
-    def __init__(self, enabled: bool):
-        self.enabled = enabled
-
-    def to_json(self):
-        return {"enabled": self.enabled}
-
-
-class ThirdPartyConfig:
-    def __init__(self, enabled: bool, providers: List[ProviderConfig]):
-        self.enabled = enabled
-        self.providers = providers
-
-    def to_json(self):
-        return {
-            "enabled": self.enabled,
-            "providers": [provider.to_json() for provider in self.providers],
-        }
-
-
-class TenantConfigResponse:
-    def __init__(
-        self,
-        emailpassword: EmailPasswordConfig,
-        passwordless: PasswordlessConfig,
-        third_party: ThirdPartyConfig,
-        core_config: Dict[str, Any],
-    ):
-        self.emailpassword = emailpassword
-        self.passwordless = passwordless
-        self.third_party = third_party
-        self.core_config = core_config
-
-
-class GetTenantOkResult(TenantConfigResponse):
-    status = "OK"
-
-
-class ListAllTenantsItem(TenantConfigResponse):
-    def __init__(
-        self,
-        tenant_id: str,
-        emailpassword: EmailPasswordConfig,
-        passwordless: PasswordlessConfig,
-        third_party: ThirdPartyConfig,
-        core_config: Dict[str, Any],
-    ):
-        super().__init__(emailpassword, passwordless, third_party, core_config)
-        self.tenant_id = tenant_id
-
-    def to_json(self):
-        res = {
-            "tenantId": self.tenant_id,
-            "emailPassword": self.emailpassword.to_json(),
-            "passwordless": self.passwordless.to_json(),
-            "thirdParty": self.third_party.to_json(),
-            "coreConfig": self.core_config,
-        }
-
-        return res
-
-
-class ListAllTenantsOkResult:
-    status = "OK"
-
-    def __init__(self, tenants: List[ListAllTenantsItem]):
-        self.tenants = tenants
-
-
-class CreateOrUpdateThirdPartyConfigOkResult:
-    status = "OK"
-
-    def __init__(self, created_new: bool):
-        self.created_new = created_new
-
-
-class DeleteThirdPartyConfigOkResult:
-    status = "OK"
-
-    def __init__(self, did_config_exist: bool):
-        self.did_config_exist = did_config_exist
-
-
-class AssociateUserToTenantOkResult:
-    status = "OK"
-
-    def __init__(self, was_already_associated: bool):
-        self.was_already_associated = was_already_associated
-
-
-class AssociateUserToTenantUnknownUserIdError:
-    status = "UNKNOWN_USER_ID_ERROR"
-
-
-class AssociateUserToTenantEmailAlreadyExistsError:
-    status = "EMAIL_ALREADY_EXISTS_ERROR"
-
-
-class AssociateUserToTenantPhoneNumberAlreadyExistsError:
-    status = "PHONE_NUMBER_ALREADY_EXISTS_ERROR"
-
-
-class AssociateUserToTenantThirdPartyUserAlreadyExistsError:
-    status = "THIRD_PARTY_USER_ALREADY_EXISTS_ERROR"
-
-
-class DisassociateUserFromTenantOkResult:
-    status = "OK"
-
-    def __init__(self, was_associated: bool):
-        self.was_associated = was_associated
-
-
-class RecipeInterface(ABC):
-    def __init__(self):
-        pass
-
-    @abstractmethod
-    async def get_tenant_id(
-        self, tenant_id_from_frontend: str, user_context: Dict[str, Any]
-    ) -> str:
-        pass
-
-    @abstractmethod
-    async def create_or_update_tenant(
-        self,
-        tenant_id: str,
-        config: Optional[TenantConfig],
-        user_context: Dict[str, Any],
-    ) -> CreateOrUpdateTenantOkResult:
-        pass
-
-    @abstractmethod
-    async def delete_tenant(
-        self, tenant_id: str, user_context: Dict[str, Any]
-    ) -> DeleteTenantOkResult:
-        pass
-
-    @abstractmethod
-    async def get_tenant(
-        self, tenant_id: str, user_context: Dict[str, Any]
-    ) -> Optional[GetTenantOkResult]:
-        pass
-
-    @abstractmethod
-    async def list_all_tenants(
-        self, user_context: Dict[str, Any]
-    ) -> ListAllTenantsOkResult:
-        pass
-
-    # third party provider management
-    @abstractmethod
-    async def create_or_update_third_party_config(
-        self,
-        tenant_id: str,
-        config: ProviderConfig,
-        skip_validation: Optional[bool],
-        user_context: Dict[str, Any],
-    ) -> CreateOrUpdateThirdPartyConfigOkResult:
-        pass
-
-    @abstractmethod
-    async def delete_third_party_config(
-        self,
-        tenant_id: str,
-        third_party_id: str,
-        user_context: Dict[str, Any],
-    ) -> DeleteThirdPartyConfigOkResult:
-        pass
-
-    # user tenant association
-    @abstractmethod
-    async def associate_user_to_tenant(
-        self,
-        tenant_id: str,
-        user_id: str,
-        user_context: Dict[str, Any],
-    ) -> Union[
-        AssociateUserToTenantOkResult,
-        AssociateUserToTenantUnknownUserIdError,
-        AssociateUserToTenantEmailAlreadyExistsError,
-        AssociateUserToTenantPhoneNumberAlreadyExistsError,
-        AssociateUserToTenantThirdPartyUserAlreadyExistsError,
-    ]:
-        pass
-
-    @abstractmethod
-    async def dissociate_user_from_tenant(
-        self,
-        tenant_id: str,
-        user_id: str,
-        user_context: Dict[str, Any],
-    ) -> DisassociateUserFromTenantOkResult:
-        pass
-
-
-class APIOptions:
-    def __init__(
-        self,
-        request: BaseRequest,
-        response: BaseResponse,
-        recipe_id: str,
-        config: MultitenancyConfig,
-        recipe_implementation: RecipeInterface,
-        static_third_party_providers: List[ProviderInput],
-    ):
-        self.request = request
-        self.response = response
-        self.recipe_id = recipe_id
-        self.config = config
-        self.recipe_implementation = recipe_implementation
-        self.static_third_party_providers = static_third_party_providers
-
-
-class ThirdPartyProvider:
-    def __init__(
-        self, id: str, name: Optional[str]
-    ):  # pylint: disable=redefined-builtin
-        self.id = id
-        self.name = name
-
-    def to_json(self) -> Dict[str, Any]:
-        return {
-            "id": self.id,
-            "name": self.name,
-        }
-
-
-class LoginMethodEmailPassword:
-    def __init__(self, enabled: bool):
-        self.enabled = enabled
-
-    def to_json(self) -> Dict[str, Any]:
-        return {
-            "enabled": self.enabled,
-        }
-
-
-class LoginMethodPasswordless:
-    def __init__(self, enabled: bool):
-        self.enabled = enabled
-
-    def to_json(self) -> Dict[str, Any]:
-        return {
-            "enabled": self.enabled,
-        }
-
-
-class LoginMethodThirdParty:
-    def __init__(self, enabled: bool, providers: List[ThirdPartyProvider]):
-        self.enabled = enabled
-        self.providers = providers
-
-    def to_json(self) -> Dict[str, Any]:
-        return {
-            "enabled": self.enabled,
-            "providers": [provider.to_json() for provider in self.providers],
-        }
-
-
-class LoginMethodsGetOkResult(APIResponse):
-    def __init__(
-        self,
-        email_password: LoginMethodEmailPassword,
-        passwordless: LoginMethodPasswordless,
-        third_party: LoginMethodThirdParty,
-    ):
-        self.status = "OK"
-        self.email_password = email_password
-        self.passwordless = passwordless
-        self.third_party = third_party
-
-    def to_json(self) -> Dict[str, Any]:
-        return {
-            "status": self.status,
-            "emailPassword": self.email_password.to_json(),
-            "passwordless": self.passwordless.to_json(),
-            "thirdParty": self.third_party.to_json(),
-        }
-
-
-class APIInterface(ABC):
-    def __init__(self):
-        self.disable_login_methods_get = False
-
-    @abstractmethod
-    async def login_methods_get(
-        self,
-        tenant_id: str,
-        client_type: Optional[str],
-        api_options: APIOptions,
-        user_context: Dict[str, Any],
-    ) -> Union[LoginMethodsGetOkResult, GeneralErrorResponse]:
-        pass
-
-
-TypeGetAllowedDomainsForTenantId = Callable[
-    [str, Dict[str, Any]], Awaitable[Optional[List[str]]]
-]
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class APIInterface -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class APIInterface(ABC):
-    def __init__(self):
-        self.disable_login_methods_get = False
-
-    @abstractmethod
-    async def login_methods_get(
-        self,
-        tenant_id: str,
-        client_type: Optional[str],
-        api_options: APIOptions,
-        user_context: Dict[str, Any],
-    ) -> Union[LoginMethodsGetOkResult, GeneralErrorResponse]:
-        pass
-
-

Ancestors

-
    -
  • abc.ABC
  • -
-

Subclasses

- -

Methods

-
-
-async def login_methods_get(self, tenant_id: str, client_type: Optional[str], api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[LoginMethodsGetOkResultGeneralErrorResponse] -
-
-
-
- -Expand source code - -
@abstractmethod
-async def login_methods_get(
-    self,
-    tenant_id: str,
-    client_type: Optional[str],
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-) -> Union[LoginMethodsGetOkResult, GeneralErrorResponse]:
-    pass
-
-
-
-
-
-class APIOptions -(request: BaseRequest, response: BaseResponse, recipe_id: str, config: MultitenancyConfig, recipe_implementation: RecipeInterface, static_third_party_providers: List[ProviderInput]) -
-
-
-
- -Expand source code - -
class APIOptions:
-    def __init__(
-        self,
-        request: BaseRequest,
-        response: BaseResponse,
-        recipe_id: str,
-        config: MultitenancyConfig,
-        recipe_implementation: RecipeInterface,
-        static_third_party_providers: List[ProviderInput],
-    ):
-        self.request = request
-        self.response = response
-        self.recipe_id = recipe_id
-        self.config = config
-        self.recipe_implementation = recipe_implementation
-        self.static_third_party_providers = static_third_party_providers
-
-
-
-class AssociateUserToTenantEmailAlreadyExistsError -
-
-
-
- -Expand source code - -
class AssociateUserToTenantEmailAlreadyExistsError:
-    status = "EMAIL_ALREADY_EXISTS_ERROR"
-
-

Class variables

-
-
var status
-
-
-
-
-
-
-class AssociateUserToTenantOkResult -(was_already_associated: bool) -
-
-
-
- -Expand source code - -
class AssociateUserToTenantOkResult:
-    status = "OK"
-
-    def __init__(self, was_already_associated: bool):
-        self.was_already_associated = was_already_associated
-
-

Class variables

-
-
var status
-
-
-
-
-
-
-class AssociateUserToTenantPhoneNumberAlreadyExistsError -
-
-
-
- -Expand source code - -
class AssociateUserToTenantPhoneNumberAlreadyExistsError:
-    status = "PHONE_NUMBER_ALREADY_EXISTS_ERROR"
-
-

Class variables

-
-
var status
-
-
-
-
-
-
-class AssociateUserToTenantThirdPartyUserAlreadyExistsError -
-
-
-
- -Expand source code - -
class AssociateUserToTenantThirdPartyUserAlreadyExistsError:
-    status = "THIRD_PARTY_USER_ALREADY_EXISTS_ERROR"
-
-

Class variables

-
-
var status
-
-
-
-
-
-
-class AssociateUserToTenantUnknownUserIdError -
-
-
-
- -Expand source code - -
class AssociateUserToTenantUnknownUserIdError:
-    status = "UNKNOWN_USER_ID_ERROR"
-
-

Class variables

-
-
var status
-
-
-
-
-
-
-class CreateOrUpdateTenantOkResult -(created_new: bool) -
-
-
-
- -Expand source code - -
class CreateOrUpdateTenantOkResult:
-    status = "OK"
-
-    def __init__(self, created_new: bool):
-        self.created_new = created_new
-
-

Class variables

-
-
var status
-
-
-
-
-
-
-class CreateOrUpdateThirdPartyConfigOkResult -(created_new: bool) -
-
-
-
- -Expand source code - -
class CreateOrUpdateThirdPartyConfigOkResult:
-    status = "OK"
-
-    def __init__(self, created_new: bool):
-        self.created_new = created_new
-
-

Class variables

-
-
var status
-
-
-
-
-
-
-class DeleteTenantOkResult -(did_exist: bool) -
-
-
-
- -Expand source code - -
class DeleteTenantOkResult:
-    status = "OK"
-
-    def __init__(self, did_exist: bool):
-        self.did_exist = did_exist
-
-

Class variables

-
-
var status
-
-
-
-
-
-
-class DeleteThirdPartyConfigOkResult -(did_config_exist: bool) -
-
-
-
- -Expand source code - -
class DeleteThirdPartyConfigOkResult:
-    status = "OK"
-
-    def __init__(self, did_config_exist: bool):
-        self.did_config_exist = did_config_exist
-
-

Class variables

-
-
var status
-
-
-
-
-
-
-class DisassociateUserFromTenantOkResult -(was_associated: bool) -
-
-
-
- -Expand source code - -
class DisassociateUserFromTenantOkResult:
-    status = "OK"
-
-    def __init__(self, was_associated: bool):
-        self.was_associated = was_associated
-
-

Class variables

-
-
var status
-
-
-
-
-
-
-class EmailPasswordConfig -(enabled: bool) -
-
-
-
- -Expand source code - -
class EmailPasswordConfig:
-    def __init__(self, enabled: bool):
-        self.enabled = enabled
-
-    def to_json(self):
-        return {"enabled": self.enabled}
-
-

Methods

-
-
-def to_json(self) -
-
-
-
- -Expand source code - -
def to_json(self):
-    return {"enabled": self.enabled}
-
-
-
-
-
-class GetTenantOkResult -(emailpassword: EmailPasswordConfig, passwordless: PasswordlessConfig, third_party: ThirdPartyConfig, core_config: Dict[str, Any]) -
-
-
-
- -Expand source code - -
class GetTenantOkResult(TenantConfigResponse):
-    status = "OK"
-
-

Ancestors

- -

Class variables

-
-
var status
-
-
-
-
-
-
-class ListAllTenantsItem -(tenant_id: str, emailpassword: EmailPasswordConfig, passwordless: PasswordlessConfig, third_party: ThirdPartyConfig, core_config: Dict[str, Any]) -
-
-
-
- -Expand source code - -
class ListAllTenantsItem(TenantConfigResponse):
-    def __init__(
-        self,
-        tenant_id: str,
-        emailpassword: EmailPasswordConfig,
-        passwordless: PasswordlessConfig,
-        third_party: ThirdPartyConfig,
-        core_config: Dict[str, Any],
-    ):
-        super().__init__(emailpassword, passwordless, third_party, core_config)
-        self.tenant_id = tenant_id
-
-    def to_json(self):
-        res = {
-            "tenantId": self.tenant_id,
-            "emailPassword": self.emailpassword.to_json(),
-            "passwordless": self.passwordless.to_json(),
-            "thirdParty": self.third_party.to_json(),
-            "coreConfig": self.core_config,
-        }
-
-        return res
-
-

Ancestors

- -

Methods

-
-
-def to_json(self) -
-
-
-
- -Expand source code - -
def to_json(self):
-    res = {
-        "tenantId": self.tenant_id,
-        "emailPassword": self.emailpassword.to_json(),
-        "passwordless": self.passwordless.to_json(),
-        "thirdParty": self.third_party.to_json(),
-        "coreConfig": self.core_config,
-    }
-
-    return res
-
-
-
-
-
-class ListAllTenantsOkResult -(tenants: List[ListAllTenantsItem]) -
-
-
-
- -Expand source code - -
class ListAllTenantsOkResult:
-    status = "OK"
-
-    def __init__(self, tenants: List[ListAllTenantsItem]):
-        self.tenants = tenants
-
-

Class variables

-
-
var status
-
-
-
-
-
-
-class LoginMethodEmailPassword -(enabled: bool) -
-
-
-
- -Expand source code - -
class LoginMethodEmailPassword:
-    def __init__(self, enabled: bool):
-        self.enabled = enabled
-
-    def to_json(self) -> Dict[str, Any]:
-        return {
-            "enabled": self.enabled,
-        }
-
-

Methods

-
-
-def to_json(self) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
def to_json(self) -> Dict[str, Any]:
-    return {
-        "enabled": self.enabled,
-    }
-
-
-
-
-
-class LoginMethodPasswordless -(enabled: bool) -
-
-
-
- -Expand source code - -
class LoginMethodPasswordless:
-    def __init__(self, enabled: bool):
-        self.enabled = enabled
-
-    def to_json(self) -> Dict[str, Any]:
-        return {
-            "enabled": self.enabled,
-        }
-
-

Methods

-
-
-def to_json(self) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
def to_json(self) -> Dict[str, Any]:
-    return {
-        "enabled": self.enabled,
-    }
-
-
-
-
-
-class LoginMethodThirdParty -(enabled: bool, providers: List[ThirdPartyProvider]) -
-
-
-
- -Expand source code - -
class LoginMethodThirdParty:
-    def __init__(self, enabled: bool, providers: List[ThirdPartyProvider]):
-        self.enabled = enabled
-        self.providers = providers
-
-    def to_json(self) -> Dict[str, Any]:
-        return {
-            "enabled": self.enabled,
-            "providers": [provider.to_json() for provider in self.providers],
-        }
-
-

Methods

-
-
-def to_json(self) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
def to_json(self) -> Dict[str, Any]:
-    return {
-        "enabled": self.enabled,
-        "providers": [provider.to_json() for provider in self.providers],
-    }
-
-
-
-
-
-class LoginMethodsGetOkResult -(email_password: LoginMethodEmailPassword, passwordless: LoginMethodPasswordless, third_party: LoginMethodThirdParty) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class LoginMethodsGetOkResult(APIResponse):
-    def __init__(
-        self,
-        email_password: LoginMethodEmailPassword,
-        passwordless: LoginMethodPasswordless,
-        third_party: LoginMethodThirdParty,
-    ):
-        self.status = "OK"
-        self.email_password = email_password
-        self.passwordless = passwordless
-        self.third_party = third_party
-
-    def to_json(self) -> Dict[str, Any]:
-        return {
-            "status": self.status,
-            "emailPassword": self.email_password.to_json(),
-            "passwordless": self.passwordless.to_json(),
-            "thirdParty": self.third_party.to_json(),
-        }
-
-

Ancestors

- -

Methods

-
-
-def to_json(self) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
def to_json(self) -> Dict[str, Any]:
-    return {
-        "status": self.status,
-        "emailPassword": self.email_password.to_json(),
-        "passwordless": self.passwordless.to_json(),
-        "thirdParty": self.third_party.to_json(),
-    }
-
-
-
-
-
-class PasswordlessConfig -(enabled: bool) -
-
-
-
- -Expand source code - -
class PasswordlessConfig:
-    def __init__(self, enabled: bool):
-        self.enabled = enabled
-
-    def to_json(self):
-        return {"enabled": self.enabled}
-
-

Methods

-
-
-def to_json(self) -
-
-
-
- -Expand source code - -
def to_json(self):
-    return {"enabled": self.enabled}
-
-
-
-
-
-class RecipeInterface -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class RecipeInterface(ABC):
-    def __init__(self):
-        pass
-
-    @abstractmethod
-    async def get_tenant_id(
-        self, tenant_id_from_frontend: str, user_context: Dict[str, Any]
-    ) -> str:
-        pass
-
-    @abstractmethod
-    async def create_or_update_tenant(
-        self,
-        tenant_id: str,
-        config: Optional[TenantConfig],
-        user_context: Dict[str, Any],
-    ) -> CreateOrUpdateTenantOkResult:
-        pass
-
-    @abstractmethod
-    async def delete_tenant(
-        self, tenant_id: str, user_context: Dict[str, Any]
-    ) -> DeleteTenantOkResult:
-        pass
-
-    @abstractmethod
-    async def get_tenant(
-        self, tenant_id: str, user_context: Dict[str, Any]
-    ) -> Optional[GetTenantOkResult]:
-        pass
-
-    @abstractmethod
-    async def list_all_tenants(
-        self, user_context: Dict[str, Any]
-    ) -> ListAllTenantsOkResult:
-        pass
-
-    # third party provider management
-    @abstractmethod
-    async def create_or_update_third_party_config(
-        self,
-        tenant_id: str,
-        config: ProviderConfig,
-        skip_validation: Optional[bool],
-        user_context: Dict[str, Any],
-    ) -> CreateOrUpdateThirdPartyConfigOkResult:
-        pass
-
-    @abstractmethod
-    async def delete_third_party_config(
-        self,
-        tenant_id: str,
-        third_party_id: str,
-        user_context: Dict[str, Any],
-    ) -> DeleteThirdPartyConfigOkResult:
-        pass
-
-    # user tenant association
-    @abstractmethod
-    async def associate_user_to_tenant(
-        self,
-        tenant_id: str,
-        user_id: str,
-        user_context: Dict[str, Any],
-    ) -> Union[
-        AssociateUserToTenantOkResult,
-        AssociateUserToTenantUnknownUserIdError,
-        AssociateUserToTenantEmailAlreadyExistsError,
-        AssociateUserToTenantPhoneNumberAlreadyExistsError,
-        AssociateUserToTenantThirdPartyUserAlreadyExistsError,
-    ]:
-        pass
-
-    @abstractmethod
-    async def dissociate_user_from_tenant(
-        self,
-        tenant_id: str,
-        user_id: str,
-        user_context: Dict[str, Any],
-    ) -> DisassociateUserFromTenantOkResult:
-        pass
-
-

Ancestors

-
    -
  • abc.ABC
  • -
-

Subclasses

- -

Methods

-
-
-async def associate_user_to_tenant(self, tenant_id: str, user_id: str, user_context: Dict[str, Any]) ‑> Union[AssociateUserToTenantOkResultAssociateUserToTenantUnknownUserIdErrorAssociateUserToTenantEmailAlreadyExistsErrorAssociateUserToTenantPhoneNumberAlreadyExistsErrorAssociateUserToTenantThirdPartyUserAlreadyExistsError] -
-
-
-
- -Expand source code - -
@abstractmethod
-async def associate_user_to_tenant(
-    self,
-    tenant_id: str,
-    user_id: str,
-    user_context: Dict[str, Any],
-) -> Union[
-    AssociateUserToTenantOkResult,
-    AssociateUserToTenantUnknownUserIdError,
-    AssociateUserToTenantEmailAlreadyExistsError,
-    AssociateUserToTenantPhoneNumberAlreadyExistsError,
-    AssociateUserToTenantThirdPartyUserAlreadyExistsError,
-]:
-    pass
-
-
-
-async def create_or_update_tenant(self, tenant_id: str, config: Optional[TenantConfig], user_context: Dict[str, Any]) ‑> CreateOrUpdateTenantOkResult -
-
-
-
- -Expand source code - -
@abstractmethod
-async def create_or_update_tenant(
-    self,
-    tenant_id: str,
-    config: Optional[TenantConfig],
-    user_context: Dict[str, Any],
-) -> CreateOrUpdateTenantOkResult:
-    pass
-
-
-
-async def create_or_update_third_party_config(self, tenant_id: str, config: ProviderConfig, skip_validation: Optional[bool], user_context: Dict[str, Any]) ‑> CreateOrUpdateThirdPartyConfigOkResult -
-
-
-
- -Expand source code - -
@abstractmethod
-async def create_or_update_third_party_config(
-    self,
-    tenant_id: str,
-    config: ProviderConfig,
-    skip_validation: Optional[bool],
-    user_context: Dict[str, Any],
-) -> CreateOrUpdateThirdPartyConfigOkResult:
-    pass
-
-
-
-async def delete_tenant(self, tenant_id: str, user_context: Dict[str, Any]) ‑> DeleteTenantOkResult -
-
-
-
- -Expand source code - -
@abstractmethod
-async def delete_tenant(
-    self, tenant_id: str, user_context: Dict[str, Any]
-) -> DeleteTenantOkResult:
-    pass
-
-
-
-async def delete_third_party_config(self, tenant_id: str, third_party_id: str, user_context: Dict[str, Any]) ‑> DeleteThirdPartyConfigOkResult -
-
-
-
- -Expand source code - -
@abstractmethod
-async def delete_third_party_config(
-    self,
-    tenant_id: str,
-    third_party_id: str,
-    user_context: Dict[str, Any],
-) -> DeleteThirdPartyConfigOkResult:
-    pass
-
-
-
-async def dissociate_user_from_tenant(self, tenant_id: str, user_id: str, user_context: Dict[str, Any]) ‑> DisassociateUserFromTenantOkResult -
-
-
-
- -Expand source code - -
@abstractmethod
-async def dissociate_user_from_tenant(
-    self,
-    tenant_id: str,
-    user_id: str,
-    user_context: Dict[str, Any],
-) -> DisassociateUserFromTenantOkResult:
-    pass
-
-
-
-async def get_tenant(self, tenant_id: str, user_context: Dict[str, Any]) ‑> Optional[GetTenantOkResult] -
-
-
-
- -Expand source code - -
@abstractmethod
-async def get_tenant(
-    self, tenant_id: str, user_context: Dict[str, Any]
-) -> Optional[GetTenantOkResult]:
-    pass
-
-
-
-async def get_tenant_id(self, tenant_id_from_frontend: str, user_context: Dict[str, Any]) ‑> str -
-
-
-
- -Expand source code - -
@abstractmethod
-async def get_tenant_id(
-    self, tenant_id_from_frontend: str, user_context: Dict[str, Any]
-) -> str:
-    pass
-
-
-
-async def list_all_tenants(self, user_context: Dict[str, Any]) ‑> ListAllTenantsOkResult -
-
-
-
- -Expand source code - -
@abstractmethod
-async def list_all_tenants(
-    self, user_context: Dict[str, Any]
-) -> ListAllTenantsOkResult:
-    pass
-
-
-
-
-
-class TenantConfig -(email_password_enabled: Union[bool, None] = None, passwordless_enabled: Union[bool, None] = None, third_party_enabled: Union[bool, None] = None, core_config: Union[Dict[str, Any], None] = None) -
-
-
-
- -Expand source code - -
class TenantConfig:
-    def __init__(
-        self,
-        email_password_enabled: Union[bool, None] = None,
-        passwordless_enabled: Union[bool, None] = None,
-        third_party_enabled: Union[bool, None] = None,
-        core_config: Union[Dict[str, Any], None] = None,
-    ):
-        self.email_password_enabled = email_password_enabled
-        self.passwordless_enabled = passwordless_enabled
-        self.third_party_enabled = third_party_enabled
-        self.core_config = core_config
-
-    def to_json(self) -> Dict[str, Any]:
-        res: Dict[str, Any] = {}
-        if self.email_password_enabled is not None:
-            res["emailPasswordEnabled"] = self.email_password_enabled
-        if self.passwordless_enabled is not None:
-            res["passwordlessEnabled"] = self.passwordless_enabled
-        if self.third_party_enabled is not None:
-            res["thirdPartyEnabled"] = self.third_party_enabled
-        if self.core_config is not None:
-            res["coreConfig"] = self.core_config
-        return res
-
-

Methods

-
-
-def to_json(self) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
def to_json(self) -> Dict[str, Any]:
-    res: Dict[str, Any] = {}
-    if self.email_password_enabled is not None:
-        res["emailPasswordEnabled"] = self.email_password_enabled
-    if self.passwordless_enabled is not None:
-        res["passwordlessEnabled"] = self.passwordless_enabled
-    if self.third_party_enabled is not None:
-        res["thirdPartyEnabled"] = self.third_party_enabled
-    if self.core_config is not None:
-        res["coreConfig"] = self.core_config
-    return res
-
-
-
-
-
-class TenantConfigResponse -(emailpassword: EmailPasswordConfig, passwordless: PasswordlessConfig, third_party: ThirdPartyConfig, core_config: Dict[str, Any]) -
-
-
-
- -Expand source code - -
class TenantConfigResponse:
-    def __init__(
-        self,
-        emailpassword: EmailPasswordConfig,
-        passwordless: PasswordlessConfig,
-        third_party: ThirdPartyConfig,
-        core_config: Dict[str, Any],
-    ):
-        self.emailpassword = emailpassword
-        self.passwordless = passwordless
-        self.third_party = third_party
-        self.core_config = core_config
-
-

Subclasses

- -
-
-class ThirdPartyConfig -(enabled: bool, providers: List[ProviderConfig]) -
-
-
-
- -Expand source code - -
class ThirdPartyConfig:
-    def __init__(self, enabled: bool, providers: List[ProviderConfig]):
-        self.enabled = enabled
-        self.providers = providers
-
-    def to_json(self):
-        return {
-            "enabled": self.enabled,
-            "providers": [provider.to_json() for provider in self.providers],
-        }
-
-

Methods

-
-
-def to_json(self) -
-
-
-
- -Expand source code - -
def to_json(self):
-    return {
-        "enabled": self.enabled,
-        "providers": [provider.to_json() for provider in self.providers],
-    }
-
-
-
-
-
-class ThirdPartyProvider -(id: str, name: Optional[str]) -
-
-
-
- -Expand source code - -
class ThirdPartyProvider:
-    def __init__(
-        self, id: str, name: Optional[str]
-    ):  # pylint: disable=redefined-builtin
-        self.id = id
-        self.name = name
-
-    def to_json(self) -> Dict[str, Any]:
-        return {
-            "id": self.id,
-            "name": self.name,
-        }
-
-

Methods

-
-
-def to_json(self) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
def to_json(self) -> Dict[str, Any]:
-    return {
-        "id": self.id,
-        "name": self.name,
-    }
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/multitenancy/recipe.html b/html/supertokens_python/recipe/multitenancy/recipe.html deleted file mode 100644 index 81f991bda..000000000 --- a/html/supertokens_python/recipe/multitenancy/recipe.html +++ /dev/null @@ -1,743 +0,0 @@ - - - - - - -supertokens_python.recipe.multitenancy.recipe API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.multitenancy.recipe

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from os import environ
-from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union
-
-from supertokens_python.exceptions import SuperTokensError, raise_general_exception
-from supertokens_python.recipe.session.claim_base_classes.primitive_array_claim import (
-    PrimitiveArrayClaim,
-)
-from supertokens_python.recipe_module import APIHandled, RecipeModule
-
-from ...post_init_callbacks import PostSTInitCallbacks
-
-from .interfaces import (
-    APIOptions,
-    TypeGetAllowedDomainsForTenantId,
-)
-
-from .recipe_implementation import RecipeImplementation
-
-if TYPE_CHECKING:
-    from supertokens_python.framework.request import BaseRequest
-    from supertokens_python.framework.response import BaseResponse
-    from supertokens_python.supertokens import AppInfo
-    from supertokens_python.recipe.thirdparty.provider import ProviderInput
-
-from supertokens_python.normalised_url_path import NormalisedURLPath
-from supertokens_python.querier import Querier
-from supertokens_python.recipe.multitenancy.api.implementation import APIImplementation
-
-
-from .api import handle_login_methods_api
-from .constants import LOGIN_METHODS
-from .exceptions import MultitenancyError
-from .utils import (
-    InputOverrideConfig,
-    validate_and_normalise_user_input,
-)
-
-
-class MultitenancyRecipe(RecipeModule):
-    recipe_id = "multitenancy"
-    __instance = None
-
-    def __init__(
-        self,
-        recipe_id: str,
-        app_info: AppInfo,
-        get_allowed_domains_for_tenant_id: Optional[
-            TypeGetAllowedDomainsForTenantId
-        ] = None,
-        override: Union[InputOverrideConfig, None] = None,
-    ) -> None:
-        super().__init__(recipe_id, app_info)
-        self.config = validate_and_normalise_user_input(
-            get_allowed_domains_for_tenant_id,
-            override,
-        )
-
-        recipe_implementation = RecipeImplementation(
-            Querier.get_instance(recipe_id), self.config
-        )
-        self.recipe_implementation = (
-            recipe_implementation
-            if self.config.override.functions is None
-            else self.config.override.functions(recipe_implementation)
-        )
-
-        api_implementation = APIImplementation()
-        self.api_implementation = (
-            api_implementation
-            if self.config.override.apis is None
-            else self.config.override.apis(api_implementation)
-        )
-
-        self.static_third_party_providers: List[ProviderInput] = []
-        self.get_allowed_domains_for_tenant_id = (
-            self.config.get_allowed_domains_for_tenant_id
-        )
-
-        RecipeModule.get_tenant_id = recipe_implementation.get_tenant_id
-
-    def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
-        return isinstance(err, MultitenancyError)
-
-    def get_apis_handled(self) -> List[APIHandled]:
-        return [
-            APIHandled(
-                NormalisedURLPath(LOGIN_METHODS),
-                "get",
-                LOGIN_METHODS,
-                self.api_implementation.disable_login_methods_get,
-            ),
-        ]
-
-    async def handle_api_request(
-        self,
-        request_id: str,
-        tenant_id: str,
-        request: BaseRequest,
-        path: NormalisedURLPath,
-        method: str,
-        response: BaseResponse,
-        user_context: Dict[str, Any],
-    ) -> Union[BaseResponse, None]:
-        api_options = APIOptions(
-            request,
-            response,
-            self.recipe_id,
-            self.config,
-            self.recipe_implementation,
-            self.static_third_party_providers,
-        )
-        return await handle_login_methods_api(
-            self.api_implementation,
-            tenant_id,
-            api_options,
-            user_context,
-        )
-
-    async def handle_error(
-        self,
-        request: BaseRequest,
-        err: SuperTokensError,
-        response: BaseResponse,
-        user_context: Dict[str, Any],
-    ) -> BaseResponse:
-        raise err
-
-    def get_all_cors_headers(self) -> List[str]:
-        return []
-
-    @staticmethod
-    def init(
-        get_allowed_domains_for_tenant_id: Union[
-            TypeGetAllowedDomainsForTenantId, None
-        ] = None,
-        override: Union[InputOverrideConfig, None] = None,
-    ):
-        def func(app_info: AppInfo):
-            if MultitenancyRecipe.__instance is None:
-                MultitenancyRecipe.__instance = MultitenancyRecipe(
-                    MultitenancyRecipe.recipe_id,
-                    app_info,
-                    get_allowed_domains_for_tenant_id,
-                    override,
-                )
-
-                def callback():
-                    try:
-                        from supertokens_python.recipe.session import SessionRecipe
-
-                        SessionRecipe.get_instance().add_claim_from_other_recipe(
-                            AllowedDomainsClaim
-                        )
-                    except Exception:
-                        # Skip adding claims if session recipe is not initilised
-                        pass
-
-                PostSTInitCallbacks.add_post_init_callback(callback)
-
-                return MultitenancyRecipe.__instance
-            raise_general_exception(
-                "Multitenancy recipe has already been initialised. Please check your code for bugs."
-            )
-
-        return func
-
-    @staticmethod
-    def get_instance() -> MultitenancyRecipe:
-        if MultitenancyRecipe.__instance is not None:
-            return MultitenancyRecipe.__instance
-        raise_general_exception(
-            "Initialisation not done. Did you forget to call the SuperTokens.init function?"
-        )
-
-    @staticmethod
-    def get_instance_optional() -> Optional[MultitenancyRecipe]:
-        return MultitenancyRecipe.__instance
-
-    @staticmethod
-    def reset():
-        if ("SUPERTOKENS_ENV" not in environ) or (
-            environ["SUPERTOKENS_ENV"] != "testing"
-        ):
-            raise_general_exception("calling testing function in non testing env")
-        MultitenancyRecipe.__instance = None
-
-
-class AllowedDomainsClaimClass(PrimitiveArrayClaim[List[str]]):
-    def __init__(self):
-        default_max_age_in_sec = 60 * 60
-
-        async def fetch_value(
-            _: str, tenant_id: str, user_context: Dict[str, Any]
-        ) -> Optional[List[str]]:
-            recipe = MultitenancyRecipe.get_instance()
-
-            if recipe.get_allowed_domains_for_tenant_id is None:
-                # User did not provide a function to get allowed domains, but is using a validator. So we don't allow any domains by default
-                return None
-
-            return await recipe.get_allowed_domains_for_tenant_id(
-                tenant_id, user_context
-            )
-
-        super().__init__("st-t-dmns", fetch_value, default_max_age_in_sec)
-
-
-AllowedDomainsClaim = AllowedDomainsClaimClass()
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class AllowedDomainsClaimClass -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-

Args

-
-
key
-
The key to use when storing the claim in the payload.
-
fetch_value
-
a method that fetches the current value of this claim for the user. -A None return value signifies that we don't want to update the claim payload and or the claim value is -not present in the database. For example, this can happen with a second factor auth claim, where we -don't want to add the claim to the session automatically
-
-
- -Expand source code - -
class AllowedDomainsClaimClass(PrimitiveArrayClaim[List[str]]):
-    def __init__(self):
-        default_max_age_in_sec = 60 * 60
-
-        async def fetch_value(
-            _: str, tenant_id: str, user_context: Dict[str, Any]
-        ) -> Optional[List[str]]:
-            recipe = MultitenancyRecipe.get_instance()
-
-            if recipe.get_allowed_domains_for_tenant_id is None:
-                # User did not provide a function to get allowed domains, but is using a validator. So we don't allow any domains by default
-                return None
-
-            return await recipe.get_allowed_domains_for_tenant_id(
-                tenant_id, user_context
-            )
-
-        super().__init__("st-t-dmns", fetch_value, default_max_age_in_sec)
-
-

Ancestors

- -

Inherited members

- -
-
-class MultitenancyRecipe -(recipe_id: str, app_info: AppInfo, get_allowed_domains_for_tenant_id: Optional[TypeGetAllowedDomainsForTenantId] = None, override: Union[InputOverrideConfig, None] = None) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class MultitenancyRecipe(RecipeModule):
-    recipe_id = "multitenancy"
-    __instance = None
-
-    def __init__(
-        self,
-        recipe_id: str,
-        app_info: AppInfo,
-        get_allowed_domains_for_tenant_id: Optional[
-            TypeGetAllowedDomainsForTenantId
-        ] = None,
-        override: Union[InputOverrideConfig, None] = None,
-    ) -> None:
-        super().__init__(recipe_id, app_info)
-        self.config = validate_and_normalise_user_input(
-            get_allowed_domains_for_tenant_id,
-            override,
-        )
-
-        recipe_implementation = RecipeImplementation(
-            Querier.get_instance(recipe_id), self.config
-        )
-        self.recipe_implementation = (
-            recipe_implementation
-            if self.config.override.functions is None
-            else self.config.override.functions(recipe_implementation)
-        )
-
-        api_implementation = APIImplementation()
-        self.api_implementation = (
-            api_implementation
-            if self.config.override.apis is None
-            else self.config.override.apis(api_implementation)
-        )
-
-        self.static_third_party_providers: List[ProviderInput] = []
-        self.get_allowed_domains_for_tenant_id = (
-            self.config.get_allowed_domains_for_tenant_id
-        )
-
-        RecipeModule.get_tenant_id = recipe_implementation.get_tenant_id
-
-    def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
-        return isinstance(err, MultitenancyError)
-
-    def get_apis_handled(self) -> List[APIHandled]:
-        return [
-            APIHandled(
-                NormalisedURLPath(LOGIN_METHODS),
-                "get",
-                LOGIN_METHODS,
-                self.api_implementation.disable_login_methods_get,
-            ),
-        ]
-
-    async def handle_api_request(
-        self,
-        request_id: str,
-        tenant_id: str,
-        request: BaseRequest,
-        path: NormalisedURLPath,
-        method: str,
-        response: BaseResponse,
-        user_context: Dict[str, Any],
-    ) -> Union[BaseResponse, None]:
-        api_options = APIOptions(
-            request,
-            response,
-            self.recipe_id,
-            self.config,
-            self.recipe_implementation,
-            self.static_third_party_providers,
-        )
-        return await handle_login_methods_api(
-            self.api_implementation,
-            tenant_id,
-            api_options,
-            user_context,
-        )
-
-    async def handle_error(
-        self,
-        request: BaseRequest,
-        err: SuperTokensError,
-        response: BaseResponse,
-        user_context: Dict[str, Any],
-    ) -> BaseResponse:
-        raise err
-
-    def get_all_cors_headers(self) -> List[str]:
-        return []
-
-    @staticmethod
-    def init(
-        get_allowed_domains_for_tenant_id: Union[
-            TypeGetAllowedDomainsForTenantId, None
-        ] = None,
-        override: Union[InputOverrideConfig, None] = None,
-    ):
-        def func(app_info: AppInfo):
-            if MultitenancyRecipe.__instance is None:
-                MultitenancyRecipe.__instance = MultitenancyRecipe(
-                    MultitenancyRecipe.recipe_id,
-                    app_info,
-                    get_allowed_domains_for_tenant_id,
-                    override,
-                )
-
-                def callback():
-                    try:
-                        from supertokens_python.recipe.session import SessionRecipe
-
-                        SessionRecipe.get_instance().add_claim_from_other_recipe(
-                            AllowedDomainsClaim
-                        )
-                    except Exception:
-                        # Skip adding claims if session recipe is not initilised
-                        pass
-
-                PostSTInitCallbacks.add_post_init_callback(callback)
-
-                return MultitenancyRecipe.__instance
-            raise_general_exception(
-                "Multitenancy recipe has already been initialised. Please check your code for bugs."
-            )
-
-        return func
-
-    @staticmethod
-    def get_instance() -> MultitenancyRecipe:
-        if MultitenancyRecipe.__instance is not None:
-            return MultitenancyRecipe.__instance
-        raise_general_exception(
-            "Initialisation not done. Did you forget to call the SuperTokens.init function?"
-        )
-
-    @staticmethod
-    def get_instance_optional() -> Optional[MultitenancyRecipe]:
-        return MultitenancyRecipe.__instance
-
-    @staticmethod
-    def reset():
-        if ("SUPERTOKENS_ENV" not in environ) or (
-            environ["SUPERTOKENS_ENV"] != "testing"
-        ):
-            raise_general_exception("calling testing function in non testing env")
-        MultitenancyRecipe.__instance = None
-
-

Ancestors

- -

Class variables

-
-
var get_tenant_id : Optional[Callable[[str, Dict[str, Any]], Awaitable[str]]]
-
-
-
-
var recipe_id
-
-
-
-
-

Static methods

-
-
-def get_instance() ‑> MultitenancyRecipe -
-
-
-
- -Expand source code - -
@staticmethod
-def get_instance() -> MultitenancyRecipe:
-    if MultitenancyRecipe.__instance is not None:
-        return MultitenancyRecipe.__instance
-    raise_general_exception(
-        "Initialisation not done. Did you forget to call the SuperTokens.init function?"
-    )
-
-
-
-def get_instance_optional() ‑> Optional[MultitenancyRecipe] -
-
-
-
- -Expand source code - -
@staticmethod
-def get_instance_optional() -> Optional[MultitenancyRecipe]:
-    return MultitenancyRecipe.__instance
-
-
-
-def init(get_allowed_domains_for_tenant_id: Union[TypeGetAllowedDomainsForTenantId, None] = None, override: Union[InputOverrideConfig, None] = None) -
-
-
-
- -Expand source code - -
@staticmethod
-def init(
-    get_allowed_domains_for_tenant_id: Union[
-        TypeGetAllowedDomainsForTenantId, None
-    ] = None,
-    override: Union[InputOverrideConfig, None] = None,
-):
-    def func(app_info: AppInfo):
-        if MultitenancyRecipe.__instance is None:
-            MultitenancyRecipe.__instance = MultitenancyRecipe(
-                MultitenancyRecipe.recipe_id,
-                app_info,
-                get_allowed_domains_for_tenant_id,
-                override,
-            )
-
-            def callback():
-                try:
-                    from supertokens_python.recipe.session import SessionRecipe
-
-                    SessionRecipe.get_instance().add_claim_from_other_recipe(
-                        AllowedDomainsClaim
-                    )
-                except Exception:
-                    # Skip adding claims if session recipe is not initilised
-                    pass
-
-            PostSTInitCallbacks.add_post_init_callback(callback)
-
-            return MultitenancyRecipe.__instance
-        raise_general_exception(
-            "Multitenancy recipe has already been initialised. Please check your code for bugs."
-        )
-
-    return func
-
-
-
-def reset() -
-
-
-
- -Expand source code - -
@staticmethod
-def reset():
-    if ("SUPERTOKENS_ENV" not in environ) or (
-        environ["SUPERTOKENS_ENV"] != "testing"
-    ):
-        raise_general_exception("calling testing function in non testing env")
-    MultitenancyRecipe.__instance = None
-
-
-
-

Methods

-
-
-def get_all_cors_headers(self) ‑> List[str] -
-
-
-
- -Expand source code - -
def get_all_cors_headers(self) -> List[str]:
-    return []
-
-
-
-def get_apis_handled(self) ‑> List[APIHandled] -
-
-
-
- -Expand source code - -
def get_apis_handled(self) -> List[APIHandled]:
-    return [
-        APIHandled(
-            NormalisedURLPath(LOGIN_METHODS),
-            "get",
-            LOGIN_METHODS,
-            self.api_implementation.disable_login_methods_get,
-        ),
-    ]
-
-
-
-async def handle_api_request(self, request_id: str, tenant_id: str, request: BaseRequest, path: NormalisedURLPath, method: str, response: BaseResponse, user_context: Dict[str, Any]) ‑> Union[BaseResponse, None] -
-
-
-
- -Expand source code - -
async def handle_api_request(
-    self,
-    request_id: str,
-    tenant_id: str,
-    request: BaseRequest,
-    path: NormalisedURLPath,
-    method: str,
-    response: BaseResponse,
-    user_context: Dict[str, Any],
-) -> Union[BaseResponse, None]:
-    api_options = APIOptions(
-        request,
-        response,
-        self.recipe_id,
-        self.config,
-        self.recipe_implementation,
-        self.static_third_party_providers,
-    )
-    return await handle_login_methods_api(
-        self.api_implementation,
-        tenant_id,
-        api_options,
-        user_context,
-    )
-
-
-
-async def handle_error(self, request: BaseRequest, err: SuperTokensError, response: BaseResponse, user_context: Dict[str, Any]) ‑> BaseResponse -
-
-
-
- -Expand source code - -
async def handle_error(
-    self,
-    request: BaseRequest,
-    err: SuperTokensError,
-    response: BaseResponse,
-    user_context: Dict[str, Any],
-) -> BaseResponse:
-    raise err
-
-
-
-def is_error_from_this_recipe_based_on_instance(self, err: Exception) ‑> bool -
-
-
-
- -Expand source code - -
def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
-    return isinstance(err, MultitenancyError)
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/multitenancy/recipe_implementation.html b/html/supertokens_python/recipe/multitenancy/recipe_implementation.html deleted file mode 100644 index c292ec5c7..000000000 --- a/html/supertokens_python/recipe/multitenancy/recipe_implementation.html +++ /dev/null @@ -1,960 +0,0 @@ - - - - - - -supertokens_python.recipe.multitenancy.recipe_implementation API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.multitenancy.recipe_implementation

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from typing import TYPE_CHECKING, Optional, Dict, Any, Union, List
-from supertokens_python.recipe.multitenancy.interfaces import (
-    AssociateUserToTenantOkResult,
-    AssociateUserToTenantUnknownUserIdError,
-    AssociateUserToTenantEmailAlreadyExistsError,
-    AssociateUserToTenantPhoneNumberAlreadyExistsError,
-    AssociateUserToTenantThirdPartyUserAlreadyExistsError,
-    DisassociateUserFromTenantOkResult,
-)
-
-from .interfaces import (
-    RecipeInterface,
-    TenantConfig,
-    CreateOrUpdateTenantOkResult,
-    DeleteTenantOkResult,
-    TenantConfigResponse,
-    GetTenantOkResult,
-    EmailPasswordConfig,
-    PasswordlessConfig,
-    ThirdPartyConfig,
-    ListAllTenantsOkResult,
-    CreateOrUpdateThirdPartyConfigOkResult,
-    DeleteThirdPartyConfigOkResult,
-    ListAllTenantsItem,
-)
-
-if TYPE_CHECKING:
-    from supertokens_python.querier import Querier
-    from supertokens_python.recipe.thirdparty.provider import ProviderConfig
-    from .utils import MultitenancyConfig
-
-from supertokens_python.querier import NormalisedURLPath
-from .constants import DEFAULT_TENANT_ID
-
-
-def parse_tenant_config(tenant: Dict[str, Any]) -> TenantConfigResponse:
-    from supertokens_python.recipe.thirdparty.provider import (
-        UserInfoMap,
-        UserFields,
-        ProviderClientConfig,
-        ProviderConfig,
-    )
-
-    providers: List[ProviderConfig] = []
-    for p in tenant["thirdParty"]["providers"]:
-        user_info_map: Optional[UserInfoMap] = None
-        if "userInfoMap" in p:
-            map_from_payload = p["userInfoMap"].get("fromIdTokenPayload", {})
-            map_from_api = p["userInfoMap"].get("fromUserInfoAPI", {})
-            user_info_map = UserInfoMap(
-                UserFields(
-                    map_from_payload.get("userId"),
-                    map_from_payload.get("email"),
-                    map_from_payload.get("emailVerified"),
-                ),
-                UserFields(
-                    map_from_api.get("userId"),
-                    map_from_api.get("email"),
-                    map_from_api.get("emailVerified"),
-                ),
-            )
-
-        providers.append(
-            ProviderConfig(
-                third_party_id=p["thirdPartyId"],
-                name=p.get("name"),
-                clients=[
-                    ProviderClientConfig(
-                        client_id=c["clientId"],
-                        client_secret=c.get("clientSecret"),
-                        client_type=c.get("clientType"),
-                        scope=c.get("scope"),
-                        force_pkce=c.get("forcePKCE", False),
-                        additional_config=c.get("additionalConfig"),
-                    )
-                    for c in p["clients"]
-                ],
-                authorization_endpoint=p.get("authorizationEndpoint"),
-                authorization_endpoint_query_params=p.get(
-                    "authorizationEndpointQueryParams"
-                ),
-                token_endpoint=p.get("tokenEndpoint"),
-                token_endpoint_body_params=p.get("tokenEndpointBodyParams"),
-                user_info_endpoint=p.get("userInfoEndpoint"),
-                user_info_endpoint_query_params=p.get("userInfoEndpointQueryParams"),
-                user_info_endpoint_headers=p.get("userInfoEndpointHeaders"),
-                jwks_uri=p.get("jwksURI"),
-                oidc_discovery_endpoint=p.get("oidcDiscoveryEndpoint"),
-                user_info_map=user_info_map,
-                require_email=p.get("requireEmail", True),
-                validate_id_token_payload=None,
-                generate_fake_email=None,
-                validate_access_token=None,
-            )
-        )
-
-    return TenantConfigResponse(
-        emailpassword=EmailPasswordConfig(tenant["emailPassword"]["enabled"]),
-        passwordless=PasswordlessConfig(tenant["passwordless"]["enabled"]),
-        third_party=ThirdPartyConfig(
-            tenant["thirdParty"]["enabled"],
-            providers,
-        ),
-        core_config=tenant["coreConfig"],
-    )
-
-
-class RecipeImplementation(RecipeInterface):
-    def __init__(self, querier: Querier, config: MultitenancyConfig):
-        super().__init__()
-        self.querier = querier
-        self.config = config
-
-    async def get_tenant_id(
-        self, tenant_id_from_frontend: str, user_context: Dict[str, Any]
-    ) -> str:
-        return tenant_id_from_frontend
-
-    async def create_or_update_tenant(
-        self,
-        tenant_id: str,
-        config: Optional[TenantConfig],
-        user_context: Dict[str, Any],
-    ) -> CreateOrUpdateTenantOkResult:
-        response = await self.querier.send_put_request(
-            NormalisedURLPath("/recipe/multitenancy/tenant"),
-            {
-                "tenantId": tenant_id,
-                **(config.to_json() if config is not None else {}),
-            },
-            user_context=user_context,
-        )
-        return CreateOrUpdateTenantOkResult(
-            created_new=response["createdNew"],
-        )
-
-    async def delete_tenant(
-        self, tenant_id: str, user_context: Dict[str, Any]
-    ) -> DeleteTenantOkResult:
-        response = await self.querier.send_post_request(
-            NormalisedURLPath("/recipe/multitenancy/tenant/remove"),
-            {"tenantId": tenant_id},
-            user_context=user_context,
-        )
-        return DeleteTenantOkResult(
-            did_exist=response["didExist"],
-        )
-
-    async def get_tenant(
-        self, tenant_id: Optional[str], user_context: Dict[str, Any]
-    ) -> Optional[GetTenantOkResult]:
-        res = await self.querier.send_get_request(
-            NormalisedURLPath(
-                f"{tenant_id or DEFAULT_TENANT_ID}/recipe/multitenancy/tenant"
-            ),
-            None,
-            user_context=user_context,
-        )
-
-        if res["status"] == "TENANT_NOT_FOUND_ERROR":
-            return None
-
-        tenant_config = parse_tenant_config(res)
-
-        return GetTenantOkResult(
-            emailpassword=tenant_config.emailpassword,
-            passwordless=tenant_config.passwordless,
-            third_party=tenant_config.third_party,
-            core_config=tenant_config.core_config,
-        )
-
-    async def list_all_tenants(
-        self, user_context: Dict[str, Any]
-    ) -> ListAllTenantsOkResult:
-        response = await self.querier.send_get_request(
-            NormalisedURLPath("/recipe/multitenancy/tenant/list"),
-            {},
-            user_context=user_context,
-        )
-
-        tenant_items: List[ListAllTenantsItem] = []
-
-        for tenant in response["tenants"]:
-            config = parse_tenant_config(tenant)
-            item = ListAllTenantsItem(
-                tenant["tenantId"],
-                config.emailpassword,
-                config.passwordless,
-                config.third_party,
-                config.core_config,
-            )
-            tenant_items.append(item)
-
-        return ListAllTenantsOkResult(
-            tenants=tenant_items,
-        )
-
-    async def create_or_update_third_party_config(
-        self,
-        tenant_id: Optional[str],
-        config: ProviderConfig,
-        skip_validation: Optional[bool],
-        user_context: Dict[str, Any],
-    ) -> CreateOrUpdateThirdPartyConfigOkResult:
-        response = await self.querier.send_put_request(
-            NormalisedURLPath(
-                f"{tenant_id or DEFAULT_TENANT_ID}/recipe/multitenancy/config/thirdparty"
-            ),
-            {
-                "config": config.to_json(),
-                "skipValidation": skip_validation is True,
-            },
-            user_context=user_context,
-        )
-
-        return CreateOrUpdateThirdPartyConfigOkResult(
-            created_new=response["createdNew"],
-        )
-
-    async def delete_third_party_config(
-        self,
-        tenant_id: Optional[str],
-        third_party_id: str,
-        user_context: Dict[str, Any],
-    ) -> DeleteThirdPartyConfigOkResult:
-        response = await self.querier.send_post_request(
-            NormalisedURLPath(
-                f"{tenant_id or DEFAULT_TENANT_ID}/recipe/multitenancy/config/thirdparty/remove"
-            ),
-            {
-                "thirdPartyId": third_party_id,
-            },
-            user_context=user_context,
-        )
-
-        return DeleteThirdPartyConfigOkResult(
-            did_config_exist=response["didConfigExist"],
-        )
-
-    async def associate_user_to_tenant(
-        self, tenant_id: Optional[str], user_id: str, user_context: Dict[str, Any]
-    ) -> Union[
-        AssociateUserToTenantOkResult,
-        AssociateUserToTenantUnknownUserIdError,
-        AssociateUserToTenantEmailAlreadyExistsError,
-        AssociateUserToTenantPhoneNumberAlreadyExistsError,
-        AssociateUserToTenantThirdPartyUserAlreadyExistsError,
-    ]:
-        response = await self.querier.send_post_request(
-            NormalisedURLPath(
-                f"{tenant_id or DEFAULT_TENANT_ID}/recipe/multitenancy/tenant/user"
-            ),
-            {
-                "userId": user_id,
-            },
-            user_context=user_context,
-        )
-
-        if response["status"] == "OK":
-            return AssociateUserToTenantOkResult(
-                was_already_associated=response["wasAlreadyAssociated"],
-            )
-        if response["status"] == AssociateUserToTenantUnknownUserIdError.status:
-            return AssociateUserToTenantUnknownUserIdError()
-        if response["status"] == AssociateUserToTenantEmailAlreadyExistsError.status:
-            return AssociateUserToTenantEmailAlreadyExistsError()
-        if (
-            response["status"]
-            == AssociateUserToTenantPhoneNumberAlreadyExistsError.status
-        ):
-            return AssociateUserToTenantPhoneNumberAlreadyExistsError()
-        if (
-            response["status"]
-            == AssociateUserToTenantThirdPartyUserAlreadyExistsError.status
-        ):
-            return AssociateUserToTenantThirdPartyUserAlreadyExistsError()
-
-        raise Exception("Should never come here")
-
-    async def dissociate_user_from_tenant(
-        self, tenant_id: Optional[str], user_id: str, user_context: Dict[str, Any]
-    ) -> DisassociateUserFromTenantOkResult:
-        response = await self.querier.send_post_request(
-            NormalisedURLPath(
-                f"{tenant_id or DEFAULT_TENANT_ID}/recipe/multitenancy/tenant/user/remove"
-            ),
-            {
-                "userId": user_id,
-            },
-            user_context=user_context,
-        )
-
-        return DisassociateUserFromTenantOkResult(
-            was_associated=response["wasAssociated"],
-        )
-
-
-
-
-
-
-
-

Functions

-
-
-def parse_tenant_config(tenant: Dict[str, Any]) ‑> TenantConfigResponse -
-
-
-
- -Expand source code - -
def parse_tenant_config(tenant: Dict[str, Any]) -> TenantConfigResponse:
-    from supertokens_python.recipe.thirdparty.provider import (
-        UserInfoMap,
-        UserFields,
-        ProviderClientConfig,
-        ProviderConfig,
-    )
-
-    providers: List[ProviderConfig] = []
-    for p in tenant["thirdParty"]["providers"]:
-        user_info_map: Optional[UserInfoMap] = None
-        if "userInfoMap" in p:
-            map_from_payload = p["userInfoMap"].get("fromIdTokenPayload", {})
-            map_from_api = p["userInfoMap"].get("fromUserInfoAPI", {})
-            user_info_map = UserInfoMap(
-                UserFields(
-                    map_from_payload.get("userId"),
-                    map_from_payload.get("email"),
-                    map_from_payload.get("emailVerified"),
-                ),
-                UserFields(
-                    map_from_api.get("userId"),
-                    map_from_api.get("email"),
-                    map_from_api.get("emailVerified"),
-                ),
-            )
-
-        providers.append(
-            ProviderConfig(
-                third_party_id=p["thirdPartyId"],
-                name=p.get("name"),
-                clients=[
-                    ProviderClientConfig(
-                        client_id=c["clientId"],
-                        client_secret=c.get("clientSecret"),
-                        client_type=c.get("clientType"),
-                        scope=c.get("scope"),
-                        force_pkce=c.get("forcePKCE", False),
-                        additional_config=c.get("additionalConfig"),
-                    )
-                    for c in p["clients"]
-                ],
-                authorization_endpoint=p.get("authorizationEndpoint"),
-                authorization_endpoint_query_params=p.get(
-                    "authorizationEndpointQueryParams"
-                ),
-                token_endpoint=p.get("tokenEndpoint"),
-                token_endpoint_body_params=p.get("tokenEndpointBodyParams"),
-                user_info_endpoint=p.get("userInfoEndpoint"),
-                user_info_endpoint_query_params=p.get("userInfoEndpointQueryParams"),
-                user_info_endpoint_headers=p.get("userInfoEndpointHeaders"),
-                jwks_uri=p.get("jwksURI"),
-                oidc_discovery_endpoint=p.get("oidcDiscoveryEndpoint"),
-                user_info_map=user_info_map,
-                require_email=p.get("requireEmail", True),
-                validate_id_token_payload=None,
-                generate_fake_email=None,
-                validate_access_token=None,
-            )
-        )
-
-    return TenantConfigResponse(
-        emailpassword=EmailPasswordConfig(tenant["emailPassword"]["enabled"]),
-        passwordless=PasswordlessConfig(tenant["passwordless"]["enabled"]),
-        third_party=ThirdPartyConfig(
-            tenant["thirdParty"]["enabled"],
-            providers,
-        ),
-        core_config=tenant["coreConfig"],
-    )
-
-
-
-
-
-

Classes

-
-
-class RecipeImplementation -(querier: Querier, config: MultitenancyConfig) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class RecipeImplementation(RecipeInterface):
-    def __init__(self, querier: Querier, config: MultitenancyConfig):
-        super().__init__()
-        self.querier = querier
-        self.config = config
-
-    async def get_tenant_id(
-        self, tenant_id_from_frontend: str, user_context: Dict[str, Any]
-    ) -> str:
-        return tenant_id_from_frontend
-
-    async def create_or_update_tenant(
-        self,
-        tenant_id: str,
-        config: Optional[TenantConfig],
-        user_context: Dict[str, Any],
-    ) -> CreateOrUpdateTenantOkResult:
-        response = await self.querier.send_put_request(
-            NormalisedURLPath("/recipe/multitenancy/tenant"),
-            {
-                "tenantId": tenant_id,
-                **(config.to_json() if config is not None else {}),
-            },
-            user_context=user_context,
-        )
-        return CreateOrUpdateTenantOkResult(
-            created_new=response["createdNew"],
-        )
-
-    async def delete_tenant(
-        self, tenant_id: str, user_context: Dict[str, Any]
-    ) -> DeleteTenantOkResult:
-        response = await self.querier.send_post_request(
-            NormalisedURLPath("/recipe/multitenancy/tenant/remove"),
-            {"tenantId": tenant_id},
-            user_context=user_context,
-        )
-        return DeleteTenantOkResult(
-            did_exist=response["didExist"],
-        )
-
-    async def get_tenant(
-        self, tenant_id: Optional[str], user_context: Dict[str, Any]
-    ) -> Optional[GetTenantOkResult]:
-        res = await self.querier.send_get_request(
-            NormalisedURLPath(
-                f"{tenant_id or DEFAULT_TENANT_ID}/recipe/multitenancy/tenant"
-            ),
-            None,
-            user_context=user_context,
-        )
-
-        if res["status"] == "TENANT_NOT_FOUND_ERROR":
-            return None
-
-        tenant_config = parse_tenant_config(res)
-
-        return GetTenantOkResult(
-            emailpassword=tenant_config.emailpassword,
-            passwordless=tenant_config.passwordless,
-            third_party=tenant_config.third_party,
-            core_config=tenant_config.core_config,
-        )
-
-    async def list_all_tenants(
-        self, user_context: Dict[str, Any]
-    ) -> ListAllTenantsOkResult:
-        response = await self.querier.send_get_request(
-            NormalisedURLPath("/recipe/multitenancy/tenant/list"),
-            {},
-            user_context=user_context,
-        )
-
-        tenant_items: List[ListAllTenantsItem] = []
-
-        for tenant in response["tenants"]:
-            config = parse_tenant_config(tenant)
-            item = ListAllTenantsItem(
-                tenant["tenantId"],
-                config.emailpassword,
-                config.passwordless,
-                config.third_party,
-                config.core_config,
-            )
-            tenant_items.append(item)
-
-        return ListAllTenantsOkResult(
-            tenants=tenant_items,
-        )
-
-    async def create_or_update_third_party_config(
-        self,
-        tenant_id: Optional[str],
-        config: ProviderConfig,
-        skip_validation: Optional[bool],
-        user_context: Dict[str, Any],
-    ) -> CreateOrUpdateThirdPartyConfigOkResult:
-        response = await self.querier.send_put_request(
-            NormalisedURLPath(
-                f"{tenant_id or DEFAULT_TENANT_ID}/recipe/multitenancy/config/thirdparty"
-            ),
-            {
-                "config": config.to_json(),
-                "skipValidation": skip_validation is True,
-            },
-            user_context=user_context,
-        )
-
-        return CreateOrUpdateThirdPartyConfigOkResult(
-            created_new=response["createdNew"],
-        )
-
-    async def delete_third_party_config(
-        self,
-        tenant_id: Optional[str],
-        third_party_id: str,
-        user_context: Dict[str, Any],
-    ) -> DeleteThirdPartyConfigOkResult:
-        response = await self.querier.send_post_request(
-            NormalisedURLPath(
-                f"{tenant_id or DEFAULT_TENANT_ID}/recipe/multitenancy/config/thirdparty/remove"
-            ),
-            {
-                "thirdPartyId": third_party_id,
-            },
-            user_context=user_context,
-        )
-
-        return DeleteThirdPartyConfigOkResult(
-            did_config_exist=response["didConfigExist"],
-        )
-
-    async def associate_user_to_tenant(
-        self, tenant_id: Optional[str], user_id: str, user_context: Dict[str, Any]
-    ) -> Union[
-        AssociateUserToTenantOkResult,
-        AssociateUserToTenantUnknownUserIdError,
-        AssociateUserToTenantEmailAlreadyExistsError,
-        AssociateUserToTenantPhoneNumberAlreadyExistsError,
-        AssociateUserToTenantThirdPartyUserAlreadyExistsError,
-    ]:
-        response = await self.querier.send_post_request(
-            NormalisedURLPath(
-                f"{tenant_id or DEFAULT_TENANT_ID}/recipe/multitenancy/tenant/user"
-            ),
-            {
-                "userId": user_id,
-            },
-            user_context=user_context,
-        )
-
-        if response["status"] == "OK":
-            return AssociateUserToTenantOkResult(
-                was_already_associated=response["wasAlreadyAssociated"],
-            )
-        if response["status"] == AssociateUserToTenantUnknownUserIdError.status:
-            return AssociateUserToTenantUnknownUserIdError()
-        if response["status"] == AssociateUserToTenantEmailAlreadyExistsError.status:
-            return AssociateUserToTenantEmailAlreadyExistsError()
-        if (
-            response["status"]
-            == AssociateUserToTenantPhoneNumberAlreadyExistsError.status
-        ):
-            return AssociateUserToTenantPhoneNumberAlreadyExistsError()
-        if (
-            response["status"]
-            == AssociateUserToTenantThirdPartyUserAlreadyExistsError.status
-        ):
-            return AssociateUserToTenantThirdPartyUserAlreadyExistsError()
-
-        raise Exception("Should never come here")
-
-    async def dissociate_user_from_tenant(
-        self, tenant_id: Optional[str], user_id: str, user_context: Dict[str, Any]
-    ) -> DisassociateUserFromTenantOkResult:
-        response = await self.querier.send_post_request(
-            NormalisedURLPath(
-                f"{tenant_id or DEFAULT_TENANT_ID}/recipe/multitenancy/tenant/user/remove"
-            ),
-            {
-                "userId": user_id,
-            },
-            user_context=user_context,
-        )
-
-        return DisassociateUserFromTenantOkResult(
-            was_associated=response["wasAssociated"],
-        )
-
-

Ancestors

- -

Methods

-
-
-async def associate_user_to_tenant(self, tenant_id: Optional[str], user_id: str, user_context: Dict[str, Any]) ‑> Union[AssociateUserToTenantOkResultAssociateUserToTenantUnknownUserIdErrorAssociateUserToTenantEmailAlreadyExistsErrorAssociateUserToTenantPhoneNumberAlreadyExistsErrorAssociateUserToTenantThirdPartyUserAlreadyExistsError] -
-
-
-
- -Expand source code - -
async def associate_user_to_tenant(
-    self, tenant_id: Optional[str], user_id: str, user_context: Dict[str, Any]
-) -> Union[
-    AssociateUserToTenantOkResult,
-    AssociateUserToTenantUnknownUserIdError,
-    AssociateUserToTenantEmailAlreadyExistsError,
-    AssociateUserToTenantPhoneNumberAlreadyExistsError,
-    AssociateUserToTenantThirdPartyUserAlreadyExistsError,
-]:
-    response = await self.querier.send_post_request(
-        NormalisedURLPath(
-            f"{tenant_id or DEFAULT_TENANT_ID}/recipe/multitenancy/tenant/user"
-        ),
-        {
-            "userId": user_id,
-        },
-        user_context=user_context,
-    )
-
-    if response["status"] == "OK":
-        return AssociateUserToTenantOkResult(
-            was_already_associated=response["wasAlreadyAssociated"],
-        )
-    if response["status"] == AssociateUserToTenantUnknownUserIdError.status:
-        return AssociateUserToTenantUnknownUserIdError()
-    if response["status"] == AssociateUserToTenantEmailAlreadyExistsError.status:
-        return AssociateUserToTenantEmailAlreadyExistsError()
-    if (
-        response["status"]
-        == AssociateUserToTenantPhoneNumberAlreadyExistsError.status
-    ):
-        return AssociateUserToTenantPhoneNumberAlreadyExistsError()
-    if (
-        response["status"]
-        == AssociateUserToTenantThirdPartyUserAlreadyExistsError.status
-    ):
-        return AssociateUserToTenantThirdPartyUserAlreadyExistsError()
-
-    raise Exception("Should never come here")
-
-
-
-async def create_or_update_tenant(self, tenant_id: str, config: Optional[TenantConfig], user_context: Dict[str, Any]) ‑> CreateOrUpdateTenantOkResult -
-
-
-
- -Expand source code - -
async def create_or_update_tenant(
-    self,
-    tenant_id: str,
-    config: Optional[TenantConfig],
-    user_context: Dict[str, Any],
-) -> CreateOrUpdateTenantOkResult:
-    response = await self.querier.send_put_request(
-        NormalisedURLPath("/recipe/multitenancy/tenant"),
-        {
-            "tenantId": tenant_id,
-            **(config.to_json() if config is not None else {}),
-        },
-        user_context=user_context,
-    )
-    return CreateOrUpdateTenantOkResult(
-        created_new=response["createdNew"],
-    )
-
-
-
-async def create_or_update_third_party_config(self, tenant_id: Optional[str], config: ProviderConfig, skip_validation: Optional[bool], user_context: Dict[str, Any]) ‑> CreateOrUpdateThirdPartyConfigOkResult -
-
-
-
- -Expand source code - -
async def create_or_update_third_party_config(
-    self,
-    tenant_id: Optional[str],
-    config: ProviderConfig,
-    skip_validation: Optional[bool],
-    user_context: Dict[str, Any],
-) -> CreateOrUpdateThirdPartyConfigOkResult:
-    response = await self.querier.send_put_request(
-        NormalisedURLPath(
-            f"{tenant_id or DEFAULT_TENANT_ID}/recipe/multitenancy/config/thirdparty"
-        ),
-        {
-            "config": config.to_json(),
-            "skipValidation": skip_validation is True,
-        },
-        user_context=user_context,
-    )
-
-    return CreateOrUpdateThirdPartyConfigOkResult(
-        created_new=response["createdNew"],
-    )
-
-
-
-async def delete_tenant(self, tenant_id: str, user_context: Dict[str, Any]) ‑> DeleteTenantOkResult -
-
-
-
- -Expand source code - -
async def delete_tenant(
-    self, tenant_id: str, user_context: Dict[str, Any]
-) -> DeleteTenantOkResult:
-    response = await self.querier.send_post_request(
-        NormalisedURLPath("/recipe/multitenancy/tenant/remove"),
-        {"tenantId": tenant_id},
-        user_context=user_context,
-    )
-    return DeleteTenantOkResult(
-        did_exist=response["didExist"],
-    )
-
-
-
-async def delete_third_party_config(self, tenant_id: Optional[str], third_party_id: str, user_context: Dict[str, Any]) ‑> DeleteThirdPartyConfigOkResult -
-
-
-
- -Expand source code - -
async def delete_third_party_config(
-    self,
-    tenant_id: Optional[str],
-    third_party_id: str,
-    user_context: Dict[str, Any],
-) -> DeleteThirdPartyConfigOkResult:
-    response = await self.querier.send_post_request(
-        NormalisedURLPath(
-            f"{tenant_id or DEFAULT_TENANT_ID}/recipe/multitenancy/config/thirdparty/remove"
-        ),
-        {
-            "thirdPartyId": third_party_id,
-        },
-        user_context=user_context,
-    )
-
-    return DeleteThirdPartyConfigOkResult(
-        did_config_exist=response["didConfigExist"],
-    )
-
-
-
-async def dissociate_user_from_tenant(self, tenant_id: Optional[str], user_id: str, user_context: Dict[str, Any]) ‑> DisassociateUserFromTenantOkResult -
-
-
-
- -Expand source code - -
async def dissociate_user_from_tenant(
-    self, tenant_id: Optional[str], user_id: str, user_context: Dict[str, Any]
-) -> DisassociateUserFromTenantOkResult:
-    response = await self.querier.send_post_request(
-        NormalisedURLPath(
-            f"{tenant_id or DEFAULT_TENANT_ID}/recipe/multitenancy/tenant/user/remove"
-        ),
-        {
-            "userId": user_id,
-        },
-        user_context=user_context,
-    )
-
-    return DisassociateUserFromTenantOkResult(
-        was_associated=response["wasAssociated"],
-    )
-
-
-
-async def get_tenant(self, tenant_id: Optional[str], user_context: Dict[str, Any]) ‑> Optional[GetTenantOkResult] -
-
-
-
- -Expand source code - -
async def get_tenant(
-    self, tenant_id: Optional[str], user_context: Dict[str, Any]
-) -> Optional[GetTenantOkResult]:
-    res = await self.querier.send_get_request(
-        NormalisedURLPath(
-            f"{tenant_id or DEFAULT_TENANT_ID}/recipe/multitenancy/tenant"
-        ),
-        None,
-        user_context=user_context,
-    )
-
-    if res["status"] == "TENANT_NOT_FOUND_ERROR":
-        return None
-
-    tenant_config = parse_tenant_config(res)
-
-    return GetTenantOkResult(
-        emailpassword=tenant_config.emailpassword,
-        passwordless=tenant_config.passwordless,
-        third_party=tenant_config.third_party,
-        core_config=tenant_config.core_config,
-    )
-
-
-
-async def get_tenant_id(self, tenant_id_from_frontend: str, user_context: Dict[str, Any]) ‑> str -
-
-
-
- -Expand source code - -
async def get_tenant_id(
-    self, tenant_id_from_frontend: str, user_context: Dict[str, Any]
-) -> str:
-    return tenant_id_from_frontend
-
-
-
-async def list_all_tenants(self, user_context: Dict[str, Any]) ‑> ListAllTenantsOkResult -
-
-
-
- -Expand source code - -
async def list_all_tenants(
-    self, user_context: Dict[str, Any]
-) -> ListAllTenantsOkResult:
-    response = await self.querier.send_get_request(
-        NormalisedURLPath("/recipe/multitenancy/tenant/list"),
-        {},
-        user_context=user_context,
-    )
-
-    tenant_items: List[ListAllTenantsItem] = []
-
-    for tenant in response["tenants"]:
-        config = parse_tenant_config(tenant)
-        item = ListAllTenantsItem(
-            tenant["tenantId"],
-            config.emailpassword,
-            config.passwordless,
-            config.third_party,
-            config.core_config,
-        )
-        tenant_items.append(item)
-
-    return ListAllTenantsOkResult(
-        tenants=tenant_items,
-    )
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/multitenancy/syncio/index.html b/html/supertokens_python/recipe/multitenancy/syncio/index.html deleted file mode 100644 index 045db6e69..000000000 --- a/html/supertokens_python/recipe/multitenancy/syncio/index.html +++ /dev/null @@ -1,367 +0,0 @@ - - - - - - -supertokens_python.recipe.multitenancy.syncio API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.multitenancy.syncio

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-from typing import Any, Dict, Optional, TYPE_CHECKING
-
-from supertokens_python.async_to_sync_wrapper import sync
-
-if TYPE_CHECKING:
-    from ..interfaces import TenantConfig, ProviderConfig
-
-
-def create_or_update_tenant(
-    tenant_id: str,
-    config: Optional[TenantConfig],
-    user_context: Optional[Dict[str, Any]] = None,
-):
-    if user_context is None:
-        user_context = {}
-
-    from supertokens_python.recipe.multitenancy.asyncio import create_or_update_tenant
-
-    return sync(create_or_update_tenant(tenant_id, config, user_context))
-
-
-def delete_tenant(tenant_id: str, user_context: Optional[Dict[str, Any]] = None):
-    if user_context is None:
-        user_context = {}
-
-    from supertokens_python.recipe.multitenancy.asyncio import delete_tenant
-
-    return sync(delete_tenant(tenant_id, user_context))
-
-
-def get_tenant(tenant_id: str, user_context: Optional[Dict[str, Any]] = None):
-    if user_context is None:
-        user_context = {}
-
-    from supertokens_python.recipe.multitenancy.asyncio import get_tenant
-
-    return sync(get_tenant(tenant_id, user_context))
-
-
-def list_all_tenants(user_context: Optional[Dict[str, Any]] = None):
-    if user_context is None:
-        user_context = {}
-
-    from supertokens_python.recipe.multitenancy.asyncio import list_all_tenants
-
-    return sync(list_all_tenants(user_context))
-
-
-def create_or_update_third_party_config(
-    tenant_id: str,
-    config: ProviderConfig,
-    skip_validation: Optional[bool] = None,
-    user_context: Optional[Dict[str, Any]] = None,
-):
-    if user_context is None:
-        user_context = {}
-
-    from supertokens_python.recipe.multitenancy.asyncio import (
-        create_or_update_third_party_config,
-    )
-
-    return sync(
-        create_or_update_third_party_config(
-            tenant_id, config, skip_validation, user_context
-        )
-    )
-
-
-def delete_third_party_config(
-    tenant_id: str,
-    third_party_id: str,
-    user_context: Optional[Dict[str, Any]] = None,
-):
-    if user_context is None:
-        user_context = {}
-
-    from supertokens_python.recipe.multitenancy.asyncio import delete_third_party_config
-
-    return sync(delete_third_party_config(tenant_id, third_party_id, user_context))
-
-
-def associate_user_to_tenant(
-    tenant_id: str,
-    user_id: str,
-    user_context: Optional[Dict[str, Any]] = None,
-):
-    if user_context is None:
-        user_context = {}
-
-    from supertokens_python.recipe.multitenancy.asyncio import associate_user_to_tenant
-
-    return sync(associate_user_to_tenant(tenant_id, user_id, user_context))
-
-
-def dissociate_user_from_tenant(
-    tenant_id: str,
-    user_id: str,
-    user_context: Optional[Dict[str, Any]] = None,
-):
-    if user_context is None:
-        user_context = {}
-
-    from supertokens_python.recipe.multitenancy.asyncio import (
-        dissociate_user_from_tenant,
-    )
-
-    return sync(dissociate_user_from_tenant(tenant_id, user_id, user_context))
-
-
-
-
-
-
-
-

Functions

-
-
-def associate_user_to_tenant(tenant_id: str, user_id: str, user_context: Optional[Dict[str, Any]] = None) -
-
-
-
- -Expand source code - -
def associate_user_to_tenant(
-    tenant_id: str,
-    user_id: str,
-    user_context: Optional[Dict[str, Any]] = None,
-):
-    if user_context is None:
-        user_context = {}
-
-    from supertokens_python.recipe.multitenancy.asyncio import associate_user_to_tenant
-
-    return sync(associate_user_to_tenant(tenant_id, user_id, user_context))
-
-
-
-def create_or_update_tenant(tenant_id: str, config: Optional[TenantConfig], user_context: Optional[Dict[str, Any]] = None) -
-
-
-
- -Expand source code - -
def create_or_update_tenant(
-    tenant_id: str,
-    config: Optional[TenantConfig],
-    user_context: Optional[Dict[str, Any]] = None,
-):
-    if user_context is None:
-        user_context = {}
-
-    from supertokens_python.recipe.multitenancy.asyncio import create_or_update_tenant
-
-    return sync(create_or_update_tenant(tenant_id, config, user_context))
-
-
-
-def create_or_update_third_party_config(tenant_id: str, config: ProviderConfig, skip_validation: Optional[bool] = None, user_context: Optional[Dict[str, Any]] = None) -
-
-
-
- -Expand source code - -
def create_or_update_third_party_config(
-    tenant_id: str,
-    config: ProviderConfig,
-    skip_validation: Optional[bool] = None,
-    user_context: Optional[Dict[str, Any]] = None,
-):
-    if user_context is None:
-        user_context = {}
-
-    from supertokens_python.recipe.multitenancy.asyncio import (
-        create_or_update_third_party_config,
-    )
-
-    return sync(
-        create_or_update_third_party_config(
-            tenant_id, config, skip_validation, user_context
-        )
-    )
-
-
-
-def delete_tenant(tenant_id: str, user_context: Optional[Dict[str, Any]] = None) -
-
-
-
- -Expand source code - -
def delete_tenant(tenant_id: str, user_context: Optional[Dict[str, Any]] = None):
-    if user_context is None:
-        user_context = {}
-
-    from supertokens_python.recipe.multitenancy.asyncio import delete_tenant
-
-    return sync(delete_tenant(tenant_id, user_context))
-
-
-
-def delete_third_party_config(tenant_id: str, third_party_id: str, user_context: Optional[Dict[str, Any]] = None) -
-
-
-
- -Expand source code - -
def delete_third_party_config(
-    tenant_id: str,
-    third_party_id: str,
-    user_context: Optional[Dict[str, Any]] = None,
-):
-    if user_context is None:
-        user_context = {}
-
-    from supertokens_python.recipe.multitenancy.asyncio import delete_third_party_config
-
-    return sync(delete_third_party_config(tenant_id, third_party_id, user_context))
-
-
-
-def dissociate_user_from_tenant(tenant_id: str, user_id: str, user_context: Optional[Dict[str, Any]] = None) -
-
-
-
- -Expand source code - -
def dissociate_user_from_tenant(
-    tenant_id: str,
-    user_id: str,
-    user_context: Optional[Dict[str, Any]] = None,
-):
-    if user_context is None:
-        user_context = {}
-
-    from supertokens_python.recipe.multitenancy.asyncio import (
-        dissociate_user_from_tenant,
-    )
-
-    return sync(dissociate_user_from_tenant(tenant_id, user_id, user_context))
-
-
-
-def get_tenant(tenant_id: str, user_context: Optional[Dict[str, Any]] = None) -
-
-
-
- -Expand source code - -
def get_tenant(tenant_id: str, user_context: Optional[Dict[str, Any]] = None):
-    if user_context is None:
-        user_context = {}
-
-    from supertokens_python.recipe.multitenancy.asyncio import get_tenant
-
-    return sync(get_tenant(tenant_id, user_context))
-
-
-
-def list_all_tenants(user_context: Optional[Dict[str, Any]] = None) -
-
-
-
- -Expand source code - -
def list_all_tenants(user_context: Optional[Dict[str, Any]] = None):
-    if user_context is None:
-        user_context = {}
-
-    from supertokens_python.recipe.multitenancy.asyncio import list_all_tenants
-
-    return sync(list_all_tenants(user_context))
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/multitenancy/utils.html b/html/supertokens_python/recipe/multitenancy/utils.html deleted file mode 100644 index 1da5d4229..000000000 --- a/html/supertokens_python/recipe/multitenancy/utils.html +++ /dev/null @@ -1,360 +0,0 @@ - - - - - - -supertokens_python.recipe.multitenancy.utils API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.multitenancy.utils

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from __future__ import annotations
-
-from typing import TYPE_CHECKING, Awaitable, Optional, Callable
-from supertokens_python.exceptions import SuperTokensError
-from supertokens_python.framework import BaseRequest, BaseResponse
-from supertokens_python.utils import (
-    resolve,
-)
-
-if TYPE_CHECKING:
-    from typing import Union
-    from .interfaces import (
-        TypeGetAllowedDomainsForTenantId,
-        RecipeInterface,
-        APIInterface,
-    )
-
-
-class ErrorHandlers:
-    def __init__(
-        self,
-        on_tenant_does_not_exist: Callable[
-            [SuperTokensError, BaseRequest, BaseResponse],
-            Union[BaseResponse, Awaitable[BaseResponse]],
-        ],
-        on_recipe_disabled_for_tenant: Callable[
-            [SuperTokensError, BaseRequest, BaseResponse],
-            Union[BaseResponse, Awaitable[BaseResponse]],
-        ],
-    ):
-        self.__on_tenant_does_not_exist = on_tenant_does_not_exist
-        self.__on_recipe_disabled_for_tenant = on_recipe_disabled_for_tenant
-
-    async def on_tenant_does_not_exist(
-        self,
-        err: SuperTokensError,
-        request: BaseRequest,
-        response: BaseResponse,
-    ) -> BaseResponse:
-        return await resolve(self.__on_tenant_does_not_exist(err, request, response))
-
-    async def on_recipe_disabled_for_tenant(
-        self, err: SuperTokensError, request: BaseRequest, response: BaseResponse
-    ) -> BaseResponse:
-        return await resolve(
-            self.__on_recipe_disabled_for_tenant(err, request, response)
-        )
-
-
-class InputOverrideConfig:
-    def __init__(
-        self,
-        functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None,
-        apis: Union[Callable[[APIInterface], APIInterface], None] = None,
-    ):
-        self.functions = functions
-        self.apis = apis
-
-
-class OverrideConfig:
-    def __init__(
-        self,
-        functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None,
-        apis: Union[Callable[[APIInterface], APIInterface], None] = None,
-    ):
-        self.functions = functions
-        self.apis = apis
-
-
-class MultitenancyConfig:
-    def __init__(
-        self,
-        get_allowed_domains_for_tenant_id: Optional[TypeGetAllowedDomainsForTenantId],
-        override: OverrideConfig,
-    ):
-        self.get_allowed_domains_for_tenant_id = get_allowed_domains_for_tenant_id
-        self.override = override
-
-
-def validate_and_normalise_user_input(
-    get_allowed_domains_for_tenant_id: Optional[TypeGetAllowedDomainsForTenantId],
-    override: Union[InputOverrideConfig, None] = None,
-) -> MultitenancyConfig:
-    if override is not None and not isinstance(override, OverrideConfig):  # type: ignore
-        raise ValueError("override must be of type OverrideConfig or None")
-
-    if override is None:
-        override = InputOverrideConfig()
-
-    return MultitenancyConfig(
-        get_allowed_domains_for_tenant_id,
-        OverrideConfig(override.functions, override.apis),
-    )
-
-
-
-
-
-
-
-

Functions

-
-
-def validate_and_normalise_user_input(get_allowed_domains_for_tenant_id: Optional[TypeGetAllowedDomainsForTenantId], override: Union[InputOverrideConfig, None] = None) ‑> MultitenancyConfig -
-
-
-
- -Expand source code - -
def validate_and_normalise_user_input(
-    get_allowed_domains_for_tenant_id: Optional[TypeGetAllowedDomainsForTenantId],
-    override: Union[InputOverrideConfig, None] = None,
-) -> MultitenancyConfig:
-    if override is not None and not isinstance(override, OverrideConfig):  # type: ignore
-        raise ValueError("override must be of type OverrideConfig or None")
-
-    if override is None:
-        override = InputOverrideConfig()
-
-    return MultitenancyConfig(
-        get_allowed_domains_for_tenant_id,
-        OverrideConfig(override.functions, override.apis),
-    )
-
-
-
-
-
-

Classes

-
-
-class ErrorHandlers -(on_tenant_does_not_exist: Callable[[SuperTokensError, BaseRequest, BaseResponse], Union[BaseResponse, Awaitable[BaseResponse]]], on_recipe_disabled_for_tenant: Callable[[SuperTokensError, BaseRequest, BaseResponse], Union[BaseResponse, Awaitable[BaseResponse]]]) -
-
-
-
- -Expand source code - -
class ErrorHandlers:
-    def __init__(
-        self,
-        on_tenant_does_not_exist: Callable[
-            [SuperTokensError, BaseRequest, BaseResponse],
-            Union[BaseResponse, Awaitable[BaseResponse]],
-        ],
-        on_recipe_disabled_for_tenant: Callable[
-            [SuperTokensError, BaseRequest, BaseResponse],
-            Union[BaseResponse, Awaitable[BaseResponse]],
-        ],
-    ):
-        self.__on_tenant_does_not_exist = on_tenant_does_not_exist
-        self.__on_recipe_disabled_for_tenant = on_recipe_disabled_for_tenant
-
-    async def on_tenant_does_not_exist(
-        self,
-        err: SuperTokensError,
-        request: BaseRequest,
-        response: BaseResponse,
-    ) -> BaseResponse:
-        return await resolve(self.__on_tenant_does_not_exist(err, request, response))
-
-    async def on_recipe_disabled_for_tenant(
-        self, err: SuperTokensError, request: BaseRequest, response: BaseResponse
-    ) -> BaseResponse:
-        return await resolve(
-            self.__on_recipe_disabled_for_tenant(err, request, response)
-        )
-
-

Methods

-
-
-async def on_recipe_disabled_for_tenant(self, err: SuperTokensError, request: BaseRequest, response: BaseResponse) ‑> BaseResponse -
-
-
-
- -Expand source code - -
async def on_recipe_disabled_for_tenant(
-    self, err: SuperTokensError, request: BaseRequest, response: BaseResponse
-) -> BaseResponse:
-    return await resolve(
-        self.__on_recipe_disabled_for_tenant(err, request, response)
-    )
-
-
-
-async def on_tenant_does_not_exist(self, err: SuperTokensError, request: BaseRequest, response: BaseResponse) ‑> BaseResponse -
-
-
-
- -Expand source code - -
async def on_tenant_does_not_exist(
-    self,
-    err: SuperTokensError,
-    request: BaseRequest,
-    response: BaseResponse,
-) -> BaseResponse:
-    return await resolve(self.__on_tenant_does_not_exist(err, request, response))
-
-
-
-
-
-class InputOverrideConfig -(functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None, apis: Union[Callable[[APIInterface], APIInterface], None] = None) -
-
-
-
- -Expand source code - -
class InputOverrideConfig:
-    def __init__(
-        self,
-        functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None,
-        apis: Union[Callable[[APIInterface], APIInterface], None] = None,
-    ):
-        self.functions = functions
-        self.apis = apis
-
-
-
-class MultitenancyConfig -(get_allowed_domains_for_tenant_id: Optional[TypeGetAllowedDomainsForTenantId], override: OverrideConfig) -
-
-
-
- -Expand source code - -
class MultitenancyConfig:
-    def __init__(
-        self,
-        get_allowed_domains_for_tenant_id: Optional[TypeGetAllowedDomainsForTenantId],
-        override: OverrideConfig,
-    ):
-        self.get_allowed_domains_for_tenant_id = get_allowed_domains_for_tenant_id
-        self.override = override
-
-
-
-class OverrideConfig -(functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None, apis: Union[Callable[[APIInterface], APIInterface], None] = None) -
-
-
-
- -Expand source code - -
class OverrideConfig:
-    def __init__(
-        self,
-        functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None,
-        apis: Union[Callable[[APIInterface], APIInterface], None] = None,
-    ):
-        self.functions = functions
-        self.apis = apis
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/openid/api/implementation.html b/html/supertokens_python/recipe/openid/api/implementation.html deleted file mode 100644 index 511b2ca8e..000000000 --- a/html/supertokens_python/recipe/openid/api/implementation.html +++ /dev/null @@ -1,157 +0,0 @@ - - - - - - -supertokens_python.recipe.openid.api.implementation API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.openid.api.implementation

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from typing import Any, Dict
-
-from supertokens_python.recipe.openid.interfaces import (
-    APIInterface,
-    APIOptions,
-    OpenIdDiscoveryConfigurationGetResponse,
-)
-
-
-class APIImplementation(APIInterface):
-    async def open_id_discovery_configuration_get(
-        self, api_options: APIOptions, user_context: Dict[str, Any]
-    ) -> OpenIdDiscoveryConfigurationGetResponse:
-        response = (
-            await api_options.recipe_implementation.get_open_id_discovery_configuration(
-                user_context
-            )
-        )
-        return OpenIdDiscoveryConfigurationGetResponse(
-            response.issuer, response.jwks_uri
-        )
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class APIImplementation -
-
-
-
- -Expand source code - -
class APIImplementation(APIInterface):
-    async def open_id_discovery_configuration_get(
-        self, api_options: APIOptions, user_context: Dict[str, Any]
-    ) -> OpenIdDiscoveryConfigurationGetResponse:
-        response = (
-            await api_options.recipe_implementation.get_open_id_discovery_configuration(
-                user_context
-            )
-        )
-        return OpenIdDiscoveryConfigurationGetResponse(
-            response.issuer, response.jwks_uri
-        )
-
-

Ancestors

- -

Methods

-
-
-async def open_id_discovery_configuration_get(self, api_options: APIOptions, user_context: Dict[str, Any]) ‑> OpenIdDiscoveryConfigurationGetResponse -
-
-
-
- -Expand source code - -
async def open_id_discovery_configuration_get(
-    self, api_options: APIOptions, user_context: Dict[str, Any]
-) -> OpenIdDiscoveryConfigurationGetResponse:
-    response = (
-        await api_options.recipe_implementation.get_open_id_discovery_configuration(
-            user_context
-        )
-    )
-    return OpenIdDiscoveryConfigurationGetResponse(
-        response.issuer, response.jwks_uri
-    )
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/openid/api/index.html b/html/supertokens_python/recipe/openid/api/index.html deleted file mode 100644 index a874415f4..000000000 --- a/html/supertokens_python/recipe/openid/api/index.html +++ /dev/null @@ -1,70 +0,0 @@ - - - - - - -supertokens_python.recipe.openid.api API documentation - - - - - - - - - - - -
- - -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/openid/api/open_id_discovery_configuration_get.html b/html/supertokens_python/recipe/openid/api/open_id_discovery_configuration_get.html deleted file mode 100644 index c855f23a3..000000000 --- a/html/supertokens_python/recipe/openid/api/open_id_discovery_configuration_get.html +++ /dev/null @@ -1,128 +0,0 @@ - - - - - - -supertokens_python.recipe.openid.api.open_id_discovery_configuration_get API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.openid.api.open_id_discovery_configuration_get

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-from typing import Any, Dict
-from supertokens_python.recipe.openid.interfaces import APIInterface, APIOptions
-from supertokens_python.utils import send_200_response
-
-from ..interfaces import OpenIdDiscoveryConfigurationGetResponse
-
-
-async def open_id_discovery_configuration_get(
-    api_implementation: APIInterface,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-):
-    if api_implementation.disable_open_id_discovery_configuration_get:
-        return None
-
-    result = await api_implementation.open_id_discovery_configuration_get(
-        api_options, user_context
-    )
-
-    if isinstance(result, OpenIdDiscoveryConfigurationGetResponse):
-        api_options.response.set_header("Access-Control-Allow-Origin", "*")
-    return send_200_response(result.to_json(), api_options.response)
-
-
-
-
-
-
-
-

Functions

-
-
-async def open_id_discovery_configuration_get(api_implementation: APIInterface, api_options: APIOptions, user_context: Dict[str, Any]) -
-
-
-
- -Expand source code - -
async def open_id_discovery_configuration_get(
-    api_implementation: APIInterface,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-):
-    if api_implementation.disable_open_id_discovery_configuration_get:
-        return None
-
-    result = await api_implementation.open_id_discovery_configuration_get(
-        api_options, user_context
-    )
-
-    if isinstance(result, OpenIdDiscoveryConfigurationGetResponse):
-        api_options.response.set_header("Access-Control-Allow-Origin", "*")
-    return send_200_response(result.to_json(), api_options.response)
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/openid/asyncio/index.html b/html/supertokens_python/recipe/openid/asyncio/index.html deleted file mode 100644 index 5e595dfd0..000000000 --- a/html/supertokens_python/recipe/openid/asyncio/index.html +++ /dev/null @@ -1,188 +0,0 @@ - - - - - - -supertokens_python.recipe.openid.asyncio API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.openid.asyncio

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from typing import Any, Dict, Union, Optional
-
-from supertokens_python.recipe.openid.interfaces import (
-    GetOpenIdDiscoveryConfigurationResult,
-)
-from supertokens_python.recipe.openid.recipe import OpenIdRecipe
-
-from ...jwt.interfaces import (
-    CreateJwtOkResult,
-    CreateJwtResultUnsupportedAlgorithm,
-    GetJWKSResult,
-)
-
-
-async def create_jwt(
-    payload: Optional[Dict[str, Any]] = None,
-    validity_seconds: Optional[int] = None,
-    use_static_signing_key: Optional[bool] = None,
-    user_context: Optional[Dict[str, Any]] = None,
-) -> Union[CreateJwtOkResult, CreateJwtResultUnsupportedAlgorithm]:
-    if user_context is None:
-        user_context = {}
-    if payload is None:
-        payload = {}
-
-    return await OpenIdRecipe.get_instance().recipe_implementation.create_jwt(
-        payload, validity_seconds, use_static_signing_key, user_context
-    )
-
-
-async def get_jwks(user_context: Optional[Dict[str, Any]] = None) -> GetJWKSResult:
-    if user_context is None:
-        user_context = {}
-    return await OpenIdRecipe.get_instance().recipe_implementation.get_jwks(
-        user_context
-    )
-
-
-async def get_open_id_discovery_configuration(
-    user_context: Optional[Dict[str, Any]] = None
-) -> GetOpenIdDiscoveryConfigurationResult:
-    if user_context is None:
-        user_context = {}
-    return await OpenIdRecipe.get_instance().recipe_implementation.get_open_id_discovery_configuration(
-        user_context
-    )
-
-
-
-
-
-
-
-

Functions

-
-
-async def create_jwt(payload: Optional[Dict[str, Any]] = None, validity_seconds: Optional[int] = None, use_static_signing_key: Optional[bool] = None, user_context: Optional[Dict[str, Any]] = None) ‑> Union[CreateJwtOkResultCreateJwtResultUnsupportedAlgorithm] -
-
-
-
- -Expand source code - -
async def create_jwt(
-    payload: Optional[Dict[str, Any]] = None,
-    validity_seconds: Optional[int] = None,
-    use_static_signing_key: Optional[bool] = None,
-    user_context: Optional[Dict[str, Any]] = None,
-) -> Union[CreateJwtOkResult, CreateJwtResultUnsupportedAlgorithm]:
-    if user_context is None:
-        user_context = {}
-    if payload is None:
-        payload = {}
-
-    return await OpenIdRecipe.get_instance().recipe_implementation.create_jwt(
-        payload, validity_seconds, use_static_signing_key, user_context
-    )
-
-
-
-async def get_jwks(user_context: Optional[Dict[str, Any]] = None) ‑> GetJWKSResult -
-
-
-
- -Expand source code - -
async def get_jwks(user_context: Optional[Dict[str, Any]] = None) -> GetJWKSResult:
-    if user_context is None:
-        user_context = {}
-    return await OpenIdRecipe.get_instance().recipe_implementation.get_jwks(
-        user_context
-    )
-
-
-
-async def get_open_id_discovery_configuration(user_context: Optional[Dict[str, Any]] = None) ‑> GetOpenIdDiscoveryConfigurationResult -
-
-
-
- -Expand source code - -
async def get_open_id_discovery_configuration(
-    user_context: Optional[Dict[str, Any]] = None
-) -> GetOpenIdDiscoveryConfigurationResult:
-    if user_context is None:
-        user_context = {}
-    return await OpenIdRecipe.get_instance().recipe_implementation.get_open_id_discovery_configuration(
-        user_context
-    )
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/openid/constants.html b/html/supertokens_python/recipe/openid/constants.html deleted file mode 100644 index 215fdab42..000000000 --- a/html/supertokens_python/recipe/openid/constants.html +++ /dev/null @@ -1,73 +0,0 @@ - - - - - - -supertokens_python.recipe.openid.constants API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.openid.constants

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-GET_DISCOVERY_CONFIG_URL = "/.well-known/openid-configuration"
-
-
-
-
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/openid/exceptions.html b/html/supertokens_python/recipe/openid/exceptions.html deleted file mode 100644 index fd7db557f..000000000 --- a/html/supertokens_python/recipe/openid/exceptions.html +++ /dev/null @@ -1,108 +0,0 @@ - - - - - - -supertokens_python.recipe.openid.exceptions API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.openid.exceptions

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from supertokens_python.exceptions import SuperTokensError
-
-
-class SuperTokensOpenIdError(SuperTokensError):
-    pass
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class SuperTokensOpenIdError -(*args, **kwargs) -
-
-

Common base class for all non-exit exceptions.

-
- -Expand source code - -
class SuperTokensOpenIdError(SuperTokensError):
-    pass
-
-

Ancestors

- -
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/openid/index.html b/html/supertokens_python/recipe/openid/index.html deleted file mode 100644 index 84a318a3f..000000000 --- a/html/supertokens_python/recipe/openid/index.html +++ /dev/null @@ -1,167 +0,0 @@ - - - - - - -supertokens_python.recipe.openid API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.openid

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from typing import TYPE_CHECKING, Callable, Union
-
-from .recipe import OpenIdRecipe
-from .utils import InputOverrideConfig
-
-if TYPE_CHECKING:
-    from supertokens_python.supertokens import AppInfo
-
-    from ...recipe_module import RecipeModule
-
-
-def init(
-    jwt_validity_seconds: Union[int, None] = None,
-    issuer: Union[str, None] = None,
-    override: Union[InputOverrideConfig, None] = None,
-) -> Callable[[AppInfo], RecipeModule]:
-    return OpenIdRecipe.init(jwt_validity_seconds, issuer, override)
-
-
-
-

Sub-modules

-
-
supertokens_python.recipe.openid.api
-
-
-
-
supertokens_python.recipe.openid.asyncio
-
-
-
-
supertokens_python.recipe.openid.constants
-
-
-
-
supertokens_python.recipe.openid.exceptions
-
-
-
-
supertokens_python.recipe.openid.interfaces
-
-
-
-
supertokens_python.recipe.openid.recipe
-
-
-
-
supertokens_python.recipe.openid.recipe_implementation
-
-
-
-
supertokens_python.recipe.openid.syncio
-
-
-
-
supertokens_python.recipe.openid.utils
-
-
-
-
-
-
-
-
-

Functions

-
-
-def init(jwt_validity_seconds: Union[int, None] = None, issuer: Union[str, None] = None, override: Union[InputOverrideConfig, None] = None) ‑> Callable[[AppInfo], RecipeModule] -
-
-
-
- -Expand source code - -
def init(
-    jwt_validity_seconds: Union[int, None] = None,
-    issuer: Union[str, None] = None,
-    override: Union[InputOverrideConfig, None] = None,
-) -> Callable[[AppInfo], RecipeModule]:
-    return OpenIdRecipe.init(jwt_validity_seconds, issuer, override)
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/openid/interfaces.html b/html/supertokens_python/recipe/openid/interfaces.html deleted file mode 100644 index dfa934768..000000000 --- a/html/supertokens_python/recipe/openid/interfaces.html +++ /dev/null @@ -1,417 +0,0 @@ - - - - - - -supertokens_python.recipe.openid.interfaces API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.openid.interfaces

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from abc import ABC, abstractmethod
-from typing import Any, Dict, Union, Optional
-
-from supertokens_python.framework import BaseRequest, BaseResponse
-from supertokens_python.recipe.jwt.interfaces import (
-    CreateJwtOkResult,
-    CreateJwtResultUnsupportedAlgorithm,
-    GetJWKSResult,
-)
-from supertokens_python.types import APIResponse, GeneralErrorResponse
-
-from .utils import OpenIdConfig
-
-
-class GetOpenIdDiscoveryConfigurationResult:
-    def __init__(self, issuer: str, jwks_uri: str):
-        self.issuer = issuer
-        self.jwks_uri = jwks_uri
-
-
-class RecipeInterface(ABC):
-    def __init__(self):
-        pass
-
-    @abstractmethod
-    async def create_jwt(
-        self,
-        payload: Dict[str, Any],
-        validity_seconds: Optional[int],
-        use_static_signing_key: Optional[bool],
-        user_context: Dict[str, Any],
-    ) -> Union[CreateJwtOkResult, CreateJwtResultUnsupportedAlgorithm]:
-        pass
-
-    @abstractmethod
-    async def get_jwks(self, user_context: Dict[str, Any]) -> GetJWKSResult:
-        pass
-
-    @abstractmethod
-    async def get_open_id_discovery_configuration(
-        self, user_context: Dict[str, Any]
-    ) -> GetOpenIdDiscoveryConfigurationResult:
-        pass
-
-
-class APIOptions:
-    def __init__(
-        self,
-        request: BaseRequest,
-        response: BaseResponse,
-        recipe_id: str,
-        config: OpenIdConfig,
-        recipe_implementation: RecipeInterface,
-    ):
-        self.request = request
-        self.response = response
-        self.recipe_id = recipe_id
-        self.config = config
-        self.recipe_implementation = recipe_implementation
-
-
-class OpenIdDiscoveryConfigurationGetResponse(APIResponse):
-    status: str = "OK"
-
-    def __init__(self, issuer: str, jwks_uri: str):
-        self.issuer = issuer
-        self.jwks_uri = jwks_uri
-
-    def to_json(self):
-        return {"status": self.status, "issuer": self.issuer, "jwks_uri": self.jwks_uri}
-
-
-class APIInterface:
-    def __init__(self):
-        self.disable_open_id_discovery_configuration_get = False
-
-    @abstractmethod
-    async def open_id_discovery_configuration_get(
-        self, api_options: APIOptions, user_context: Dict[str, Any]
-    ) -> Union[OpenIdDiscoveryConfigurationGetResponse, GeneralErrorResponse]:
-        pass
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class APIInterface -
-
-
-
- -Expand source code - -
class APIInterface:
-    def __init__(self):
-        self.disable_open_id_discovery_configuration_get = False
-
-    @abstractmethod
-    async def open_id_discovery_configuration_get(
-        self, api_options: APIOptions, user_context: Dict[str, Any]
-    ) -> Union[OpenIdDiscoveryConfigurationGetResponse, GeneralErrorResponse]:
-        pass
-
-

Subclasses

- -

Methods

-
-
-async def open_id_discovery_configuration_get(self, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[OpenIdDiscoveryConfigurationGetResponseGeneralErrorResponse] -
-
-
-
- -Expand source code - -
@abstractmethod
-async def open_id_discovery_configuration_get(
-    self, api_options: APIOptions, user_context: Dict[str, Any]
-) -> Union[OpenIdDiscoveryConfigurationGetResponse, GeneralErrorResponse]:
-    pass
-
-
-
-
-
-class APIOptions -(request: BaseRequest, response: BaseResponse, recipe_id: str, config: OpenIdConfig, recipe_implementation: RecipeInterface) -
-
-
-
- -Expand source code - -
class APIOptions:
-    def __init__(
-        self,
-        request: BaseRequest,
-        response: BaseResponse,
-        recipe_id: str,
-        config: OpenIdConfig,
-        recipe_implementation: RecipeInterface,
-    ):
-        self.request = request
-        self.response = response
-        self.recipe_id = recipe_id
-        self.config = config
-        self.recipe_implementation = recipe_implementation
-
-
-
-class GetOpenIdDiscoveryConfigurationResult -(issuer: str, jwks_uri: str) -
-
-
-
- -Expand source code - -
class GetOpenIdDiscoveryConfigurationResult:
-    def __init__(self, issuer: str, jwks_uri: str):
-        self.issuer = issuer
-        self.jwks_uri = jwks_uri
-
-
-
-class OpenIdDiscoveryConfigurationGetResponse -(issuer: str, jwks_uri: str) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class OpenIdDiscoveryConfigurationGetResponse(APIResponse):
-    status: str = "OK"
-
-    def __init__(self, issuer: str, jwks_uri: str):
-        self.issuer = issuer
-        self.jwks_uri = jwks_uri
-
-    def to_json(self):
-        return {"status": self.status, "issuer": self.issuer, "jwks_uri": self.jwks_uri}
-
-

Ancestors

- -

Class variables

-
-
var status : str
-
-
-
-
-

Methods

-
-
-def to_json(self) -
-
-
-
- -Expand source code - -
def to_json(self):
-    return {"status": self.status, "issuer": self.issuer, "jwks_uri": self.jwks_uri}
-
-
-
-
-
-class RecipeInterface -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class RecipeInterface(ABC):
-    def __init__(self):
-        pass
-
-    @abstractmethod
-    async def create_jwt(
-        self,
-        payload: Dict[str, Any],
-        validity_seconds: Optional[int],
-        use_static_signing_key: Optional[bool],
-        user_context: Dict[str, Any],
-    ) -> Union[CreateJwtOkResult, CreateJwtResultUnsupportedAlgorithm]:
-        pass
-
-    @abstractmethod
-    async def get_jwks(self, user_context: Dict[str, Any]) -> GetJWKSResult:
-        pass
-
-    @abstractmethod
-    async def get_open_id_discovery_configuration(
-        self, user_context: Dict[str, Any]
-    ) -> GetOpenIdDiscoveryConfigurationResult:
-        pass
-
-

Ancestors

-
    -
  • abc.ABC
  • -
-

Subclasses

- -

Methods

-
-
-async def create_jwt(self, payload: Dict[str, Any], validity_seconds: Optional[int], use_static_signing_key: Optional[bool], user_context: Dict[str, Any]) ‑> Union[CreateJwtOkResultCreateJwtResultUnsupportedAlgorithm] -
-
-
-
- -Expand source code - -
@abstractmethod
-async def create_jwt(
-    self,
-    payload: Dict[str, Any],
-    validity_seconds: Optional[int],
-    use_static_signing_key: Optional[bool],
-    user_context: Dict[str, Any],
-) -> Union[CreateJwtOkResult, CreateJwtResultUnsupportedAlgorithm]:
-    pass
-
-
-
-async def get_jwks(self, user_context: Dict[str, Any]) ‑> GetJWKSResult -
-
-
-
- -Expand source code - -
@abstractmethod
-async def get_jwks(self, user_context: Dict[str, Any]) -> GetJWKSResult:
-    pass
-
-
-
-async def get_open_id_discovery_configuration(self, user_context: Dict[str, Any]) ‑> GetOpenIdDiscoveryConfigurationResult -
-
-
-
- -Expand source code - -
@abstractmethod
-async def get_open_id_discovery_configuration(
-    self, user_context: Dict[str, Any]
-) -> GetOpenIdDiscoveryConfigurationResult:
-    pass
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/openid/recipe.html b/html/supertokens_python/recipe/openid/recipe.html deleted file mode 100644 index f75bfc1a7..000000000 --- a/html/supertokens_python/recipe/openid/recipe.html +++ /dev/null @@ -1,595 +0,0 @@ - - - - - - -supertokens_python.recipe.openid.recipe API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.openid.recipe

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from os import environ
-from typing import TYPE_CHECKING, List, Union, Any, Dict
-
-from supertokens_python.querier import Querier
-
-from .api.implementation import APIImplementation
-from .api.open_id_discovery_configuration_get import open_id_discovery_configuration_get
-from .constants import GET_DISCOVERY_CONFIG_URL
-from .exceptions import SuperTokensOpenIdError
-from .interfaces import APIOptions
-from .recipe_implementation import RecipeImplementation
-from .utils import InputOverrideConfig, validate_and_normalise_user_input
-
-if TYPE_CHECKING:
-    from supertokens_python.framework.request import BaseRequest
-    from supertokens_python.framework.response import BaseResponse
-    from supertokens_python.supertokens import AppInfo
-
-from supertokens_python.exceptions import SuperTokensError, raise_general_exception
-from supertokens_python.normalised_url_path import NormalisedURLPath
-from supertokens_python.recipe_module import APIHandled, RecipeModule
-
-
-class OpenIdRecipe(RecipeModule):
-    recipe_id = "openid"
-    __instance = None
-
-    def __init__(
-        self,
-        recipe_id: str,
-        app_info: AppInfo,
-        jwt_validity_seconds: Union[int, None] = None,
-        issuer: Union[str, None] = None,
-        override: Union[InputOverrideConfig, None] = None,
-    ):
-        from supertokens_python.recipe.jwt import JWTRecipe
-
-        super().__init__(recipe_id, app_info)
-        self.config = validate_and_normalise_user_input(app_info, issuer, override)
-        jwt_feature = None
-        if override is not None:
-            jwt_feature = override.jwt_feature
-        self.jwt_recipe = JWTRecipe(
-            recipe_id, app_info, jwt_validity_seconds, jwt_feature
-        )
-
-        recipe_implementation = RecipeImplementation(
-            Querier.get_instance(recipe_id),
-            self.config,
-            app_info,
-            self.jwt_recipe.recipe_implementation,
-        )
-        self.recipe_implementation = (
-            recipe_implementation
-            if self.config.override.functions is None
-            else self.config.override.functions(recipe_implementation)
-        )
-        api_implementation = APIImplementation()
-        self.api_implementation = (
-            api_implementation
-            if self.config.override.apis is None
-            else self.config.override.apis(api_implementation)
-        )
-
-    def get_apis_handled(self) -> List[APIHandled]:
-        return [
-            APIHandled(
-                method="get",
-                path_without_api_base_path=NormalisedURLPath(GET_DISCOVERY_CONFIG_URL),
-                request_id=GET_DISCOVERY_CONFIG_URL,
-                disabled=self.api_implementation.disable_open_id_discovery_configuration_get,
-            )
-        ] + self.jwt_recipe.get_apis_handled()
-
-    async def handle_api_request(
-        self,
-        request_id: str,
-        tenant_id: str,
-        request: BaseRequest,
-        path: NormalisedURLPath,
-        method: str,
-        response: BaseResponse,
-        user_context: Dict[str, Any],
-    ):
-        options = APIOptions(
-            request,
-            response,
-            self.get_recipe_id(),
-            self.config,
-            self.recipe_implementation,
-        )
-
-        if request_id == GET_DISCOVERY_CONFIG_URL:
-            return await open_id_discovery_configuration_get(
-                self.api_implementation, options, user_context
-            )
-        return await self.jwt_recipe.handle_api_request(
-            request_id, tenant_id, request, path, method, response, user_context
-        )
-
-    async def handle_error(
-        self,
-        request: BaseRequest,
-        err: SuperTokensError,
-        response: BaseResponse,
-        user_context: Dict[str, Any],
-    ):
-        if isinstance(err, SuperTokensOpenIdError):
-            raise err
-        return await self.jwt_recipe.handle_error(request, err, response, user_context)
-
-    def get_all_cors_headers(self) -> List[str]:
-        return self.jwt_recipe.get_all_cors_headers()
-
-    def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
-        return isinstance(err, SuperTokensError) and (
-            isinstance(err, SuperTokensOpenIdError)
-            or self.jwt_recipe.is_error_from_this_recipe_based_on_instance(err)
-        )
-
-    @staticmethod
-    def init(
-        jwt_validity_seconds: Union[int, None] = None,
-        issuer: Union[str, None] = None,
-        override: Union[InputOverrideConfig, None] = None,
-    ):
-        def func(app_info: AppInfo):
-            if OpenIdRecipe.__instance is None:
-                OpenIdRecipe.__instance = OpenIdRecipe(
-                    OpenIdRecipe.recipe_id,
-                    app_info,
-                    jwt_validity_seconds,
-                    issuer,
-                    override,
-                )
-                return OpenIdRecipe.__instance
-            raise_general_exception(
-                "OpenId recipe has already been initialised. Please check your code for bugs."
-            )
-
-        return func
-
-    @staticmethod
-    def get_instance() -> OpenIdRecipe:
-        if OpenIdRecipe.__instance is not None:
-            return OpenIdRecipe.__instance
-        raise_general_exception(
-            "Initialisation not done. Did you forget to call the SuperTokens.init function?"
-        )
-
-    @staticmethod
-    def reset():
-        if ("SUPERTOKENS_ENV" not in environ) or (
-            environ["SUPERTOKENS_ENV"] != "testing"
-        ):
-            raise_general_exception("calling testing function in non testing env")
-        OpenIdRecipe.__instance = None
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class OpenIdRecipe -(recipe_id: str, app_info: AppInfo, jwt_validity_seconds: Union[int, None] = None, issuer: Union[str, None] = None, override: Union[InputOverrideConfig, None] = None) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class OpenIdRecipe(RecipeModule):
-    recipe_id = "openid"
-    __instance = None
-
-    def __init__(
-        self,
-        recipe_id: str,
-        app_info: AppInfo,
-        jwt_validity_seconds: Union[int, None] = None,
-        issuer: Union[str, None] = None,
-        override: Union[InputOverrideConfig, None] = None,
-    ):
-        from supertokens_python.recipe.jwt import JWTRecipe
-
-        super().__init__(recipe_id, app_info)
-        self.config = validate_and_normalise_user_input(app_info, issuer, override)
-        jwt_feature = None
-        if override is not None:
-            jwt_feature = override.jwt_feature
-        self.jwt_recipe = JWTRecipe(
-            recipe_id, app_info, jwt_validity_seconds, jwt_feature
-        )
-
-        recipe_implementation = RecipeImplementation(
-            Querier.get_instance(recipe_id),
-            self.config,
-            app_info,
-            self.jwt_recipe.recipe_implementation,
-        )
-        self.recipe_implementation = (
-            recipe_implementation
-            if self.config.override.functions is None
-            else self.config.override.functions(recipe_implementation)
-        )
-        api_implementation = APIImplementation()
-        self.api_implementation = (
-            api_implementation
-            if self.config.override.apis is None
-            else self.config.override.apis(api_implementation)
-        )
-
-    def get_apis_handled(self) -> List[APIHandled]:
-        return [
-            APIHandled(
-                method="get",
-                path_without_api_base_path=NormalisedURLPath(GET_DISCOVERY_CONFIG_URL),
-                request_id=GET_DISCOVERY_CONFIG_URL,
-                disabled=self.api_implementation.disable_open_id_discovery_configuration_get,
-            )
-        ] + self.jwt_recipe.get_apis_handled()
-
-    async def handle_api_request(
-        self,
-        request_id: str,
-        tenant_id: str,
-        request: BaseRequest,
-        path: NormalisedURLPath,
-        method: str,
-        response: BaseResponse,
-        user_context: Dict[str, Any],
-    ):
-        options = APIOptions(
-            request,
-            response,
-            self.get_recipe_id(),
-            self.config,
-            self.recipe_implementation,
-        )
-
-        if request_id == GET_DISCOVERY_CONFIG_URL:
-            return await open_id_discovery_configuration_get(
-                self.api_implementation, options, user_context
-            )
-        return await self.jwt_recipe.handle_api_request(
-            request_id, tenant_id, request, path, method, response, user_context
-        )
-
-    async def handle_error(
-        self,
-        request: BaseRequest,
-        err: SuperTokensError,
-        response: BaseResponse,
-        user_context: Dict[str, Any],
-    ):
-        if isinstance(err, SuperTokensOpenIdError):
-            raise err
-        return await self.jwt_recipe.handle_error(request, err, response, user_context)
-
-    def get_all_cors_headers(self) -> List[str]:
-        return self.jwt_recipe.get_all_cors_headers()
-
-    def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
-        return isinstance(err, SuperTokensError) and (
-            isinstance(err, SuperTokensOpenIdError)
-            or self.jwt_recipe.is_error_from_this_recipe_based_on_instance(err)
-        )
-
-    @staticmethod
-    def init(
-        jwt_validity_seconds: Union[int, None] = None,
-        issuer: Union[str, None] = None,
-        override: Union[InputOverrideConfig, None] = None,
-    ):
-        def func(app_info: AppInfo):
-            if OpenIdRecipe.__instance is None:
-                OpenIdRecipe.__instance = OpenIdRecipe(
-                    OpenIdRecipe.recipe_id,
-                    app_info,
-                    jwt_validity_seconds,
-                    issuer,
-                    override,
-                )
-                return OpenIdRecipe.__instance
-            raise_general_exception(
-                "OpenId recipe has already been initialised. Please check your code for bugs."
-            )
-
-        return func
-
-    @staticmethod
-    def get_instance() -> OpenIdRecipe:
-        if OpenIdRecipe.__instance is not None:
-            return OpenIdRecipe.__instance
-        raise_general_exception(
-            "Initialisation not done. Did you forget to call the SuperTokens.init function?"
-        )
-
-    @staticmethod
-    def reset():
-        if ("SUPERTOKENS_ENV" not in environ) or (
-            environ["SUPERTOKENS_ENV"] != "testing"
-        ):
-            raise_general_exception("calling testing function in non testing env")
-        OpenIdRecipe.__instance = None
-
-

Ancestors

- -

Class variables

-
-
var get_tenant_id : Optional[Callable[[str, Dict[str, Any]], Awaitable[str]]]
-
-
-
-
var recipe_id
-
-
-
-
-

Static methods

-
-
-def get_instance() ‑> OpenIdRecipe -
-
-
-
- -Expand source code - -
@staticmethod
-def get_instance() -> OpenIdRecipe:
-    if OpenIdRecipe.__instance is not None:
-        return OpenIdRecipe.__instance
-    raise_general_exception(
-        "Initialisation not done. Did you forget to call the SuperTokens.init function?"
-    )
-
-
-
-def init(jwt_validity_seconds: Union[int, None] = None, issuer: Union[str, None] = None, override: Union[InputOverrideConfig, None] = None) -
-
-
-
- -Expand source code - -
@staticmethod
-def init(
-    jwt_validity_seconds: Union[int, None] = None,
-    issuer: Union[str, None] = None,
-    override: Union[InputOverrideConfig, None] = None,
-):
-    def func(app_info: AppInfo):
-        if OpenIdRecipe.__instance is None:
-            OpenIdRecipe.__instance = OpenIdRecipe(
-                OpenIdRecipe.recipe_id,
-                app_info,
-                jwt_validity_seconds,
-                issuer,
-                override,
-            )
-            return OpenIdRecipe.__instance
-        raise_general_exception(
-            "OpenId recipe has already been initialised. Please check your code for bugs."
-        )
-
-    return func
-
-
-
-def reset() -
-
-
-
- -Expand source code - -
@staticmethod
-def reset():
-    if ("SUPERTOKENS_ENV" not in environ) or (
-        environ["SUPERTOKENS_ENV"] != "testing"
-    ):
-        raise_general_exception("calling testing function in non testing env")
-    OpenIdRecipe.__instance = None
-
-
-
-

Methods

-
-
-def get_all_cors_headers(self) ‑> List[str] -
-
-
-
- -Expand source code - -
def get_all_cors_headers(self) -> List[str]:
-    return self.jwt_recipe.get_all_cors_headers()
-
-
-
-def get_apis_handled(self) ‑> List[APIHandled] -
-
-
-
- -Expand source code - -
def get_apis_handled(self) -> List[APIHandled]:
-    return [
-        APIHandled(
-            method="get",
-            path_without_api_base_path=NormalisedURLPath(GET_DISCOVERY_CONFIG_URL),
-            request_id=GET_DISCOVERY_CONFIG_URL,
-            disabled=self.api_implementation.disable_open_id_discovery_configuration_get,
-        )
-    ] + self.jwt_recipe.get_apis_handled()
-
-
-
-async def handle_api_request(self, request_id: str, tenant_id: str, request: BaseRequest, path: NormalisedURLPath, method: str, response: BaseResponse, user_context: Dict[str, Any]) -
-
-
-
- -Expand source code - -
async def handle_api_request(
-    self,
-    request_id: str,
-    tenant_id: str,
-    request: BaseRequest,
-    path: NormalisedURLPath,
-    method: str,
-    response: BaseResponse,
-    user_context: Dict[str, Any],
-):
-    options = APIOptions(
-        request,
-        response,
-        self.get_recipe_id(),
-        self.config,
-        self.recipe_implementation,
-    )
-
-    if request_id == GET_DISCOVERY_CONFIG_URL:
-        return await open_id_discovery_configuration_get(
-            self.api_implementation, options, user_context
-        )
-    return await self.jwt_recipe.handle_api_request(
-        request_id, tenant_id, request, path, method, response, user_context
-    )
-
-
-
-async def handle_error(self, request: BaseRequest, err: SuperTokensError, response: BaseResponse, user_context: Dict[str, Any]) -
-
-
-
- -Expand source code - -
async def handle_error(
-    self,
-    request: BaseRequest,
-    err: SuperTokensError,
-    response: BaseResponse,
-    user_context: Dict[str, Any],
-):
-    if isinstance(err, SuperTokensOpenIdError):
-        raise err
-    return await self.jwt_recipe.handle_error(request, err, response, user_context)
-
-
-
-def is_error_from_this_recipe_based_on_instance(self, err: Exception) ‑> bool -
-
-
-
- -Expand source code - -
def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
-    return isinstance(err, SuperTokensError) and (
-        isinstance(err, SuperTokensOpenIdError)
-        or self.jwt_recipe.is_error_from_this_recipe_based_on_instance(err)
-    )
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/openid/recipe_implementation.html b/html/supertokens_python/recipe/openid/recipe_implementation.html deleted file mode 100644 index 614da5d80..000000000 --- a/html/supertokens_python/recipe/openid/recipe_implementation.html +++ /dev/null @@ -1,295 +0,0 @@ - - - - - - -supertokens_python.recipe.openid.recipe_implementation API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.openid.recipe_implementation

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from typing import TYPE_CHECKING, Any, Dict, Union, Optional
-
-from supertokens_python.querier import Querier
-
-if TYPE_CHECKING:
-    from .utils import OpenIdConfig
-    from .interfaces import CreateJwtOkResult, CreateJwtResultUnsupportedAlgorithm
-    from supertokens_python.supertokens import AppInfo
-
-from supertokens_python.normalised_url_path import NormalisedURLPath
-from supertokens_python.recipe.jwt.constants import GET_JWKS_API
-from supertokens_python.recipe.jwt.interfaces import (
-    RecipeInterface as JWTRecipeInterface,
-)
-
-from .interfaces import (
-    GetJWKSResult,
-    GetOpenIdDiscoveryConfigurationResult,
-    RecipeInterface,
-)
-
-
-class RecipeImplementation(RecipeInterface):
-    async def get_open_id_discovery_configuration(
-        self, user_context: Dict[str, Any]
-    ) -> GetOpenIdDiscoveryConfigurationResult:
-        issuer = (
-            self.config.issuer_domain.get_as_string_dangerous()
-            + self.config.issuer_path.get_as_string_dangerous()
-        )
-
-        jwks_uri = (
-            self.config.issuer_domain.get_as_string_dangerous()
-            + self.config.issuer_path.append(
-                NormalisedURLPath(GET_JWKS_API)
-            ).get_as_string_dangerous()
-        )
-
-        return GetOpenIdDiscoveryConfigurationResult(issuer, jwks_uri)
-
-    def __init__(
-        self,
-        querier: Querier,
-        config: OpenIdConfig,
-        app_info: AppInfo,
-        jwt_recipe_implementation: JWTRecipeInterface,
-    ):
-        super().__init__()
-        self.querier = querier
-        self.config = config
-        self.app_info = app_info
-        self.jwt_recipe_implementation = jwt_recipe_implementation
-
-    async def create_jwt(
-        self,
-        payload: Dict[str, Any],
-        validity_seconds: Optional[int],
-        use_static_signing_key: Optional[bool],
-        user_context: Dict[str, Any],
-    ) -> Union[CreateJwtOkResult, CreateJwtResultUnsupportedAlgorithm]:
-        issuer = (
-            self.config.issuer_domain.get_as_string_dangerous()
-            + self.config.issuer_path.get_as_string_dangerous()
-        )
-        payload = {"iss": issuer, **payload}
-        return await self.jwt_recipe_implementation.create_jwt(
-            payload, validity_seconds, use_static_signing_key, user_context
-        )
-
-    async def get_jwks(self, user_context: Dict[str, Any]) -> GetJWKSResult:
-        return await self.jwt_recipe_implementation.get_jwks(user_context)
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class RecipeImplementation -(querier: Querier, config: OpenIdConfig, app_info: AppInfo, jwt_recipe_implementation: JWTRecipeInterface) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class RecipeImplementation(RecipeInterface):
-    async def get_open_id_discovery_configuration(
-        self, user_context: Dict[str, Any]
-    ) -> GetOpenIdDiscoveryConfigurationResult:
-        issuer = (
-            self.config.issuer_domain.get_as_string_dangerous()
-            + self.config.issuer_path.get_as_string_dangerous()
-        )
-
-        jwks_uri = (
-            self.config.issuer_domain.get_as_string_dangerous()
-            + self.config.issuer_path.append(
-                NormalisedURLPath(GET_JWKS_API)
-            ).get_as_string_dangerous()
-        )
-
-        return GetOpenIdDiscoveryConfigurationResult(issuer, jwks_uri)
-
-    def __init__(
-        self,
-        querier: Querier,
-        config: OpenIdConfig,
-        app_info: AppInfo,
-        jwt_recipe_implementation: JWTRecipeInterface,
-    ):
-        super().__init__()
-        self.querier = querier
-        self.config = config
-        self.app_info = app_info
-        self.jwt_recipe_implementation = jwt_recipe_implementation
-
-    async def create_jwt(
-        self,
-        payload: Dict[str, Any],
-        validity_seconds: Optional[int],
-        use_static_signing_key: Optional[bool],
-        user_context: Dict[str, Any],
-    ) -> Union[CreateJwtOkResult, CreateJwtResultUnsupportedAlgorithm]:
-        issuer = (
-            self.config.issuer_domain.get_as_string_dangerous()
-            + self.config.issuer_path.get_as_string_dangerous()
-        )
-        payload = {"iss": issuer, **payload}
-        return await self.jwt_recipe_implementation.create_jwt(
-            payload, validity_seconds, use_static_signing_key, user_context
-        )
-
-    async def get_jwks(self, user_context: Dict[str, Any]) -> GetJWKSResult:
-        return await self.jwt_recipe_implementation.get_jwks(user_context)
-
-

Ancestors

- -

Methods

-
-
-async def create_jwt(self, payload: Dict[str, Any], validity_seconds: Optional[int], use_static_signing_key: Optional[bool], user_context: Dict[str, Any]) ‑> Union[CreateJwtOkResult, CreateJwtResultUnsupportedAlgorithm] -
-
-
-
- -Expand source code - -
async def create_jwt(
-    self,
-    payload: Dict[str, Any],
-    validity_seconds: Optional[int],
-    use_static_signing_key: Optional[bool],
-    user_context: Dict[str, Any],
-) -> Union[CreateJwtOkResult, CreateJwtResultUnsupportedAlgorithm]:
-    issuer = (
-        self.config.issuer_domain.get_as_string_dangerous()
-        + self.config.issuer_path.get_as_string_dangerous()
-    )
-    payload = {"iss": issuer, **payload}
-    return await self.jwt_recipe_implementation.create_jwt(
-        payload, validity_seconds, use_static_signing_key, user_context
-    )
-
-
-
-async def get_jwks(self, user_context: Dict[str, Any]) ‑> GetJWKSResult -
-
-
-
- -Expand source code - -
async def get_jwks(self, user_context: Dict[str, Any]) -> GetJWKSResult:
-    return await self.jwt_recipe_implementation.get_jwks(user_context)
-
-
-
-async def get_open_id_discovery_configuration(self, user_context: Dict[str, Any]) ‑> GetOpenIdDiscoveryConfigurationResult -
-
-
-
- -Expand source code - -
async def get_open_id_discovery_configuration(
-    self, user_context: Dict[str, Any]
-) -> GetOpenIdDiscoveryConfigurationResult:
-    issuer = (
-        self.config.issuer_domain.get_as_string_dangerous()
-        + self.config.issuer_path.get_as_string_dangerous()
-    )
-
-    jwks_uri = (
-        self.config.issuer_domain.get_as_string_dangerous()
-        + self.config.issuer_path.append(
-            NormalisedURLPath(GET_JWKS_API)
-        ).get_as_string_dangerous()
-    )
-
-    return GetOpenIdDiscoveryConfigurationResult(issuer, jwks_uri)
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/openid/syncio/index.html b/html/supertokens_python/recipe/openid/syncio/index.html deleted file mode 100644 index d35d2a0da..000000000 --- a/html/supertokens_python/recipe/openid/syncio/index.html +++ /dev/null @@ -1,167 +0,0 @@ - - - - - - -supertokens_python.recipe.openid.syncio API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.openid.syncio

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from typing import Any, Dict, Union, Optional
-
-from supertokens_python.async_to_sync_wrapper import sync
-from supertokens_python.recipe.openid import asyncio
-from supertokens_python.recipe.openid.interfaces import (
-    GetOpenIdDiscoveryConfigurationResult,
-)
-
-from ...jwt.interfaces import (
-    CreateJwtOkResult,
-    CreateJwtResultUnsupportedAlgorithm,
-    GetJWKSResult,
-)
-
-
-def create_jwt(
-    payload: Optional[Dict[str, Any]] = None,
-    validity_seconds: Optional[int] = None,
-    use_static_signing_key: Optional[bool] = None,
-    user_context: Optional[Dict[str, Any]] = None,
-) -> Union[CreateJwtOkResult, CreateJwtResultUnsupportedAlgorithm]:
-    return sync(
-        asyncio.create_jwt(
-            payload, validity_seconds, use_static_signing_key, user_context
-        )
-    )
-
-
-def get_jwks(user_context: Optional[Dict[str, Any]] = None) -> GetJWKSResult:
-    return sync(asyncio.get_jwks(user_context))
-
-
-def get_open_id_discovery_configuration(
-    user_context: Optional[Dict[str, Any]] = None
-) -> GetOpenIdDiscoveryConfigurationResult:
-    return sync(asyncio.get_open_id_discovery_configuration(user_context))
-
-
-
-
-
-
-
-

Functions

-
-
-def create_jwt(payload: Optional[Dict[str, Any]] = None, validity_seconds: Optional[int] = None, use_static_signing_key: Optional[bool] = None, user_context: Optional[Dict[str, Any]] = None) ‑> Union[CreateJwtOkResultCreateJwtResultUnsupportedAlgorithm] -
-
-
-
- -Expand source code - -
def create_jwt(
-    payload: Optional[Dict[str, Any]] = None,
-    validity_seconds: Optional[int] = None,
-    use_static_signing_key: Optional[bool] = None,
-    user_context: Optional[Dict[str, Any]] = None,
-) -> Union[CreateJwtOkResult, CreateJwtResultUnsupportedAlgorithm]:
-    return sync(
-        asyncio.create_jwt(
-            payload, validity_seconds, use_static_signing_key, user_context
-        )
-    )
-
-
-
-def get_jwks(user_context: Optional[Dict[str, Any]] = None) ‑> GetJWKSResult -
-
-
-
- -Expand source code - -
def get_jwks(user_context: Optional[Dict[str, Any]] = None) -> GetJWKSResult:
-    return sync(asyncio.get_jwks(user_context))
-
-
-
-def get_open_id_discovery_configuration(user_context: Optional[Dict[str, Any]] = None) ‑> GetOpenIdDiscoveryConfigurationResult -
-
-
-
- -Expand source code - -
def get_open_id_discovery_configuration(
-    user_context: Optional[Dict[str, Any]] = None
-) -> GetOpenIdDiscoveryConfigurationResult:
-    return sync(asyncio.get_open_id_discovery_configuration(user_context))
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/openid/utils.html b/html/supertokens_python/recipe/openid/utils.html deleted file mode 100644 index 186a3f426..000000000 --- a/html/supertokens_python/recipe/openid/utils.html +++ /dev/null @@ -1,273 +0,0 @@ - - - - - - -supertokens_python.recipe.openid.utils API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.openid.utils

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from typing import TYPE_CHECKING, Callable, Union
-
-if TYPE_CHECKING:
-    from .interfaces import RecipeInterface, APIInterface
-    from supertokens_python import AppInfo
-    from supertokens_python.recipe.jwt import OverrideConfig as JWTOverrideConfig
-
-from supertokens_python.normalised_url_domain import NormalisedURLDomain
-from supertokens_python.normalised_url_path import NormalisedURLPath
-
-
-class InputOverrideConfig:
-    def __init__(
-        self,
-        functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None,
-        apis: Union[Callable[[APIInterface], APIInterface], None] = None,
-        jwt_feature: Union[JWTOverrideConfig, None] = None,
-    ):
-        self.functions = functions
-        self.apis = apis
-        self.jwt_feature = jwt_feature
-
-
-class OverrideConfig:
-    def __init__(
-        self,
-        functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None,
-        apis: Union[Callable[[APIInterface], APIInterface], None] = None,
-    ):
-        self.functions = functions
-        self.apis = apis
-
-
-class OpenIdConfig:
-    def __init__(
-        self,
-        override: OverrideConfig,
-        issuer_domain: NormalisedURLDomain,
-        issuer_path: NormalisedURLPath,
-    ):
-        self.override = override
-        self.issuer_domain = issuer_domain
-        self.issuer_path = issuer_path
-
-
-def validate_and_normalise_user_input(
-    app_info: AppInfo,
-    issuer: Union[str, None] = None,
-    override: Union[InputOverrideConfig, None] = None,
-):
-    if issuer is None:
-        issuer_domain = app_info.api_domain
-        issuer_path = app_info.api_base_path
-    else:
-        issuer_domain = NormalisedURLDomain(issuer)
-        issuer_path = NormalisedURLPath(issuer)
-
-    if not issuer_path.equals(app_info.api_base_path):
-        raise Exception(
-            "The path of the issuer URL must be equal to the apiBasePath. The default value is /auth"
-        )
-
-    if override is not None and not isinstance(override, InputOverrideConfig):  # type: ignore
-        raise ValueError("override must be an instance of InputOverrideConfig or None")
-
-    if override is None:
-        override = InputOverrideConfig()
-
-    return OpenIdConfig(
-        OverrideConfig(functions=override.functions, apis=override.apis),
-        issuer_domain,
-        issuer_path,
-    )
-
-
-
-
-
-
-
-

Functions

-
-
-def validate_and_normalise_user_input(app_info: AppInfo, issuer: Union[str, None] = None, override: Union[InputOverrideConfig, None] = None) -
-
-
-
- -Expand source code - -
def validate_and_normalise_user_input(
-    app_info: AppInfo,
-    issuer: Union[str, None] = None,
-    override: Union[InputOverrideConfig, None] = None,
-):
-    if issuer is None:
-        issuer_domain = app_info.api_domain
-        issuer_path = app_info.api_base_path
-    else:
-        issuer_domain = NormalisedURLDomain(issuer)
-        issuer_path = NormalisedURLPath(issuer)
-
-    if not issuer_path.equals(app_info.api_base_path):
-        raise Exception(
-            "The path of the issuer URL must be equal to the apiBasePath. The default value is /auth"
-        )
-
-    if override is not None and not isinstance(override, InputOverrideConfig):  # type: ignore
-        raise ValueError("override must be an instance of InputOverrideConfig or None")
-
-    if override is None:
-        override = InputOverrideConfig()
-
-    return OpenIdConfig(
-        OverrideConfig(functions=override.functions, apis=override.apis),
-        issuer_domain,
-        issuer_path,
-    )
-
-
-
-
-
-

Classes

-
-
-class InputOverrideConfig -(functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None, apis: Union[Callable[[APIInterface], APIInterface], None] = None, jwt_feature: Union[JWTOverrideConfig, None] = None) -
-
-
-
- -Expand source code - -
class InputOverrideConfig:
-    def __init__(
-        self,
-        functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None,
-        apis: Union[Callable[[APIInterface], APIInterface], None] = None,
-        jwt_feature: Union[JWTOverrideConfig, None] = None,
-    ):
-        self.functions = functions
-        self.apis = apis
-        self.jwt_feature = jwt_feature
-
-
-
-class OpenIdConfig -(override: OverrideConfig, issuer_domain: NormalisedURLDomain, issuer_path: NormalisedURLPath) -
-
-
-
- -Expand source code - -
class OpenIdConfig:
-    def __init__(
-        self,
-        override: OverrideConfig,
-        issuer_domain: NormalisedURLDomain,
-        issuer_path: NormalisedURLPath,
-    ):
-        self.override = override
-        self.issuer_domain = issuer_domain
-        self.issuer_path = issuer_path
-
-
-
-class OverrideConfig -(functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None, apis: Union[Callable[[APIInterface], APIInterface], None] = None) -
-
-
-
- -Expand source code - -
class OverrideConfig:
-    def __init__(
-        self,
-        functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None,
-        apis: Union[Callable[[APIInterface], APIInterface], None] = None,
-    ):
-        self.functions = functions
-        self.apis = apis
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/passwordless/api/consume_code.html b/html/supertokens_python/recipe/passwordless/api/consume_code.html deleted file mode 100644 index 1592367ee..000000000 --- a/html/supertokens_python/recipe/passwordless/api/consume_code.html +++ /dev/null @@ -1,194 +0,0 @@ - - - - - - -supertokens_python.recipe.passwordless.api.consume_code API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.passwordless.api.consume_code

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from typing import Any, Dict
-from supertokens_python.exceptions import raise_bad_input_exception
-from supertokens_python.recipe.passwordless.interfaces import APIInterface, APIOptions
-from supertokens_python.utils import send_200_response
-
-
-async def consume_code(
-    api_implementation: APIInterface,
-    tenant_id: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-):
-    if api_implementation.disable_consume_code_post:
-        return None
-
-    body = await api_options.request.json()
-
-    if body is None:
-        raise_bad_input_exception("Please provide a JSON body")
-
-    user_input_code = None
-    device_id = None
-    link_code = None
-
-    if "preAuthSessionId" not in body:
-        raise_bad_input_exception("Please provide preAuthSessionId")
-
-    if "deviceId" in body or "userInputCode" in body:
-        if "linkCode" in body:
-            raise_bad_input_exception(
-                "Please provide one of (linkCode) or (deviceId+userInputCode) and not both"
-            )
-        if "deviceId" not in body or "userInputCode" not in body:
-            raise_bad_input_exception("Please provide both deviceId and userInputCode")
-        device_id = body["deviceId"]
-        user_input_code = body["userInputCode"]
-    elif "linkCode" in body:
-        link_code = body["linkCode"]
-    else:
-        raise_bad_input_exception(
-            "Please provide one of (linkCode) or (deviceId+userInputCode) and not both"
-        )
-
-    pre_auth_session_id = body["preAuthSessionId"]
-
-    result = await api_implementation.consume_code_post(
-        pre_auth_session_id,
-        user_input_code,
-        device_id,
-        link_code,
-        tenant_id,
-        api_options,
-        user_context,
-    )
-    return send_200_response(result.to_json(), api_options.response)
-
-
-
-
-
-
-
-

Functions

-
-
-async def consume_code(api_implementation: APIInterface, tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) -
-
-
-
- -Expand source code - -
async def consume_code(
-    api_implementation: APIInterface,
-    tenant_id: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-):
-    if api_implementation.disable_consume_code_post:
-        return None
-
-    body = await api_options.request.json()
-
-    if body is None:
-        raise_bad_input_exception("Please provide a JSON body")
-
-    user_input_code = None
-    device_id = None
-    link_code = None
-
-    if "preAuthSessionId" not in body:
-        raise_bad_input_exception("Please provide preAuthSessionId")
-
-    if "deviceId" in body or "userInputCode" in body:
-        if "linkCode" in body:
-            raise_bad_input_exception(
-                "Please provide one of (linkCode) or (deviceId+userInputCode) and not both"
-            )
-        if "deviceId" not in body or "userInputCode" not in body:
-            raise_bad_input_exception("Please provide both deviceId and userInputCode")
-        device_id = body["deviceId"]
-        user_input_code = body["userInputCode"]
-    elif "linkCode" in body:
-        link_code = body["linkCode"]
-    else:
-        raise_bad_input_exception(
-            "Please provide one of (linkCode) or (deviceId+userInputCode) and not both"
-        )
-
-    pre_auth_session_id = body["preAuthSessionId"]
-
-    result = await api_implementation.consume_code_post(
-        pre_auth_session_id,
-        user_input_code,
-        device_id,
-        link_code,
-        tenant_id,
-        api_options,
-        user_context,
-    )
-    return send_200_response(result.to_json(), api_options.response)
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/passwordless/api/create_code.html b/html/supertokens_python/recipe/passwordless/api/create_code.html deleted file mode 100644 index 23b8bff04..000000000 --- a/html/supertokens_python/recipe/passwordless/api/create_code.html +++ /dev/null @@ -1,287 +0,0 @@ - - - - - - -supertokens_python.recipe.passwordless.api.create_code API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.passwordless.api.create_code

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from typing import Union, Any, Dict
-
-import phonenumbers  # type: ignore
-from phonenumbers import format_number, parse  # type: ignore
-from supertokens_python.exceptions import raise_bad_input_exception
-from supertokens_python.recipe.passwordless.interfaces import APIInterface, APIOptions
-from supertokens_python.recipe.passwordless.utils import (
-    ContactEmailOnlyConfig,
-    ContactEmailOrPhoneConfig,
-    ContactPhoneOnlyConfig,
-)
-from supertokens_python.types import GeneralErrorResponse
-from supertokens_python.utils import send_200_response
-
-
-async def create_code(
-    api_implementation: APIInterface,
-    tenant_id: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-):
-    if api_implementation.disable_create_code_post:
-        return None
-
-    body = await api_options.request.json()
-
-    if body is None:
-        raise_bad_input_exception("Please provide a JSON body")
-
-    email: Union[str, None] = None
-    phone_number: Union[str, None] = None
-
-    if ("email" in body and "phoneNumber" in body) or (
-        "email" not in body and "phoneNumber" not in body
-    ):
-        raise_bad_input_exception("Please provide exactly one of email or phoneNumber")
-
-    if "email" not in body and isinstance(
-        api_options.config.contact_config, ContactEmailOnlyConfig
-    ):
-        raise_bad_input_exception(
-            'Please provide an email since you have set the contactMethod to "EMAIL"'
-        )
-
-    if "phoneNumber" not in body and isinstance(
-        api_options.config.contact_config, ContactPhoneOnlyConfig
-    ):
-        raise_bad_input_exception(
-            'Please provide a phoneNumber since you have set the contactMethod to "PHONE"'
-        )
-
-    if "email" in body:
-        email = body["email"]
-    if "phoneNumber" in body:
-        phone_number = body["phoneNumber"]
-
-    if email is not None and (
-        isinstance(
-            api_options.config.contact_config,
-            (ContactEmailOnlyConfig, ContactEmailOrPhoneConfig),
-        )
-    ):
-        email = email.strip()
-        validation_error = (
-            await api_options.config.contact_config.validate_email_address(
-                email, tenant_id
-            )
-        )
-        if validation_error is not None:
-            api_options.response.set_json_content(
-                GeneralErrorResponse(validation_error).to_json()
-            )
-            return api_options.response
-
-    if phone_number is not None and (
-        isinstance(
-            api_options.config.contact_config,
-            (ContactEmailOrPhoneConfig, ContactPhoneOnlyConfig),
-        )
-    ):
-        validation_error = (
-            await api_options.config.contact_config.validate_phone_number(
-                phone_number, tenant_id
-            )
-        )
-        if validation_error is not None:
-            api_options.response.set_json_content(
-                GeneralErrorResponse(validation_error).to_json()
-            )
-            return api_options.response
-        try:
-            phone_number_formatted: str = format_number(
-                parse(phone_number, None), phonenumbers.PhoneNumberFormat.E164
-            )  # type: ignore
-            phone_number = phone_number_formatted
-        except Exception:
-            phone_number = phone_number.strip()
-
-    result = await api_implementation.create_code_post(
-        email=email,
-        phone_number=phone_number,
-        tenant_id=tenant_id,
-        api_options=api_options,
-        user_context=user_context,
-    )
-    return send_200_response(result.to_json(), api_options.response)
-
-
-
-
-
-
-
-

Functions

-
-
-async def create_code(api_implementation: APIInterface, tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) -
-
-
-
- -Expand source code - -
async def create_code(
-    api_implementation: APIInterface,
-    tenant_id: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-):
-    if api_implementation.disable_create_code_post:
-        return None
-
-    body = await api_options.request.json()
-
-    if body is None:
-        raise_bad_input_exception("Please provide a JSON body")
-
-    email: Union[str, None] = None
-    phone_number: Union[str, None] = None
-
-    if ("email" in body and "phoneNumber" in body) or (
-        "email" not in body and "phoneNumber" not in body
-    ):
-        raise_bad_input_exception("Please provide exactly one of email or phoneNumber")
-
-    if "email" not in body and isinstance(
-        api_options.config.contact_config, ContactEmailOnlyConfig
-    ):
-        raise_bad_input_exception(
-            'Please provide an email since you have set the contactMethod to "EMAIL"'
-        )
-
-    if "phoneNumber" not in body and isinstance(
-        api_options.config.contact_config, ContactPhoneOnlyConfig
-    ):
-        raise_bad_input_exception(
-            'Please provide a phoneNumber since you have set the contactMethod to "PHONE"'
-        )
-
-    if "email" in body:
-        email = body["email"]
-    if "phoneNumber" in body:
-        phone_number = body["phoneNumber"]
-
-    if email is not None and (
-        isinstance(
-            api_options.config.contact_config,
-            (ContactEmailOnlyConfig, ContactEmailOrPhoneConfig),
-        )
-    ):
-        email = email.strip()
-        validation_error = (
-            await api_options.config.contact_config.validate_email_address(
-                email, tenant_id
-            )
-        )
-        if validation_error is not None:
-            api_options.response.set_json_content(
-                GeneralErrorResponse(validation_error).to_json()
-            )
-            return api_options.response
-
-    if phone_number is not None and (
-        isinstance(
-            api_options.config.contact_config,
-            (ContactEmailOrPhoneConfig, ContactPhoneOnlyConfig),
-        )
-    ):
-        validation_error = (
-            await api_options.config.contact_config.validate_phone_number(
-                phone_number, tenant_id
-            )
-        )
-        if validation_error is not None:
-            api_options.response.set_json_content(
-                GeneralErrorResponse(validation_error).to_json()
-            )
-            return api_options.response
-        try:
-            phone_number_formatted: str = format_number(
-                parse(phone_number, None), phonenumbers.PhoneNumberFormat.E164
-            )  # type: ignore
-            phone_number = phone_number_formatted
-        except Exception:
-            phone_number = phone_number.strip()
-
-    result = await api_implementation.create_code_post(
-        email=email,
-        phone_number=phone_number,
-        tenant_id=tenant_id,
-        api_options=api_options,
-        user_context=user_context,
-    )
-    return send_200_response(result.to_json(), api_options.response)
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/passwordless/api/email_exists.html b/html/supertokens_python/recipe/passwordless/api/email_exists.html deleted file mode 100644 index 86bdda666..000000000 --- a/html/supertokens_python/recipe/passwordless/api/email_exists.html +++ /dev/null @@ -1,131 +0,0 @@ - - - - - - -supertokens_python.recipe.passwordless.api.email_exists API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.passwordless.api.email_exists

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-from typing import Any, Dict
-from supertokens_python.exceptions import raise_bad_input_exception
-from supertokens_python.recipe.passwordless.interfaces import APIInterface, APIOptions
-from supertokens_python.utils import send_200_response
-
-
-async def email_exists(
-    api_implementation: APIInterface,
-    tenant_id: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-):
-    if api_implementation.disable_email_exists_get:
-        return None
-
-    email = api_options.request.get_query_param("email")
-    if email is None:
-        raise_bad_input_exception("Please provide the email as a GET param")
-
-    result = await api_implementation.email_exists_get(
-        email, tenant_id, api_options, user_context
-    )
-    return send_200_response(result.to_json(), api_options.response)
-
-
-
-
-
-
-
-

Functions

-
-
-async def email_exists(api_implementation: APIInterface, tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) -
-
-
-
- -Expand source code - -
async def email_exists(
-    api_implementation: APIInterface,
-    tenant_id: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-):
-    if api_implementation.disable_email_exists_get:
-        return None
-
-    email = api_options.request.get_query_param("email")
-    if email is None:
-        raise_bad_input_exception("Please provide the email as a GET param")
-
-    result = await api_implementation.email_exists_get(
-        email, tenant_id, api_options, user_context
-    )
-    return send_200_response(result.to_json(), api_options.response)
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/passwordless/api/implementation.html b/html/supertokens_python/recipe/passwordless/api/implementation.html deleted file mode 100644 index 3d24bf691..000000000 --- a/html/supertokens_python/recipe/passwordless/api/implementation.html +++ /dev/null @@ -1,1056 +0,0 @@ - - - - - - -supertokens_python.recipe.passwordless.api.implementation API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.passwordless.api.implementation

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from typing import Any, Dict, Union
-
-from supertokens_python.logger import log_debug_message
-from supertokens_python.recipe.passwordless.interfaces import (
-    APIInterface,
-    APIOptions,
-    ConsumeCodeExpiredUserInputCodeError,
-    ConsumeCodeIncorrectUserInputCodeError,
-    ConsumeCodePostExpiredUserInputCodeError,
-    ConsumeCodePostIncorrectUserInputCodeError,
-    ConsumeCodePostOkResult,
-    ConsumeCodePostRestartFlowError,
-    ConsumeCodeRestartFlowError,
-    CreateCodePostOkResult,
-    CreateNewCodeForDeviceOkResult,
-    CreateNewCodeForDeviceUserInputCodeAlreadyUsedError,
-    EmailExistsGetOkResult,
-    PasswordlessLoginEmailTemplateVars,
-    PhoneNumberExistsGetOkResult,
-    ResendCodePostOkResult,
-    ResendCodePostRestartFlowError,
-)
-from supertokens_python.recipe.passwordless.types import (
-    PasswordlessLoginSMSTemplateVars,
-)
-from supertokens_python.recipe.passwordless.utils import (
-    ContactEmailOnlyConfig,
-    ContactEmailOrPhoneConfig,
-    ContactPhoneOnlyConfig,
-)
-from supertokens_python.recipe.session.asyncio import create_new_session
-from supertokens_python.types import GeneralErrorResponse
-from ...emailverification import EmailVerificationRecipe
-from ...emailverification.interfaces import CreateEmailVerificationTokenOkResult
-
-
-class APIImplementation(APIInterface):
-    async def create_code_post(
-        self,
-        email: Union[str, None],
-        phone_number: Union[str, None],
-        tenant_id: str,
-        api_options: APIOptions,
-        user_context: Dict[str, Any],
-    ) -> Union[CreateCodePostOkResult, GeneralErrorResponse]:
-        user_input_code = None
-        if api_options.config.get_custom_user_input_code is not None:
-            user_input_code = await api_options.config.get_custom_user_input_code(
-                tenant_id, user_context
-            )
-        response = await api_options.recipe_implementation.create_code(
-            email, phone_number, user_input_code, tenant_id, user_context
-        )
-        magic_link = None
-        user_input_code = None
-        flow_type = api_options.config.flow_type
-        if flow_type in ("MAGIC_LINK", "USER_INPUT_CODE_AND_MAGIC_LINK"):
-            magic_link = (
-                api_options.app_info.get_origin(
-                    api_options.request, user_context
-                ).get_as_string_dangerous()
-                + api_options.app_info.website_base_path.get_as_string_dangerous()
-                + "/verify"
-                + "?preAuthSessionId="
-                + response.pre_auth_session_id
-                + "&tenantId="
-                + tenant_id
-                + "#"
-                + response.link_code
-            )
-        if flow_type in ("USER_INPUT_CODE", "USER_INPUT_CODE_AND_MAGIC_LINK"):
-            user_input_code = response.user_input_code
-
-        if isinstance(api_options.config.contact_config, ContactEmailOnlyConfig) or (
-            isinstance(api_options.config.contact_config, ContactEmailOrPhoneConfig)
-            and email is not None
-        ):
-            if email is None:
-                raise Exception("Should never come here")
-
-            log_debug_message("Sending passwordless login email to %s", email)
-            passwordless_email_delivery_input = PasswordlessLoginEmailTemplateVars(
-                email=email,
-                user_input_code=user_input_code,
-                url_with_link_code=magic_link,
-                code_life_time=response.code_life_time,
-                pre_auth_session_id=response.pre_auth_session_id,
-                tenant_id=tenant_id,
-            )
-            await api_options.email_delivery.ingredient_interface_impl.send_email(
-                passwordless_email_delivery_input, user_context
-            )
-        elif isinstance(
-            api_options.config.contact_config,
-            (ContactEmailOrPhoneConfig, ContactPhoneOnlyConfig),
-        ):
-            if phone_number is None:
-                raise Exception("Should never come here")
-            log_debug_message("Sending passwordless login SMS to %s", phone_number)
-            sms_input = PasswordlessLoginSMSTemplateVars(
-                phone_number=phone_number,
-                user_input_code=user_input_code,
-                url_with_link_code=magic_link,
-                code_life_time=response.code_life_time,
-                pre_auth_session_id=response.pre_auth_session_id,
-                tenant_id=tenant_id,
-            )
-            await api_options.sms_delivery.ingredient_interface_impl.send_sms(
-                sms_input, user_context
-            )
-
-        return CreateCodePostOkResult(
-            response.device_id, response.pre_auth_session_id, flow_type
-        )
-
-    async def resend_code_post(
-        self,
-        device_id: str,
-        pre_auth_session_id: str,
-        tenant_id: str,
-        api_options: APIOptions,
-        user_context: Dict[str, Any],
-    ) -> Union[
-        ResendCodePostOkResult, ResendCodePostRestartFlowError, GeneralErrorResponse
-    ]:
-        device_info = await api_options.recipe_implementation.list_codes_by_device_id(
-            device_id=device_id, tenant_id=tenant_id, user_context=user_context
-        )
-        if device_info is None:
-            return ResendCodePostRestartFlowError()
-        if (
-            isinstance(api_options.config.contact_config, ContactEmailOnlyConfig)
-            and device_info.email is None
-        ) or (
-            isinstance(api_options.config.contact_config, ContactPhoneOnlyConfig)
-            and device_info.phone_number is None
-        ):
-            return ResendCodePostRestartFlowError()
-        number_of_tries_to_create_new_code = 0
-        while True:
-            number_of_tries_to_create_new_code += 1
-            user_input_code = None
-            if api_options.config.get_custom_user_input_code is not None:
-                user_input_code = await api_options.config.get_custom_user_input_code(
-                    tenant_id, user_context
-                )
-            response = (
-                await api_options.recipe_implementation.create_new_code_for_device(
-                    device_id=device_id,
-                    user_input_code=user_input_code,
-                    tenant_id=tenant_id,
-                    user_context=user_context,
-                )
-            )
-            if isinstance(
-                response, CreateNewCodeForDeviceUserInputCodeAlreadyUsedError
-            ):
-                if number_of_tries_to_create_new_code >= 3:
-                    return GeneralErrorResponse(
-                        "Failed to generate a one time code. Please try again"
-                    )
-                continue
-
-            if isinstance(response, CreateNewCodeForDeviceOkResult):
-                magic_link = None
-                user_input_code = None
-                flow_type = api_options.config.flow_type
-                if flow_type in ("MAGIC_LINK", "USER_INPUT_CODE_AND_MAGIC_LINK"):
-                    magic_link = (
-                        api_options.app_info.get_origin(
-                            api_options.request, user_context
-                        ).get_as_string_dangerous()
-                        + api_options.app_info.website_base_path.get_as_string_dangerous()
-                        + "/verify"
-                        + "?preAuthSessionId="
-                        + response.pre_auth_session_id
-                        + "&tenantId="
-                        + tenant_id
-                        + "#"
-                        + response.link_code
-                    )
-                if flow_type in ("USER_INPUT_CODE", "USER_INPUT_CODE_AND_MAGIC_LINK"):
-                    user_input_code = response.user_input_code
-
-                if isinstance(
-                    api_options.config.contact_config, ContactEmailOnlyConfig
-                ) or (
-                    isinstance(
-                        api_options.config.contact_config, ContactEmailOrPhoneConfig
-                    )
-                    and device_info.email is not None
-                ):
-                    if device_info.email is None:
-                        raise Exception("Should never come here")
-
-                    log_debug_message(
-                        "Sending passwordless login email to %s", device_info.email
-                    )
-                    passwordless_email_delivery_input = (
-                        PasswordlessLoginEmailTemplateVars(
-                            email=device_info.email,
-                            user_input_code=user_input_code,
-                            url_with_link_code=magic_link,
-                            code_life_time=response.code_life_time,
-                            pre_auth_session_id=response.pre_auth_session_id,
-                            tenant_id=tenant_id,
-                        )
-                    )
-                    await api_options.email_delivery.ingredient_interface_impl.send_email(
-                        passwordless_email_delivery_input, user_context
-                    )
-                elif isinstance(
-                    api_options.config.contact_config,
-                    (ContactEmailOrPhoneConfig, ContactPhoneOnlyConfig),
-                ):
-                    if device_info.phone_number is None:
-                        raise Exception("Should never come here")
-                    log_debug_message(
-                        "Sending passwordless login SMS to %s", device_info.phone_number
-                    )
-                    sms_input = PasswordlessLoginSMSTemplateVars(
-                        phone_number=device_info.phone_number,
-                        user_input_code=user_input_code,
-                        url_with_link_code=magic_link,
-                        code_life_time=response.code_life_time,
-                        pre_auth_session_id=response.pre_auth_session_id,
-                        tenant_id=tenant_id,
-                    )
-                    await api_options.sms_delivery.ingredient_interface_impl.send_sms(
-                        sms_input, user_context
-                    )
-                return ResendCodePostOkResult()
-            return ResendCodePostRestartFlowError()
-
-    async def consume_code_post(
-        self,
-        pre_auth_session_id: str,
-        user_input_code: Union[str, None],
-        device_id: Union[str, None],
-        link_code: Union[str, None],
-        tenant_id: str,
-        api_options: APIOptions,
-        user_context: Dict[str, Any],
-    ) -> Union[
-        ConsumeCodePostOkResult,
-        ConsumeCodePostRestartFlowError,
-        ConsumeCodePostIncorrectUserInputCodeError,
-        ConsumeCodePostExpiredUserInputCodeError,
-        GeneralErrorResponse,
-    ]:
-        response = await api_options.recipe_implementation.consume_code(
-            pre_auth_session_id=pre_auth_session_id,
-            user_input_code=user_input_code,
-            device_id=device_id,
-            link_code=link_code,
-            tenant_id=tenant_id,
-            user_context=user_context,
-        )
-
-        if isinstance(response, ConsumeCodeExpiredUserInputCodeError):
-            return ConsumeCodePostExpiredUserInputCodeError(
-                failed_code_input_attempt_count=response.failed_code_input_attempt_count,
-                maximum_code_input_attempts=response.maximum_code_input_attempts,
-            )
-        if isinstance(response, ConsumeCodeIncorrectUserInputCodeError):
-            return ConsumeCodePostIncorrectUserInputCodeError(
-                failed_code_input_attempt_count=response.failed_code_input_attempt_count,
-                maximum_code_input_attempts=response.maximum_code_input_attempts,
-            )
-        if isinstance(response, ConsumeCodeRestartFlowError):
-            return ConsumeCodePostRestartFlowError()
-
-        user = response.user
-
-        if user.email is not None:
-            ev_instance = EmailVerificationRecipe.get_instance_optional()
-            if ev_instance is not None:
-                token_response = await ev_instance.recipe_implementation.create_email_verification_token(
-                    user.user_id, user.email, tenant_id, user_context
-                )
-
-                if isinstance(token_response, CreateEmailVerificationTokenOkResult):
-                    await ev_instance.recipe_implementation.verify_email_using_token(
-                        token_response.token, tenant_id, user_context
-                    )
-
-        session = await create_new_session(
-            request=api_options.request,
-            tenant_id=tenant_id,
-            user_id=user.user_id,
-            access_token_payload={},
-            session_data_in_database={},
-            user_context=user_context,
-        )
-
-        return ConsumeCodePostOkResult(
-            created_new_user=response.created_new_user,
-            user=response.user,
-            session=session,
-        )
-
-    async def email_exists_get(
-        self,
-        email: str,
-        tenant_id: str,
-        api_options: APIOptions,
-        user_context: Dict[str, Any],
-    ) -> Union[EmailExistsGetOkResult, GeneralErrorResponse]:
-        response = await api_options.recipe_implementation.get_user_by_email(
-            email, tenant_id, user_context
-        )
-        return EmailExistsGetOkResult(exists=response is not None)
-
-    async def phone_number_exists_get(
-        self,
-        phone_number: str,
-        tenant_id: str,
-        api_options: APIOptions,
-        user_context: Dict[str, Any],
-    ) -> Union[PhoneNumberExistsGetOkResult, GeneralErrorResponse]:
-        response = await api_options.recipe_implementation.get_user_by_phone_number(
-            phone_number, tenant_id, user_context
-        )
-        return PhoneNumberExistsGetOkResult(exists=response is not None)
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class APIImplementation -
-
-
-
- -Expand source code - -
class APIImplementation(APIInterface):
-    async def create_code_post(
-        self,
-        email: Union[str, None],
-        phone_number: Union[str, None],
-        tenant_id: str,
-        api_options: APIOptions,
-        user_context: Dict[str, Any],
-    ) -> Union[CreateCodePostOkResult, GeneralErrorResponse]:
-        user_input_code = None
-        if api_options.config.get_custom_user_input_code is not None:
-            user_input_code = await api_options.config.get_custom_user_input_code(
-                tenant_id, user_context
-            )
-        response = await api_options.recipe_implementation.create_code(
-            email, phone_number, user_input_code, tenant_id, user_context
-        )
-        magic_link = None
-        user_input_code = None
-        flow_type = api_options.config.flow_type
-        if flow_type in ("MAGIC_LINK", "USER_INPUT_CODE_AND_MAGIC_LINK"):
-            magic_link = (
-                api_options.app_info.get_origin(
-                    api_options.request, user_context
-                ).get_as_string_dangerous()
-                + api_options.app_info.website_base_path.get_as_string_dangerous()
-                + "/verify"
-                + "?preAuthSessionId="
-                + response.pre_auth_session_id
-                + "&tenantId="
-                + tenant_id
-                + "#"
-                + response.link_code
-            )
-        if flow_type in ("USER_INPUT_CODE", "USER_INPUT_CODE_AND_MAGIC_LINK"):
-            user_input_code = response.user_input_code
-
-        if isinstance(api_options.config.contact_config, ContactEmailOnlyConfig) or (
-            isinstance(api_options.config.contact_config, ContactEmailOrPhoneConfig)
-            and email is not None
-        ):
-            if email is None:
-                raise Exception("Should never come here")
-
-            log_debug_message("Sending passwordless login email to %s", email)
-            passwordless_email_delivery_input = PasswordlessLoginEmailTemplateVars(
-                email=email,
-                user_input_code=user_input_code,
-                url_with_link_code=magic_link,
-                code_life_time=response.code_life_time,
-                pre_auth_session_id=response.pre_auth_session_id,
-                tenant_id=tenant_id,
-            )
-            await api_options.email_delivery.ingredient_interface_impl.send_email(
-                passwordless_email_delivery_input, user_context
-            )
-        elif isinstance(
-            api_options.config.contact_config,
-            (ContactEmailOrPhoneConfig, ContactPhoneOnlyConfig),
-        ):
-            if phone_number is None:
-                raise Exception("Should never come here")
-            log_debug_message("Sending passwordless login SMS to %s", phone_number)
-            sms_input = PasswordlessLoginSMSTemplateVars(
-                phone_number=phone_number,
-                user_input_code=user_input_code,
-                url_with_link_code=magic_link,
-                code_life_time=response.code_life_time,
-                pre_auth_session_id=response.pre_auth_session_id,
-                tenant_id=tenant_id,
-            )
-            await api_options.sms_delivery.ingredient_interface_impl.send_sms(
-                sms_input, user_context
-            )
-
-        return CreateCodePostOkResult(
-            response.device_id, response.pre_auth_session_id, flow_type
-        )
-
-    async def resend_code_post(
-        self,
-        device_id: str,
-        pre_auth_session_id: str,
-        tenant_id: str,
-        api_options: APIOptions,
-        user_context: Dict[str, Any],
-    ) -> Union[
-        ResendCodePostOkResult, ResendCodePostRestartFlowError, GeneralErrorResponse
-    ]:
-        device_info = await api_options.recipe_implementation.list_codes_by_device_id(
-            device_id=device_id, tenant_id=tenant_id, user_context=user_context
-        )
-        if device_info is None:
-            return ResendCodePostRestartFlowError()
-        if (
-            isinstance(api_options.config.contact_config, ContactEmailOnlyConfig)
-            and device_info.email is None
-        ) or (
-            isinstance(api_options.config.contact_config, ContactPhoneOnlyConfig)
-            and device_info.phone_number is None
-        ):
-            return ResendCodePostRestartFlowError()
-        number_of_tries_to_create_new_code = 0
-        while True:
-            number_of_tries_to_create_new_code += 1
-            user_input_code = None
-            if api_options.config.get_custom_user_input_code is not None:
-                user_input_code = await api_options.config.get_custom_user_input_code(
-                    tenant_id, user_context
-                )
-            response = (
-                await api_options.recipe_implementation.create_new_code_for_device(
-                    device_id=device_id,
-                    user_input_code=user_input_code,
-                    tenant_id=tenant_id,
-                    user_context=user_context,
-                )
-            )
-            if isinstance(
-                response, CreateNewCodeForDeviceUserInputCodeAlreadyUsedError
-            ):
-                if number_of_tries_to_create_new_code >= 3:
-                    return GeneralErrorResponse(
-                        "Failed to generate a one time code. Please try again"
-                    )
-                continue
-
-            if isinstance(response, CreateNewCodeForDeviceOkResult):
-                magic_link = None
-                user_input_code = None
-                flow_type = api_options.config.flow_type
-                if flow_type in ("MAGIC_LINK", "USER_INPUT_CODE_AND_MAGIC_LINK"):
-                    magic_link = (
-                        api_options.app_info.get_origin(
-                            api_options.request, user_context
-                        ).get_as_string_dangerous()
-                        + api_options.app_info.website_base_path.get_as_string_dangerous()
-                        + "/verify"
-                        + "?preAuthSessionId="
-                        + response.pre_auth_session_id
-                        + "&tenantId="
-                        + tenant_id
-                        + "#"
-                        + response.link_code
-                    )
-                if flow_type in ("USER_INPUT_CODE", "USER_INPUT_CODE_AND_MAGIC_LINK"):
-                    user_input_code = response.user_input_code
-
-                if isinstance(
-                    api_options.config.contact_config, ContactEmailOnlyConfig
-                ) or (
-                    isinstance(
-                        api_options.config.contact_config, ContactEmailOrPhoneConfig
-                    )
-                    and device_info.email is not None
-                ):
-                    if device_info.email is None:
-                        raise Exception("Should never come here")
-
-                    log_debug_message(
-                        "Sending passwordless login email to %s", device_info.email
-                    )
-                    passwordless_email_delivery_input = (
-                        PasswordlessLoginEmailTemplateVars(
-                            email=device_info.email,
-                            user_input_code=user_input_code,
-                            url_with_link_code=magic_link,
-                            code_life_time=response.code_life_time,
-                            pre_auth_session_id=response.pre_auth_session_id,
-                            tenant_id=tenant_id,
-                        )
-                    )
-                    await api_options.email_delivery.ingredient_interface_impl.send_email(
-                        passwordless_email_delivery_input, user_context
-                    )
-                elif isinstance(
-                    api_options.config.contact_config,
-                    (ContactEmailOrPhoneConfig, ContactPhoneOnlyConfig),
-                ):
-                    if device_info.phone_number is None:
-                        raise Exception("Should never come here")
-                    log_debug_message(
-                        "Sending passwordless login SMS to %s", device_info.phone_number
-                    )
-                    sms_input = PasswordlessLoginSMSTemplateVars(
-                        phone_number=device_info.phone_number,
-                        user_input_code=user_input_code,
-                        url_with_link_code=magic_link,
-                        code_life_time=response.code_life_time,
-                        pre_auth_session_id=response.pre_auth_session_id,
-                        tenant_id=tenant_id,
-                    )
-                    await api_options.sms_delivery.ingredient_interface_impl.send_sms(
-                        sms_input, user_context
-                    )
-                return ResendCodePostOkResult()
-            return ResendCodePostRestartFlowError()
-
-    async def consume_code_post(
-        self,
-        pre_auth_session_id: str,
-        user_input_code: Union[str, None],
-        device_id: Union[str, None],
-        link_code: Union[str, None],
-        tenant_id: str,
-        api_options: APIOptions,
-        user_context: Dict[str, Any],
-    ) -> Union[
-        ConsumeCodePostOkResult,
-        ConsumeCodePostRestartFlowError,
-        ConsumeCodePostIncorrectUserInputCodeError,
-        ConsumeCodePostExpiredUserInputCodeError,
-        GeneralErrorResponse,
-    ]:
-        response = await api_options.recipe_implementation.consume_code(
-            pre_auth_session_id=pre_auth_session_id,
-            user_input_code=user_input_code,
-            device_id=device_id,
-            link_code=link_code,
-            tenant_id=tenant_id,
-            user_context=user_context,
-        )
-
-        if isinstance(response, ConsumeCodeExpiredUserInputCodeError):
-            return ConsumeCodePostExpiredUserInputCodeError(
-                failed_code_input_attempt_count=response.failed_code_input_attempt_count,
-                maximum_code_input_attempts=response.maximum_code_input_attempts,
-            )
-        if isinstance(response, ConsumeCodeIncorrectUserInputCodeError):
-            return ConsumeCodePostIncorrectUserInputCodeError(
-                failed_code_input_attempt_count=response.failed_code_input_attempt_count,
-                maximum_code_input_attempts=response.maximum_code_input_attempts,
-            )
-        if isinstance(response, ConsumeCodeRestartFlowError):
-            return ConsumeCodePostRestartFlowError()
-
-        user = response.user
-
-        if user.email is not None:
-            ev_instance = EmailVerificationRecipe.get_instance_optional()
-            if ev_instance is not None:
-                token_response = await ev_instance.recipe_implementation.create_email_verification_token(
-                    user.user_id, user.email, tenant_id, user_context
-                )
-
-                if isinstance(token_response, CreateEmailVerificationTokenOkResult):
-                    await ev_instance.recipe_implementation.verify_email_using_token(
-                        token_response.token, tenant_id, user_context
-                    )
-
-        session = await create_new_session(
-            request=api_options.request,
-            tenant_id=tenant_id,
-            user_id=user.user_id,
-            access_token_payload={},
-            session_data_in_database={},
-            user_context=user_context,
-        )
-
-        return ConsumeCodePostOkResult(
-            created_new_user=response.created_new_user,
-            user=response.user,
-            session=session,
-        )
-
-    async def email_exists_get(
-        self,
-        email: str,
-        tenant_id: str,
-        api_options: APIOptions,
-        user_context: Dict[str, Any],
-    ) -> Union[EmailExistsGetOkResult, GeneralErrorResponse]:
-        response = await api_options.recipe_implementation.get_user_by_email(
-            email, tenant_id, user_context
-        )
-        return EmailExistsGetOkResult(exists=response is not None)
-
-    async def phone_number_exists_get(
-        self,
-        phone_number: str,
-        tenant_id: str,
-        api_options: APIOptions,
-        user_context: Dict[str, Any],
-    ) -> Union[PhoneNumberExistsGetOkResult, GeneralErrorResponse]:
-        response = await api_options.recipe_implementation.get_user_by_phone_number(
-            phone_number, tenant_id, user_context
-        )
-        return PhoneNumberExistsGetOkResult(exists=response is not None)
-
-

Ancestors

- -

Methods

-
-
-async def consume_code_post(self, pre_auth_session_id: str, user_input_code: Optional[str], device_id: Optional[str], link_code: Optional[str], tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[ConsumeCodePostOkResultConsumeCodePostRestartFlowErrorConsumeCodePostIncorrectUserInputCodeErrorConsumeCodePostExpiredUserInputCodeErrorGeneralErrorResponse] -
-
-
-
- -Expand source code - -
async def consume_code_post(
-    self,
-    pre_auth_session_id: str,
-    user_input_code: Union[str, None],
-    device_id: Union[str, None],
-    link_code: Union[str, None],
-    tenant_id: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-) -> Union[
-    ConsumeCodePostOkResult,
-    ConsumeCodePostRestartFlowError,
-    ConsumeCodePostIncorrectUserInputCodeError,
-    ConsumeCodePostExpiredUserInputCodeError,
-    GeneralErrorResponse,
-]:
-    response = await api_options.recipe_implementation.consume_code(
-        pre_auth_session_id=pre_auth_session_id,
-        user_input_code=user_input_code,
-        device_id=device_id,
-        link_code=link_code,
-        tenant_id=tenant_id,
-        user_context=user_context,
-    )
-
-    if isinstance(response, ConsumeCodeExpiredUserInputCodeError):
-        return ConsumeCodePostExpiredUserInputCodeError(
-            failed_code_input_attempt_count=response.failed_code_input_attempt_count,
-            maximum_code_input_attempts=response.maximum_code_input_attempts,
-        )
-    if isinstance(response, ConsumeCodeIncorrectUserInputCodeError):
-        return ConsumeCodePostIncorrectUserInputCodeError(
-            failed_code_input_attempt_count=response.failed_code_input_attempt_count,
-            maximum_code_input_attempts=response.maximum_code_input_attempts,
-        )
-    if isinstance(response, ConsumeCodeRestartFlowError):
-        return ConsumeCodePostRestartFlowError()
-
-    user = response.user
-
-    if user.email is not None:
-        ev_instance = EmailVerificationRecipe.get_instance_optional()
-        if ev_instance is not None:
-            token_response = await ev_instance.recipe_implementation.create_email_verification_token(
-                user.user_id, user.email, tenant_id, user_context
-            )
-
-            if isinstance(token_response, CreateEmailVerificationTokenOkResult):
-                await ev_instance.recipe_implementation.verify_email_using_token(
-                    token_response.token, tenant_id, user_context
-                )
-
-    session = await create_new_session(
-        request=api_options.request,
-        tenant_id=tenant_id,
-        user_id=user.user_id,
-        access_token_payload={},
-        session_data_in_database={},
-        user_context=user_context,
-    )
-
-    return ConsumeCodePostOkResult(
-        created_new_user=response.created_new_user,
-        user=response.user,
-        session=session,
-    )
-
-
-
-async def create_code_post(self, email: Optional[str], phone_number: Optional[str], tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[CreateCodePostOkResultGeneralErrorResponse] -
-
-
-
- -Expand source code - -
async def create_code_post(
-    self,
-    email: Union[str, None],
-    phone_number: Union[str, None],
-    tenant_id: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-) -> Union[CreateCodePostOkResult, GeneralErrorResponse]:
-    user_input_code = None
-    if api_options.config.get_custom_user_input_code is not None:
-        user_input_code = await api_options.config.get_custom_user_input_code(
-            tenant_id, user_context
-        )
-    response = await api_options.recipe_implementation.create_code(
-        email, phone_number, user_input_code, tenant_id, user_context
-    )
-    magic_link = None
-    user_input_code = None
-    flow_type = api_options.config.flow_type
-    if flow_type in ("MAGIC_LINK", "USER_INPUT_CODE_AND_MAGIC_LINK"):
-        magic_link = (
-            api_options.app_info.get_origin(
-                api_options.request, user_context
-            ).get_as_string_dangerous()
-            + api_options.app_info.website_base_path.get_as_string_dangerous()
-            + "/verify"
-            + "?preAuthSessionId="
-            + response.pre_auth_session_id
-            + "&tenantId="
-            + tenant_id
-            + "#"
-            + response.link_code
-        )
-    if flow_type in ("USER_INPUT_CODE", "USER_INPUT_CODE_AND_MAGIC_LINK"):
-        user_input_code = response.user_input_code
-
-    if isinstance(api_options.config.contact_config, ContactEmailOnlyConfig) or (
-        isinstance(api_options.config.contact_config, ContactEmailOrPhoneConfig)
-        and email is not None
-    ):
-        if email is None:
-            raise Exception("Should never come here")
-
-        log_debug_message("Sending passwordless login email to %s", email)
-        passwordless_email_delivery_input = PasswordlessLoginEmailTemplateVars(
-            email=email,
-            user_input_code=user_input_code,
-            url_with_link_code=magic_link,
-            code_life_time=response.code_life_time,
-            pre_auth_session_id=response.pre_auth_session_id,
-            tenant_id=tenant_id,
-        )
-        await api_options.email_delivery.ingredient_interface_impl.send_email(
-            passwordless_email_delivery_input, user_context
-        )
-    elif isinstance(
-        api_options.config.contact_config,
-        (ContactEmailOrPhoneConfig, ContactPhoneOnlyConfig),
-    ):
-        if phone_number is None:
-            raise Exception("Should never come here")
-        log_debug_message("Sending passwordless login SMS to %s", phone_number)
-        sms_input = PasswordlessLoginSMSTemplateVars(
-            phone_number=phone_number,
-            user_input_code=user_input_code,
-            url_with_link_code=magic_link,
-            code_life_time=response.code_life_time,
-            pre_auth_session_id=response.pre_auth_session_id,
-            tenant_id=tenant_id,
-        )
-        await api_options.sms_delivery.ingredient_interface_impl.send_sms(
-            sms_input, user_context
-        )
-
-    return CreateCodePostOkResult(
-        response.device_id, response.pre_auth_session_id, flow_type
-    )
-
-
-
-async def email_exists_get(self, email: str, tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[EmailExistsGetOkResultGeneralErrorResponse] -
-
-
-
- -Expand source code - -
async def email_exists_get(
-    self,
-    email: str,
-    tenant_id: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-) -> Union[EmailExistsGetOkResult, GeneralErrorResponse]:
-    response = await api_options.recipe_implementation.get_user_by_email(
-        email, tenant_id, user_context
-    )
-    return EmailExistsGetOkResult(exists=response is not None)
-
-
-
-async def phone_number_exists_get(self, phone_number: str, tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[PhoneNumberExistsGetOkResultGeneralErrorResponse] -
-
-
-
- -Expand source code - -
async def phone_number_exists_get(
-    self,
-    phone_number: str,
-    tenant_id: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-) -> Union[PhoneNumberExistsGetOkResult, GeneralErrorResponse]:
-    response = await api_options.recipe_implementation.get_user_by_phone_number(
-        phone_number, tenant_id, user_context
-    )
-    return PhoneNumberExistsGetOkResult(exists=response is not None)
-
-
-
-async def resend_code_post(self, device_id: str, pre_auth_session_id: str, tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[ResendCodePostOkResultResendCodePostRestartFlowErrorGeneralErrorResponse] -
-
-
-
- -Expand source code - -
async def resend_code_post(
-    self,
-    device_id: str,
-    pre_auth_session_id: str,
-    tenant_id: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-) -> Union[
-    ResendCodePostOkResult, ResendCodePostRestartFlowError, GeneralErrorResponse
-]:
-    device_info = await api_options.recipe_implementation.list_codes_by_device_id(
-        device_id=device_id, tenant_id=tenant_id, user_context=user_context
-    )
-    if device_info is None:
-        return ResendCodePostRestartFlowError()
-    if (
-        isinstance(api_options.config.contact_config, ContactEmailOnlyConfig)
-        and device_info.email is None
-    ) or (
-        isinstance(api_options.config.contact_config, ContactPhoneOnlyConfig)
-        and device_info.phone_number is None
-    ):
-        return ResendCodePostRestartFlowError()
-    number_of_tries_to_create_new_code = 0
-    while True:
-        number_of_tries_to_create_new_code += 1
-        user_input_code = None
-        if api_options.config.get_custom_user_input_code is not None:
-            user_input_code = await api_options.config.get_custom_user_input_code(
-                tenant_id, user_context
-            )
-        response = (
-            await api_options.recipe_implementation.create_new_code_for_device(
-                device_id=device_id,
-                user_input_code=user_input_code,
-                tenant_id=tenant_id,
-                user_context=user_context,
-            )
-        )
-        if isinstance(
-            response, CreateNewCodeForDeviceUserInputCodeAlreadyUsedError
-        ):
-            if number_of_tries_to_create_new_code >= 3:
-                return GeneralErrorResponse(
-                    "Failed to generate a one time code. Please try again"
-                )
-            continue
-
-        if isinstance(response, CreateNewCodeForDeviceOkResult):
-            magic_link = None
-            user_input_code = None
-            flow_type = api_options.config.flow_type
-            if flow_type in ("MAGIC_LINK", "USER_INPUT_CODE_AND_MAGIC_LINK"):
-                magic_link = (
-                    api_options.app_info.get_origin(
-                        api_options.request, user_context
-                    ).get_as_string_dangerous()
-                    + api_options.app_info.website_base_path.get_as_string_dangerous()
-                    + "/verify"
-                    + "?preAuthSessionId="
-                    + response.pre_auth_session_id
-                    + "&tenantId="
-                    + tenant_id
-                    + "#"
-                    + response.link_code
-                )
-            if flow_type in ("USER_INPUT_CODE", "USER_INPUT_CODE_AND_MAGIC_LINK"):
-                user_input_code = response.user_input_code
-
-            if isinstance(
-                api_options.config.contact_config, ContactEmailOnlyConfig
-            ) or (
-                isinstance(
-                    api_options.config.contact_config, ContactEmailOrPhoneConfig
-                )
-                and device_info.email is not None
-            ):
-                if device_info.email is None:
-                    raise Exception("Should never come here")
-
-                log_debug_message(
-                    "Sending passwordless login email to %s", device_info.email
-                )
-                passwordless_email_delivery_input = (
-                    PasswordlessLoginEmailTemplateVars(
-                        email=device_info.email,
-                        user_input_code=user_input_code,
-                        url_with_link_code=magic_link,
-                        code_life_time=response.code_life_time,
-                        pre_auth_session_id=response.pre_auth_session_id,
-                        tenant_id=tenant_id,
-                    )
-                )
-                await api_options.email_delivery.ingredient_interface_impl.send_email(
-                    passwordless_email_delivery_input, user_context
-                )
-            elif isinstance(
-                api_options.config.contact_config,
-                (ContactEmailOrPhoneConfig, ContactPhoneOnlyConfig),
-            ):
-                if device_info.phone_number is None:
-                    raise Exception("Should never come here")
-                log_debug_message(
-                    "Sending passwordless login SMS to %s", device_info.phone_number
-                )
-                sms_input = PasswordlessLoginSMSTemplateVars(
-                    phone_number=device_info.phone_number,
-                    user_input_code=user_input_code,
-                    url_with_link_code=magic_link,
-                    code_life_time=response.code_life_time,
-                    pre_auth_session_id=response.pre_auth_session_id,
-                    tenant_id=tenant_id,
-                )
-                await api_options.sms_delivery.ingredient_interface_impl.send_sms(
-                    sms_input, user_context
-                )
-            return ResendCodePostOkResult()
-        return ResendCodePostRestartFlowError()
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/passwordless/api/index.html b/html/supertokens_python/recipe/passwordless/api/index.html deleted file mode 100644 index 2ba44e4dd..000000000 --- a/html/supertokens_python/recipe/passwordless/api/index.html +++ /dev/null @@ -1,114 +0,0 @@ - - - - - - -supertokens_python.recipe.passwordless.api API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.passwordless.api

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from .consume_code import consume_code  # type: ignore
-from .create_code import create_code  # type: ignore
-from .email_exists import email_exists  # type: ignore
-from .phone_number_exists import phone_number_exists  # type: ignore
-from .resend_code import resend_code  # type: ignore
-
-
-
-

Sub-modules

-
-
supertokens_python.recipe.passwordless.api.consume_code
-
-
-
-
supertokens_python.recipe.passwordless.api.create_code
-
-
-
-
supertokens_python.recipe.passwordless.api.email_exists
-
-
-
-
supertokens_python.recipe.passwordless.api.implementation
-
-
-
-
supertokens_python.recipe.passwordless.api.phone_number_exists
-
-
-
-
supertokens_python.recipe.passwordless.api.resend_code
-
-
-
-
-
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/passwordless/api/phone_number_exists.html b/html/supertokens_python/recipe/passwordless/api/phone_number_exists.html deleted file mode 100644 index 6caf5508e..000000000 --- a/html/supertokens_python/recipe/passwordless/api/phone_number_exists.html +++ /dev/null @@ -1,130 +0,0 @@ - - - - - - -supertokens_python.recipe.passwordless.api.phone_number_exists API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.passwordless.api.phone_number_exists

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from typing import Any, Dict
-from supertokens_python.exceptions import raise_bad_input_exception
-from supertokens_python.recipe.passwordless.interfaces import APIInterface, APIOptions
-from supertokens_python.utils import send_200_response
-
-
-async def phone_number_exists(
-    api_implementation: APIInterface,
-    tenant_id: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-):
-    if api_implementation.disable_phone_number_exists_get:
-        return None
-
-    phone_number = api_options.request.get_query_param("phoneNumber")
-    if phone_number is None:
-        raise_bad_input_exception("Please provide the phoneNumber as a GET param")
-
-    result = await api_implementation.phone_number_exists_get(
-        phone_number, tenant_id, api_options, user_context
-    )
-    return send_200_response(result.to_json(), api_options.response)
-
-
-
-
-
-
-
-

Functions

-
-
-async def phone_number_exists(api_implementation: APIInterface, tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) -
-
-
-
- -Expand source code - -
async def phone_number_exists(
-    api_implementation: APIInterface,
-    tenant_id: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-):
-    if api_implementation.disable_phone_number_exists_get:
-        return None
-
-    phone_number = api_options.request.get_query_param("phoneNumber")
-    if phone_number is None:
-        raise_bad_input_exception("Please provide the phoneNumber as a GET param")
-
-    result = await api_implementation.phone_number_exists_get(
-        phone_number, tenant_id, api_options, user_context
-    )
-    return send_200_response(result.to_json(), api_options.response)
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/passwordless/api/resend_code.html b/html/supertokens_python/recipe/passwordless/api/resend_code.html deleted file mode 100644 index 25178d232..000000000 --- a/html/supertokens_python/recipe/passwordless/api/resend_code.html +++ /dev/null @@ -1,148 +0,0 @@ - - - - - - -supertokens_python.recipe.passwordless.api.resend_code API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.passwordless.api.resend_code

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from typing import Any, Dict
-from supertokens_python.exceptions import raise_bad_input_exception
-from supertokens_python.recipe.passwordless.interfaces import APIInterface, APIOptions
-from supertokens_python.utils import send_200_response
-
-
-async def resend_code(
-    api_implementation: APIInterface,
-    tenant_id: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-):
-    if api_implementation.disable_resend_code_post:
-        return None
-    body = await api_options.request.json()
-
-    if body is None:
-        raise_bad_input_exception("Please provide a JSON body")
-
-    if "preAuthSessionId" not in body:
-        raise_bad_input_exception("Please provide preAuthSessionId")
-
-    if "deviceId" not in body:
-        raise_bad_input_exception("Please provide deviceId")
-
-    pre_auth_session_id = body["preAuthSessionId"]
-    device_id = body["deviceId"]
-
-    result = await api_implementation.resend_code_post(
-        device_id, pre_auth_session_id, tenant_id, api_options, user_context
-    )
-    return send_200_response(result.to_json(), api_options.response)
-
-
-
-
-
-
-
-

Functions

-
-
-async def resend_code(api_implementation: APIInterface, tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) -
-
-
-
- -Expand source code - -
async def resend_code(
-    api_implementation: APIInterface,
-    tenant_id: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-):
-    if api_implementation.disable_resend_code_post:
-        return None
-    body = await api_options.request.json()
-
-    if body is None:
-        raise_bad_input_exception("Please provide a JSON body")
-
-    if "preAuthSessionId" not in body:
-        raise_bad_input_exception("Please provide preAuthSessionId")
-
-    if "deviceId" not in body:
-        raise_bad_input_exception("Please provide deviceId")
-
-    pre_auth_session_id = body["preAuthSessionId"]
-    device_id = body["deviceId"]
-
-    result = await api_implementation.resend_code_post(
-        device_id, pre_auth_session_id, tenant_id, api_options, user_context
-    )
-    return send_200_response(result.to_json(), api_options.response)
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/passwordless/asyncio/index.html b/html/supertokens_python/recipe/passwordless/asyncio/index.html deleted file mode 100644 index 6b5c4df05..000000000 --- a/html/supertokens_python/recipe/passwordless/asyncio/index.html +++ /dev/null @@ -1,848 +0,0 @@ - - - - - - -supertokens_python.recipe.passwordless.asyncio API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.passwordless.asyncio

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from typing import Any, Dict, List, Union
-from supertokens_python import get_request_from_user_context
-
-from supertokens_python.recipe.passwordless.interfaces import (
-    ConsumeCodeExpiredUserInputCodeError,
-    ConsumeCodeIncorrectUserInputCodeError,
-    ConsumeCodeOkResult,
-    ConsumeCodeRestartFlowError,
-    CreateCodeOkResult,
-    CreateNewCodeForDeviceOkResult,
-    CreateNewCodeForDeviceRestartFlowError,
-    CreateNewCodeForDeviceUserInputCodeAlreadyUsedError,
-    DeleteUserInfoOkResult,
-    DeleteUserInfoUnknownUserIdError,
-    RevokeAllCodesOkResult,
-    RevokeCodeOkResult,
-    UpdateUserEmailAlreadyExistsError,
-    UpdateUserOkResult,
-    UpdateUserPhoneNumberAlreadyExistsError,
-    UpdateUserUnknownUserIdError,
-)
-from supertokens_python.recipe.passwordless.recipe import PasswordlessRecipe
-from supertokens_python.recipe.passwordless.types import (
-    DeviceType,
-    EmailTemplateVars,
-    SMSTemplateVars,
-    User,
-)
-
-
-async def create_code(
-    tenant_id: str,
-    email: Union[None, str] = None,
-    phone_number: Union[None, str] = None,
-    user_input_code: Union[None, str] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> CreateCodeOkResult:
-    if user_context is None:
-        user_context = {}
-    return await PasswordlessRecipe.get_instance().recipe_implementation.create_code(
-        email=email,
-        phone_number=phone_number,
-        user_input_code=user_input_code,
-        tenant_id=tenant_id,
-        user_context=user_context,
-    )
-
-
-async def create_new_code_for_device(
-    tenant_id: str,
-    device_id: str,
-    user_input_code: Union[str, None] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> Union[
-    CreateNewCodeForDeviceOkResult,
-    CreateNewCodeForDeviceRestartFlowError,
-    CreateNewCodeForDeviceUserInputCodeAlreadyUsedError,
-]:
-    if user_context is None:
-        user_context = {}
-    return await PasswordlessRecipe.get_instance().recipe_implementation.create_new_code_for_device(
-        device_id=device_id,
-        user_input_code=user_input_code,
-        tenant_id=tenant_id,
-        user_context=user_context,
-    )
-
-
-async def consume_code(
-    tenant_id: str,
-    pre_auth_session_id: str,
-    user_input_code: Union[str, None] = None,
-    device_id: Union[str, None] = None,
-    link_code: Union[str, None] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> Union[
-    ConsumeCodeOkResult,
-    ConsumeCodeIncorrectUserInputCodeError,
-    ConsumeCodeExpiredUserInputCodeError,
-    ConsumeCodeRestartFlowError,
-]:
-    if user_context is None:
-        user_context = {}
-    return await PasswordlessRecipe.get_instance().recipe_implementation.consume_code(
-        pre_auth_session_id=pre_auth_session_id,
-        user_input_code=user_input_code,
-        device_id=device_id,
-        link_code=link_code,
-        tenant_id=tenant_id,
-        user_context=user_context,
-    )
-
-
-async def get_user_by_id(
-    user_id: str, user_context: Union[None, Dict[str, Any]] = None
-) -> Union[User, None]:
-    if user_context is None:
-        user_context = {}
-    return await PasswordlessRecipe.get_instance().recipe_implementation.get_user_by_id(
-        user_id=user_id, user_context=user_context
-    )
-
-
-async def get_user_by_email(
-    tenant_id: str, email: str, user_context: Union[None, Dict[str, Any]] = None
-) -> Union[User, None]:
-    if user_context is None:
-        user_context = {}
-    return (
-        await PasswordlessRecipe.get_instance().recipe_implementation.get_user_by_email(
-            email=email,
-            tenant_id=tenant_id,
-            user_context=user_context,
-        )
-    )
-
-
-async def get_user_by_phone_number(
-    tenant_id: str, phone_number: str, user_context: Union[None, Dict[str, Any]] = None
-) -> Union[User, None]:
-    if user_context is None:
-        user_context = {}
-    return await PasswordlessRecipe.get_instance().recipe_implementation.get_user_by_phone_number(
-        phone_number=phone_number,
-        tenant_id=tenant_id,
-        user_context=user_context,
-    )
-
-
-async def update_user(
-    user_id: str,
-    email: Union[str, None] = None,
-    phone_number: Union[str, None] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> Union[
-    UpdateUserOkResult,
-    UpdateUserUnknownUserIdError,
-    UpdateUserEmailAlreadyExistsError,
-    UpdateUserPhoneNumberAlreadyExistsError,
-]:
-    if user_context is None:
-        user_context = {}
-    return await PasswordlessRecipe.get_instance().recipe_implementation.update_user(
-        user_id=user_id,
-        email=email,
-        phone_number=phone_number,
-        user_context=user_context,
-    )
-
-
-async def delete_email_for_user(
-    user_id: str, user_context: Union[None, Dict[str, Any]] = None
-) -> Union[DeleteUserInfoOkResult, DeleteUserInfoUnknownUserIdError]:
-    if user_context is None:
-        user_context = {}
-    return await PasswordlessRecipe.get_instance().recipe_implementation.delete_email_for_user(
-        user_id=user_id, user_context=user_context
-    )
-
-
-async def delete_phone_number_for_user(
-    user_id: str, user_context: Union[None, Dict[str, Any]] = None
-) -> Union[DeleteUserInfoOkResult, DeleteUserInfoUnknownUserIdError]:
-    if user_context is None:
-        user_context = {}
-    return await PasswordlessRecipe.get_instance().recipe_implementation.delete_phone_number_for_user(
-        user_id=user_id, user_context=user_context
-    )
-
-
-async def revoke_all_codes(
-    tenant_id: str,
-    email: Union[str, None] = None,
-    phone_number: Union[str, None] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> RevokeAllCodesOkResult:
-    if user_context is None:
-        user_context = {}
-    return (
-        await PasswordlessRecipe.get_instance().recipe_implementation.revoke_all_codes(
-            email=email,
-            phone_number=phone_number,
-            tenant_id=tenant_id,
-            user_context=user_context,
-        )
-    )
-
-
-async def revoke_code(
-    tenant_id: str, code_id: str, user_context: Union[None, Dict[str, Any]] = None
-) -> RevokeCodeOkResult:
-    if user_context is None:
-        user_context = {}
-    return await PasswordlessRecipe.get_instance().recipe_implementation.revoke_code(
-        tenant_id=tenant_id,
-        code_id=code_id,
-        user_context=user_context,
-    )
-
-
-async def list_codes_by_email(
-    tenant_id: str, email: str, user_context: Union[None, Dict[str, Any]] = None
-) -> List[DeviceType]:
-    if user_context is None:
-        user_context = {}
-    return await PasswordlessRecipe.get_instance().recipe_implementation.list_codes_by_email(
-        email=email, tenant_id=tenant_id, user_context=user_context
-    )
-
-
-async def list_codes_by_phone_number(
-    tenant_id: str, phone_number: str, user_context: Union[None, Dict[str, Any]] = None
-) -> List[DeviceType]:
-    if user_context is None:
-        user_context = {}
-    return await PasswordlessRecipe.get_instance().recipe_implementation.list_codes_by_phone_number(
-        phone_number=phone_number,
-        tenant_id=tenant_id,
-        user_context=user_context,
-    )
-
-
-async def list_codes_by_device_id(
-    tenant_id: str, device_id: str, user_context: Union[None, Dict[str, Any]] = None
-) -> Union[DeviceType, None]:
-    if user_context is None:
-        user_context = {}
-    return await PasswordlessRecipe.get_instance().recipe_implementation.list_codes_by_device_id(
-        device_id=device_id,
-        tenant_id=tenant_id,
-        user_context=user_context,
-    )
-
-
-async def list_codes_by_pre_auth_session_id(
-    tenant_id: str,
-    pre_auth_session_id: str,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> Union[DeviceType, None]:
-    if user_context is None:
-        user_context = {}
-    return await PasswordlessRecipe.get_instance().recipe_implementation.list_codes_by_pre_auth_session_id(
-        pre_auth_session_id=pre_auth_session_id,
-        tenant_id=tenant_id,
-        user_context=user_context,
-    )
-
-
-async def create_magic_link(
-    tenant_id: str,
-    email: Union[str, None],
-    phone_number: Union[str, None],
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> str:
-    if user_context is None:
-        user_context = {}
-    request = get_request_from_user_context(user_context)
-    return await PasswordlessRecipe.get_instance().create_magic_link(
-        email=email,
-        phone_number=phone_number,
-        tenant_id=tenant_id,
-        request=request,
-        user_context=user_context,
-    )
-
-
-async def signinup(
-    tenant_id: str,
-    email: Union[str, None],
-    phone_number: Union[str, None],
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> ConsumeCodeOkResult:
-    if user_context is None:
-        user_context = {}
-    return await PasswordlessRecipe.get_instance().signinup(
-        email=email,
-        phone_number=phone_number,
-        tenant_id=tenant_id,
-        user_context=user_context,
-    )
-
-
-async def send_email(
-    input_: EmailTemplateVars,
-    user_context: Union[None, Dict[str, Any]] = None,
-):
-    if user_context is None:
-        user_context = {}
-
-    return await PasswordlessRecipe.get_instance().email_delivery.ingredient_interface_impl.send_email(
-        input_, user_context
-    )
-
-
-async def send_sms(
-    input_: SMSTemplateVars,
-    user_context: Union[None, Dict[str, Any]] = None,
-):
-    if user_context is None:
-        user_context = {}
-
-    return await PasswordlessRecipe.get_instance().sms_delivery.ingredient_interface_impl.send_sms(
-        input_, user_context
-    )
-
-
-
-
-
-
-
-

Functions

-
-
-async def consume_code(tenant_id: str, pre_auth_session_id: str, user_input_code: Optional[str] = None, device_id: Optional[str] = None, link_code: Optional[str] = None, user_context: Optional[Dict[str, Any]] = None) ‑> Union[ConsumeCodeOkResultConsumeCodeIncorrectUserInputCodeErrorConsumeCodeExpiredUserInputCodeErrorConsumeCodeRestartFlowError] -
-
-
-
- -Expand source code - -
async def consume_code(
-    tenant_id: str,
-    pre_auth_session_id: str,
-    user_input_code: Union[str, None] = None,
-    device_id: Union[str, None] = None,
-    link_code: Union[str, None] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> Union[
-    ConsumeCodeOkResult,
-    ConsumeCodeIncorrectUserInputCodeError,
-    ConsumeCodeExpiredUserInputCodeError,
-    ConsumeCodeRestartFlowError,
-]:
-    if user_context is None:
-        user_context = {}
-    return await PasswordlessRecipe.get_instance().recipe_implementation.consume_code(
-        pre_auth_session_id=pre_auth_session_id,
-        user_input_code=user_input_code,
-        device_id=device_id,
-        link_code=link_code,
-        tenant_id=tenant_id,
-        user_context=user_context,
-    )
-
-
-
-async def create_code(tenant_id: str, email: Optional[str] = None, phone_number: Optional[str] = None, user_input_code: Optional[str] = None, user_context: Optional[Dict[str, Any]] = None) ‑> CreateCodeOkResult -
-
-
-
- -Expand source code - -
async def create_code(
-    tenant_id: str,
-    email: Union[None, str] = None,
-    phone_number: Union[None, str] = None,
-    user_input_code: Union[None, str] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> CreateCodeOkResult:
-    if user_context is None:
-        user_context = {}
-    return await PasswordlessRecipe.get_instance().recipe_implementation.create_code(
-        email=email,
-        phone_number=phone_number,
-        user_input_code=user_input_code,
-        tenant_id=tenant_id,
-        user_context=user_context,
-    )
-
-
- -
-
-
- -Expand source code - -
async def create_magic_link(
-    tenant_id: str,
-    email: Union[str, None],
-    phone_number: Union[str, None],
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> str:
-    if user_context is None:
-        user_context = {}
-    request = get_request_from_user_context(user_context)
-    return await PasswordlessRecipe.get_instance().create_magic_link(
-        email=email,
-        phone_number=phone_number,
-        tenant_id=tenant_id,
-        request=request,
-        user_context=user_context,
-    )
-
-
-
-async def create_new_code_for_device(tenant_id: str, device_id: str, user_input_code: Optional[str] = None, user_context: Optional[Dict[str, Any]] = None) ‑> Union[CreateNewCodeForDeviceOkResultCreateNewCodeForDeviceRestartFlowErrorCreateNewCodeForDeviceUserInputCodeAlreadyUsedError] -
-
-
-
- -Expand source code - -
async def create_new_code_for_device(
-    tenant_id: str,
-    device_id: str,
-    user_input_code: Union[str, None] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> Union[
-    CreateNewCodeForDeviceOkResult,
-    CreateNewCodeForDeviceRestartFlowError,
-    CreateNewCodeForDeviceUserInputCodeAlreadyUsedError,
-]:
-    if user_context is None:
-        user_context = {}
-    return await PasswordlessRecipe.get_instance().recipe_implementation.create_new_code_for_device(
-        device_id=device_id,
-        user_input_code=user_input_code,
-        tenant_id=tenant_id,
-        user_context=user_context,
-    )
-
-
-
-async def delete_email_for_user(user_id: str, user_context: Optional[Dict[str, Any]] = None) ‑> Union[DeleteUserInfoOkResultDeleteUserInfoUnknownUserIdError] -
-
-
-
- -Expand source code - -
async def delete_email_for_user(
-    user_id: str, user_context: Union[None, Dict[str, Any]] = None
-) -> Union[DeleteUserInfoOkResult, DeleteUserInfoUnknownUserIdError]:
-    if user_context is None:
-        user_context = {}
-    return await PasswordlessRecipe.get_instance().recipe_implementation.delete_email_for_user(
-        user_id=user_id, user_context=user_context
-    )
-
-
-
-async def delete_phone_number_for_user(user_id: str, user_context: Optional[Dict[str, Any]] = None) ‑> Union[DeleteUserInfoOkResultDeleteUserInfoUnknownUserIdError] -
-
-
-
- -Expand source code - -
async def delete_phone_number_for_user(
-    user_id: str, user_context: Union[None, Dict[str, Any]] = None
-) -> Union[DeleteUserInfoOkResult, DeleteUserInfoUnknownUserIdError]:
-    if user_context is None:
-        user_context = {}
-    return await PasswordlessRecipe.get_instance().recipe_implementation.delete_phone_number_for_user(
-        user_id=user_id, user_context=user_context
-    )
-
-
-
-async def get_user_by_email(tenant_id: str, email: str, user_context: Optional[Dict[str, Any]] = None) ‑> Optional[User] -
-
-
-
- -Expand source code - -
async def get_user_by_email(
-    tenant_id: str, email: str, user_context: Union[None, Dict[str, Any]] = None
-) -> Union[User, None]:
-    if user_context is None:
-        user_context = {}
-    return (
-        await PasswordlessRecipe.get_instance().recipe_implementation.get_user_by_email(
-            email=email,
-            tenant_id=tenant_id,
-            user_context=user_context,
-        )
-    )
-
-
-
-async def get_user_by_id(user_id: str, user_context: Optional[Dict[str, Any]] = None) ‑> Optional[User] -
-
-
-
- -Expand source code - -
async def get_user_by_id(
-    user_id: str, user_context: Union[None, Dict[str, Any]] = None
-) -> Union[User, None]:
-    if user_context is None:
-        user_context = {}
-    return await PasswordlessRecipe.get_instance().recipe_implementation.get_user_by_id(
-        user_id=user_id, user_context=user_context
-    )
-
-
-
-async def get_user_by_phone_number(tenant_id: str, phone_number: str, user_context: Optional[Dict[str, Any]] = None) ‑> Optional[User] -
-
-
-
- -Expand source code - -
async def get_user_by_phone_number(
-    tenant_id: str, phone_number: str, user_context: Union[None, Dict[str, Any]] = None
-) -> Union[User, None]:
-    if user_context is None:
-        user_context = {}
-    return await PasswordlessRecipe.get_instance().recipe_implementation.get_user_by_phone_number(
-        phone_number=phone_number,
-        tenant_id=tenant_id,
-        user_context=user_context,
-    )
-
-
-
-async def list_codes_by_device_id(tenant_id: str, device_id: str, user_context: Optional[Dict[str, Any]] = None) ‑> Optional[DeviceType] -
-
-
-
- -Expand source code - -
async def list_codes_by_device_id(
-    tenant_id: str, device_id: str, user_context: Union[None, Dict[str, Any]] = None
-) -> Union[DeviceType, None]:
-    if user_context is None:
-        user_context = {}
-    return await PasswordlessRecipe.get_instance().recipe_implementation.list_codes_by_device_id(
-        device_id=device_id,
-        tenant_id=tenant_id,
-        user_context=user_context,
-    )
-
-
-
-async def list_codes_by_email(tenant_id: str, email: str, user_context: Optional[Dict[str, Any]] = None) ‑> List[DeviceType] -
-
-
-
- -Expand source code - -
async def list_codes_by_email(
-    tenant_id: str, email: str, user_context: Union[None, Dict[str, Any]] = None
-) -> List[DeviceType]:
-    if user_context is None:
-        user_context = {}
-    return await PasswordlessRecipe.get_instance().recipe_implementation.list_codes_by_email(
-        email=email, tenant_id=tenant_id, user_context=user_context
-    )
-
-
-
-async def list_codes_by_phone_number(tenant_id: str, phone_number: str, user_context: Optional[Dict[str, Any]] = None) ‑> List[DeviceType] -
-
-
-
- -Expand source code - -
async def list_codes_by_phone_number(
-    tenant_id: str, phone_number: str, user_context: Union[None, Dict[str, Any]] = None
-) -> List[DeviceType]:
-    if user_context is None:
-        user_context = {}
-    return await PasswordlessRecipe.get_instance().recipe_implementation.list_codes_by_phone_number(
-        phone_number=phone_number,
-        tenant_id=tenant_id,
-        user_context=user_context,
-    )
-
-
-
-async def list_codes_by_pre_auth_session_id(tenant_id: str, pre_auth_session_id: str, user_context: Optional[Dict[str, Any]] = None) ‑> Optional[DeviceType] -
-
-
-
- -Expand source code - -
async def list_codes_by_pre_auth_session_id(
-    tenant_id: str,
-    pre_auth_session_id: str,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> Union[DeviceType, None]:
-    if user_context is None:
-        user_context = {}
-    return await PasswordlessRecipe.get_instance().recipe_implementation.list_codes_by_pre_auth_session_id(
-        pre_auth_session_id=pre_auth_session_id,
-        tenant_id=tenant_id,
-        user_context=user_context,
-    )
-
-
-
-async def revoke_all_codes(tenant_id: str, email: Optional[str] = None, phone_number: Optional[str] = None, user_context: Optional[Dict[str, Any]] = None) ‑> RevokeAllCodesOkResult -
-
-
-
- -Expand source code - -
async def revoke_all_codes(
-    tenant_id: str,
-    email: Union[str, None] = None,
-    phone_number: Union[str, None] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> RevokeAllCodesOkResult:
-    if user_context is None:
-        user_context = {}
-    return (
-        await PasswordlessRecipe.get_instance().recipe_implementation.revoke_all_codes(
-            email=email,
-            phone_number=phone_number,
-            tenant_id=tenant_id,
-            user_context=user_context,
-        )
-    )
-
-
-
-async def revoke_code(tenant_id: str, code_id: str, user_context: Optional[Dict[str, Any]] = None) ‑> RevokeCodeOkResult -
-
-
-
- -Expand source code - -
async def revoke_code(
-    tenant_id: str, code_id: str, user_context: Union[None, Dict[str, Any]] = None
-) -> RevokeCodeOkResult:
-    if user_context is None:
-        user_context = {}
-    return await PasswordlessRecipe.get_instance().recipe_implementation.revoke_code(
-        tenant_id=tenant_id,
-        code_id=code_id,
-        user_context=user_context,
-    )
-
-
-
-async def send_email(input_: CreateAndSendCustomEmailParameters, user_context: Optional[Dict[str, Any]] = None) -
-
-
-
- -Expand source code - -
async def send_email(
-    input_: EmailTemplateVars,
-    user_context: Union[None, Dict[str, Any]] = None,
-):
-    if user_context is None:
-        user_context = {}
-
-    return await PasswordlessRecipe.get_instance().email_delivery.ingredient_interface_impl.send_email(
-        input_, user_context
-    )
-
-
-
-async def send_sms(input_: CreateAndSendCustomTextMessageParameters, user_context: Optional[Dict[str, Any]] = None) -
-
-
-
- -Expand source code - -
async def send_sms(
-    input_: SMSTemplateVars,
-    user_context: Union[None, Dict[str, Any]] = None,
-):
-    if user_context is None:
-        user_context = {}
-
-    return await PasswordlessRecipe.get_instance().sms_delivery.ingredient_interface_impl.send_sms(
-        input_, user_context
-    )
-
-
-
-async def signinup(tenant_id: str, email: Optional[str], phone_number: Optional[str], user_context: Optional[Dict[str, Any]] = None) ‑> ConsumeCodeOkResult -
-
-
-
- -Expand source code - -
async def signinup(
-    tenant_id: str,
-    email: Union[str, None],
-    phone_number: Union[str, None],
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> ConsumeCodeOkResult:
-    if user_context is None:
-        user_context = {}
-    return await PasswordlessRecipe.get_instance().signinup(
-        email=email,
-        phone_number=phone_number,
-        tenant_id=tenant_id,
-        user_context=user_context,
-    )
-
-
-
-async def update_user(user_id: str, email: Optional[str] = None, phone_number: Optional[str] = None, user_context: Optional[Dict[str, Any]] = None) ‑> Union[UpdateUserOkResultUpdateUserUnknownUserIdErrorUpdateUserEmailAlreadyExistsErrorUpdateUserPhoneNumberAlreadyExistsError] -
-
-
-
- -Expand source code - -
async def update_user(
-    user_id: str,
-    email: Union[str, None] = None,
-    phone_number: Union[str, None] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> Union[
-    UpdateUserOkResult,
-    UpdateUserUnknownUserIdError,
-    UpdateUserEmailAlreadyExistsError,
-    UpdateUserPhoneNumberAlreadyExistsError,
-]:
-    if user_context is None:
-        user_context = {}
-    return await PasswordlessRecipe.get_instance().recipe_implementation.update_user(
-        user_id=user_id,
-        email=email,
-        phone_number=phone_number,
-        user_context=user_context,
-    )
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/passwordless/constants.html b/html/supertokens_python/recipe/passwordless/constants.html deleted file mode 100644 index eab31385c..000000000 --- a/html/supertokens_python/recipe/passwordless/constants.html +++ /dev/null @@ -1,79 +0,0 @@ - - - - - - -supertokens_python.recipe.passwordless.constants API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.passwordless.constants

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-CREATE_CODE_API = "/signinup/code"
-RESEND_CODE_API = "/signinup/code/resend"
-CONSUME_CODE_API = "/signinup/code/consume"
-DOES_EMAIL_EXIST_API_OLD = "/signup/email/exists"
-DOES_PHONE_NUMBER_EXIST_API_OLD = "/signup/phonenumber/exists"
-DOES_EMAIL_EXIST_API = "/passwordless/email/exists"
-DOES_PHONE_NUMBER_EXIST_API = "/passwordless/phonenumber/exists"
-
-
-
-
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/passwordless/emaildelivery/index.html b/html/supertokens_python/recipe/passwordless/emaildelivery/index.html deleted file mode 100644 index dc4476125..000000000 --- a/html/supertokens_python/recipe/passwordless/emaildelivery/index.html +++ /dev/null @@ -1,83 +0,0 @@ - - - - - - -supertokens_python.recipe.passwordless.emaildelivery API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.passwordless.emaildelivery

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-
-
-

Sub-modules

-
-
supertokens_python.recipe.passwordless.emaildelivery.services
-
-
-
-
-
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/passwordless/emaildelivery/services/backward_compatibility/index.html b/html/supertokens_python/recipe/passwordless/emaildelivery/services/backward_compatibility/index.html deleted file mode 100644 index e6be99a9f..000000000 --- a/html/supertokens_python/recipe/passwordless/emaildelivery/services/backward_compatibility/index.html +++ /dev/null @@ -1,267 +0,0 @@ - - - - - - -supertokens_python.recipe.passwordless.emaildelivery.services.backward_compatibility API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.passwordless.emaildelivery.services.backward_compatibility

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from __future__ import annotations
-
-import json
-from os import environ
-from typing import Any, Dict
-
-from httpx import AsyncClient, HTTPStatusError
-from supertokens_python.ingredients.emaildelivery import EmailDeliveryInterface
-from supertokens_python.logger import log_debug_message
-from supertokens_python.recipe.passwordless.types import (
-    PasswordlessLoginEmailTemplateVars,
-)
-from supertokens_python.supertokens import AppInfo
-from supertokens_python.utils import handle_httpx_client_exceptions
-
-
-async def create_and_send_email_with_supertokens_service(
-    app_info: AppInfo, input_: PasswordlessLoginEmailTemplateVars
-) -> None:
-    if ("SUPERTOKENS_ENV" in environ) and (environ["SUPERTOKENS_ENV"] == "testing"):
-        return
-
-    data = {
-        "email": input_.email,
-        "appName": app_info.app_name,
-        "codeLifetime": input_.code_life_time,
-    }
-    if input_.url_with_link_code:
-        data["urlWithLinkCode"] = input_.url_with_link_code
-    if input_.user_input_code:
-        data["userInputCode"] = input_.user_input_code
-
-    try:
-        async with AsyncClient(timeout=30.0) as client:
-            resp = await client.post("https://api.supertokens.io/0/st/auth/passwordless/login", json=data, headers={"api-version": "0"})  # type: ignore
-            resp.raise_for_status()
-            log_debug_message("Passwordless login email sent to %s", input_.email)
-    except Exception as e:
-        log_debug_message("Error sending passwordless login email")
-        handle_httpx_client_exceptions(e, data)
-        # If the error is thrown from the API:
-        if isinstance(e, HTTPStatusError):
-            body: Dict[str, Any] = e.response.json()  # type: ignore
-            if body.get("err"):
-                msg = body["err"]
-            else:
-                msg = json.dumps(body)
-
-            raise Exception(msg)
-
-        raise e
-
-
-class BackwardCompatibilityService(
-    EmailDeliveryInterface[PasswordlessLoginEmailTemplateVars]
-):
-    def __init__(
-        self,
-        app_info: AppInfo,
-    ) -> None:
-        self.app_info = app_info
-
-    async def send_email(
-        self,
-        template_vars: PasswordlessLoginEmailTemplateVars,
-        user_context: Dict[str, Any],
-    ) -> None:
-        await create_and_send_email_with_supertokens_service(
-            self.app_info, template_vars
-        )  # Note: intentionally not using try-except (unlike other recipes)
-
-
-
-
-
-
-
-

Functions

-
-
-async def create_and_send_email_with_supertokens_service(app_info: AppInfo, input_: PasswordlessLoginEmailTemplateVars) ‑> None -
-
-
-
- -Expand source code - -
async def create_and_send_email_with_supertokens_service(
-    app_info: AppInfo, input_: PasswordlessLoginEmailTemplateVars
-) -> None:
-    if ("SUPERTOKENS_ENV" in environ) and (environ["SUPERTOKENS_ENV"] == "testing"):
-        return
-
-    data = {
-        "email": input_.email,
-        "appName": app_info.app_name,
-        "codeLifetime": input_.code_life_time,
-    }
-    if input_.url_with_link_code:
-        data["urlWithLinkCode"] = input_.url_with_link_code
-    if input_.user_input_code:
-        data["userInputCode"] = input_.user_input_code
-
-    try:
-        async with AsyncClient(timeout=30.0) as client:
-            resp = await client.post("https://api.supertokens.io/0/st/auth/passwordless/login", json=data, headers={"api-version": "0"})  # type: ignore
-            resp.raise_for_status()
-            log_debug_message("Passwordless login email sent to %s", input_.email)
-    except Exception as e:
-        log_debug_message("Error sending passwordless login email")
-        handle_httpx_client_exceptions(e, data)
-        # If the error is thrown from the API:
-        if isinstance(e, HTTPStatusError):
-            body: Dict[str, Any] = e.response.json()  # type: ignore
-            if body.get("err"):
-                msg = body["err"]
-            else:
-                msg = json.dumps(body)
-
-            raise Exception(msg)
-
-        raise e
-
-
-
-
-
-

Classes

-
-
-class BackwardCompatibilityService -(app_info: AppInfo) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class BackwardCompatibilityService(
-    EmailDeliveryInterface[PasswordlessLoginEmailTemplateVars]
-):
-    def __init__(
-        self,
-        app_info: AppInfo,
-    ) -> None:
-        self.app_info = app_info
-
-    async def send_email(
-        self,
-        template_vars: PasswordlessLoginEmailTemplateVars,
-        user_context: Dict[str, Any],
-    ) -> None:
-        await create_and_send_email_with_supertokens_service(
-            self.app_info, template_vars
-        )  # Note: intentionally not using try-except (unlike other recipes)
-
-

Ancestors

- -

Methods

-
-
-async def send_email(self, template_vars: PasswordlessLoginEmailTemplateVars, user_context: Dict[str, Any]) ‑> None -
-
-
-
- -Expand source code - -
async def send_email(
-    self,
-    template_vars: PasswordlessLoginEmailTemplateVars,
-    user_context: Dict[str, Any],
-) -> None:
-    await create_and_send_email_with_supertokens_service(
-        self.app_info, template_vars
-    )  # Note: intentionally not using try-except (unlike other recipes)
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/passwordless/emaildelivery/services/index.html b/html/supertokens_python/recipe/passwordless/emaildelivery/services/index.html deleted file mode 100644 index 6a23ea0a8..000000000 --- a/html/supertokens_python/recipe/passwordless/emaildelivery/services/index.html +++ /dev/null @@ -1,92 +0,0 @@ - - - - - - -supertokens_python.recipe.passwordless.emaildelivery.services API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.passwordless.emaildelivery.services

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from . import smtp
-
-SMTPService = smtp.SMTPService
-
-
-
-

Sub-modules

-
-
supertokens_python.recipe.passwordless.emaildelivery.services.backward_compatibility
-
-
-
-
supertokens_python.recipe.passwordless.emaildelivery.services.smtp
-
-
-
-
-
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/passwordless/emaildelivery/services/smtp/index.html b/html/supertokens_python/recipe/passwordless/emaildelivery/services/smtp/index.html deleted file mode 100644 index 0b0fc2c31..000000000 --- a/html/supertokens_python/recipe/passwordless/emaildelivery/services/smtp/index.html +++ /dev/null @@ -1,217 +0,0 @@ - - - - - - -supertokens_python.recipe.passwordless.emaildelivery.services.smtp API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.passwordless.emaildelivery.services.smtp

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from __future__ import annotations
-
-from typing import Any, Dict, Callable, Union
-
-from supertokens_python.ingredients.emaildelivery.services.smtp import Transporter
-from supertokens_python.ingredients.emaildelivery.types import (
-    EmailDeliveryInterface,
-    SMTPServiceInterface,
-    SMTPSettings,
-)
-from supertokens_python.recipe.passwordless.types import (
-    PasswordlessLoginEmailTemplateVars,
-    SMTPOverrideInput,
-)
-
-from .service_implementation import ServiceImplementation
-
-
-class SMTPService(EmailDeliveryInterface[PasswordlessLoginEmailTemplateVars]):
-    service_implementation: SMTPServiceInterface[PasswordlessLoginEmailTemplateVars]
-
-    def __init__(
-        self,
-        smtp_settings: SMTPSettings,
-        override: Union[Callable[[SMTPOverrideInput], SMTPOverrideInput], None] = None,
-    ) -> None:
-        self.transporter = Transporter(smtp_settings)
-        oi = ServiceImplementation(self.transporter)
-        self.service_implementation = oi if override is None else override(oi)
-
-    async def send_email(
-        self,
-        template_vars: PasswordlessLoginEmailTemplateVars,
-        user_context: Dict[str, Any],
-    ) -> None:
-        content = await self.service_implementation.get_content(
-            template_vars, user_context
-        )
-        await self.service_implementation.send_raw_email(content, user_context)
-
-
-
-

Sub-modules

-
-
supertokens_python.recipe.passwordless.emaildelivery.services.smtp.pless_login
-
-
-
-
supertokens_python.recipe.passwordless.emaildelivery.services.smtp.pless_login_email
-
-
-
-
supertokens_python.recipe.passwordless.emaildelivery.services.smtp.service_implementation
-
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class SMTPService -(smtp_settings: SMTPSettings, override: Union[Callable[[SMTPOverrideInput], SMTPOverrideInput], None] = None) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class SMTPService(EmailDeliveryInterface[PasswordlessLoginEmailTemplateVars]):
-    service_implementation: SMTPServiceInterface[PasswordlessLoginEmailTemplateVars]
-
-    def __init__(
-        self,
-        smtp_settings: SMTPSettings,
-        override: Union[Callable[[SMTPOverrideInput], SMTPOverrideInput], None] = None,
-    ) -> None:
-        self.transporter = Transporter(smtp_settings)
-        oi = ServiceImplementation(self.transporter)
-        self.service_implementation = oi if override is None else override(oi)
-
-    async def send_email(
-        self,
-        template_vars: PasswordlessLoginEmailTemplateVars,
-        user_context: Dict[str, Any],
-    ) -> None:
-        content = await self.service_implementation.get_content(
-            template_vars, user_context
-        )
-        await self.service_implementation.send_raw_email(content, user_context)
-
-

Ancestors

- -

Class variables

-
-
var service_implementationSMTPServiceInterface[CreateAndSendCustomEmailParameters]
-
-
-
-
-

Methods

-
-
-async def send_email(self, template_vars: PasswordlessLoginEmailTemplateVars, user_context: Dict[str, Any]) ‑> None -
-
-
-
- -Expand source code - -
async def send_email(
-    self,
-    template_vars: PasswordlessLoginEmailTemplateVars,
-    user_context: Dict[str, Any],
-) -> None:
-    content = await self.service_implementation.get_content(
-        template_vars, user_context
-    )
-    await self.service_implementation.send_raw_email(content, user_context)
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/passwordless/emaildelivery/services/smtp/pless_login.html b/html/supertokens_python/recipe/passwordless/emaildelivery/services/smtp/pless_login.html deleted file mode 100644 index e660dbf3a..000000000 --- a/html/supertokens_python/recipe/passwordless/emaildelivery/services/smtp/pless_login.html +++ /dev/null @@ -1,193 +0,0 @@ - - - - - - -supertokens_python.recipe.passwordless.emaildelivery.services.smtp.pless_login API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.passwordless.emaildelivery.services.smtp.pless_login

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from string import Template
-from typing import TYPE_CHECKING, Union
-
-from supertokens_python.ingredients.emaildelivery.types import EmailContent
-from supertokens_python.supertokens import Supertokens
-from supertokens_python.utils import humanize_time
-
-from .pless_login_email import magic_link_body, otp_and_magic_link_body, otp_body
-
-if TYPE_CHECKING:
-    from supertokens_python.recipe.passwordless.interfaces import (
-        PasswordlessLoginEmailTemplateVars,
-    )
-
-
-def pless_email_content(input_: PasswordlessLoginEmailTemplateVars) -> EmailContent:
-    supertokens = Supertokens.get_instance()
-    app_name = supertokens.app_info.app_name
-    code_lifetime = humanize_time(input_.code_life_time)
-    body = get_pless_email_html(
-        app_name,
-        code_lifetime,
-        input_.email,
-        input_.url_with_link_code,
-        input_.user_input_code,
-    )
-    content_result = EmailContent(body, "Login to your account", input_.email, True)
-    return content_result
-
-
-def get_pless_email_html(
-    app_name: str,
-    code_lifetime: str,
-    email: str,
-    url_with_link_code: Union[str, None] = None,
-    user_input_code: Union[str, None] = None,
-):
-    if (user_input_code is not None) and (url_with_link_code is not None):
-        html_template = otp_and_magic_link_body
-    elif user_input_code is not None:
-        html_template = otp_body
-    elif url_with_link_code is not None:
-        html_template = magic_link_body
-    else:
-        raise Exception("This should never be thrown.")
-
-    return Template(html_template).substitute(
-        appname=app_name,
-        time=code_lifetime,
-        toEmail=email,
-        otp=user_input_code,
-        urlWithLinkCode=url_with_link_code,
-    )
-
-
-
-
-
-
-
-

Functions

-
-
-def get_pless_email_html(app_name: str, code_lifetime: str, email: str, url_with_link_code: Union[str, None] = None, user_input_code: Union[str, None] = None) -
-
-
-
- -Expand source code - -
def get_pless_email_html(
-    app_name: str,
-    code_lifetime: str,
-    email: str,
-    url_with_link_code: Union[str, None] = None,
-    user_input_code: Union[str, None] = None,
-):
-    if (user_input_code is not None) and (url_with_link_code is not None):
-        html_template = otp_and_magic_link_body
-    elif user_input_code is not None:
-        html_template = otp_body
-    elif url_with_link_code is not None:
-        html_template = magic_link_body
-    else:
-        raise Exception("This should never be thrown.")
-
-    return Template(html_template).substitute(
-        appname=app_name,
-        time=code_lifetime,
-        toEmail=email,
-        otp=user_input_code,
-        urlWithLinkCode=url_with_link_code,
-    )
-
-
-
-def pless_email_content(input_: PasswordlessLoginEmailTemplateVars) ‑> EmailContent -
-
-
-
- -Expand source code - -
def pless_email_content(input_: PasswordlessLoginEmailTemplateVars) -> EmailContent:
-    supertokens = Supertokens.get_instance()
-    app_name = supertokens.app_info.app_name
-    code_lifetime = humanize_time(input_.code_life_time)
-    body = get_pless_email_html(
-        app_name,
-        code_lifetime,
-        input_.email,
-        input_.url_with_link_code,
-        input_.user_input_code,
-    )
-    content_result = EmailContent(body, "Login to your account", input_.email, True)
-    return content_result
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/passwordless/emaildelivery/services/smtp/pless_login_email.html b/html/supertokens_python/recipe/passwordless/emaildelivery/services/smtp/pless_login_email.html deleted file mode 100644 index 69c5987b9..000000000 --- a/html/supertokens_python/recipe/passwordless/emaildelivery/services/smtp/pless_login_email.html +++ /dev/null @@ -1,2879 +0,0 @@ - - - - - - -supertokens_python.recipe.passwordless.emaildelivery.services.smtp.pless_login_email API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.passwordless.emaildelivery.services.smtp.pless_login_email

-
-
-
- -Expand source code - -
otp_body = """
-<!doctype html>
-<html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml"
-        xmlns:o="urn:schemas-microsoft-com:office:office">
-
-<head>
-        <meta charset="UTF-8">
-        <meta http-equiv="X-UA-Compatible" content="IE=edge">
-        <meta name="viewport" content="width=device-width, initial-scale=1">
-        <title>*|MC:SUBJECT|*</title>
-
-        <style type="text/css">
-        body {
-                        max-width: 100vw;
-                        overflow: hidden;
-                }
-                p {
-                        margin: 10px 0;
-                        padding: 0;
-                }
-
-                table {
-                        border-collapse: collapse;
-                }
-
-                h1,
-                h2,
-                h3,
-                h4,
-                h5,
-                h6 {
-                        display: block;
-                        margin: 0;
-                        padding: 0;
-                }
-
-                img,
-                a img {
-                        border: 0;
-                        height: auto;
-                        outline: none;
-                        text-decoration: none;
-                }
-
-                body,
-                #bodyTable,
-                #bodyCell {
-                        height: 100%;
-                        margin: 0;
-                        padding: 0;
-                        width: 100%;
-                }
-
-                .mcnPreviewText {
-                        display: none !important;
-                }
-
-                #outlook a {
-                        padding: 0;
-                }
-
-                img {
-                        -ms-interpolation-mode: bicubic;
-                }
-
-                table {
-                        mso-table-lspace: 0pt;
-                        mso-table-rspace: 0pt;
-                }
-
-                .ReadMsgBody {
-                        width: 100%;
-                }
-
-                .ExternalClass {
-                        width: 100%;
-                }
-
-                p,
-                a,
-                li,
-                td,
-                blockquote {
-                        mso-line-height-rule: exactly;
-                }
-
-                a[href^=tel],
-                a[href^=sms] {
-                        color: inherit;
-                        cursor: default;
-                        text-decoration: none;
-                }
-
-                p,
-                a,
-                li,
-                td,
-                body,
-                table,
-                blockquote {
-                        -ms-text-size-adjust: 100%;
-                        -webkit-text-size-adjust: 100%;
-                }
-
-                .ExternalClass,
-                .ExternalClass p,
-                .ExternalClass td,
-                .ExternalClass div,
-                .ExternalClass span,
-                .ExternalClass font {
-                        line-height: 100%;
-                }
-
-                a[x-apple-data-detectors] {
-                        color: inherit !important;
-                        text-decoration: none !important;
-                        font-size: inherit !important;
-                        font-family: inherit !important;
-                        font-weight: inherit !important;
-                        line-height: inherit !important;
-                }
-
-                .templateContainer {
-                        max-width: 600px !important;
-                }
-
-                a.mcnButton {
-                        display: block;
-                }
-
-                .mcnImage,
-                .mcnRetinaImage {
-                        vertical-align: bottom;
-                }
-
-                .mcnTextContent {
-                        word-break: break-word;
-                }
-
-                .mcnTextContent img {
-                        height: auto !important;
-                }
-
-                .mcnDividerBlock {
-                        table-layout: fixed !important;
-                }
-
-                /*
-        @tab Page
-        @section Heading 1
-        @style heading 1
-        */
-                h1 {
-                        /*@editable*/
-                        color: #222222;
-                        /*@editable*/
-                        font-family: 'Open Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
-                        /*@editable*/
-                        font-size: 40px;
-                        /*@editable*/
-                        font-style: normal;
-                        /*@editable*/
-                        font-weight: bold;
-                        /*@editable*/
-                        line-height: 150%;
-                        /*@editable*/
-                        letter-spacing: normal;
-                        /*@editable*/
-                        text-align: center;
-                }
-
-                /*
-        @tab Page
-        @section Heading 2
-        @style heading 2
-        */
-                h2 {
-                        /*@editable*/
-                        color: #222222;
-                        /*@editable*/
-                        font-family: Helvetica;
-                        /*@editable*/
-                        font-size: 34px;
-                        /*@editable*/
-                        font-style: normal;
-                        /*@editable*/
-                        font-weight: bold;
-                        /*@editable*/
-                        line-height: 150%;
-                        /*@editable*/
-                        letter-spacing: normal;
-                        /*@editable*/
-                        text-align: left;
-                }
-
-                /*
-        @tab Page
-        @section Heading 3
-        @style heading 3
-        */
-                h3 {
-                        /*@editable*/
-                        color: #444444;
-                        /*@editable*/
-                        font-family: Helvetica;
-                        /*@editable*/
-                        font-size: 22px;
-                        /*@editable*/
-                        font-style: normal;
-                        /*@editable*/
-                        font-weight: bold;
-                        /*@editable*/
-                        line-height: 150%;
-                        /*@editable*/
-                        letter-spacing: normal;
-                        /*@editable*/
-                        text-align: left;
-                }
-
-                /*
-        @tab Page
-        @section Heading 4
-        @style heading 4
-        */
-                h4 {
-                        /*@editable*/
-                        color: #949494;
-                        /*@editable*/
-                        font-family: Georgia;
-                        /*@editable*/
-                        font-size: 20px;
-                        /*@editable*/
-                        font-style: italic;
-                        /*@editable*/
-                        font-weight: normal;
-                        /*@editable*/
-                        line-height: 125%;
-                        /*@editable*/
-                        letter-spacing: normal;
-                        /*@editable*/
-                        text-align: left;
-                }
-
-                /*
-        @tab Header
-        @section Header Container Style
-        */
-                #templateHeader {
-                        /*@editable*/
-                        background-color: #f4f4f4;
-                        /*@editable*/
-                        background-image: none;
-                        /*@editable*/
-                        background-repeat: no-repeat;
-                        /*@editable*/
-                        background-position: center;
-                        /*@editable*/
-                        background-size: cover;
-                        /*@editable*/
-                        border-top: 0;
-                        /*@editable*/
-                        border-bottom: 0;
-                        /*@editable*/
-                        padding-top: 0px;
-                        /*@editable*/
-                        padding-bottom: 0px;
-                }
-
-                /*
-        @tab Header
-        @section Header Interior Style
-        */
-                .headerContainer {
-                        /*@editable*/
-                        background-color: #transparent;
-                        /*@editable*/
-                        background-image: none;
-                        /*@editable*/
-                        background-repeat: no-repeat;
-                        /*@editable*/
-                        background-position: center;
-                        /*@editable*/
-                        background-size: cover;
-                        /*@editable*/
-                        border-top: 0;
-                        /*@editable*/
-                        border-bottom: 0;
-                        /*@editable*/
-                        padding-top: 0;
-                        /*@editable*/
-                        padding-bottom: 0;
-                }
-
-                /*
-        @tab Header
-        @section Header Text
-        */
-                .headerContainer .mcnTextContent,
-                .headerContainer .mcnTextContent p {
-                        /*@editable*/
-                        color: #757575;
-                        /*@editable*/
-                        font-family: Helvetica;
-                        /*@editable*/
-                        font-size: 16px;
-                        /*@editable*/
-                        line-height: 150%;
-                        /*@editable*/
-                        text-align: left;
-                }
-
-                /*
-        @tab Header
-        @section Header Link
-        */
-                .headerContainer .mcnTextContent a,
-                .headerContainer .mcnTextContent p a {
-                        /*@editable*/
-                        color: #007C89;
-                        /*@editable*/
-                        font-weight: normal;
-                        /*@editable*/
-                        text-decoration: underline;
-                }
-
-                /*
-        @tab Body
-        @section Body Container Style
-        */
-                #templateBody {
-                        /*@editable*/
-                        background-color: #f4f4f4;
-                        /*@editable*/
-                        background-image: none;
-                        /*@editable*/
-                        background-repeat: no-repeat;
-                        /*@editable*/
-                        background-position: center;
-                        /*@editable*/
-                        background-size: cover;
-                        /*@editable*/
-                        border-top: 0;
-                        /*@editable*/
-                        border-bottom: 0;
-                        /*@editable*/
-                        padding-top: 0px;
-                        /*@editable*/
-                        padding-bottom: 20px;
-                }
-
-                /*
-        @tab Body
-        @section Body Interior Style
-        */
-                .bodyContainer {
-                        /*@editable*/
-                        background-color: #f4f4f4;
-                        /*@editable*/
-                        background-image: none;
-                        /*@editable*/
-                        background-repeat: no-repeat;
-                        /*@editable*/
-                        background-position: center;
-                        /*@editable*/
-                        background-size: cover;
-                        /*@editable*/
-                        border-top: 2px none #ff9933;
-                        /*@editable*/
-                        border-bottom: 2px none #ff9933;
-                        /*@editable*/
-                        padding-top: 10px;
-                        /*@editable*/
-                        padding-bottom: 10px;
-                }
-
-                /*
-        @tab Body
-        @section Body Text
-        */
-                .bodyContainer .mcnTextContent,
-                .bodyContainer .mcnTextContent p {
-                        /*@editable*/
-                        color: #757575;
-                        /*@editable*/
-                        font-family: Helvetica;
-                        /*@editable*/
-                        font-size: 16px;
-                        /*@editable*/
-                        line-height: 150%;
-                        /*@editable*/
-                        text-align: left;
-                }
-
-                /*
-        @tab Body
-        @section Body Link
-        */
-                .bodyContainer .mcnTextContent a,
-                .bodyContainer .mcnTextContent p a {
-                        /*@editable*/
-                        color: #222222;
-                        /*@editable*/
-                        font-weight: normal;
-                        /*@editable*/
-                        text-decoration: underline;
-                }
-
-                /*
-        @tab Footer
-        @section Footer Style
-        */
-                #templateFooter {
-                        /*@editable*/
-                        background-color: #f4f4f4;
-                        /*@editable*/
-                        background-image: none;
-                        /*@editable*/
-                        background-repeat: no-repeat;
-                        /*@editable*/
-                        background-position: center;
-                        /*@editable*/
-                        background-size: cover;
-                        /*@editable*/
-                        border-top: 0;
-                        /*@editable*/
-                        border-bottom: 0;
-                        /*@editable*/
-                        padding-top: 0px;
-                        /*@editable*/
-                        padding-bottom: 20px;
-                }
-
-                /*
-        @tab Footer
-        @section Footer Interior Style
-        */
-                .footerContainer {
-                        /*@editable*/
-                        background-color: #transparent;
-                        /*@editable*/
-                        background-image: none;
-                        /*@editable*/
-                        background-repeat: no-repeat;
-                        /*@editable*/
-                        background-position: center;
-                        /*@editable*/
-                        background-size: cover;
-                        /*@editable*/
-                        border-top: 0;
-                        /*@editable*/
-                        border-bottom: 0;
-                        /*@editable*/
-                        padding-top: 0;
-                        /*@editable*/
-                        padding-bottom: 0;
-                }
-
-                /*
-        @tab Footer
-        @section Footer Text
-        */
-                .footerContainer .mcnTextContent,
-                .footerContainer .mcnTextContent p {
-                        /*@editable*/
-                        color: #FFFFFF;
-                        /*@editable*/
-                        font-family: 'Helvetica Neue', Helvetica, Arial, Verdana, sans-serif;
-                        /*@editable*/
-                        font-size: 12px;
-                        /*@editable*/
-                        line-height: 150%;
-                        /*@editable*/
-                        text-align: center;
-                }
-
-                /*
-        @tab Footer
-        @section Footer Link
-        */
-                .footerContainer .mcnTextContent a,
-                .footerContainer .mcnTextContent p a {
-                        /*@editable*/
-                        color: #FFFFFF;
-                        /*@editable*/
-                        font-weight: normal;
-                        /*@editable*/
-                        text-decoration: underline;
-                }
-
-                @media only screen and (max-width: 480px) {
-
-                        body,
-                        table,
-                        td,
-                        p,
-                        a,
-                        li,
-                        blockquote {
-                                -webkit-text-size-adjust: none !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-                        body {
-                                width: 100% !important;
-                                min-width: 100% !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-                        .mcnRetinaImage {
-                                max-width: 100% !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-                        .mcnImage {
-                                width: 100% !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-
-                        .mcnCartContainer,
-                        .mcnCaptionTopContent,
-                        .mcnRecContentContainer,
-                        .mcnCaptionBottomContent,
-                        .mcnTextContentContainer,
-                        .mcnBoxedTextContentContainer,
-                        .mcnImageGroupContentContainer,
-                        .mcnCaptionLeftTextContentContainer,
-                        .mcnCaptionRightTextContentContainer,
-                        .mcnCaptionLeftImageContentContainer,
-                        .mcnCaptionRightImageContentContainer,
-                        .mcnImageCardLeftTextContentContainer,
-                        .mcnImageCardRightTextContentContainer,
-                        .mcnImageCardLeftImageContentContainer,
-                        .mcnImageCardRightImageContentContainer {
-                                max-width: 100% !important;
-                                width: 100% !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-                        .mcnBoxedTextContentContainer {
-                                min-width: 100% !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-                        .mcnImageGroupContent {
-                                padding: 9px !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-
-                        .mcnCaptionLeftContentOuter .mcnTextContent,
-                        .mcnCaptionRightContentOuter .mcnTextContent {
-                                padding-top: 9px !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-
-                        .mcnImageCardTopImageContent,
-                        .mcnCaptionBottomContent:last-child .mcnCaptionBottomImageContent,
-                        .mcnCaptionBlockInner .mcnCaptionTopContent:last-child .mcnTextContent {
-                                padding-top: 18px !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-                        .mcnImageCardBottomImageContent {
-                                padding-bottom: 9px !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-                        .mcnImageGroupBlockInner {
-                                padding-top: 0 !important;
-                                padding-bottom: 0 !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-                        .mcnImageGroupBlockOuter {
-                                padding-top: 9px !important;
-                                padding-bottom: 9px !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-
-                        .mcnTextContent,
-                        .mcnBoxedTextContentColumn {
-                                padding-right: 18px !important;
-                                padding-left: 18px !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-
-                        .mcnImageCardLeftImageContent,
-                        .mcnImageCardRightImageContent {
-                                padding-right: 18px !important;
-                                padding-bottom: 0 !important;
-                                padding-left: 18px !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-                        .mcpreview-image-uploader {
-                                display: none !important;
-                                width: 100% !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-
-                        /*
-        @tab Mobile Styles
-        @section Heading 1
-        @tip Make the first-level headings larger in size for better readability on small screens.
-        */
-                        h1 {
-                                /*@editable*/
-                                font-size: 30px !important;
-                                /*@editable*/
-                                line-height: 125% !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-
-                        /*
-        @tab Mobile Styles
-        @section Heading 2
-        @tip Make the second-level headings larger in size for better readability on small screens.
-        */
-                        h2 {
-                                /*@editable*/
-                                font-size: 26px !important;
-                                /*@editable*/
-                                line-height: 125% !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-
-                        /*
-        @tab Mobile Styles
-        @section Heading 3
-        @tip Make the third-level headings larger in size for better readability on small screens.
-        */
-                        h3 {
-                                /*@editable*/
-                                font-size: 20px !important;
-                                /*@editable*/
-                                line-height: 150% !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-
-                        /*
-        @tab Mobile Styles
-        @section Heading 4
-        @tip Make the fourth-level headings larger in size for better readability on small screens.
-        */
-                        h4 {
-                                /*@editable*/
-                                font-size: 18px !important;
-                                /*@editable*/
-                                line-height: 150% !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-
-                        /*
-        @tab Mobile Styles
-        @section Boxed Text
-        @tip Make the boxed text larger in size for better readability on small screens. We recommend a font size of at least 16px.
-        */
-                        .mcnBoxedTextContentContainer .mcnTextContent,
-                        .mcnBoxedTextContentContainer .mcnTextContent p {
-                                /*@editable*/
-                                font-size: 14px !important;
-                                /*@editable*/
-                                line-height: 150% !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-
-                        /*
-        @tab Mobile Styles
-        @section Header Text
-        @tip Make the header text larger in size for better readability on small screens.
-        */
-                        .headerContainer .mcnTextContent,
-                        .headerContainer .mcnTextContent p {
-                                /*@editable*/
-                                font-size: 16px !important;
-                                /*@editable*/
-                                line-height: 150% !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-
-                        /*
-        @tab Mobile Styles
-        @section Body Text
-        @tip Make the body text larger in size for better readability on small screens. We recommend a font size of at least 16px.
-        */
-                        .bodyContainer .mcnTextContent,
-                        .bodyContainer .mcnTextContent p {
-                                /*@editable*/
-                                font-size: 16px !important;
-                                /*@editable*/
-                                line-height: 150% !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-
-                        /*
-        @tab Mobile Styles
-        @section Footer Text
-        @tip Make the footer content text larger in size for better readability on small screens.
-        */
-                        .footerContainer .mcnTextContent,
-                        .footerContainer .mcnTextContent p {
-                                /*@editable*/
-                                font-size: 14px !important;
-                                /*@editable*/
-                                line-height: 150% !important;
-                        }
-
-                }
-        </style>
-</head>
-
-<body>
-        <!--*|IF:MC_PREVIEW_TEXT|*-->
-        <!--[if !gte mso 9]><!----><span class="mcnPreviewText"
-                style="display:none; font-size:0px; line-height:0px; max-height:0px; max-width:0px; opacity:0; overflow:hidden; visibility:hidden; mso-hide:all;"></span>
-        <!--<![endif]-->
-        <!--*|END:IF|*-->
-        <center>
-                <table align="center" border="0" cellpadding="0" cellspacing="0" height="100%" width="100%" id="bodyTable">
-                        <tr>
-                                <td align="center" valign="top" id="bodyCell">
-                                        <!-- BEGIN TEMPLATE // -->
-                                        <table border="0" cellpadding="0" cellspacing="0" width="100%">
-                                                <tr>
-                                                        <td align="center" valign="top" id="templateHeader" data-template-container>
-                                                                <!--[if (gte mso 9)|(IE)]>
-                                    <table align="center" border="0" cellspacing="0" cellpadding="0" width="600" style="width:600px;">
-                                    <tr>
-                                    <td align="center" valign="top" width="600" style="width:600px;">
-                                    <![endif]-->
-                                                                <table align="center" border="0" cellpadding="0" cellspacing="0" width="100%"
-                                                                        class="templateContainer">
-                                                                        <tr>
-                                                                                <td valign="top" class="headerContainer"></td>
-                                                                        </tr>
-                                                                </table>
-                                                                <!--[if (gte mso 9)|(IE)]>
-                                    </td>
-                                    </tr>
-                                    </table>
-                                    <![endif]-->
-                                                        </td>
-                                                </tr>
-                                                <tr>
-                                                        <td align="center" valign="top" id="templateBody" data-template-container>
-                                                                <!--[if (gte mso 9)|(IE)]>
-                                    <table align="center" border="0" cellspacing="0" cellpadding="0" width="600" style="width:600px;">
-                                    <tr>
-                                    <td align="center" valign="top" width="600" style="width:600px;">
-                                    <![endif]-->
-                                                                <table align="center" border="0" cellpadding="0" cellspacing="0" width="100%"
-                                                                        class="templateContainer">
-                                                                        <tr>
-                                                                                <td valign="top" class="bodyContainer">
-                                                                                        <table border="0" cellpadding="0" cellspacing="0" width="100%"
-                                                                                                class="mcnCodeBlock">
-                                                                                                <tbody class="mcnTextBlockOuter">
-                                                                                                        <tr>
-                                                                                                                <td valign="top" class="mcnTextBlockInner">
-                                                                                                                        <p
-                                                                                                                                style="font-family:'Helvetica', sans-serif; margin-left: 3%; margin-right: 3%; font-size: 28px; line-height: 26px; font-weight:700; margin-bottom: 40px; margin-top: 48px; text-align: center; color: #222">
-                                                                                                                                Login to ${appname}</p>
-                                                                                                                </td>
-                                                                                                        </tr>
-                                                                                                </tbody>
-                                                                                        </table>
-                                                                                        <table border="0" cellpadding="0" cellspacing="0" width="100%"
-                                                                                                class="mcnCodeBlock">
-                                                                                                <tbody class="mcnTextBlockOuter">
-                                                                                                        <tr>
-                                                                                                                <td valign="top" class="mcnTextBlockInner">
-
-
-                                                                                                                        <div
-                                                                                                                                style="background-color:#fff; margin-left: 3%; margin-right: 3%; border: 1px solid #ddd; border-radius: 6px;">
-                                                                                                                                <div style="padding-left: 15%; padding-right: 15%;">
-
-                                                                                                                                        <p
-                                                                                                                                                style="font-family:'Helvetica', sans-serif; font-size: 16px; line-height: 26px; font-weight:700; text-align: center; padding-top: 24px; padding-bottom: 24px; padding-left: 8%; padding-right: 8%; ">
-                                                                                                                                                Enter the below OTP in your login screen. Note
-                                                                                                                                                that the OTP expires in ${time}.</p>
-
-                                                                                                                                        <div
-                                                                                                                                                style="display: block; flex-direction: row; justify-content: center; margin-bottom: 40px; text-align: center">
-                                                                                                                                                <div class="mcnTextContent"
-                                                                                                                                                        style="padding: 10px 20px; background-color: #fafafa; border: 1px solid #DDD; color: #222; font-family: 'Helvetica', sans-serif; font-size: 32px; line-height: 40px; font-weight: 700; text-align: center; display: block; border-radius: 6px; width: fit-content;margin: 0 auto">
-                                                                                                                                                        ${otp}</div>
-
-                                                                                                                                        </div>
-                                                                                                                                </div>
-                                                                                                                        </div>
-                                                                                                                </td>
-                                                                                                        </tr>
-                                                                                                </tbody>
-                                                                                        </table>
-                                                                                        <table border="0" cellpadding="0" cellspacing="0" width="100%"
-                                                                                                class="mcnCodeBlock">
-                                                                                                <tbody class="mcnTextBlockOuter">
-                                                                                                        <tr>
-                                                                                                                <td valign="top" class="mcnTextBlockInner">
-
-
-                                                                                                                        <p
-                                                                                                                                style="font-family:'Helvetica', sans-serif; font-size: 16px; line-height: 26px; font-weight:400; margin-top: 40px; text-align: center; color: #808080">
-                                                                                                                                This email is meant for <a
-                                                                                                                                        style="font-family: 'Helvetica', sans-serif; text-align: center; word-break: break-all; font-weight: 400; font-size: 16px; line-height: 26px; color: #808080 !important;"
-                                                                                                                                        target="_blank"
-                                                                                                                                        href="mailto:${toEmail}">${toEmail}</a>
-                                                                                                                        </p>
-                                                                                                                </td>
-                                                                                                        </tr>
-                                                                                                </tbody>
-                                                                                        </table>
-                                                                                </td>
-                                                                        </tr>
-                                                                </table>
-                                                                <!--[if (gte mso 9)|(IE)]>
-                                    </td>
-                                    </tr>
-                                    </table>
-                                    <![endif]-->
-                                                        </td>
-                                                </tr>
-                                                <tr>
-                                                        <td align="center" valign="top" id="templateFooter" data-template-container>
-                                                                <!--[if (gte mso 9)|(IE)]>
-                                    <table align="center" border="0" cellspacing="0" cellpadding="0" width="600" style="width:600px;">
-                                    <tr>
-                                    <td align="center" valign="top" width="600" style="width:600px;">
-                                    <![endif]-->
-                                                                <table align="center" border="0" cellpadding="0" cellspacing="0" width="100%"
-                                                                        class="templateContainer">
-                                                                        <tr>
-                                                                                <td valign="top" class="footerContainer"></td>
-                                                                        </tr>
-                                                                </table>
-                                                                <!--[if (gte mso 9)|(IE)]>
-                                    </td>
-                                    </tr>
-                                    </table>
-                                    <![endif]-->
-                                                        </td>
-                                                </tr>
-                                        </table>
-                                        <!-- // END TEMPLATE -->
-                                </td>
-                        </tr>
-                </table>
-        </center>
-</body>
-
-</html>
-"""
-
-magic_link_body = """
-<!doctype html>
-<html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml"
-        xmlns:o="urn:schemas-microsoft-com:office:office">
-
-<head>
-        <meta charset="UTF-8">
-        <meta http-equiv="X-UA-Compatible" content="IE=edge">
-        <meta name="viewport" content="width=device-width, initial-scale=1">
-        <title>*|MC:SUBJECT|*</title>
-
-        <style type="text/css">
-            body {
-                        max-width: 100vw;
-                        overflow: hidden;
-                }
-                p {
-                        margin: 10px 0;
-                        padding: 0;
-                }
-
-                table {
-                        border-collapse: collapse;
-                }
-
-                h1,
-                h2,
-                h3,
-                h4,
-                h5,
-                h6 {
-                        display: block;
-                        margin: 0;
-                        padding: 0;
-                }
-
-                img,
-                a img {
-                        border: 0;
-                        height: auto;
-                        outline: none;
-                        text-decoration: none;
-                }
-
-                body,
-                #bodyTable,
-                #bodyCell {
-                        height: 100%;
-                        margin: 0;
-                        padding: 0;
-                        width: 100%;
-                }
-
-                .mcnPreviewText {
-                        display: none !important;
-                }
-
-                #outlook a {
-                        padding: 0;
-                }
-
-                img {
-                        -ms-interpolation-mode: bicubic;
-                }
-
-                table {
-                        mso-table-lspace: 0pt;
-                        mso-table-rspace: 0pt;
-                }
-
-                .ReadMsgBody {
-                        width: 100%;
-                }
-
-                .ExternalClass {
-                        width: 100%;
-                }
-
-                p,
-                a,
-                li,
-                td,
-                blockquote {
-                        mso-line-height-rule: exactly;
-                }
-
-                a[href^=tel],
-                a[href^=sms] {
-                        color: inherit;
-                        cursor: default;
-                        text-decoration: none;
-                }
-
-                p,
-                a,
-                li,
-                td,
-                body,
-                table,
-                blockquote {
-                        -ms-text-size-adjust: 100%;
-                        -webkit-text-size-adjust: 100%;
-                }
-
-                .ExternalClass,
-                .ExternalClass p,
-                .ExternalClass td,
-                .ExternalClass div,
-                .ExternalClass span,
-                .ExternalClass font {
-                        line-height: 100%;
-                }
-
-                a[x-apple-data-detectors] {
-                        color: inherit !important;
-                        text-decoration: none !important;
-                        font-size: inherit !important;
-                        font-family: inherit !important;
-                        font-weight: inherit !important;
-                        line-height: inherit !important;
-                }
-
-                .templateContainer {
-                        max-width: 600px !important;
-                }
-
-                a.mcnButton {
-                        display: block;
-                }
-
-                .mcnImage,
-                .mcnRetinaImage {
-                        vertical-align: bottom;
-                }
-
-                .mcnTextContent {
-                        word-break: break-word;
-                }
-
-                .mcnTextContent img {
-                        height: auto !important;
-                }
-
-                .mcnDividerBlock {
-                        table-layout: fixed !important;
-                }
-
-                /*
-        @tab Page
-        @section Heading 1
-        @style heading 1
-        */
-                h1 {
-                        /*@editable*/
-                        color: #222222;
-                        /*@editable*/
-                        font-family: 'Open Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
-                        /*@editable*/
-                        font-size: 40px;
-                        /*@editable*/
-                        font-style: normal;
-                        /*@editable*/
-                        font-weight: bold;
-                        /*@editable*/
-                        line-height: 150%;
-                        /*@editable*/
-                        letter-spacing: normal;
-                        /*@editable*/
-                        text-align: center;
-                }
-
-                /*
-        @tab Page
-        @section Heading 2
-        @style heading 2
-        */
-                h2 {
-                        /*@editable*/
-                        color: #222222;
-                        /*@editable*/
-                        font-family: Helvetica;
-                        /*@editable*/
-                        font-size: 34px;
-                        /*@editable*/
-                        font-style: normal;
-                        /*@editable*/
-                        font-weight: bold;
-                        /*@editable*/
-                        line-height: 150%;
-                        /*@editable*/
-                        letter-spacing: normal;
-                        /*@editable*/
-                        text-align: left;
-                }
-
-                /*
-        @tab Page
-        @section Heading 3
-        @style heading 3
-        */
-                h3 {
-                        /*@editable*/
-                        color: #444444;
-                        /*@editable*/
-                        font-family: Helvetica;
-                        /*@editable*/
-                        font-size: 22px;
-                        /*@editable*/
-                        font-style: normal;
-                        /*@editable*/
-                        font-weight: bold;
-                        /*@editable*/
-                        line-height: 150%;
-                        /*@editable*/
-                        letter-spacing: normal;
-                        /*@editable*/
-                        text-align: left;
-                }
-
-                /*
-        @tab Page
-        @section Heading 4
-        @style heading 4
-        */
-                h4 {
-                        /*@editable*/
-                        color: #949494;
-                        /*@editable*/
-                        font-family: Georgia;
-                        /*@editable*/
-                        font-size: 20px;
-                        /*@editable*/
-                        font-style: italic;
-                        /*@editable*/
-                        font-weight: normal;
-                        /*@editable*/
-                        line-height: 125%;
-                        /*@editable*/
-                        letter-spacing: normal;
-                        /*@editable*/
-                        text-align: left;
-                }
-
-                /*
-        @tab Header
-        @section Header Container Style
-        */
-                #templateHeader {
-                        /*@editable*/
-                        background-color: #f4f4f4;
-                        /*@editable*/
-                        background-image: none;
-                        /*@editable*/
-                        background-repeat: no-repeat;
-                        /*@editable*/
-                        background-position: center;
-                        /*@editable*/
-                        background-size: cover;
-                        /*@editable*/
-                        border-top: 0;
-                        /*@editable*/
-                        border-bottom: 0;
-                        /*@editable*/
-                        padding-top: 0px;
-                        /*@editable*/
-                        padding-bottom: 0px;
-                }
-
-                /*
-        @tab Header
-        @section Header Interior Style
-        */
-                .headerContainer {
-                        /*@editable*/
-                        background-color: #transparent;
-                        /*@editable*/
-                        background-image: none;
-                        /*@editable*/
-                        background-repeat: no-repeat;
-                        /*@editable*/
-                        background-position: center;
-                        /*@editable*/
-                        background-size: cover;
-                        /*@editable*/
-                        border-top: 0;
-                        /*@editable*/
-                        border-bottom: 0;
-                        /*@editable*/
-                        padding-top: 0;
-                        /*@editable*/
-                        padding-bottom: 0;
-                }
-
-                /*
-        @tab Header
-        @section Header Text
-        */
-                .headerContainer .mcnTextContent,
-                .headerContainer .mcnTextContent p {
-                        /*@editable*/
-                        color: #757575;
-                        /*@editable*/
-                        font-family: Helvetica;
-                        /*@editable*/
-                        font-size: 16px;
-                        /*@editable*/
-                        line-height: 150%;
-                        /*@editable*/
-                        text-align: left;
-                }
-
-                /*
-        @tab Header
-        @section Header Link
-        */
-                .headerContainer .mcnTextContent a,
-                .headerContainer .mcnTextContent p a {
-                        /*@editable*/
-                        color: #007C89;
-                        /*@editable*/
-                        font-weight: normal;
-                        /*@editable*/
-                        text-decoration: underline;
-                }
-
-                /*
-        @tab Body
-        @section Body Container Style
-        */
-                #templateBody {
-                        /*@editable*/
-                        background-color: #f4f4f4;
-                        /*@editable*/
-                        background-image: none;
-                        /*@editable*/
-                        background-repeat: no-repeat;
-                        /*@editable*/
-                        background-position: center;
-                        /*@editable*/
-                        background-size: cover;
-                        /*@editable*/
-                        border-top: 0;
-                        /*@editable*/
-                        border-bottom: 0;
-                        /*@editable*/
-                        padding-top: 0px;
-                        /*@editable*/
-                        padding-bottom: 20px;
-                }
-
-                /*
-        @tab Body
-        @section Body Interior Style
-        */
-                .bodyContainer {
-                        /*@editable*/
-                        background-color: #f4f4f4;
-                        /*@editable*/
-                        background-image: none;
-                        /*@editable*/
-                        background-repeat: no-repeat;
-                        /*@editable*/
-                        background-position: center;
-                        /*@editable*/
-                        background-size: cover;
-                        /*@editable*/
-                        border-top: 2px none #ff9933;
-                        /*@editable*/
-                        border-bottom: 2px none #ff9933;
-                        /*@editable*/
-                        padding-top: 10px;
-                        /*@editable*/
-                        padding-bottom: 10px;
-                }
-
-                /*
-        @tab Body
-        @section Body Text
-        */
-                .bodyContainer .mcnTextContent,
-                .bodyContainer .mcnTextContent p {
-                        /*@editable*/
-                        color: #757575;
-                        /*@editable*/
-                        font-family: Helvetica;
-                        /*@editable*/
-                        font-size: 16px;
-                        /*@editable*/
-                        line-height: 150%;
-                        /*@editable*/
-                        text-align: left;
-                }
-
-                /*
-        @tab Body
-        @section Body Link
-        */
-                .bodyContainer .mcnTextContent a,
-                .bodyContainer .mcnTextContent p a {
-                        /*@editable*/
-                        color: #222222;
-                        /*@editable*/
-                        font-weight: normal;
-                        /*@editable*/
-                        text-decoration: underline;
-                }
-
-                /*
-        @tab Footer
-        @section Footer Style
-        */
-                #templateFooter {
-                        /*@editable*/
-                        background-color: #f4f4f4;
-                        /*@editable*/
-                        background-image: none;
-                        /*@editable*/
-                        background-repeat: no-repeat;
-                        /*@editable*/
-                        background-position: center;
-                        /*@editable*/
-                        background-size: cover;
-                        /*@editable*/
-                        border-top: 0;
-                        /*@editable*/
-                        border-bottom: 0;
-                        /*@editable*/
-                        padding-top: 0px;
-                        /*@editable*/
-                        padding-bottom: 20px;
-                }
-
-                /*
-        @tab Footer
-        @section Footer Interior Style
-        */
-                .footerContainer {
-                        /*@editable*/
-                        background-color: #transparent;
-                        /*@editable*/
-                        background-image: none;
-                        /*@editable*/
-                        background-repeat: no-repeat;
-                        /*@editable*/
-                        background-position: center;
-                        /*@editable*/
-                        background-size: cover;
-                        /*@editable*/
-                        border-top: 0;
-                        /*@editable*/
-                        border-bottom: 0;
-                        /*@editable*/
-                        padding-top: 0;
-                        /*@editable*/
-                        padding-bottom: 0;
-                }
-
-                /*
-        @tab Footer
-        @section Footer Text
-        */
-                .footerContainer .mcnTextContent,
-                .footerContainer .mcnTextContent p {
-                        /*@editable*/
-                        color: #FFFFFF;
-                        /*@editable*/
-                        font-family: 'Helvetica Neue', Helvetica, Arial, Verdana, sans-serif;
-                        /*@editable*/
-                        font-size: 12px;
-                        /*@editable*/
-                        line-height: 150%;
-                        /*@editable*/
-                        text-align: center;
-                }
-
-                /*
-        @tab Footer
-        @section Footer Link
-        */
-                .footerContainer .mcnTextContent a,
-                .footerContainer .mcnTextContent p a {
-                        /*@editable*/
-                        color: #FFFFFF;
-                        /*@editable*/
-                        font-weight: normal;
-                        /*@editable*/
-                        text-decoration: underline;
-                }
-
-                @media only screen and (max-width: 480px) {
-
-                        body,
-                        table,
-                        td,
-                        p,
-                        a,
-                        li,
-                        blockquote {
-                                -webkit-text-size-adjust: none !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-                        body {
-                                width: 100% !important;
-                                min-width: 100% !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-                        .mcnRetinaImage {
-                                max-width: 100% !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-                        .mcnImage {
-                                width: 100% !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-
-                        .mcnCartContainer,
-                        .mcnCaptionTopContent,
-                        .mcnRecContentContainer,
-                        .mcnCaptionBottomContent,
-                        .mcnTextContentContainer,
-                        .mcnBoxedTextContentContainer,
-                        .mcnImageGroupContentContainer,
-                        .mcnCaptionLeftTextContentContainer,
-                        .mcnCaptionRightTextContentContainer,
-                        .mcnCaptionLeftImageContentContainer,
-                        .mcnCaptionRightImageContentContainer,
-                        .mcnImageCardLeftTextContentContainer,
-                        .mcnImageCardRightTextContentContainer,
-                        .mcnImageCardLeftImageContentContainer,
-                        .mcnImageCardRightImageContentContainer {
-                                max-width: 100% !important;
-                                width: 100% !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-                        .mcnBoxedTextContentContainer {
-                                min-width: 100% !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-                        .mcnImageGroupContent {
-                                padding: 9px !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-
-                        .mcnCaptionLeftContentOuter .mcnTextContent,
-                        .mcnCaptionRightContentOuter .mcnTextContent {
-                                padding-top: 9px !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-
-                        .mcnImageCardTopImageContent,
-                        .mcnCaptionBottomContent:last-child .mcnCaptionBottomImageContent,
-                        .mcnCaptionBlockInner .mcnCaptionTopContent:last-child .mcnTextContent {
-                                padding-top: 18px !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-                        .mcnImageCardBottomImageContent {
-                                padding-bottom: 9px !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-                        .mcnImageGroupBlockInner {
-                                padding-top: 0 !important;
-                                padding-bottom: 0 !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-                        .mcnImageGroupBlockOuter {
-                                padding-top: 9px !important;
-                                padding-bottom: 9px !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-
-                        .mcnTextContent,
-                        .mcnBoxedTextContentColumn {
-                                padding-right: 18px !important;
-                                padding-left: 18px !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-
-                        .mcnImageCardLeftImageContent,
-                        .mcnImageCardRightImageContent {
-                                padding-right: 18px !important;
-                                padding-bottom: 0 !important;
-                                padding-left: 18px !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-                        .mcpreview-image-uploader {
-                                display: none !important;
-                                width: 100% !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-
-                        /*
-        @tab Mobile Styles
-        @section Heading 1
-        @tip Make the first-level headings larger in size for better readability on small screens.
-        */
-                        h1 {
-                                /*@editable*/
-                                font-size: 30px !important;
-                                /*@editable*/
-                                line-height: 125% !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-
-                        /*
-        @tab Mobile Styles
-        @section Heading 2
-        @tip Make the second-level headings larger in size for better readability on small screens.
-        */
-                        h2 {
-                                /*@editable*/
-                                font-size: 26px !important;
-                                /*@editable*/
-                                line-height: 125% !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-
-                        /*
-        @tab Mobile Styles
-        @section Heading 3
-        @tip Make the third-level headings larger in size for better readability on small screens.
-        */
-                        h3 {
-                                /*@editable*/
-                                font-size: 20px !important;
-                                /*@editable*/
-                                line-height: 150% !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-
-                        /*
-        @tab Mobile Styles
-        @section Heading 4
-        @tip Make the fourth-level headings larger in size for better readability on small screens.
-        */
-                        h4 {
-                                /*@editable*/
-                                font-size: 18px !important;
-                                /*@editable*/
-                                line-height: 150% !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-
-                        /*
-        @tab Mobile Styles
-        @section Boxed Text
-        @tip Make the boxed text larger in size for better readability on small screens. We recommend a font size of at least 16px.
-        */
-                        .mcnBoxedTextContentContainer .mcnTextContent,
-                        .mcnBoxedTextContentContainer .mcnTextContent p {
-                                /*@editable*/
-                                font-size: 14px !important;
-                                /*@editable*/
-                                line-height: 150% !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-
-                        /*
-        @tab Mobile Styles
-        @section Header Text
-        @tip Make the header text larger in size for better readability on small screens.
-        */
-                        .headerContainer .mcnTextContent,
-                        .headerContainer .mcnTextContent p {
-                                /*@editable*/
-                                font-size: 16px !important;
-                                /*@editable*/
-                                line-height: 150% !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-
-                        /*
-        @tab Mobile Styles
-        @section Body Text
-        @tip Make the body text larger in size for better readability on small screens. We recommend a font size of at least 16px.
-        */
-                        .bodyContainer .mcnTextContent,
-                        .bodyContainer .mcnTextContent p {
-                                /*@editable*/
-                                font-size: 16px !important;
-                                /*@editable*/
-                                line-height: 150% !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-
-                        /*
-        @tab Mobile Styles
-        @section Footer Text
-        @tip Make the footer content text larger in size for better readability on small screens.
-        */
-                        .footerContainer .mcnTextContent,
-                        .footerContainer .mcnTextContent p {
-                                /*@editable*/
-                                font-size: 14px !important;
-                                /*@editable*/
-                                line-height: 150% !important;
-                        }
-
-                }
-        </style>
-</head>
-
-<body>
-        <!--*|IF:MC_PREVIEW_TEXT|*-->
-        <!--[if !gte mso 9]><!----><span class="mcnPreviewText"
-                style="display:none; font-size:0px; line-height:0px; max-height:0px; max-width:0px; opacity:0; overflow:hidden; visibility:hidden; mso-hide:all;"></span>
-        <!--<![endif]-->
-        <!--*|END:IF|*-->
-        <center>
-                <table align="center" border="0" cellpadding="0" cellspacing="0" height="100%" width="100%" id="bodyTable">
-                        <tr>
-                                <td align="center" valign="top" id="bodyCell">
-                                        <!-- BEGIN TEMPLATE // -->
-                                        <table border="0" cellpadding="0" cellspacing="0" width="100%">
-                                                <tr>
-                                                        <td align="center" valign="top" id="templateHeader" data-template-container>
-                                                                <!--[if (gte mso 9)|(IE)]>
-                                    <table align="center" border="0" cellspacing="0" cellpadding="0" width="600" style="width:600px;">
-                                    <tr>
-                                    <td align="center" valign="top" width="600" style="width:600px;">
-                                    <![endif]-->
-                                                                <table align="center" border="0" cellpadding="0" cellspacing="0" width="100%"
-                                                                        class="templateContainer">
-                                                                        <tr>
-                                                                                <td valign="top" class="headerContainer"></td>
-                                                                        </tr>
-                                                                </table>
-                                                                <!--[if (gte mso 9)|(IE)]>
-                                    </td>
-                                    </tr>
-                                    </table>
-                                    <![endif]-->
-                                                        </td>
-                                                </tr>
-                                                <tr>
-                                                        <td align="center" valign="top" id="templateBody" data-template-container>
-                                                                <!--[if (gte mso 9)|(IE)]>
-                                    <table align="center" border="0" cellspacing="0" cellpadding="0" width="600" style="width:600px;">
-                                    <tr>
-                                    <td align="center" valign="top" width="600" style="width:600px;">
-                                    <![endif]-->
-                                                                <table align="center" border="0" cellpadding="0" cellspacing="0" width="100%"
-                                                                        class="templateContainer">
-                                                                        <tr>
-                                                                                <td valign="top" class="bodyContainer">
-                                                                                        <table border="0" cellpadding="0" cellspacing="0" width="100%"
-                                                                                                class="mcnCodeBlock">
-                                                                                                <tbody class="mcnTextBlockOuter">
-                                                                                                        <tr>
-                                                                                                                <td valign="top" class="mcnTextBlockInner">
-                                                                                                                        <p
-                                                                                                                                style="font-family:'Helvetica', sans-serif; margin-left: 3%; margin-right: 3%; font-size: 28px; line-height: 26px; font-weight:700; margin-bottom: 40px; margin-top: 48px; text-align: center; color: #222">
-                                                                                                                                Login to ${appname}</p>
-                                                                                                                </td>
-                                                                                                        </tr>
-                                                                                                </tbody>
-                                                                                        </table>
-                                                                                        <table border="0" cellpadding="0" cellspacing="0" width="100%"
-                                                                                                class="mcnCodeBlock">
-                                                                                                <tbody class="mcnTextBlockOuter">
-                                                                                                        <tr>
-                                                                                                                <td valign="top" class="mcnTextBlockInner">
-
-
-                                                                                                                        <div
-                                                                                                                                style="background-color:#fff; margin-left: 3%; margin-right: 3%; border: 1px solid #ddd; border-radius: 6px;">
-                                                                                                                                <div style="padding-left: 15%; padding-right: 15%;">
-
-                                                                                                                                        <p
-                                                                                                                                                style="font-family:'Helvetica', sans-serif; font-size: 16px; line-height: 26px; font-weight:700; text-align: center; padding-top: 24px; padding-bottom: 24px; padding-left: 8%; padding-right: 8%; ">
-                                                                                                                                                Please click the button below to sign in / up.
-                                                                                                                                                Note that the link expires in ${time}.
-                                                                                                                                        </p>
-
-                                                                                                                                        <div class="button-td button-td-primary"
-                                                                                                                                                style="border-radius: 6px; margin-bottom: 40px; display: block; text-align: center;">
-                                                                                                                                                <a class="button-a button-a-primary"
-                                                                                                                                                        href="${urlWithLinkCode}" target="_blank"
-                                                                                                                                                        style="background: #52B56E;font-size: 17px;line-height: 24px;font-weight: 700;font-family: 'Helvetica', sans-serif;text-decoration: none;padding: 9px 25px 9px 25px;color: #ffffff;display: block;border-radius: 6px;width: fit-content;margin: 0 auto;">Login</a>
-                                                                                                                                        </div>
-                                                                                                                                </div>
-                                                                                                                                <div
-                                                                                                                                        style="background-color:#fafafa; border-top: 1px solid #ddd; padding-left: 15%; padding-right: 15%; padding-bottom: 24px; padding-top: 24px">
-                                                                                                                                        <p
-                                                                                                                                                style="font-family: 'Hevetica', sans-serif; font-size: 14px; line-height: 23px; font-weight:400;  text-align: center; color: #808080;">
-                                                                                                                                                Alternatively, you can directly paste this link
-                                                                                                                                                in your browser <br>
-                                                                                                                                                <a style="font-family: 'Helvetica', sans-serif, sans-serif; text-align: center; word-break: break-all; font-weight: 400; font-size: 14px; line-height: 23px; color: #007aff !important;"
-                                                                                                                                                        target="_blank"
-                                                                                                                                                        href="${urlWithLinkCode}">${urlWithLinkCode}</a>
-                                                                                                                                        </p>
-                                                                                                                                </div>
-                                                                                                                        </div>
-
-
-
-
-                                                                                                                </td>
-                                                                                                        </tr>
-                                                                                                </tbody>
-                                                                                        </table>
-                                                                                        <table border="0" cellpadding="0" cellspacing="0" width="100%"
-                                                                                                class="mcnCodeBlock">
-                                                                                                <tbody class="mcnTextBlockOuter">
-                                                                                                        <tr>
-                                                                                                                <td valign="top" class="mcnTextBlockInner">
-
-
-                                                                                                                        <p
-                                                                                                                                style="font-family:'Helvetica', sans-serif; font-size: 16px; line-height: 26px; font-weight:400; margin-top: 40px; text-align: center; color: #808080">
-                                                                                                                                This email is meant for <a
-                                                                                                                                        style="font-family: 'Helvetica', sans-serif; text-align: center; word-break: break-all; font-weight: 400; font-size: 16px; line-height: 26px; color: #808080 !important;"
-                                                                                                                                        target="_blank"
-                                                                                                                                        href="mailto:${toEmail}">${toEmail}</a>
-                                                                                                                        </p>
-                                                                                                                </td>
-                                                                                                        </tr>
-                                                                                                </tbody>
-                                                                                        </table>
-                                                                                </td>
-                                                                        </tr>
-                                                                </table>
-                                                                <!--[if (gte mso 9)|(IE)]>
-                                    </td>
-                                    </tr>
-                                    </table>
-                                    <![endif]-->
-                                                        </td>
-                                                </tr>
-                                                <tr>
-                                                        <td align="center" valign="top" id="templateFooter" data-template-container>
-                                                                <!--[if (gte mso 9)|(IE)]>
-                                    <table align="center" border="0" cellspacing="0" cellpadding="0" width="600" style="width:600px;">
-                                    <tr>
-                                    <td align="center" valign="top" width="600" style="width:600px;">
-                                    <![endif]-->
-                                                                <table align="center" border="0" cellpadding="0" cellspacing="0" width="100%"
-                                                                        class="templateContainer">
-                                                                        <tr>
-                                                                                <td valign="top" class="footerContainer"></td>
-                                                                        </tr>
-                                                                </table>
-                                                                <!--[if (gte mso 9)|(IE)]>
-                                    </td>
-                                    </tr>
-                                    </table>
-                                    <![endif]-->
-                                                        </td>
-                                                </tr>
-                                        </table>
-                                        <!-- // END TEMPLATE -->
-                                </td>
-                        </tr>
-                </table>
-        </center>
-</body>
-
-</html>
-"""
-
-
-otp_and_magic_link_body = """
-<!doctype html>
-<html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml"
-        xmlns:o="urn:schemas-microsoft-com:office:office">
-
-<head>
-        <meta charset="UTF-8">
-        <meta http-equiv="X-UA-Compatible" content="IE=edge">
-        <meta name="viewport" content="width=device-width, initial-scale=1">
-        <title>*|MC:SUBJECT|*</title>
-
-        <style type="text/css">
-            body {
-                        max-width: 100vw;
-                        overflow: hidden;
-                }
-                p {
-                        margin: 10px 0;
-                        padding: 0;
-                }
-
-                table {
-                        border-collapse: collapse;
-                }
-
-                h1,
-                h2,
-                h3,
-                h4,
-                h5,
-                h6 {
-                        display: block;
-                        margin: 0;
-                        padding: 0;
-                }
-
-                img,
-                a img {
-                        border: 0;
-                        height: auto;
-                        outline: none;
-                        text-decoration: none;
-                }
-
-                body,
-                #bodyTable,
-                #bodyCell {
-                        height: 100%;
-                        margin: 0;
-                        padding: 0;
-                        width: 100%;
-                }
-
-                .mcnPreviewText {
-                        display: none !important;
-                }
-
-                #outlook a {
-                        padding: 0;
-                }
-
-                img {
-                        -ms-interpolation-mode: bicubic;
-                }
-
-                table {
-                        mso-table-lspace: 0pt;
-                        mso-table-rspace: 0pt;
-                }
-
-                .ReadMsgBody {
-                        width: 100%;
-                }
-
-                .ExternalClass {
-                        width: 100%;
-                }
-
-                p,
-                a,
-                li,
-                td,
-                blockquote {
-                        mso-line-height-rule: exactly;
-                }
-
-                a[href^=tel],
-                a[href^=sms] {
-                        color: inherit;
-                        cursor: default;
-                        text-decoration: none;
-                }
-
-                p,
-                a,
-                li,
-                td,
-                body,
-                table,
-                blockquote {
-                        -ms-text-size-adjust: 100%;
-                        -webkit-text-size-adjust: 100%;
-                }
-
-                .ExternalClass,
-                .ExternalClass p,
-                .ExternalClass td,
-                .ExternalClass div,
-                .ExternalClass span,
-                .ExternalClass font {
-                        line-height: 100%;
-                }
-
-                a[x-apple-data-detectors] {
-                        color: inherit !important;
-                        text-decoration: none !important;
-                        font-size: inherit !important;
-                        font-family: inherit !important;
-                        font-weight: inherit !important;
-                        line-height: inherit !important;
-                }
-
-                .templateContainer {
-                        max-width: 600px !important;
-                }
-
-                a.mcnButton {
-                        display: block;
-                }
-
-                .mcnImage,
-                .mcnRetinaImage {
-                        vertical-align: bottom;
-                }
-
-                .mcnTextContent {
-                        word-break: break-word;
-                }
-
-                .mcnTextContent img {
-                        height: auto !important;
-                }
-
-                .mcnDividerBlock {
-                        table-layout: fixed !important;
-                }
-
-                /*
-        @tab Page
-        @section Heading 1
-        @style heading 1
-        */
-                h1 {
-                        /*@editable*/
-                        color: #222222;
-                        /*@editable*/
-                        font-family: 'Open Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
-                        /*@editable*/
-                        font-size: 40px;
-                        /*@editable*/
-                        font-style: normal;
-                        /*@editable*/
-                        font-weight: bold;
-                        /*@editable*/
-                        line-height: 150%;
-                        /*@editable*/
-                        letter-spacing: normal;
-                        /*@editable*/
-                        text-align: center;
-                }
-
-                /*
-        @tab Page
-        @section Heading 2
-        @style heading 2
-        */
-                h2 {
-                        /*@editable*/
-                        color: #222222;
-                        /*@editable*/
-                        font-family: Helvetica;
-                        /*@editable*/
-                        font-size: 34px;
-                        /*@editable*/
-                        font-style: normal;
-                        /*@editable*/
-                        font-weight: bold;
-                        /*@editable*/
-                        line-height: 150%;
-                        /*@editable*/
-                        letter-spacing: normal;
-                        /*@editable*/
-                        text-align: left;
-                }
-
-                /*
-        @tab Page
-        @section Heading 3
-        @style heading 3
-        */
-                h3 {
-                        /*@editable*/
-                        color: #444444;
-                        /*@editable*/
-                        font-family: Helvetica;
-                        /*@editable*/
-                        font-size: 22px;
-                        /*@editable*/
-                        font-style: normal;
-                        /*@editable*/
-                        font-weight: bold;
-                        /*@editable*/
-                        line-height: 150%;
-                        /*@editable*/
-                        letter-spacing: normal;
-                        /*@editable*/
-                        text-align: left;
-                }
-
-                /*
-        @tab Page
-        @section Heading 4
-        @style heading 4
-        */
-                h4 {
-                        /*@editable*/
-                        color: #949494;
-                        /*@editable*/
-                        font-family: Georgia;
-                        /*@editable*/
-                        font-size: 20px;
-                        /*@editable*/
-                        font-style: italic;
-                        /*@editable*/
-                        font-weight: normal;
-                        /*@editable*/
-                        line-height: 125%;
-                        /*@editable*/
-                        letter-spacing: normal;
-                        /*@editable*/
-                        text-align: left;
-                }
-
-                /*
-        @tab Header
-        @section Header Container Style
-        */
-                #templateHeader {
-                        /*@editable*/
-                        background-color: #f4f4f4;
-                        /*@editable*/
-                        background-image: none;
-                        /*@editable*/
-                        background-repeat: no-repeat;
-                        /*@editable*/
-                        background-position: center;
-                        /*@editable*/
-                        background-size: cover;
-                        /*@editable*/
-                        border-top: 0;
-                        /*@editable*/
-                        border-bottom: 0;
-                        /*@editable*/
-                        padding-top: 0px;
-                        /*@editable*/
-                        padding-bottom: 0px;
-                }
-
-                /*
-        @tab Header
-        @section Header Interior Style
-        */
-                .headerContainer {
-                        /*@editable*/
-                        background-color: #transparent;
-                        /*@editable*/
-                        background-image: none;
-                        /*@editable*/
-                        background-repeat: no-repeat;
-                        /*@editable*/
-                        background-position: center;
-                        /*@editable*/
-                        background-size: cover;
-                        /*@editable*/
-                        border-top: 0;
-                        /*@editable*/
-                        border-bottom: 0;
-                        /*@editable*/
-                        padding-top: 0;
-                        /*@editable*/
-                        padding-bottom: 0;
-                }
-
-                /*
-        @tab Header
-        @section Header Text
-        */
-                .headerContainer .mcnTextContent,
-                .headerContainer .mcnTextContent p {
-                        /*@editable*/
-                        color: #757575;
-                        /*@editable*/
-                        font-family: Helvetica;
-                        /*@editable*/
-                        font-size: 16px;
-                        /*@editable*/
-                        line-height: 150%;
-                        /*@editable*/
-                        text-align: left;
-                }
-
-                /*
-        @tab Header
-        @section Header Link
-        */
-                .headerContainer .mcnTextContent a,
-                .headerContainer .mcnTextContent p a {
-                        /*@editable*/
-                        color: #007C89;
-                        /*@editable*/
-                        font-weight: normal;
-                        /*@editable*/
-                        text-decoration: underline;
-                }
-
-                /*
-        @tab Body
-        @section Body Container Style
-        */
-                #templateBody {
-                        /*@editable*/
-                        background-color: #f4f4f4;
-                        /*@editable*/
-                        background-image: none;
-                        /*@editable*/
-                        background-repeat: no-repeat;
-                        /*@editable*/
-                        background-position: center;
-                        /*@editable*/
-                        background-size: cover;
-                        /*@editable*/
-                        border-top: 0;
-                        /*@editable*/
-                        border-bottom: 0;
-                        /*@editable*/
-                        padding-top: 0px;
-                        /*@editable*/
-                        padding-bottom: 20px;
-                }
-
-                /*
-        @tab Body
-        @section Body Interior Style
-        */
-                .bodyContainer {
-                        /*@editable*/
-                        background-color: #f4f4f4;
-                        /*@editable*/
-                        background-image: none;
-                        /*@editable*/
-                        background-repeat: no-repeat;
-                        /*@editable*/
-                        background-position: center;
-                        /*@editable*/
-                        background-size: cover;
-                        /*@editable*/
-                        border-top: 2px none #ff9933;
-                        /*@editable*/
-                        border-bottom: 2px none #ff9933;
-                        /*@editable*/
-                        padding-top: 10px;
-                        /*@editable*/
-                        padding-bottom: 10px;
-                }
-
-                /*
-        @tab Body
-        @section Body Text
-        */
-                .bodyContainer .mcnTextContent,
-                .bodyContainer .mcnTextContent p {
-                        /*@editable*/
-                        color: #757575;
-                        /*@editable*/
-                        font-family: Helvetica;
-                        /*@editable*/
-                        font-size: 16px;
-                        /*@editable*/
-                        line-height: 150%;
-                        /*@editable*/
-                        text-align: left;
-                }
-
-                /*
-        @tab Body
-        @section Body Link
-        */
-                .bodyContainer .mcnTextContent a,
-                .bodyContainer .mcnTextContent p a {
-                        /*@editable*/
-                        color: #222222;
-                        /*@editable*/
-                        font-weight: normal;
-                        /*@editable*/
-                        text-decoration: underline;
-                }
-
-                /*
-        @tab Footer
-        @section Footer Style
-        */
-                #templateFooter {
-                        /*@editable*/
-                        background-color: #f4f4f4;
-                        /*@editable*/
-                        background-image: none;
-                        /*@editable*/
-                        background-repeat: no-repeat;
-                        /*@editable*/
-                        background-position: center;
-                        /*@editable*/
-                        background-size: cover;
-                        /*@editable*/
-                        border-top: 0;
-                        /*@editable*/
-                        border-bottom: 0;
-                        /*@editable*/
-                        padding-top: 0px;
-                        /*@editable*/
-                        padding-bottom: 20px;
-                }
-
-                /*
-        @tab Footer
-        @section Footer Interior Style
-        */
-                .footerContainer {
-                        /*@editable*/
-                        background-color: #transparent;
-                        /*@editable*/
-                        background-image: none;
-                        /*@editable*/
-                        background-repeat: no-repeat;
-                        /*@editable*/
-                        background-position: center;
-                        /*@editable*/
-                        background-size: cover;
-                        /*@editable*/
-                        border-top: 0;
-                        /*@editable*/
-                        border-bottom: 0;
-                        /*@editable*/
-                        padding-top: 0;
-                        /*@editable*/
-                        padding-bottom: 0;
-                }
-
-                /*
-        @tab Footer
-        @section Footer Text
-        */
-                .footerContainer .mcnTextContent,
-                .footerContainer .mcnTextContent p {
-                        /*@editable*/
-                        color: #FFFFFF;
-                        /*@editable*/
-                        font-family: 'Helvetica Neue', Helvetica, Arial, Verdana, sans-serif;
-                        /*@editable*/
-                        font-size: 12px;
-                        /*@editable*/
-                        line-height: 150%;
-                        /*@editable*/
-                        text-align: center;
-                }
-
-                /*
-        @tab Footer
-        @section Footer Link
-        */
-                .footerContainer .mcnTextContent a,
-                .footerContainer .mcnTextContent p a {
-                        /*@editable*/
-                        color: #FFFFFF;
-                        /*@editable*/
-                        font-weight: normal;
-                        /*@editable*/
-                        text-decoration: underline;
-                }
-
-                @media only screen and (max-width: 480px) {
-
-                        body,
-                        table,
-                        td,
-                        p,
-                        a,
-                        li,
-                        blockquote {
-                                -webkit-text-size-adjust: none !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-                        body {
-                                width: 100% !important;
-                                min-width: 100% !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-                        .mcnRetinaImage {
-                                max-width: 100% !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-                        .mcnImage {
-                                width: 100% !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-
-                        .mcnCartContainer,
-                        .mcnCaptionTopContent,
-                        .mcnRecContentContainer,
-                        .mcnCaptionBottomContent,
-                        .mcnTextContentContainer,
-                        .mcnBoxedTextContentContainer,
-                        .mcnImageGroupContentContainer,
-                        .mcnCaptionLeftTextContentContainer,
-                        .mcnCaptionRightTextContentContainer,
-                        .mcnCaptionLeftImageContentContainer,
-                        .mcnCaptionRightImageContentContainer,
-                        .mcnImageCardLeftTextContentContainer,
-                        .mcnImageCardRightTextContentContainer,
-                        .mcnImageCardLeftImageContentContainer,
-                        .mcnImageCardRightImageContentContainer {
-                                max-width: 100% !important;
-                                width: 100% !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-                        .mcnBoxedTextContentContainer {
-                                min-width: 100% !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-                        .mcnImageGroupContent {
-                                padding: 9px !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-
-                        .mcnCaptionLeftContentOuter .mcnTextContent,
-                        .mcnCaptionRightContentOuter .mcnTextContent {
-                                padding-top: 9px !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-
-                        .mcnImageCardTopImageContent,
-                        .mcnCaptionBottomContent:last-child .mcnCaptionBottomImageContent,
-                        .mcnCaptionBlockInner .mcnCaptionTopContent:last-child .mcnTextContent {
-                                padding-top: 18px !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-                        .mcnImageCardBottomImageContent {
-                                padding-bottom: 9px !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-                        .mcnImageGroupBlockInner {
-                                padding-top: 0 !important;
-                                padding-bottom: 0 !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-                        .mcnImageGroupBlockOuter {
-                                padding-top: 9px !important;
-                                padding-bottom: 9px !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-
-                        .mcnTextContent,
-                        .mcnBoxedTextContentColumn {
-                                padding-right: 18px !important;
-                                padding-left: 18px !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-
-                        .mcnImageCardLeftImageContent,
-                        .mcnImageCardRightImageContent {
-                                padding-right: 18px !important;
-                                padding-bottom: 0 !important;
-                                padding-left: 18px !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-                        .mcpreview-image-uploader {
-                                display: none !important;
-                                width: 100% !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-
-                        /*
-        @tab Mobile Styles
-        @section Heading 1
-        @tip Make the first-level headings larger in size for better readability on small screens.
-        */
-                        h1 {
-                                /*@editable*/
-                                font-size: 30px !important;
-                                /*@editable*/
-                                line-height: 125% !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-
-                        /*
-        @tab Mobile Styles
-        @section Heading 2
-        @tip Make the second-level headings larger in size for better readability on small screens.
-        */
-                        h2 {
-                                /*@editable*/
-                                font-size: 26px !important;
-                                /*@editable*/
-                                line-height: 125% !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-
-                        /*
-        @tab Mobile Styles
-        @section Heading 3
-        @tip Make the third-level headings larger in size for better readability on small screens.
-        */
-                        h3 {
-                                /*@editable*/
-                                font-size: 20px !important;
-                                /*@editable*/
-                                line-height: 150% !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-
-                        /*
-        @tab Mobile Styles
-        @section Heading 4
-        @tip Make the fourth-level headings larger in size for better readability on small screens.
-        */
-                        h4 {
-                                /*@editable*/
-                                font-size: 18px !important;
-                                /*@editable*/
-                                line-height: 150% !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-
-                        /*
-        @tab Mobile Styles
-        @section Boxed Text
-        @tip Make the boxed text larger in size for better readability on small screens. We recommend a font size of at least 16px.
-        */
-                        .mcnBoxedTextContentContainer .mcnTextContent,
-                        .mcnBoxedTextContentContainer .mcnTextContent p {
-                                /*@editable*/
-                                font-size: 14px !important;
-                                /*@editable*/
-                                line-height: 150% !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-
-                        /*
-        @tab Mobile Styles
-        @section Header Text
-        @tip Make the header text larger in size for better readability on small screens.
-        */
-                        .headerContainer .mcnTextContent,
-                        .headerContainer .mcnTextContent p {
-                                /*@editable*/
-                                font-size: 16px !important;
-                                /*@editable*/
-                                line-height: 150% !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-
-                        /*
-        @tab Mobile Styles
-        @section Body Text
-        @tip Make the body text larger in size for better readability on small screens. We recommend a font size of at least 16px.
-        */
-                        .bodyContainer .mcnTextContent,
-                        .bodyContainer .mcnTextContent p {
-                                /*@editable*/
-                                font-size: 16px !important;
-                                /*@editable*/
-                                line-height: 150% !important;
-                        }
-
-                }
-
-                @media only screen and (max-width: 480px) {
-
-                        /*
-        @tab Mobile Styles
-        @section Footer Text
-        @tip Make the footer content text larger in size for better readability on small screens.
-        */
-                        .footerContainer .mcnTextContent,
-                        .footerContainer .mcnTextContent p {
-                                /*@editable*/
-                                font-size: 14px !important;
-                                /*@editable*/
-                                line-height: 150% !important;
-                        }
-
-                }
-        </style>
-</head>
-
-<body>
-        <!--*|IF:MC_PREVIEW_TEXT|*-->
-        <!--[if !gte mso 9]><!----><span class="mcnPreviewText"
-                style="display:none; font-size:0px; line-height:0px; max-height:0px; max-width:0px; opacity:0; overflow:hidden; visibility:hidden; mso-hide:all;"></span>
-        <!--<![endif]-->
-        <!--*|END:IF|*-->
-        <center>
-                <table align="center" border="0" cellpadding="0" cellspacing="0" height="100%" width="100%" id="bodyTable">
-                        <tr>
-                                <td align="center" valign="top" id="bodyCell">
-                                        <!-- BEGIN TEMPLATE // -->
-                                        <table border="0" cellpadding="0" cellspacing="0" width="100%">
-                                                <tr>
-                                                        <td align="center" valign="top" id="templateHeader" data-template-container>
-                                                                <!--[if (gte mso 9)|(IE)]>
-                                    <table align="center" border="0" cellspacing="0" cellpadding="0" width="600" style="width:600px;">
-                                    <tr>
-                                    <td align="center" valign="top" width="600" style="width:600px;">
-                                    <![endif]-->
-                                                                <table align="center" border="0" cellpadding="0" cellspacing="0" width="100%"
-                                                                        class="templateContainer">
-                                                                        <tr>
-                                                                                <td valign="top" class="headerContainer"></td>
-                                                                        </tr>
-                                                                </table>
-                                                                <!--[if (gte mso 9)|(IE)]>
-                                    </td>
-                                    </tr>
-                                    </table>
-                                    <![endif]-->
-                                                        </td>
-                                                </tr>
-                                                <tr>
-                                                        <td align="center" valign="top" id="templateBody" data-template-container>
-                                                                <!--[if (gte mso 9)|(IE)]>
-                                    <table align="center" border="0" cellspacing="0" cellpadding="0" width="600" style="width:600px;">
-                                    <tr>
-                                    <td align="center" valign="top" width="600" style="width:600px;">
-                                    <![endif]-->
-                                                                <table align="center" border="0" cellpadding="0" cellspacing="0" width="100%"
-                                                                        class="templateContainer">
-                                                                        <tr>
-                                                                                <td valign="top" class="bodyContainer">
-                                                                                        <table border="0" cellpadding="0" cellspacing="0" width="100%"
-                                                                                                class="mcnCodeBlock">
-                                                                                                <tbody class="mcnTextBlockOuter">
-                                                                                                        <tr>
-                                                                                                                <td valign="top" class="mcnTextBlockInner">
-                                                                                                                        <p
-                                                                                                                                style="font-family:'Helvetica', sans-serif; margin-left: 3%; margin-right: 3%; font-size: 28px; line-height: 26px; font-weight:700; margin-bottom: 40px; margin-top: 48px; text-align: center; color: #222">
-                                                                                                                                Login to ${appname}</p>
-                                                                                                                </td>
-                                                                                                        </tr>
-                                                                                                </tbody>
-                                                                                        </table>
-                                                                                        <table border="0" cellpadding="0" cellspacing="0" width="100%"
-                                                                                                class="mcnCodeBlock">
-                                                                                                <tbody class="mcnTextBlockOuter">
-                                                                                                        <tr>
-                                                                                                                <td valign="top" class="mcnTextBlockInner">
-                                                                                                                        <div
-                                                                                                                                style="background-color:#fff; margin-left: 3%; margin-right: 3%; border: 1px solid #ddd; border-radius: 6px">
-                                                                                                                                <div style="padding-left: 15%; padding-right: 15%;">
-                                                                                                                                        <p
-                                                                                                                                                style="font-family: 'Helvetica' , sans-serif; font-size: 16px; line-height: 26px; font-weight:700; text-align: center; padding-top: 24px; padding-bottom: 8px; padding-left: 10%; padding-right: 10%; ">
-                                                                                                                                                Enter the below OTP in your login screen. Note
-                                                                                                                                                that the OTP expires in ${time}.</p>
-                                                                                                                                </div>
-
-                                                                                                                                <div
-                                                                                                                                        style="display: block; flex-direction: row; justify-content: center; margin-bottom: 40px">
-                                                                                                                                        <div class="mcnTextContent"
-                                                                                                                                                style="padding: 10px 20px; background-color: #fafafa; border: 1px solid #DDD; color: #222; font-family: 'Helvetica' , sans-serif; font-size: 32px; line-height: 40px; font-weight: 700; text-align: center; display: block; width: fit-content; border-radius: 6px; margin: 0 auto; margin-top: 8px;">
-                                                                                                                                                ${otp}</div>
-                                                                                                                                </div>
-                                                                                                                        </div>
-                                                                                                                </td>
-                                                                                                        </tr>
-                                                                                                </tbody>
-                                                                                        </table>
-                                                                                        <table border="0" cellpadding="0" cellspacing="0" width="100%"
-                                                                                                class="mcnTextBlock" style="min-width:100%;">
-                                                                                                <tbody class="mcnTextBlockOuter">
-                                                                                                        <tr>
-                                                                                                                <td valign="top" class="mcnTextBlockInner"
-                                                                                                                        style="padding-top:9px;">
-                                                                                                                        <!--[if mso]>
-                                <table align="left" border="0" cellspacing="0" cellpadding="0" width="100%" style="width:100%;">
-                                <tr>
-                                <![endif]-->
-
-                                                                                                                        <!--[if mso]>
-                                <td valign="top" width="600" style="width:600px;">
-                                <![endif]-->
-                                                                                                                        <table align="left" border="0" cellpadding="0"
-                                                                                                                                cellspacing="0" style="max-width:100%; min-width:100%;"
-                                                                                                                                width="100%" class="mcnTextContentContainer">
-                                                                                                                                <tbody>
-                                                                                                                                        <tr>
-
-                                                                                                                                                <td valign="top" class="mcnTextContent"
-                                                                                                                                                        style="padding: 0px 18px 9px; text-align: center;">
-
-                                                                                                                                                        or
-                                                                                                                                                </td>
-                                                                                                                                        </tr>
-                                                                                                                                </tbody>
-                                                                                                                        </table>
-                                                                                                                        <!--[if mso]>
-                                </td>
-                                <![endif]-->
-
-                                                                                                                        <!--[if mso]>
-                                </tr>
-                                </table>
-                                <![endif]-->
-                                                                                                                </td>
-                                                                                                        </tr>
-                                                                                                </tbody>
-                                                                                        </table>
-                                                                                        <table border="0" cellpadding="0" cellspacing="0" width="100%"
-                                                                                                class="mcnCodeBlock">
-                                                                                                <tbody class="mcnTextBlockOuter">
-                                                                                                        <tr>
-                                                                                                                <td valign="top" class="mcnTextBlockInner">
-
-
-                                                                                                                        <div
-                                                                                                                                style="background-color:#fff; margin-left: 3%; margin-right: 3%; border: 1px solid #ddd; border-radius: 6px; ">
-                                                                                                                                <div style="padding-left: 15%; padding-right: 15%;">
-
-                                                                                                                                        <p
-                                                                                                                                                style="font-family: 'Helvetica' , sans-serif; font-size: 16px; line-height: 26px; font-weight:700; text-align: center; padding-top: 24px; padding-bottom: 24px; padding-left: 10%; padding-right: 10%; ">
-                                                                                                                                                Please click the button below to sign in / up.
-                                                                                                                                                Note that the link expires in ${time}.</p>
-
-                                                                                                                                        <div class="button-td button-td-primary"
-                                                                                                                                                style="border-radius: 6px; margin-bottom: 40px; display: block; flex-direction: row; justify-content: center;">
-                                                                                                                                                <a class="button-a button-a-primary"
-                                                                                                                                                        href="${urlWithLinkCode}" target="_blank"
-                                                                                                                                                        style="background: #52B56E;font-size: 17px;line-height: 24px;font-weight: 700;font-family: 'Helvetica' , sans-serif;text-decoration: none;padding: 9px 25px 9px 25px;color: #ffffff;margin: 0 auto;width: fit-content;display: block;border-radius: 6px;">Login</a>
-                                                                                                                                        </div>
-                                                                                                                                </div>
-
-                                                                                                                                <div
-                                                                                                                                        style="background-color:#fafafa; border-top: 1px solid #ddd; padding-left: 15%; padding-right: 15%; padding-bottom: 24px; padding-top: 24px">
-                                                                                                                                        <p
-                                                                                                                                                style="font-family: 'Hevetica', sans-serif; font-size: 14px; line-height: 23px; font-weight:400;  text-align: center; color: #808080;">
-                                                                                                                                                Alternatively, you can directly paste this link
-                                                                                                                                                in your browser <br>
-                                                                                                                                                <a style="font-family: 'Helvetica', sans-serif, sans-serif; text-align: center; word-break: break-all; font-weight: 400; font-size: 14px; line-height: 23px; color: #007aff !important;"
-                                                                                                                                                        target="_blank"
-                                                                                                                                                        href="${urlWithLinkCode}">${urlWithLinkCode}</a>
-                                                                                                                                        </p>
-                                                                                                                                </div>
-                                                                                                                        </div>
-
-
-                                                                                                                </td>
-                                                                                                        </tr>
-                                                                                                </tbody>
-                                                                                        </table>
-                                                                                        <table border="0" cellpadding="0" cellspacing="0" width="100%"
-                                                                                                class="mcnCodeBlock">
-                                                                                                <tbody class="mcnTextBlockOuter">
-                                                                                                        <tr>
-                                                                                                                <td valign="top" class="mcnTextBlockInner">
-                                                                                                                        <p
-                                                                                                                                style="font-family:'Helvetica', sans-serif; font-size: 16px;margin-left: 3%; margin-right: 3%; line-height: 26px; font-weight:400; margin-top: 40px; text-align: center; color: #808080">
-                                                                                                                                This email is meant for <a
-                                                                                                                                        style="font-family: 'Helvetica', sans-serif; text-align: center; word-break: break-all; font-weight: 400; font-size: 16px; line-height: 26px; color: #808080 !important;"
-                                                                                                                                        target="_blank"
-                                                                                                                                        href="mailto:${toEmail}">${toEmail}</a>
-                                                                                                                        </p>
-                                                                                                                </td>
-                                                                                                        </tr>
-                                                                                                </tbody>
-                                                                                        </table>
-                                                                                </td>
-                                                                        </tr>
-                                                                </table>
-                                                                <!--[if (gte mso 9)|(IE)]>
-                                    </td>
-                                    </tr>
-                                    </table>
-                                    <![endif]-->
-                                                        </td>
-                                                </tr>
-                                                <tr>
-                                                        <td align="center" valign="top" id="templateFooter" data-template-container>
-                                                                <!--[if (gte mso 9)|(IE)]>
-                                    <table align="center" border="0" cellspacing="0" cellpadding="0" width="600" style="width:600px;">
-                                    <tr>
-                                    <td align="center" valign="top" width="600" style="width:600px;">
-                                    <![endif]-->
-                                                                <table align="center" border="0" cellpadding="0" cellspacing="0" width="100%"
-                                                                        class="templateContainer">
-                                                                        <tr>
-                                                                                <td valign="top" class="footerContainer"></td>
-                                                                        </tr>
-                                                                </table>
-                                                                <!--[if (gte mso 9)|(IE)]>
-                                    </td>
-                                    </tr>
-                                    </table>
-                                    <![endif]-->
-                                                        </td>
-                                                </tr>
-                                        </table>
-                                        <!-- // END TEMPLATE -->
-                                </td>
-                        </tr>
-                </table>
-        </center>
-</body>
-
-</html>
-"""
-
-
-
-
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/passwordless/emaildelivery/services/smtp/service_implementation.html b/html/supertokens_python/recipe/passwordless/emaildelivery/services/smtp/service_implementation.html deleted file mode 100644 index 36ddcc9be..000000000 --- a/html/supertokens_python/recipe/passwordless/emaildelivery/services/smtp/service_implementation.html +++ /dev/null @@ -1,183 +0,0 @@ - - - - - - -supertokens_python.recipe.passwordless.emaildelivery.services.smtp.service_implementation API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.passwordless.emaildelivery.services.smtp.service_implementation

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from __future__ import annotations
-
-from typing import Any, Dict
-
-from supertokens_python.ingredients.emaildelivery.types import (
-    EmailContent,
-    SMTPServiceInterface,
-)
-from supertokens_python.recipe.passwordless.emaildelivery.services.smtp.pless_login import (
-    pless_email_content,
-)
-from supertokens_python.recipe.passwordless.types import (
-    PasswordlessLoginEmailTemplateVars,
-)
-
-
-class ServiceImplementation(SMTPServiceInterface[PasswordlessLoginEmailTemplateVars]):
-    async def send_raw_email(
-        self, content: EmailContent, user_context: Dict[str, Any]
-    ) -> None:
-        await self.transporter.send_email(content, user_context)
-
-    async def get_content(
-        self,
-        template_vars: PasswordlessLoginEmailTemplateVars,
-        user_context: Dict[str, Any],
-    ) -> EmailContent:
-        _ = user_context
-        return pless_email_content(template_vars)
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class ServiceImplementation -(transporter: Transporter) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class ServiceImplementation(SMTPServiceInterface[PasswordlessLoginEmailTemplateVars]):
-    async def send_raw_email(
-        self, content: EmailContent, user_context: Dict[str, Any]
-    ) -> None:
-        await self.transporter.send_email(content, user_context)
-
-    async def get_content(
-        self,
-        template_vars: PasswordlessLoginEmailTemplateVars,
-        user_context: Dict[str, Any],
-    ) -> EmailContent:
-        _ = user_context
-        return pless_email_content(template_vars)
-
-

Ancestors

- -

Methods

-
-
-async def get_content(self, template_vars: PasswordlessLoginEmailTemplateVars, user_context: Dict[str, Any]) ‑> EmailContent -
-
-
-
- -Expand source code - -
async def get_content(
-    self,
-    template_vars: PasswordlessLoginEmailTemplateVars,
-    user_context: Dict[str, Any],
-) -> EmailContent:
-    _ = user_context
-    return pless_email_content(template_vars)
-
-
-
-async def send_raw_email(self, content: EmailContent, user_context: Dict[str, Any]) ‑> None -
-
-
-
- -Expand source code - -
async def send_raw_email(
-    self, content: EmailContent, user_context: Dict[str, Any]
-) -> None:
-    await self.transporter.send_email(content, user_context)
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/passwordless/exceptions.html b/html/supertokens_python/recipe/passwordless/exceptions.html deleted file mode 100644 index 9879ab17f..000000000 --- a/html/supertokens_python/recipe/passwordless/exceptions.html +++ /dev/null @@ -1,108 +0,0 @@ - - - - - - -supertokens_python.recipe.passwordless.exceptions API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.passwordless.exceptions

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from supertokens_python.exceptions import SuperTokensError
-
-
-class SuperTokensPasswordlessError(SuperTokensError):
-    pass
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class SuperTokensPasswordlessError -(*args, **kwargs) -
-
-

Common base class for all non-exit exceptions.

-
- -Expand source code - -
class SuperTokensPasswordlessError(SuperTokensError):
-    pass
-
-

Ancestors

- -
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/passwordless/index.html b/html/supertokens_python/recipe/passwordless/index.html deleted file mode 100644 index 119cb7a2b..000000000 --- a/html/supertokens_python/recipe/passwordless/index.html +++ /dev/null @@ -1,237 +0,0 @@ - - - - - - -supertokens_python.recipe.passwordless API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.passwordless

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from __future__ import annotations
-
-from typing import TYPE_CHECKING, Any, Awaitable, Callable, Dict, Union
-
-from supertokens_python.ingredients.emaildelivery.types import EmailDeliveryConfig
-from supertokens_python.ingredients.smsdelivery.types import SMSDeliveryConfig
-from supertokens_python.recipe.passwordless.types import (
-    EmailTemplateVars,
-    SMSTemplateVars,
-)
-from typing_extensions import Literal
-
-from . import types, utils
-from .emaildelivery import services as emaildelivery_services
-from .recipe import PasswordlessRecipe
-from .smsdelivery import services as smsdelivery_services
-
-if TYPE_CHECKING:
-    from supertokens_python.supertokens import AppInfo
-
-    from ...recipe_module import RecipeModule
-
-InputOverrideConfig = utils.OverrideConfig
-ContactEmailOnlyConfig = utils.ContactEmailOnlyConfig
-ContactConfig = utils.ContactConfig
-PhoneOrEmailInput = utils.PhoneOrEmailInput
-CreateAndSendCustomTextMessageParameters = (
-    types.CreateAndSendCustomTextMessageParameters
-)
-CreateAndSendCustomEmailParameters = types.CreateAndSendCustomEmailParameters
-ContactPhoneOnlyConfig = utils.ContactPhoneOnlyConfig
-ContactEmailOrPhoneConfig = utils.ContactEmailOrPhoneConfig
-SMTPService = emaildelivery_services.SMTPService
-TwilioService = smsdelivery_services.TwilioService
-SuperTokensSMSService = smsdelivery_services.SuperTokensSMSService
-EmailDeliveryInterface = types.EmailDeliveryInterface
-SMSDeliveryInterface = types.SMSDeliveryInterface
-
-
-def init(
-    contact_config: ContactConfig,
-    flow_type: Literal[
-        "USER_INPUT_CODE", "MAGIC_LINK", "USER_INPUT_CODE_AND_MAGIC_LINK"
-    ],
-    override: Union[InputOverrideConfig, None] = None,
-    get_custom_user_input_code: Union[
-        Callable[[str, Dict[str, Any]], Awaitable[str]], None
-    ] = None,
-    email_delivery: Union[EmailDeliveryConfig[EmailTemplateVars], None] = None,
-    sms_delivery: Union[SMSDeliveryConfig[SMSTemplateVars], None] = None,
-) -> Callable[[AppInfo], RecipeModule]:
-    return PasswordlessRecipe.init(
-        contact_config,
-        flow_type,
-        override,
-        get_custom_user_input_code,
-        email_delivery,
-        sms_delivery,
-    )
-
-
-
-

Sub-modules

-
-
supertokens_python.recipe.passwordless.api
-
-
-
-
supertokens_python.recipe.passwordless.asyncio
-
-
-
-
supertokens_python.recipe.passwordless.constants
-
-
-
-
supertokens_python.recipe.passwordless.emaildelivery
-
-
-
-
supertokens_python.recipe.passwordless.exceptions
-
-
-
-
supertokens_python.recipe.passwordless.interfaces
-
-
-
-
supertokens_python.recipe.passwordless.recipe
-
-
-
-
supertokens_python.recipe.passwordless.recipe_implementation
-
-
-
-
supertokens_python.recipe.passwordless.smsdelivery
-
-
-
-
supertokens_python.recipe.passwordless.syncio
-
-
-
-
supertokens_python.recipe.passwordless.types
-
-
-
-
supertokens_python.recipe.passwordless.utils
-
-
-
-
-
-
-
-
-

Functions

-
-
-def init(contact_config: ContactConfig, flow_type: "Literal['USER_INPUT_CODE', 'MAGIC_LINK', 'USER_INPUT_CODE_AND_MAGIC_LINK']", override: Union[InputOverrideConfig, None] = None, get_custom_user_input_code: Union[Callable[[str, Dict[str, Any]], Awaitable[str]], None] = None, email_delivery: Union[EmailDeliveryConfig[EmailTemplateVars], None] = None, sms_delivery: Union[SMSDeliveryConfig[SMSTemplateVars], None] = None) ‑> Callable[[AppInfo], RecipeModule] -
-
-
-
- -Expand source code - -
def init(
-    contact_config: ContactConfig,
-    flow_type: Literal[
-        "USER_INPUT_CODE", "MAGIC_LINK", "USER_INPUT_CODE_AND_MAGIC_LINK"
-    ],
-    override: Union[InputOverrideConfig, None] = None,
-    get_custom_user_input_code: Union[
-        Callable[[str, Dict[str, Any]], Awaitable[str]], None
-    ] = None,
-    email_delivery: Union[EmailDeliveryConfig[EmailTemplateVars], None] = None,
-    sms_delivery: Union[SMSDeliveryConfig[SMSTemplateVars], None] = None,
-) -> Callable[[AppInfo], RecipeModule]:
-    return PasswordlessRecipe.init(
-        contact_config,
-        flow_type,
-        override,
-        get_custom_user_input_code,
-        email_delivery,
-        sms_delivery,
-    )
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/passwordless/interfaces.html b/html/supertokens_python/recipe/passwordless/interfaces.html deleted file mode 100644 index fe1f43f2b..000000000 --- a/html/supertokens_python/recipe/passwordless/interfaces.html +++ /dev/null @@ -1,2105 +0,0 @@ - - - - - - -supertokens_python.recipe.passwordless.interfaces API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.passwordless.interfaces

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from abc import ABC, abstractmethod
-from typing import Any, Dict, List, Union
-
-from typing_extensions import Literal
-
-from supertokens_python.framework import BaseRequest, BaseResponse
-from supertokens_python.ingredients.emaildelivery import EmailDeliveryIngredient
-from supertokens_python.recipe.session import SessionContainer
-from supertokens_python.types import APIResponse, GeneralErrorResponse
-
-from ...supertokens import AppInfo
-
-# if TYPE_CHECKING:
-from .types import (
-    DeviceType,
-    PasswordlessLoginEmailTemplateVars,
-    PasswordlessLoginSMSTemplateVars,
-    SMSDeliveryIngredient,
-    User,
-)
-from .utils import PasswordlessConfig
-
-
-class CreateCodeOkResult:
-    def __init__(
-        self,
-        pre_auth_session_id: str,
-        code_id: str,
-        device_id: str,
-        user_input_code: str,
-        link_code: str,
-        code_life_time: int,
-        time_created: int,
-    ):
-        self.pre_auth_session_id = pre_auth_session_id
-        self.code_id = code_id
-        self.device_id = device_id
-        self.user_input_code = user_input_code
-        self.link_code = link_code
-        self.code_life_time = code_life_time
-        self.time_created = time_created
-
-
-class CreateNewCodeForDeviceOkResult:
-    def __init__(
-        self,
-        pre_auth_session_id: str,
-        code_id: str,
-        device_id: str,
-        user_input_code: str,
-        link_code: str,
-        code_life_time: int,
-        time_created: int,
-    ):
-        self.pre_auth_session_id = pre_auth_session_id
-        self.code_id = code_id
-        self.device_id = device_id
-        self.user_input_code = user_input_code
-        self.link_code = link_code
-        self.code_life_time = code_life_time
-        self.time_created = time_created
-
-
-class CreateNewCodeForDeviceRestartFlowError:
-    pass
-
-
-class CreateNewCodeForDeviceUserInputCodeAlreadyUsedError:
-    pass
-
-
-class ConsumeCodeOkResult:
-    def __init__(self, created_new_user: bool, user: User):
-        self.created_new_user = created_new_user
-        self.user = user
-
-
-class ConsumeCodeIncorrectUserInputCodeError:
-    def __init__(
-        self, failed_code_input_attempt_count: int, maximum_code_input_attempts: int
-    ):
-        self.failed_code_input_attempt_count = failed_code_input_attempt_count
-        self.maximum_code_input_attempts = maximum_code_input_attempts
-
-
-class ConsumeCodeExpiredUserInputCodeError:
-    def __init__(
-        self, failed_code_input_attempt_count: int, maximum_code_input_attempts: int
-    ):
-        self.failed_code_input_attempt_count = failed_code_input_attempt_count
-        self.maximum_code_input_attempts = maximum_code_input_attempts
-
-
-class ConsumeCodeRestartFlowError:
-    pass
-
-
-class UpdateUserOkResult:
-    pass
-
-
-class UpdateUserUnknownUserIdError:
-    pass
-
-
-class UpdateUserEmailAlreadyExistsError:
-    pass
-
-
-class UpdateUserPhoneNumberAlreadyExistsError:
-    pass
-
-
-class DeleteUserInfoOkResult:
-    pass
-
-
-class DeleteUserInfoUnknownUserIdError:
-    pass
-
-
-class RevokeAllCodesOkResult:
-    pass
-
-
-class RevokeCodeOkResult:
-    pass
-
-
-class RecipeInterface(ABC):
-    def __init__(self):
-        pass
-
-    @abstractmethod
-    async def create_code(
-        self,
-        email: Union[None, str],
-        phone_number: Union[None, str],
-        user_input_code: Union[None, str],
-        tenant_id: str,
-        user_context: Dict[str, Any],
-    ) -> CreateCodeOkResult:
-        pass
-
-    @abstractmethod
-    async def create_new_code_for_device(
-        self,
-        device_id: str,
-        user_input_code: Union[str, None],
-        tenant_id: str,
-        user_context: Dict[str, Any],
-    ) -> Union[
-        CreateNewCodeForDeviceOkResult,
-        CreateNewCodeForDeviceRestartFlowError,
-        CreateNewCodeForDeviceUserInputCodeAlreadyUsedError,
-    ]:
-        pass
-
-    @abstractmethod
-    async def consume_code(
-        self,
-        pre_auth_session_id: str,
-        user_input_code: Union[str, None],
-        device_id: Union[str, None],
-        link_code: Union[str, None],
-        tenant_id: str,
-        user_context: Dict[str, Any],
-    ) -> Union[
-        ConsumeCodeOkResult,
-        ConsumeCodeIncorrectUserInputCodeError,
-        ConsumeCodeExpiredUserInputCodeError,
-        ConsumeCodeRestartFlowError,
-    ]:
-        pass
-
-    @abstractmethod
-    async def get_user_by_id(
-        self, user_id: str, user_context: Dict[str, Any]
-    ) -> Union[User, None]:
-        pass
-
-    @abstractmethod
-    async def get_user_by_email(
-        self, email: str, tenant_id: str, user_context: Dict[str, Any]
-    ) -> Union[User, None]:
-        pass
-
-    @abstractmethod
-    async def get_user_by_phone_number(
-        self, phone_number: str, tenant_id: str, user_context: Dict[str, Any]
-    ) -> Union[User, None]:
-        pass
-
-    @abstractmethod
-    async def update_user(
-        self,
-        user_id: str,
-        email: Union[str, None],
-        phone_number: Union[str, None],
-        user_context: Dict[str, Any],
-    ) -> Union[
-        UpdateUserOkResult,
-        UpdateUserUnknownUserIdError,
-        UpdateUserEmailAlreadyExistsError,
-        UpdateUserPhoneNumberAlreadyExistsError,
-    ]:
-        pass
-
-    @abstractmethod
-    async def delete_email_for_user(
-        self, user_id: str, user_context: Dict[str, Any]
-    ) -> Union[DeleteUserInfoOkResult, DeleteUserInfoUnknownUserIdError]:
-        pass
-
-    @abstractmethod
-    async def delete_phone_number_for_user(
-        self, user_id: str, user_context: Dict[str, Any]
-    ) -> Union[DeleteUserInfoOkResult, DeleteUserInfoUnknownUserIdError]:
-        pass
-
-    @abstractmethod
-    async def revoke_all_codes(
-        self,
-        email: Union[str, None],
-        phone_number: Union[str, None],
-        tenant_id: str,
-        user_context: Dict[str, Any],
-    ) -> RevokeAllCodesOkResult:
-        pass
-
-    @abstractmethod
-    async def revoke_code(
-        self, code_id: str, tenant_id: str, user_context: Dict[str, Any]
-    ) -> RevokeCodeOkResult:
-        pass
-
-    @abstractmethod
-    async def list_codes_by_email(
-        self, email: str, tenant_id: str, user_context: Dict[str, Any]
-    ) -> List[DeviceType]:
-        pass
-
-    @abstractmethod
-    async def list_codes_by_phone_number(
-        self, phone_number: str, tenant_id: str, user_context: Dict[str, Any]
-    ) -> List[DeviceType]:
-        pass
-
-    @abstractmethod
-    async def list_codes_by_device_id(
-        self, device_id: str, tenant_id: str, user_context: Dict[str, Any]
-    ) -> Union[DeviceType, None]:
-        pass
-
-    @abstractmethod
-    async def list_codes_by_pre_auth_session_id(
-        self, pre_auth_session_id: str, tenant_id: str, user_context: Dict[str, Any]
-    ) -> Union[DeviceType, None]:
-        pass
-
-
-class APIOptions:
-    def __init__(
-        self,
-        request: BaseRequest,
-        response: BaseResponse,
-        recipe_id: str,
-        config: PasswordlessConfig,
-        recipe_implementation: RecipeInterface,
-        app_info: AppInfo,
-        email_delivery: EmailDeliveryIngredient[PasswordlessLoginEmailTemplateVars],
-        sms_delivery: SMSDeliveryIngredient[PasswordlessLoginSMSTemplateVars],
-    ):
-        self.request = request
-        self.response = response
-        self.recipe_id = recipe_id
-        self.config = config
-        self.recipe_implementation = recipe_implementation
-        self.app_info = app_info
-        self.email_delivery = email_delivery
-        self.sms_delivery = sms_delivery
-
-
-class CreateCodePostOkResult(APIResponse):
-    status: str = "OK"
-
-    def __init__(
-        self,
-        device_id: str,
-        pre_auth_session_id: str,
-        flow_type: Literal[
-            "USER_INPUT_CODE", "MAGIC_LINK", "USER_INPUT_CODE_AND_MAGIC_LINK"
-        ],
-    ):
-        self.device_id = device_id
-        self.pre_auth_session_id = pre_auth_session_id
-        self.flow_type = flow_type
-
-    def to_json(self):
-        return {
-            "status": self.status,
-            "deviceId": self.device_id,
-            "preAuthSessionId": self.pre_auth_session_id,
-            "flowType": self.flow_type,
-        }
-
-
-class ResendCodePostOkResult(APIResponse):
-    status: str = "OK"
-
-    def to_json(self):
-        return {"status": self.status}
-
-
-class ResendCodePostRestartFlowError(APIResponse):
-    status: str = "RESTART_FLOW_ERROR"
-
-    def to_json(self):
-        return {"status": self.status}
-
-
-class ConsumeCodePostOkResult(APIResponse):
-    status: str = "OK"
-
-    def __init__(self, created_new_user: bool, user: User, session: SessionContainer):
-        self.created_new_user = created_new_user
-        self.user = user
-        self.session = session
-
-    def to_json(self):
-        user = {"id": self.user.user_id, "time_joined": self.user.time_joined}
-        if self.user.email is not None:
-            user = {**user, "email": self.user.email}
-        if self.user.phone_number is not None:
-            user = {**user, "phoneNumber": self.user.phone_number}
-        return {
-            "status": self.status,
-            "createdNewUser": self.created_new_user,
-            "user": user,
-        }
-
-
-class ConsumeCodePostRestartFlowError(APIResponse):
-    status: str = "RESTART_FLOW_ERROR"
-
-    def to_json(self):
-        return {"status": self.status}
-
-
-class ConsumeCodePostIncorrectUserInputCodeError(APIResponse):
-    status: str = "INCORRECT_USER_INPUT_CODE_ERROR"
-
-    def __init__(
-        self, failed_code_input_attempt_count: int, maximum_code_input_attempts: int
-    ):
-        self.failed_code_input_attempt_count = failed_code_input_attempt_count
-        self.maximum_code_input_attempts = maximum_code_input_attempts
-
-    def to_json(self):
-        return {
-            "status": self.status,
-            "failedCodeInputAttemptCount": self.failed_code_input_attempt_count,
-            "maximumCodeInputAttempts": self.maximum_code_input_attempts,
-        }
-
-
-class ConsumeCodePostExpiredUserInputCodeError(APIResponse):
-    status: str = "EXPIRED_USER_INPUT_CODE_ERROR"
-
-    def __init__(
-        self, failed_code_input_attempt_count: int, maximum_code_input_attempts: int
-    ):
-        self.failed_code_input_attempt_count = failed_code_input_attempt_count
-        self.maximum_code_input_attempts = maximum_code_input_attempts
-
-    def to_json(self):
-        return {
-            "status": self.status,
-            "failedCodeInputAttemptCount": self.failed_code_input_attempt_count,
-            "maximumCodeInputAttempts": self.maximum_code_input_attempts,
-        }
-
-
-class PhoneNumberExistsGetOkResult(APIResponse):
-    status: str = "OK"
-
-    def __init__(self, exists: bool):
-        self.exists = exists
-
-    def to_json(self):
-        return {"status": self.status, "exists": self.exists}
-
-
-class EmailExistsGetOkResult(APIResponse):
-    status: str = "OK"
-
-    def __init__(self, exists: bool):
-        self.exists = exists
-
-    def to_json(self):
-        return {"status": self.status, "exists": self.exists}
-
-
-class APIInterface:
-    def __init__(self):
-        self.disable_create_code_post = False
-        self.disable_resend_code_post = False
-        self.disable_consume_code_post = False
-        self.disable_email_exists_get = False
-        self.disable_phone_number_exists_get = False
-
-    @abstractmethod
-    async def create_code_post(
-        self,
-        email: Union[str, None],
-        phone_number: Union[str, None],
-        tenant_id: str,
-        api_options: APIOptions,
-        user_context: Dict[str, Any],
-    ) -> Union[CreateCodePostOkResult, GeneralErrorResponse]:
-        pass
-
-    @abstractmethod
-    async def resend_code_post(
-        self,
-        device_id: str,
-        pre_auth_session_id: str,
-        tenant_id: str,
-        api_options: APIOptions,
-        user_context: Dict[str, Any],
-    ) -> Union[
-        ResendCodePostOkResult, ResendCodePostRestartFlowError, GeneralErrorResponse
-    ]:
-        pass
-
-    @abstractmethod
-    async def consume_code_post(
-        self,
-        pre_auth_session_id: str,
-        user_input_code: Union[str, None],
-        device_id: Union[str, None],
-        link_code: Union[str, None],
-        tenant_id: str,
-        api_options: APIOptions,
-        user_context: Dict[str, Any],
-    ) -> Union[
-        ConsumeCodePostOkResult,
-        ConsumeCodePostRestartFlowError,
-        GeneralErrorResponse,
-        ConsumeCodePostIncorrectUserInputCodeError,
-        ConsumeCodePostExpiredUserInputCodeError,
-    ]:
-        pass
-
-    @abstractmethod
-    async def email_exists_get(
-        self,
-        email: str,
-        tenant_id: str,
-        api_options: APIOptions,
-        user_context: Dict[str, Any],
-    ) -> Union[EmailExistsGetOkResult, GeneralErrorResponse]:
-        pass
-
-    @abstractmethod
-    async def phone_number_exists_get(
-        self,
-        phone_number: str,
-        tenant_id: str,
-        api_options: APIOptions,
-        user_context: Dict[str, Any],
-    ) -> Union[PhoneNumberExistsGetOkResult, GeneralErrorResponse]:
-        pass
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class APIInterface -
-
-
-
- -Expand source code - -
class APIInterface:
-    def __init__(self):
-        self.disable_create_code_post = False
-        self.disable_resend_code_post = False
-        self.disable_consume_code_post = False
-        self.disable_email_exists_get = False
-        self.disable_phone_number_exists_get = False
-
-    @abstractmethod
-    async def create_code_post(
-        self,
-        email: Union[str, None],
-        phone_number: Union[str, None],
-        tenant_id: str,
-        api_options: APIOptions,
-        user_context: Dict[str, Any],
-    ) -> Union[CreateCodePostOkResult, GeneralErrorResponse]:
-        pass
-
-    @abstractmethod
-    async def resend_code_post(
-        self,
-        device_id: str,
-        pre_auth_session_id: str,
-        tenant_id: str,
-        api_options: APIOptions,
-        user_context: Dict[str, Any],
-    ) -> Union[
-        ResendCodePostOkResult, ResendCodePostRestartFlowError, GeneralErrorResponse
-    ]:
-        pass
-
-    @abstractmethod
-    async def consume_code_post(
-        self,
-        pre_auth_session_id: str,
-        user_input_code: Union[str, None],
-        device_id: Union[str, None],
-        link_code: Union[str, None],
-        tenant_id: str,
-        api_options: APIOptions,
-        user_context: Dict[str, Any],
-    ) -> Union[
-        ConsumeCodePostOkResult,
-        ConsumeCodePostRestartFlowError,
-        GeneralErrorResponse,
-        ConsumeCodePostIncorrectUserInputCodeError,
-        ConsumeCodePostExpiredUserInputCodeError,
-    ]:
-        pass
-
-    @abstractmethod
-    async def email_exists_get(
-        self,
-        email: str,
-        tenant_id: str,
-        api_options: APIOptions,
-        user_context: Dict[str, Any],
-    ) -> Union[EmailExistsGetOkResult, GeneralErrorResponse]:
-        pass
-
-    @abstractmethod
-    async def phone_number_exists_get(
-        self,
-        phone_number: str,
-        tenant_id: str,
-        api_options: APIOptions,
-        user_context: Dict[str, Any],
-    ) -> Union[PhoneNumberExistsGetOkResult, GeneralErrorResponse]:
-        pass
-
-

Subclasses

- -

Methods

-
-
-async def consume_code_post(self, pre_auth_session_id: str, user_input_code: Union[str, None], device_id: Union[str, None], link_code: Union[str, None], tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[ConsumeCodePostOkResultConsumeCodePostRestartFlowErrorConsumeCodePostIncorrectUserInputCodeErrorConsumeCodePostExpiredUserInputCodeErrorGeneralErrorResponse] -
-
-
-
- -Expand source code - -
@abstractmethod
-async def consume_code_post(
-    self,
-    pre_auth_session_id: str,
-    user_input_code: Union[str, None],
-    device_id: Union[str, None],
-    link_code: Union[str, None],
-    tenant_id: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-) -> Union[
-    ConsumeCodePostOkResult,
-    ConsumeCodePostRestartFlowError,
-    GeneralErrorResponse,
-    ConsumeCodePostIncorrectUserInputCodeError,
-    ConsumeCodePostExpiredUserInputCodeError,
-]:
-    pass
-
-
-
-async def create_code_post(self, email: Union[str, None], phone_number: Union[str, None], tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[CreateCodePostOkResultGeneralErrorResponse] -
-
-
-
- -Expand source code - -
@abstractmethod
-async def create_code_post(
-    self,
-    email: Union[str, None],
-    phone_number: Union[str, None],
-    tenant_id: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-) -> Union[CreateCodePostOkResult, GeneralErrorResponse]:
-    pass
-
-
-
-async def email_exists_get(self, email: str, tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[EmailExistsGetOkResultGeneralErrorResponse] -
-
-
-
- -Expand source code - -
@abstractmethod
-async def email_exists_get(
-    self,
-    email: str,
-    tenant_id: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-) -> Union[EmailExistsGetOkResult, GeneralErrorResponse]:
-    pass
-
-
-
-async def phone_number_exists_get(self, phone_number: str, tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[PhoneNumberExistsGetOkResultGeneralErrorResponse] -
-
-
-
- -Expand source code - -
@abstractmethod
-async def phone_number_exists_get(
-    self,
-    phone_number: str,
-    tenant_id: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-) -> Union[PhoneNumberExistsGetOkResult, GeneralErrorResponse]:
-    pass
-
-
-
-async def resend_code_post(self, device_id: str, pre_auth_session_id: str, tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[ResendCodePostOkResultResendCodePostRestartFlowErrorGeneralErrorResponse] -
-
-
-
- -Expand source code - -
@abstractmethod
-async def resend_code_post(
-    self,
-    device_id: str,
-    pre_auth_session_id: str,
-    tenant_id: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-) -> Union[
-    ResendCodePostOkResult, ResendCodePostRestartFlowError, GeneralErrorResponse
-]:
-    pass
-
-
-
-
-
-class APIOptions -(request: BaseRequest, response: BaseResponse, recipe_id: str, config: PasswordlessConfig, recipe_implementation: RecipeInterface, app_info: AppInfo, email_delivery: EmailDeliveryIngredient[PasswordlessLoginEmailTemplateVars], sms_delivery: SMSDeliveryIngredient[PasswordlessLoginSMSTemplateVars]) -
-
-
-
- -Expand source code - -
class APIOptions:
-    def __init__(
-        self,
-        request: BaseRequest,
-        response: BaseResponse,
-        recipe_id: str,
-        config: PasswordlessConfig,
-        recipe_implementation: RecipeInterface,
-        app_info: AppInfo,
-        email_delivery: EmailDeliveryIngredient[PasswordlessLoginEmailTemplateVars],
-        sms_delivery: SMSDeliveryIngredient[PasswordlessLoginSMSTemplateVars],
-    ):
-        self.request = request
-        self.response = response
-        self.recipe_id = recipe_id
-        self.config = config
-        self.recipe_implementation = recipe_implementation
-        self.app_info = app_info
-        self.email_delivery = email_delivery
-        self.sms_delivery = sms_delivery
-
-
-
-class ConsumeCodeExpiredUserInputCodeError -(failed_code_input_attempt_count: int, maximum_code_input_attempts: int) -
-
-
-
- -Expand source code - -
class ConsumeCodeExpiredUserInputCodeError:
-    def __init__(
-        self, failed_code_input_attempt_count: int, maximum_code_input_attempts: int
-    ):
-        self.failed_code_input_attempt_count = failed_code_input_attempt_count
-        self.maximum_code_input_attempts = maximum_code_input_attempts
-
-
-
-class ConsumeCodeIncorrectUserInputCodeError -(failed_code_input_attempt_count: int, maximum_code_input_attempts: int) -
-
-
-
- -Expand source code - -
class ConsumeCodeIncorrectUserInputCodeError:
-    def __init__(
-        self, failed_code_input_attempt_count: int, maximum_code_input_attempts: int
-    ):
-        self.failed_code_input_attempt_count = failed_code_input_attempt_count
-        self.maximum_code_input_attempts = maximum_code_input_attempts
-
-
-
-class ConsumeCodeOkResult -(created_new_user: bool, user: User) -
-
-
-
- -Expand source code - -
class ConsumeCodeOkResult:
-    def __init__(self, created_new_user: bool, user: User):
-        self.created_new_user = created_new_user
-        self.user = user
-
-
-
-class ConsumeCodePostExpiredUserInputCodeError -(failed_code_input_attempt_count: int, maximum_code_input_attempts: int) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class ConsumeCodePostExpiredUserInputCodeError(APIResponse):
-    status: str = "EXPIRED_USER_INPUT_CODE_ERROR"
-
-    def __init__(
-        self, failed_code_input_attempt_count: int, maximum_code_input_attempts: int
-    ):
-        self.failed_code_input_attempt_count = failed_code_input_attempt_count
-        self.maximum_code_input_attempts = maximum_code_input_attempts
-
-    def to_json(self):
-        return {
-            "status": self.status,
-            "failedCodeInputAttemptCount": self.failed_code_input_attempt_count,
-            "maximumCodeInputAttempts": self.maximum_code_input_attempts,
-        }
-
-

Ancestors

- -

Class variables

-
-
var status : str
-
-
-
-
-

Methods

-
-
-def to_json(self) -
-
-
-
- -Expand source code - -
def to_json(self):
-    return {
-        "status": self.status,
-        "failedCodeInputAttemptCount": self.failed_code_input_attempt_count,
-        "maximumCodeInputAttempts": self.maximum_code_input_attempts,
-    }
-
-
-
-
-
-class ConsumeCodePostIncorrectUserInputCodeError -(failed_code_input_attempt_count: int, maximum_code_input_attempts: int) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class ConsumeCodePostIncorrectUserInputCodeError(APIResponse):
-    status: str = "INCORRECT_USER_INPUT_CODE_ERROR"
-
-    def __init__(
-        self, failed_code_input_attempt_count: int, maximum_code_input_attempts: int
-    ):
-        self.failed_code_input_attempt_count = failed_code_input_attempt_count
-        self.maximum_code_input_attempts = maximum_code_input_attempts
-
-    def to_json(self):
-        return {
-            "status": self.status,
-            "failedCodeInputAttemptCount": self.failed_code_input_attempt_count,
-            "maximumCodeInputAttempts": self.maximum_code_input_attempts,
-        }
-
-

Ancestors

- -

Class variables

-
-
var status : str
-
-
-
-
-

Methods

-
-
-def to_json(self) -
-
-
-
- -Expand source code - -
def to_json(self):
-    return {
-        "status": self.status,
-        "failedCodeInputAttemptCount": self.failed_code_input_attempt_count,
-        "maximumCodeInputAttempts": self.maximum_code_input_attempts,
-    }
-
-
-
-
-
-class ConsumeCodePostOkResult -(created_new_user: bool, user: User, session: SessionContainer) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class ConsumeCodePostOkResult(APIResponse):
-    status: str = "OK"
-
-    def __init__(self, created_new_user: bool, user: User, session: SessionContainer):
-        self.created_new_user = created_new_user
-        self.user = user
-        self.session = session
-
-    def to_json(self):
-        user = {"id": self.user.user_id, "time_joined": self.user.time_joined}
-        if self.user.email is not None:
-            user = {**user, "email": self.user.email}
-        if self.user.phone_number is not None:
-            user = {**user, "phoneNumber": self.user.phone_number}
-        return {
-            "status": self.status,
-            "createdNewUser": self.created_new_user,
-            "user": user,
-        }
-
-

Ancestors

- -

Class variables

-
-
var status : str
-
-
-
-
-

Methods

-
-
-def to_json(self) -
-
-
-
- -Expand source code - -
def to_json(self):
-    user = {"id": self.user.user_id, "time_joined": self.user.time_joined}
-    if self.user.email is not None:
-        user = {**user, "email": self.user.email}
-    if self.user.phone_number is not None:
-        user = {**user, "phoneNumber": self.user.phone_number}
-    return {
-        "status": self.status,
-        "createdNewUser": self.created_new_user,
-        "user": user,
-    }
-
-
-
-
-
-class ConsumeCodePostRestartFlowError -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class ConsumeCodePostRestartFlowError(APIResponse):
-    status: str = "RESTART_FLOW_ERROR"
-
-    def to_json(self):
-        return {"status": self.status}
-
-

Ancestors

- -

Class variables

-
-
var status : str
-
-
-
-
-

Methods

-
-
-def to_json(self) -
-
-
-
- -Expand source code - -
def to_json(self):
-    return {"status": self.status}
-
-
-
-
-
-class ConsumeCodeRestartFlowError -
-
-
-
- -Expand source code - -
class ConsumeCodeRestartFlowError:
-    pass
-
-
-
-class CreateCodeOkResult -(pre_auth_session_id: str, code_id: str, device_id: str, user_input_code: str, link_code: str, code_life_time: int, time_created: int) -
-
-
-
- -Expand source code - -
class CreateCodeOkResult:
-    def __init__(
-        self,
-        pre_auth_session_id: str,
-        code_id: str,
-        device_id: str,
-        user_input_code: str,
-        link_code: str,
-        code_life_time: int,
-        time_created: int,
-    ):
-        self.pre_auth_session_id = pre_auth_session_id
-        self.code_id = code_id
-        self.device_id = device_id
-        self.user_input_code = user_input_code
-        self.link_code = link_code
-        self.code_life_time = code_life_time
-        self.time_created = time_created
-
-
-
-class CreateCodePostOkResult -(device_id: str, pre_auth_session_id: str, flow_type: "Literal['USER_INPUT_CODE', 'MAGIC_LINK', 'USER_INPUT_CODE_AND_MAGIC_LINK']") -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class CreateCodePostOkResult(APIResponse):
-    status: str = "OK"
-
-    def __init__(
-        self,
-        device_id: str,
-        pre_auth_session_id: str,
-        flow_type: Literal[
-            "USER_INPUT_CODE", "MAGIC_LINK", "USER_INPUT_CODE_AND_MAGIC_LINK"
-        ],
-    ):
-        self.device_id = device_id
-        self.pre_auth_session_id = pre_auth_session_id
-        self.flow_type = flow_type
-
-    def to_json(self):
-        return {
-            "status": self.status,
-            "deviceId": self.device_id,
-            "preAuthSessionId": self.pre_auth_session_id,
-            "flowType": self.flow_type,
-        }
-
-

Ancestors

- -

Class variables

-
-
var status : str
-
-
-
-
-

Methods

-
-
-def to_json(self) -
-
-
-
- -Expand source code - -
def to_json(self):
-    return {
-        "status": self.status,
-        "deviceId": self.device_id,
-        "preAuthSessionId": self.pre_auth_session_id,
-        "flowType": self.flow_type,
-    }
-
-
-
-
-
-class CreateNewCodeForDeviceOkResult -(pre_auth_session_id: str, code_id: str, device_id: str, user_input_code: str, link_code: str, code_life_time: int, time_created: int) -
-
-
-
- -Expand source code - -
class CreateNewCodeForDeviceOkResult:
-    def __init__(
-        self,
-        pre_auth_session_id: str,
-        code_id: str,
-        device_id: str,
-        user_input_code: str,
-        link_code: str,
-        code_life_time: int,
-        time_created: int,
-    ):
-        self.pre_auth_session_id = pre_auth_session_id
-        self.code_id = code_id
-        self.device_id = device_id
-        self.user_input_code = user_input_code
-        self.link_code = link_code
-        self.code_life_time = code_life_time
-        self.time_created = time_created
-
-
-
-class CreateNewCodeForDeviceRestartFlowError -
-
-
-
- -Expand source code - -
class CreateNewCodeForDeviceRestartFlowError:
-    pass
-
-
-
-class CreateNewCodeForDeviceUserInputCodeAlreadyUsedError -
-
-
-
- -Expand source code - -
class CreateNewCodeForDeviceUserInputCodeAlreadyUsedError:
-    pass
-
-
-
-class DeleteUserInfoOkResult -
-
-
-
- -Expand source code - -
class DeleteUserInfoOkResult:
-    pass
-
-
-
-class DeleteUserInfoUnknownUserIdError -
-
-
-
- -Expand source code - -
class DeleteUserInfoUnknownUserIdError:
-    pass
-
-
-
-class EmailExistsGetOkResult -(exists: bool) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class EmailExistsGetOkResult(APIResponse):
-    status: str = "OK"
-
-    def __init__(self, exists: bool):
-        self.exists = exists
-
-    def to_json(self):
-        return {"status": self.status, "exists": self.exists}
-
-

Ancestors

- -

Class variables

-
-
var status : str
-
-
-
-
-

Methods

-
-
-def to_json(self) -
-
-
-
- -Expand source code - -
def to_json(self):
-    return {"status": self.status, "exists": self.exists}
-
-
-
-
-
-class PhoneNumberExistsGetOkResult -(exists: bool) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class PhoneNumberExistsGetOkResult(APIResponse):
-    status: str = "OK"
-
-    def __init__(self, exists: bool):
-        self.exists = exists
-
-    def to_json(self):
-        return {"status": self.status, "exists": self.exists}
-
-

Ancestors

- -

Class variables

-
-
var status : str
-
-
-
-
-

Methods

-
-
-def to_json(self) -
-
-
-
- -Expand source code - -
def to_json(self):
-    return {"status": self.status, "exists": self.exists}
-
-
-
-
-
-class RecipeInterface -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class RecipeInterface(ABC):
-    def __init__(self):
-        pass
-
-    @abstractmethod
-    async def create_code(
-        self,
-        email: Union[None, str],
-        phone_number: Union[None, str],
-        user_input_code: Union[None, str],
-        tenant_id: str,
-        user_context: Dict[str, Any],
-    ) -> CreateCodeOkResult:
-        pass
-
-    @abstractmethod
-    async def create_new_code_for_device(
-        self,
-        device_id: str,
-        user_input_code: Union[str, None],
-        tenant_id: str,
-        user_context: Dict[str, Any],
-    ) -> Union[
-        CreateNewCodeForDeviceOkResult,
-        CreateNewCodeForDeviceRestartFlowError,
-        CreateNewCodeForDeviceUserInputCodeAlreadyUsedError,
-    ]:
-        pass
-
-    @abstractmethod
-    async def consume_code(
-        self,
-        pre_auth_session_id: str,
-        user_input_code: Union[str, None],
-        device_id: Union[str, None],
-        link_code: Union[str, None],
-        tenant_id: str,
-        user_context: Dict[str, Any],
-    ) -> Union[
-        ConsumeCodeOkResult,
-        ConsumeCodeIncorrectUserInputCodeError,
-        ConsumeCodeExpiredUserInputCodeError,
-        ConsumeCodeRestartFlowError,
-    ]:
-        pass
-
-    @abstractmethod
-    async def get_user_by_id(
-        self, user_id: str, user_context: Dict[str, Any]
-    ) -> Union[User, None]:
-        pass
-
-    @abstractmethod
-    async def get_user_by_email(
-        self, email: str, tenant_id: str, user_context: Dict[str, Any]
-    ) -> Union[User, None]:
-        pass
-
-    @abstractmethod
-    async def get_user_by_phone_number(
-        self, phone_number: str, tenant_id: str, user_context: Dict[str, Any]
-    ) -> Union[User, None]:
-        pass
-
-    @abstractmethod
-    async def update_user(
-        self,
-        user_id: str,
-        email: Union[str, None],
-        phone_number: Union[str, None],
-        user_context: Dict[str, Any],
-    ) -> Union[
-        UpdateUserOkResult,
-        UpdateUserUnknownUserIdError,
-        UpdateUserEmailAlreadyExistsError,
-        UpdateUserPhoneNumberAlreadyExistsError,
-    ]:
-        pass
-
-    @abstractmethod
-    async def delete_email_for_user(
-        self, user_id: str, user_context: Dict[str, Any]
-    ) -> Union[DeleteUserInfoOkResult, DeleteUserInfoUnknownUserIdError]:
-        pass
-
-    @abstractmethod
-    async def delete_phone_number_for_user(
-        self, user_id: str, user_context: Dict[str, Any]
-    ) -> Union[DeleteUserInfoOkResult, DeleteUserInfoUnknownUserIdError]:
-        pass
-
-    @abstractmethod
-    async def revoke_all_codes(
-        self,
-        email: Union[str, None],
-        phone_number: Union[str, None],
-        tenant_id: str,
-        user_context: Dict[str, Any],
-    ) -> RevokeAllCodesOkResult:
-        pass
-
-    @abstractmethod
-    async def revoke_code(
-        self, code_id: str, tenant_id: str, user_context: Dict[str, Any]
-    ) -> RevokeCodeOkResult:
-        pass
-
-    @abstractmethod
-    async def list_codes_by_email(
-        self, email: str, tenant_id: str, user_context: Dict[str, Any]
-    ) -> List[DeviceType]:
-        pass
-
-    @abstractmethod
-    async def list_codes_by_phone_number(
-        self, phone_number: str, tenant_id: str, user_context: Dict[str, Any]
-    ) -> List[DeviceType]:
-        pass
-
-    @abstractmethod
-    async def list_codes_by_device_id(
-        self, device_id: str, tenant_id: str, user_context: Dict[str, Any]
-    ) -> Union[DeviceType, None]:
-        pass
-
-    @abstractmethod
-    async def list_codes_by_pre_auth_session_id(
-        self, pre_auth_session_id: str, tenant_id: str, user_context: Dict[str, Any]
-    ) -> Union[DeviceType, None]:
-        pass
-
-

Ancestors

-
    -
  • abc.ABC
  • -
-

Subclasses

- -

Methods

-
-
-async def consume_code(self, pre_auth_session_id: str, user_input_code: Union[str, None], device_id: Union[str, None], link_code: Union[str, None], tenant_id: str, user_context: Dict[str, Any]) ‑> Union[ConsumeCodeOkResultConsumeCodeIncorrectUserInputCodeErrorConsumeCodeExpiredUserInputCodeErrorConsumeCodeRestartFlowError] -
-
-
-
- -Expand source code - -
@abstractmethod
-async def consume_code(
-    self,
-    pre_auth_session_id: str,
-    user_input_code: Union[str, None],
-    device_id: Union[str, None],
-    link_code: Union[str, None],
-    tenant_id: str,
-    user_context: Dict[str, Any],
-) -> Union[
-    ConsumeCodeOkResult,
-    ConsumeCodeIncorrectUserInputCodeError,
-    ConsumeCodeExpiredUserInputCodeError,
-    ConsumeCodeRestartFlowError,
-]:
-    pass
-
-
-
-async def create_code(self, email: Union[None, str], phone_number: Union[None, str], user_input_code: Union[None, str], tenant_id: str, user_context: Dict[str, Any]) ‑> CreateCodeOkResult -
-
-
-
- -Expand source code - -
@abstractmethod
-async def create_code(
-    self,
-    email: Union[None, str],
-    phone_number: Union[None, str],
-    user_input_code: Union[None, str],
-    tenant_id: str,
-    user_context: Dict[str, Any],
-) -> CreateCodeOkResult:
-    pass
-
-
-
-async def create_new_code_for_device(self, device_id: str, user_input_code: Union[str, None], tenant_id: str, user_context: Dict[str, Any]) ‑> Union[CreateNewCodeForDeviceOkResultCreateNewCodeForDeviceRestartFlowErrorCreateNewCodeForDeviceUserInputCodeAlreadyUsedError] -
-
-
-
- -Expand source code - -
@abstractmethod
-async def create_new_code_for_device(
-    self,
-    device_id: str,
-    user_input_code: Union[str, None],
-    tenant_id: str,
-    user_context: Dict[str, Any],
-) -> Union[
-    CreateNewCodeForDeviceOkResult,
-    CreateNewCodeForDeviceRestartFlowError,
-    CreateNewCodeForDeviceUserInputCodeAlreadyUsedError,
-]:
-    pass
-
-
-
-async def delete_email_for_user(self, user_id: str, user_context: Dict[str, Any]) ‑> Union[DeleteUserInfoOkResultDeleteUserInfoUnknownUserIdError] -
-
-
-
- -Expand source code - -
@abstractmethod
-async def delete_email_for_user(
-    self, user_id: str, user_context: Dict[str, Any]
-) -> Union[DeleteUserInfoOkResult, DeleteUserInfoUnknownUserIdError]:
-    pass
-
-
-
-async def delete_phone_number_for_user(self, user_id: str, user_context: Dict[str, Any]) ‑> Union[DeleteUserInfoOkResultDeleteUserInfoUnknownUserIdError] -
-
-
-
- -Expand source code - -
@abstractmethod
-async def delete_phone_number_for_user(
-    self, user_id: str, user_context: Dict[str, Any]
-) -> Union[DeleteUserInfoOkResult, DeleteUserInfoUnknownUserIdError]:
-    pass
-
-
-
-async def get_user_by_email(self, email: str, tenant_id: str, user_context: Dict[str, Any]) ‑> Optional[User] -
-
-
-
- -Expand source code - -
@abstractmethod
-async def get_user_by_email(
-    self, email: str, tenant_id: str, user_context: Dict[str, Any]
-) -> Union[User, None]:
-    pass
-
-
-
-async def get_user_by_id(self, user_id: str, user_context: Dict[str, Any]) ‑> Optional[User] -
-
-
-
- -Expand source code - -
@abstractmethod
-async def get_user_by_id(
-    self, user_id: str, user_context: Dict[str, Any]
-) -> Union[User, None]:
-    pass
-
-
-
-async def get_user_by_phone_number(self, phone_number: str, tenant_id: str, user_context: Dict[str, Any]) ‑> Optional[User] -
-
-
-
- -Expand source code - -
@abstractmethod
-async def get_user_by_phone_number(
-    self, phone_number: str, tenant_id: str, user_context: Dict[str, Any]
-) -> Union[User, None]:
-    pass
-
-
-
-async def list_codes_by_device_id(self, device_id: str, tenant_id: str, user_context: Dict[str, Any]) ‑> Optional[DeviceType] -
-
-
-
- -Expand source code - -
@abstractmethod
-async def list_codes_by_device_id(
-    self, device_id: str, tenant_id: str, user_context: Dict[str, Any]
-) -> Union[DeviceType, None]:
-    pass
-
-
-
-async def list_codes_by_email(self, email: str, tenant_id: str, user_context: Dict[str, Any]) ‑> List[DeviceType] -
-
-
-
- -Expand source code - -
@abstractmethod
-async def list_codes_by_email(
-    self, email: str, tenant_id: str, user_context: Dict[str, Any]
-) -> List[DeviceType]:
-    pass
-
-
-
-async def list_codes_by_phone_number(self, phone_number: str, tenant_id: str, user_context: Dict[str, Any]) ‑> List[DeviceType] -
-
-
-
- -Expand source code - -
@abstractmethod
-async def list_codes_by_phone_number(
-    self, phone_number: str, tenant_id: str, user_context: Dict[str, Any]
-) -> List[DeviceType]:
-    pass
-
-
-
-async def list_codes_by_pre_auth_session_id(self, pre_auth_session_id: str, tenant_id: str, user_context: Dict[str, Any]) ‑> Optional[DeviceType] -
-
-
-
- -Expand source code - -
@abstractmethod
-async def list_codes_by_pre_auth_session_id(
-    self, pre_auth_session_id: str, tenant_id: str, user_context: Dict[str, Any]
-) -> Union[DeviceType, None]:
-    pass
-
-
-
-async def revoke_all_codes(self, email: Union[str, None], phone_number: Union[str, None], tenant_id: str, user_context: Dict[str, Any]) ‑> RevokeAllCodesOkResult -
-
-
-
- -Expand source code - -
@abstractmethod
-async def revoke_all_codes(
-    self,
-    email: Union[str, None],
-    phone_number: Union[str, None],
-    tenant_id: str,
-    user_context: Dict[str, Any],
-) -> RevokeAllCodesOkResult:
-    pass
-
-
-
-async def revoke_code(self, code_id: str, tenant_id: str, user_context: Dict[str, Any]) ‑> RevokeCodeOkResult -
-
-
-
- -Expand source code - -
@abstractmethod
-async def revoke_code(
-    self, code_id: str, tenant_id: str, user_context: Dict[str, Any]
-) -> RevokeCodeOkResult:
-    pass
-
-
-
-async def update_user(self, user_id: str, email: Union[str, None], phone_number: Union[str, None], user_context: Dict[str, Any]) ‑> Union[UpdateUserOkResultUpdateUserUnknownUserIdErrorUpdateUserEmailAlreadyExistsErrorUpdateUserPhoneNumberAlreadyExistsError] -
-
-
-
- -Expand source code - -
@abstractmethod
-async def update_user(
-    self,
-    user_id: str,
-    email: Union[str, None],
-    phone_number: Union[str, None],
-    user_context: Dict[str, Any],
-) -> Union[
-    UpdateUserOkResult,
-    UpdateUserUnknownUserIdError,
-    UpdateUserEmailAlreadyExistsError,
-    UpdateUserPhoneNumberAlreadyExistsError,
-]:
-    pass
-
-
-
-
-
-class ResendCodePostOkResult -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class ResendCodePostOkResult(APIResponse):
-    status: str = "OK"
-
-    def to_json(self):
-        return {"status": self.status}
-
-

Ancestors

- -

Class variables

-
-
var status : str
-
-
-
-
-

Methods

-
-
-def to_json(self) -
-
-
-
- -Expand source code - -
def to_json(self):
-    return {"status": self.status}
-
-
-
-
-
-class ResendCodePostRestartFlowError -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class ResendCodePostRestartFlowError(APIResponse):
-    status: str = "RESTART_FLOW_ERROR"
-
-    def to_json(self):
-        return {"status": self.status}
-
-

Ancestors

- -

Class variables

-
-
var status : str
-
-
-
-
-

Methods

-
-
-def to_json(self) -
-
-
-
- -Expand source code - -
def to_json(self):
-    return {"status": self.status}
-
-
-
-
-
-class RevokeAllCodesOkResult -
-
-
-
- -Expand source code - -
class RevokeAllCodesOkResult:
-    pass
-
-
-
-class RevokeCodeOkResult -
-
-
-
- -Expand source code - -
class RevokeCodeOkResult:
-    pass
-
-
-
-class UpdateUserEmailAlreadyExistsError -
-
-
-
- -Expand source code - -
class UpdateUserEmailAlreadyExistsError:
-    pass
-
-
-
-class UpdateUserOkResult -
-
-
-
- -Expand source code - -
class UpdateUserOkResult:
-    pass
-
-
-
-class UpdateUserPhoneNumberAlreadyExistsError -
-
-
-
- -Expand source code - -
class UpdateUserPhoneNumberAlreadyExistsError:
-    pass
-
-
-
-class UpdateUserUnknownUserIdError -
-
-
-
- -Expand source code - -
class UpdateUserUnknownUserIdError:
-    pass
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/passwordless/recipe.html b/html/supertokens_python/recipe/passwordless/recipe.html deleted file mode 100644 index 05ac749b2..000000000 --- a/html/supertokens_python/recipe/passwordless/recipe.html +++ /dev/null @@ -1,1165 +0,0 @@ - - - - - - -supertokens_python.recipe.passwordless.recipe API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.passwordless.recipe

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from os import environ
-from typing import TYPE_CHECKING, Any, Awaitable, Callable, Dict, List, Union, Optional
-
-from supertokens_python.ingredients.emaildelivery import EmailDeliveryIngredient
-from supertokens_python.ingredients.emaildelivery.types import EmailDeliveryConfig
-from supertokens_python.ingredients.smsdelivery import SMSDeliveryIngredient
-from supertokens_python.querier import Querier
-from supertokens_python.recipe.passwordless.types import (
-    PasswordlessIngredients,
-    PasswordlessLoginSMSTemplateVars,
-)
-from typing_extensions import Literal
-
-from .api import (
-    consume_code,
-    create_code,
-    email_exists,
-    phone_number_exists,
-    resend_code,
-)
-from .api.implementation import APIImplementation
-from .constants import (
-    CONSUME_CODE_API,
-    CREATE_CODE_API,
-    DOES_EMAIL_EXIST_API,
-    DOES_PHONE_NUMBER_EXIST_API,
-    DOES_EMAIL_EXIST_API_OLD,
-    DOES_PHONE_NUMBER_EXIST_API_OLD,
-    RESEND_CODE_API,
-)
-from .exceptions import SuperTokensPasswordlessError
-from .interfaces import (
-    APIOptions,
-    ConsumeCodeOkResult,
-    RecipeInterface,
-    PasswordlessLoginEmailTemplateVars,
-)
-from .recipe_implementation import RecipeImplementation
-from .utils import (
-    ContactConfig,
-    OverrideConfig,
-    validate_and_normalise_user_input,
-)
-from ..emailverification import EmailVerificationRecipe
-from ..emailverification.interfaces import (
-    GetEmailForUserIdOkResult,
-    EmailDoesNotExistError,
-    UnknownUserIdError,
-)
-from ...post_init_callbacks import PostSTInitCallbacks
-
-if TYPE_CHECKING:
-    from supertokens_python.framework.request import BaseRequest
-    from supertokens_python.framework.response import BaseResponse
-    from supertokens_python.supertokens import AppInfo
-
-from supertokens_python.exceptions import SuperTokensError, raise_general_exception
-from supertokens_python.ingredients.smsdelivery.types import SMSDeliveryConfig
-from supertokens_python.normalised_url_path import NormalisedURLPath
-from supertokens_python.recipe_module import APIHandled, RecipeModule
-
-
-class PasswordlessRecipe(RecipeModule):
-    recipe_id = "passwordless"
-    __instance = None
-    email_delivery: EmailDeliveryIngredient[PasswordlessLoginEmailTemplateVars]
-    sms_delivery: SMSDeliveryIngredient[PasswordlessLoginSMSTemplateVars]
-
-    def __init__(
-        self,
-        recipe_id: str,
-        app_info: AppInfo,
-        contact_config: ContactConfig,
-        flow_type: Literal[
-            "USER_INPUT_CODE", "MAGIC_LINK", "USER_INPUT_CODE_AND_MAGIC_LINK"
-        ],
-        ingredients: PasswordlessIngredients,
-        override: Union[OverrideConfig, None] = None,
-        get_custom_user_input_code: Union[
-            Callable[[str, Dict[str, Any]], Awaitable[str]], None
-        ] = None,
-        email_delivery: Union[
-            EmailDeliveryConfig[PasswordlessLoginEmailTemplateVars], None
-        ] = None,
-        sms_delivery: Union[
-            SMSDeliveryConfig[PasswordlessLoginSMSTemplateVars], None
-        ] = None,
-    ):
-        super().__init__(recipe_id, app_info)
-        self.config = validate_and_normalise_user_input(
-            app_info,
-            contact_config,
-            flow_type,
-            override,
-            get_custom_user_input_code,
-            email_delivery,
-            sms_delivery,
-        )
-
-        recipe_implementation = RecipeImplementation(Querier.get_instance(recipe_id))
-        self.recipe_implementation: RecipeInterface = (
-            recipe_implementation
-            if self.config.override.functions is None
-            else self.config.override.functions(recipe_implementation)
-        )
-
-        api_implementation = APIImplementation()
-        self.api_implementation = (
-            api_implementation
-            if self.config.override.apis is None
-            else self.config.override.apis(api_implementation)
-        )
-
-        email_delivery_ingredient = ingredients.email_delivery
-        if email_delivery_ingredient is None:
-            self.email_delivery = EmailDeliveryIngredient(
-                self.config.get_email_delivery_config()
-            )
-        else:
-            self.email_delivery = email_delivery_ingredient
-
-        sms_delivery_ingredient = ingredients.sms_delivery
-        self.sms_delivery = (
-            SMSDeliveryIngredient(self.config.get_sms_delivery_config())
-            if sms_delivery_ingredient is None
-            else sms_delivery_ingredient
-        )
-
-        def callback():
-            ev_recipe = EmailVerificationRecipe.get_instance_optional()
-            if ev_recipe:
-                ev_recipe.add_get_email_for_user_id_func(self.get_email_for_user_id)
-
-        PostSTInitCallbacks.add_post_init_callback(callback)
-
-    def get_apis_handled(self) -> List[APIHandled]:
-        return [
-            APIHandled(
-                method="post",
-                path_without_api_base_path=NormalisedURLPath(CONSUME_CODE_API),
-                request_id=CONSUME_CODE_API,
-                disabled=self.api_implementation.disable_consume_code_post,
-            ),
-            APIHandled(
-                method="post",
-                path_without_api_base_path=NormalisedURLPath(CREATE_CODE_API),
-                request_id=CREATE_CODE_API,
-                disabled=self.api_implementation.disable_create_code_post,
-            ),
-            APIHandled(
-                method="get",
-                path_without_api_base_path=NormalisedURLPath(DOES_EMAIL_EXIST_API),
-                request_id=DOES_EMAIL_EXIST_API,
-                disabled=self.api_implementation.disable_email_exists_get,
-            ),
-            APIHandled(
-                method="get",
-                path_without_api_base_path=NormalisedURLPath(
-                    DOES_PHONE_NUMBER_EXIST_API
-                ),
-                request_id=DOES_PHONE_NUMBER_EXIST_API,
-                disabled=self.api_implementation.disable_phone_number_exists_get,
-            ),
-            APIHandled(
-                method="get",
-                path_without_api_base_path=NormalisedURLPath(DOES_EMAIL_EXIST_API_OLD),
-                request_id=DOES_EMAIL_EXIST_API_OLD,
-                disabled=self.api_implementation.disable_email_exists_get,
-            ),
-            APIHandled(
-                method="get",
-                path_without_api_base_path=NormalisedURLPath(
-                    DOES_PHONE_NUMBER_EXIST_API_OLD
-                ),
-                request_id=DOES_PHONE_NUMBER_EXIST_API_OLD,
-                disabled=self.api_implementation.disable_phone_number_exists_get,
-            ),
-            APIHandled(
-                method="post",
-                path_without_api_base_path=NormalisedURLPath(RESEND_CODE_API),
-                request_id=RESEND_CODE_API,
-                disabled=self.api_implementation.disable_resend_code_post,
-            ),
-        ]
-
-    async def handle_api_request(
-        self,
-        request_id: str,
-        tenant_id: str,
-        request: BaseRequest,
-        path: NormalisedURLPath,
-        method: str,
-        response: BaseResponse,
-        user_context: Dict[str, Any],
-    ):
-        options = APIOptions(
-            request,
-            response,
-            self.get_recipe_id(),
-            self.config,
-            self.recipe_implementation,
-            self.get_app_info(),
-            self.email_delivery,
-            self.sms_delivery,
-        )
-        if request_id == CONSUME_CODE_API:
-            return await consume_code(
-                self.api_implementation, tenant_id, options, user_context
-            )
-        if request_id == CREATE_CODE_API:
-            return await create_code(
-                self.api_implementation, tenant_id, options, user_context
-            )
-        if request_id in (DOES_EMAIL_EXIST_API, DOES_EMAIL_EXIST_API_OLD):
-            return await email_exists(
-                self.api_implementation, tenant_id, options, user_context
-            )
-        if request_id in (DOES_PHONE_NUMBER_EXIST_API, DOES_PHONE_NUMBER_EXIST_API_OLD):
-            return await phone_number_exists(
-                self.api_implementation, tenant_id, options, user_context
-            )
-        return await resend_code(
-            self.api_implementation, tenant_id, options, user_context
-        )
-
-    async def handle_error(
-        self,
-        request: BaseRequest,
-        err: SuperTokensError,
-        response: BaseResponse,
-        user_context: Dict[str, Any],
-    ) -> BaseResponse:  # type: ignore
-        raise err
-
-    def get_all_cors_headers(self) -> List[str]:
-        return []
-
-    def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
-        return isinstance(err, SuperTokensError) and isinstance(
-            err, SuperTokensPasswordlessError
-        )
-
-    @staticmethod
-    def init(
-        contact_config: ContactConfig,
-        flow_type: Literal[
-            "USER_INPUT_CODE", "MAGIC_LINK", "USER_INPUT_CODE_AND_MAGIC_LINK"
-        ],
-        override: Union[OverrideConfig, None] = None,
-        get_custom_user_input_code: Union[
-            Callable[[str, Dict[str, Any]], Awaitable[str]], None
-        ] = None,
-        email_delivery: Union[
-            EmailDeliveryConfig[PasswordlessLoginEmailTemplateVars], None
-        ] = None,
-        sms_delivery: Union[
-            SMSDeliveryConfig[PasswordlessLoginSMSTemplateVars], None
-        ] = None,
-    ):
-        def func(app_info: AppInfo):
-            if PasswordlessRecipe.__instance is None:
-                ingredients = PasswordlessIngredients(None, None)
-                PasswordlessRecipe.__instance = PasswordlessRecipe(
-                    PasswordlessRecipe.recipe_id,
-                    app_info,
-                    contact_config,
-                    flow_type,
-                    ingredients,
-                    override,
-                    get_custom_user_input_code,
-                    email_delivery,
-                    sms_delivery,
-                )
-                return PasswordlessRecipe.__instance
-            raise_general_exception(
-                "Passwordless recipe has already been initialised. Please check "
-                "your code for bugs."
-            )
-
-        return func
-
-    @staticmethod
-    def get_instance() -> PasswordlessRecipe:
-        if PasswordlessRecipe.__instance is not None:
-            return PasswordlessRecipe.__instance
-        raise_general_exception(
-            "Initialisation not done. Did you forget to call the SuperTokens.init function?"
-        )
-
-    @staticmethod
-    def reset():
-        if ("SUPERTOKENS_ENV" not in environ) or (
-            environ["SUPERTOKENS_ENV"] != "testing"
-        ):
-            raise_general_exception("calling testing function in non testing env")
-        PasswordlessRecipe.__instance = None
-
-    async def create_magic_link(
-        self,
-        email: Union[str, None],
-        phone_number: Union[str, None],
-        tenant_id: str,
-        request: Optional[BaseRequest],
-        user_context: Dict[str, Any],
-    ) -> str:
-        user_input_code = None
-        if self.config.get_custom_user_input_code is not None:
-            user_input_code = await self.config.get_custom_user_input_code(
-                tenant_id, user_context
-            )
-
-        code_info = await self.recipe_implementation.create_code(
-            email=email,
-            phone_number=phone_number,
-            tenant_id=tenant_id,
-            user_input_code=user_input_code,
-            user_context=user_context,
-        )
-
-        app_info = self.get_app_info()
-
-        magic_link = (
-            app_info.get_origin(request, user_context).get_as_string_dangerous()
-            + app_info.website_base_path.get_as_string_dangerous()
-            + "/verify"
-            + "?preAuthSessionId="
-            + code_info.pre_auth_session_id
-            + "&tenantId="
-            + tenant_id
-            + "#"
-            + code_info.link_code
-        )
-        return magic_link
-
-    async def signinup(
-        self,
-        email: Union[str, None],
-        phone_number: Union[str, None],
-        tenant_id: str,
-        user_context: Dict[str, Any],
-    ) -> ConsumeCodeOkResult:
-        code_info = await self.recipe_implementation.create_code(
-            email=email,
-            phone_number=phone_number,
-            user_input_code=None,
-            tenant_id=tenant_id,
-            user_context=user_context,
-        )
-        consume_code_result = await self.recipe_implementation.consume_code(
-            link_code=code_info.link_code,
-            pre_auth_session_id=code_info.pre_auth_session_id,
-            device_id=code_info.device_id,
-            user_input_code=code_info.user_input_code,
-            tenant_id=tenant_id,
-            user_context=user_context,
-        )
-        if isinstance(consume_code_result, ConsumeCodeOkResult):
-            return consume_code_result
-        raise Exception("Failed to create user. Please retry")
-
-    async def get_email_for_user_id(
-        self, user_id: str, user_context: Dict[str, Any]
-    ) -> Union[GetEmailForUserIdOkResult, EmailDoesNotExistError, UnknownUserIdError]:
-        user_info = await self.recipe_implementation.get_user_by_id(
-            user_id, user_context
-        )
-        if user_info is not None:
-            if user_info.email is not None:
-                return GetEmailForUserIdOkResult(user_info.email)
-            return EmailDoesNotExistError()
-        return UnknownUserIdError()
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class PasswordlessRecipe -(recipe_id: str, app_info: AppInfo, contact_config: ContactConfig, flow_type: "Literal['USER_INPUT_CODE', 'MAGIC_LINK', 'USER_INPUT_CODE_AND_MAGIC_LINK']", ingredients: PasswordlessIngredients, override: Union[OverrideConfig, None] = None, get_custom_user_input_code: Union[Callable[[str, Dict[str, Any]], Awaitable[str]], None] = None, email_delivery: Union[EmailDeliveryConfig[PasswordlessLoginEmailTemplateVars], None] = None, sms_delivery: Union[SMSDeliveryConfig[PasswordlessLoginSMSTemplateVars], None] = None) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class PasswordlessRecipe(RecipeModule):
-    recipe_id = "passwordless"
-    __instance = None
-    email_delivery: EmailDeliveryIngredient[PasswordlessLoginEmailTemplateVars]
-    sms_delivery: SMSDeliveryIngredient[PasswordlessLoginSMSTemplateVars]
-
-    def __init__(
-        self,
-        recipe_id: str,
-        app_info: AppInfo,
-        contact_config: ContactConfig,
-        flow_type: Literal[
-            "USER_INPUT_CODE", "MAGIC_LINK", "USER_INPUT_CODE_AND_MAGIC_LINK"
-        ],
-        ingredients: PasswordlessIngredients,
-        override: Union[OverrideConfig, None] = None,
-        get_custom_user_input_code: Union[
-            Callable[[str, Dict[str, Any]], Awaitable[str]], None
-        ] = None,
-        email_delivery: Union[
-            EmailDeliveryConfig[PasswordlessLoginEmailTemplateVars], None
-        ] = None,
-        sms_delivery: Union[
-            SMSDeliveryConfig[PasswordlessLoginSMSTemplateVars], None
-        ] = None,
-    ):
-        super().__init__(recipe_id, app_info)
-        self.config = validate_and_normalise_user_input(
-            app_info,
-            contact_config,
-            flow_type,
-            override,
-            get_custom_user_input_code,
-            email_delivery,
-            sms_delivery,
-        )
-
-        recipe_implementation = RecipeImplementation(Querier.get_instance(recipe_id))
-        self.recipe_implementation: RecipeInterface = (
-            recipe_implementation
-            if self.config.override.functions is None
-            else self.config.override.functions(recipe_implementation)
-        )
-
-        api_implementation = APIImplementation()
-        self.api_implementation = (
-            api_implementation
-            if self.config.override.apis is None
-            else self.config.override.apis(api_implementation)
-        )
-
-        email_delivery_ingredient = ingredients.email_delivery
-        if email_delivery_ingredient is None:
-            self.email_delivery = EmailDeliveryIngredient(
-                self.config.get_email_delivery_config()
-            )
-        else:
-            self.email_delivery = email_delivery_ingredient
-
-        sms_delivery_ingredient = ingredients.sms_delivery
-        self.sms_delivery = (
-            SMSDeliveryIngredient(self.config.get_sms_delivery_config())
-            if sms_delivery_ingredient is None
-            else sms_delivery_ingredient
-        )
-
-        def callback():
-            ev_recipe = EmailVerificationRecipe.get_instance_optional()
-            if ev_recipe:
-                ev_recipe.add_get_email_for_user_id_func(self.get_email_for_user_id)
-
-        PostSTInitCallbacks.add_post_init_callback(callback)
-
-    def get_apis_handled(self) -> List[APIHandled]:
-        return [
-            APIHandled(
-                method="post",
-                path_without_api_base_path=NormalisedURLPath(CONSUME_CODE_API),
-                request_id=CONSUME_CODE_API,
-                disabled=self.api_implementation.disable_consume_code_post,
-            ),
-            APIHandled(
-                method="post",
-                path_without_api_base_path=NormalisedURLPath(CREATE_CODE_API),
-                request_id=CREATE_CODE_API,
-                disabled=self.api_implementation.disable_create_code_post,
-            ),
-            APIHandled(
-                method="get",
-                path_without_api_base_path=NormalisedURLPath(DOES_EMAIL_EXIST_API),
-                request_id=DOES_EMAIL_EXIST_API,
-                disabled=self.api_implementation.disable_email_exists_get,
-            ),
-            APIHandled(
-                method="get",
-                path_without_api_base_path=NormalisedURLPath(
-                    DOES_PHONE_NUMBER_EXIST_API
-                ),
-                request_id=DOES_PHONE_NUMBER_EXIST_API,
-                disabled=self.api_implementation.disable_phone_number_exists_get,
-            ),
-            APIHandled(
-                method="get",
-                path_without_api_base_path=NormalisedURLPath(DOES_EMAIL_EXIST_API_OLD),
-                request_id=DOES_EMAIL_EXIST_API_OLD,
-                disabled=self.api_implementation.disable_email_exists_get,
-            ),
-            APIHandled(
-                method="get",
-                path_without_api_base_path=NormalisedURLPath(
-                    DOES_PHONE_NUMBER_EXIST_API_OLD
-                ),
-                request_id=DOES_PHONE_NUMBER_EXIST_API_OLD,
-                disabled=self.api_implementation.disable_phone_number_exists_get,
-            ),
-            APIHandled(
-                method="post",
-                path_without_api_base_path=NormalisedURLPath(RESEND_CODE_API),
-                request_id=RESEND_CODE_API,
-                disabled=self.api_implementation.disable_resend_code_post,
-            ),
-        ]
-
-    async def handle_api_request(
-        self,
-        request_id: str,
-        tenant_id: str,
-        request: BaseRequest,
-        path: NormalisedURLPath,
-        method: str,
-        response: BaseResponse,
-        user_context: Dict[str, Any],
-    ):
-        options = APIOptions(
-            request,
-            response,
-            self.get_recipe_id(),
-            self.config,
-            self.recipe_implementation,
-            self.get_app_info(),
-            self.email_delivery,
-            self.sms_delivery,
-        )
-        if request_id == CONSUME_CODE_API:
-            return await consume_code(
-                self.api_implementation, tenant_id, options, user_context
-            )
-        if request_id == CREATE_CODE_API:
-            return await create_code(
-                self.api_implementation, tenant_id, options, user_context
-            )
-        if request_id in (DOES_EMAIL_EXIST_API, DOES_EMAIL_EXIST_API_OLD):
-            return await email_exists(
-                self.api_implementation, tenant_id, options, user_context
-            )
-        if request_id in (DOES_PHONE_NUMBER_EXIST_API, DOES_PHONE_NUMBER_EXIST_API_OLD):
-            return await phone_number_exists(
-                self.api_implementation, tenant_id, options, user_context
-            )
-        return await resend_code(
-            self.api_implementation, tenant_id, options, user_context
-        )
-
-    async def handle_error(
-        self,
-        request: BaseRequest,
-        err: SuperTokensError,
-        response: BaseResponse,
-        user_context: Dict[str, Any],
-    ) -> BaseResponse:  # type: ignore
-        raise err
-
-    def get_all_cors_headers(self) -> List[str]:
-        return []
-
-    def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
-        return isinstance(err, SuperTokensError) and isinstance(
-            err, SuperTokensPasswordlessError
-        )
-
-    @staticmethod
-    def init(
-        contact_config: ContactConfig,
-        flow_type: Literal[
-            "USER_INPUT_CODE", "MAGIC_LINK", "USER_INPUT_CODE_AND_MAGIC_LINK"
-        ],
-        override: Union[OverrideConfig, None] = None,
-        get_custom_user_input_code: Union[
-            Callable[[str, Dict[str, Any]], Awaitable[str]], None
-        ] = None,
-        email_delivery: Union[
-            EmailDeliveryConfig[PasswordlessLoginEmailTemplateVars], None
-        ] = None,
-        sms_delivery: Union[
-            SMSDeliveryConfig[PasswordlessLoginSMSTemplateVars], None
-        ] = None,
-    ):
-        def func(app_info: AppInfo):
-            if PasswordlessRecipe.__instance is None:
-                ingredients = PasswordlessIngredients(None, None)
-                PasswordlessRecipe.__instance = PasswordlessRecipe(
-                    PasswordlessRecipe.recipe_id,
-                    app_info,
-                    contact_config,
-                    flow_type,
-                    ingredients,
-                    override,
-                    get_custom_user_input_code,
-                    email_delivery,
-                    sms_delivery,
-                )
-                return PasswordlessRecipe.__instance
-            raise_general_exception(
-                "Passwordless recipe has already been initialised. Please check "
-                "your code for bugs."
-            )
-
-        return func
-
-    @staticmethod
-    def get_instance() -> PasswordlessRecipe:
-        if PasswordlessRecipe.__instance is not None:
-            return PasswordlessRecipe.__instance
-        raise_general_exception(
-            "Initialisation not done. Did you forget to call the SuperTokens.init function?"
-        )
-
-    @staticmethod
-    def reset():
-        if ("SUPERTOKENS_ENV" not in environ) or (
-            environ["SUPERTOKENS_ENV"] != "testing"
-        ):
-            raise_general_exception("calling testing function in non testing env")
-        PasswordlessRecipe.__instance = None
-
-    async def create_magic_link(
-        self,
-        email: Union[str, None],
-        phone_number: Union[str, None],
-        tenant_id: str,
-        request: Optional[BaseRequest],
-        user_context: Dict[str, Any],
-    ) -> str:
-        user_input_code = None
-        if self.config.get_custom_user_input_code is not None:
-            user_input_code = await self.config.get_custom_user_input_code(
-                tenant_id, user_context
-            )
-
-        code_info = await self.recipe_implementation.create_code(
-            email=email,
-            phone_number=phone_number,
-            tenant_id=tenant_id,
-            user_input_code=user_input_code,
-            user_context=user_context,
-        )
-
-        app_info = self.get_app_info()
-
-        magic_link = (
-            app_info.get_origin(request, user_context).get_as_string_dangerous()
-            + app_info.website_base_path.get_as_string_dangerous()
-            + "/verify"
-            + "?preAuthSessionId="
-            + code_info.pre_auth_session_id
-            + "&tenantId="
-            + tenant_id
-            + "#"
-            + code_info.link_code
-        )
-        return magic_link
-
-    async def signinup(
-        self,
-        email: Union[str, None],
-        phone_number: Union[str, None],
-        tenant_id: str,
-        user_context: Dict[str, Any],
-    ) -> ConsumeCodeOkResult:
-        code_info = await self.recipe_implementation.create_code(
-            email=email,
-            phone_number=phone_number,
-            user_input_code=None,
-            tenant_id=tenant_id,
-            user_context=user_context,
-        )
-        consume_code_result = await self.recipe_implementation.consume_code(
-            link_code=code_info.link_code,
-            pre_auth_session_id=code_info.pre_auth_session_id,
-            device_id=code_info.device_id,
-            user_input_code=code_info.user_input_code,
-            tenant_id=tenant_id,
-            user_context=user_context,
-        )
-        if isinstance(consume_code_result, ConsumeCodeOkResult):
-            return consume_code_result
-        raise Exception("Failed to create user. Please retry")
-
-    async def get_email_for_user_id(
-        self, user_id: str, user_context: Dict[str, Any]
-    ) -> Union[GetEmailForUserIdOkResult, EmailDoesNotExistError, UnknownUserIdError]:
-        user_info = await self.recipe_implementation.get_user_by_id(
-            user_id, user_context
-        )
-        if user_info is not None:
-            if user_info.email is not None:
-                return GetEmailForUserIdOkResult(user_info.email)
-            return EmailDoesNotExistError()
-        return UnknownUserIdError()
-
-

Ancestors

- -

Class variables

-
-
var email_deliveryEmailDeliveryIngredient[CreateAndSendCustomEmailParameters]
-
-
-
-
var recipe_id
-
-
-
-
var sms_deliverySMSDeliveryIngredient[CreateAndSendCustomTextMessageParameters]
-
-
-
-
-

Static methods

-
-
-def get_instance() ‑> PasswordlessRecipe -
-
-
-
- -Expand source code - -
@staticmethod
-def get_instance() -> PasswordlessRecipe:
-    if PasswordlessRecipe.__instance is not None:
-        return PasswordlessRecipe.__instance
-    raise_general_exception(
-        "Initialisation not done. Did you forget to call the SuperTokens.init function?"
-    )
-
-
-
-def init(contact_config: ContactConfig, flow_type: "Literal['USER_INPUT_CODE', 'MAGIC_LINK', 'USER_INPUT_CODE_AND_MAGIC_LINK']", override: Union[OverrideConfig, None] = None, get_custom_user_input_code: Union[Callable[[str, Dict[str, Any]], Awaitable[str]], None] = None, email_delivery: Union[EmailDeliveryConfig[PasswordlessLoginEmailTemplateVars], None] = None, sms_delivery: Union[SMSDeliveryConfig[PasswordlessLoginSMSTemplateVars], None] = None) -
-
-
-
- -Expand source code - -
@staticmethod
-def init(
-    contact_config: ContactConfig,
-    flow_type: Literal[
-        "USER_INPUT_CODE", "MAGIC_LINK", "USER_INPUT_CODE_AND_MAGIC_LINK"
-    ],
-    override: Union[OverrideConfig, None] = None,
-    get_custom_user_input_code: Union[
-        Callable[[str, Dict[str, Any]], Awaitable[str]], None
-    ] = None,
-    email_delivery: Union[
-        EmailDeliveryConfig[PasswordlessLoginEmailTemplateVars], None
-    ] = None,
-    sms_delivery: Union[
-        SMSDeliveryConfig[PasswordlessLoginSMSTemplateVars], None
-    ] = None,
-):
-    def func(app_info: AppInfo):
-        if PasswordlessRecipe.__instance is None:
-            ingredients = PasswordlessIngredients(None, None)
-            PasswordlessRecipe.__instance = PasswordlessRecipe(
-                PasswordlessRecipe.recipe_id,
-                app_info,
-                contact_config,
-                flow_type,
-                ingredients,
-                override,
-                get_custom_user_input_code,
-                email_delivery,
-                sms_delivery,
-            )
-            return PasswordlessRecipe.__instance
-        raise_general_exception(
-            "Passwordless recipe has already been initialised. Please check "
-            "your code for bugs."
-        )
-
-    return func
-
-
-
-def reset() -
-
-
-
- -Expand source code - -
@staticmethod
-def reset():
-    if ("SUPERTOKENS_ENV" not in environ) or (
-        environ["SUPERTOKENS_ENV"] != "testing"
-    ):
-        raise_general_exception("calling testing function in non testing env")
-    PasswordlessRecipe.__instance = None
-
-
-
-

Methods

-
- -
-
-
- -Expand source code - -
async def create_magic_link(
-    self,
-    email: Union[str, None],
-    phone_number: Union[str, None],
-    tenant_id: str,
-    request: Optional[BaseRequest],
-    user_context: Dict[str, Any],
-) -> str:
-    user_input_code = None
-    if self.config.get_custom_user_input_code is not None:
-        user_input_code = await self.config.get_custom_user_input_code(
-            tenant_id, user_context
-        )
-
-    code_info = await self.recipe_implementation.create_code(
-        email=email,
-        phone_number=phone_number,
-        tenant_id=tenant_id,
-        user_input_code=user_input_code,
-        user_context=user_context,
-    )
-
-    app_info = self.get_app_info()
-
-    magic_link = (
-        app_info.get_origin(request, user_context).get_as_string_dangerous()
-        + app_info.website_base_path.get_as_string_dangerous()
-        + "/verify"
-        + "?preAuthSessionId="
-        + code_info.pre_auth_session_id
-        + "&tenantId="
-        + tenant_id
-        + "#"
-        + code_info.link_code
-    )
-    return magic_link
-
-
-
-def get_all_cors_headers(self) ‑> List[str] -
-
-
-
- -Expand source code - -
def get_all_cors_headers(self) -> List[str]:
-    return []
-
-
-
-def get_apis_handled(self) ‑> List[APIHandled] -
-
-
-
- -Expand source code - -
def get_apis_handled(self) -> List[APIHandled]:
-    return [
-        APIHandled(
-            method="post",
-            path_without_api_base_path=NormalisedURLPath(CONSUME_CODE_API),
-            request_id=CONSUME_CODE_API,
-            disabled=self.api_implementation.disable_consume_code_post,
-        ),
-        APIHandled(
-            method="post",
-            path_without_api_base_path=NormalisedURLPath(CREATE_CODE_API),
-            request_id=CREATE_CODE_API,
-            disabled=self.api_implementation.disable_create_code_post,
-        ),
-        APIHandled(
-            method="get",
-            path_without_api_base_path=NormalisedURLPath(DOES_EMAIL_EXIST_API),
-            request_id=DOES_EMAIL_EXIST_API,
-            disabled=self.api_implementation.disable_email_exists_get,
-        ),
-        APIHandled(
-            method="get",
-            path_without_api_base_path=NormalisedURLPath(
-                DOES_PHONE_NUMBER_EXIST_API
-            ),
-            request_id=DOES_PHONE_NUMBER_EXIST_API,
-            disabled=self.api_implementation.disable_phone_number_exists_get,
-        ),
-        APIHandled(
-            method="get",
-            path_without_api_base_path=NormalisedURLPath(DOES_EMAIL_EXIST_API_OLD),
-            request_id=DOES_EMAIL_EXIST_API_OLD,
-            disabled=self.api_implementation.disable_email_exists_get,
-        ),
-        APIHandled(
-            method="get",
-            path_without_api_base_path=NormalisedURLPath(
-                DOES_PHONE_NUMBER_EXIST_API_OLD
-            ),
-            request_id=DOES_PHONE_NUMBER_EXIST_API_OLD,
-            disabled=self.api_implementation.disable_phone_number_exists_get,
-        ),
-        APIHandled(
-            method="post",
-            path_without_api_base_path=NormalisedURLPath(RESEND_CODE_API),
-            request_id=RESEND_CODE_API,
-            disabled=self.api_implementation.disable_resend_code_post,
-        ),
-    ]
-
-
-
-async def get_email_for_user_id(self, user_id: str, user_context: Dict[str, Any]) ‑> Union[GetEmailForUserIdOkResultEmailDoesNotExistErrorUnknownUserIdError] -
-
-
-
- -Expand source code - -
async def get_email_for_user_id(
-    self, user_id: str, user_context: Dict[str, Any]
-) -> Union[GetEmailForUserIdOkResult, EmailDoesNotExistError, UnknownUserIdError]:
-    user_info = await self.recipe_implementation.get_user_by_id(
-        user_id, user_context
-    )
-    if user_info is not None:
-        if user_info.email is not None:
-            return GetEmailForUserIdOkResult(user_info.email)
-        return EmailDoesNotExistError()
-    return UnknownUserIdError()
-
-
-
-async def handle_api_request(self, request_id: str, tenant_id: str, request: BaseRequest, path: NormalisedURLPath, method: str, response: BaseResponse, user_context: Dict[str, Any]) -
-
-
-
- -Expand source code - -
async def handle_api_request(
-    self,
-    request_id: str,
-    tenant_id: str,
-    request: BaseRequest,
-    path: NormalisedURLPath,
-    method: str,
-    response: BaseResponse,
-    user_context: Dict[str, Any],
-):
-    options = APIOptions(
-        request,
-        response,
-        self.get_recipe_id(),
-        self.config,
-        self.recipe_implementation,
-        self.get_app_info(),
-        self.email_delivery,
-        self.sms_delivery,
-    )
-    if request_id == CONSUME_CODE_API:
-        return await consume_code(
-            self.api_implementation, tenant_id, options, user_context
-        )
-    if request_id == CREATE_CODE_API:
-        return await create_code(
-            self.api_implementation, tenant_id, options, user_context
-        )
-    if request_id in (DOES_EMAIL_EXIST_API, DOES_EMAIL_EXIST_API_OLD):
-        return await email_exists(
-            self.api_implementation, tenant_id, options, user_context
-        )
-    if request_id in (DOES_PHONE_NUMBER_EXIST_API, DOES_PHONE_NUMBER_EXIST_API_OLD):
-        return await phone_number_exists(
-            self.api_implementation, tenant_id, options, user_context
-        )
-    return await resend_code(
-        self.api_implementation, tenant_id, options, user_context
-    )
-
-
-
-async def handle_error(self, request: BaseRequest, err: SuperTokensError, response: BaseResponse, user_context: Dict[str, Any]) ‑> BaseResponse -
-
-
-
- -Expand source code - -
async def handle_error(
-    self,
-    request: BaseRequest,
-    err: SuperTokensError,
-    response: BaseResponse,
-    user_context: Dict[str, Any],
-) -> BaseResponse:  # type: ignore
-    raise err
-
-
-
-def is_error_from_this_recipe_based_on_instance(self, err: Exception) ‑> bool -
-
-
-
- -Expand source code - -
def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
-    return isinstance(err, SuperTokensError) and isinstance(
-        err, SuperTokensPasswordlessError
-    )
-
-
-
-async def signinup(self, email: Union[str, None], phone_number: Union[str, None], tenant_id: str, user_context: Dict[str, Any]) ‑> ConsumeCodeOkResult -
-
-
-
- -Expand source code - -
async def signinup(
-    self,
-    email: Union[str, None],
-    phone_number: Union[str, None],
-    tenant_id: str,
-    user_context: Dict[str, Any],
-) -> ConsumeCodeOkResult:
-    code_info = await self.recipe_implementation.create_code(
-        email=email,
-        phone_number=phone_number,
-        user_input_code=None,
-        tenant_id=tenant_id,
-        user_context=user_context,
-    )
-    consume_code_result = await self.recipe_implementation.consume_code(
-        link_code=code_info.link_code,
-        pre_auth_session_id=code_info.pre_auth_session_id,
-        device_id=code_info.device_id,
-        user_input_code=code_info.user_input_code,
-        tenant_id=tenant_id,
-        user_context=user_context,
-    )
-    if isinstance(consume_code_result, ConsumeCodeOkResult):
-        return consume_code_result
-    raise Exception("Failed to create user. Please retry")
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/passwordless/recipe_implementation.html b/html/supertokens_python/recipe/passwordless/recipe_implementation.html deleted file mode 100644 index 3973b1c13..000000000 --- a/html/supertokens_python/recipe/passwordless/recipe_implementation.html +++ /dev/null @@ -1,1625 +0,0 @@ - - - - - - -supertokens_python.recipe.passwordless.recipe_implementation API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.passwordless.recipe_implementation

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from typing import Any, Dict, List, Union
-
-from supertokens_python.querier import Querier
-
-from .types import DeviceCode, DeviceType, User
-
-from supertokens_python.normalised_url_path import NormalisedURLPath
-
-from .interfaces import (
-    ConsumeCodeExpiredUserInputCodeError,
-    ConsumeCodeIncorrectUserInputCodeError,
-    ConsumeCodeOkResult,
-    ConsumeCodeRestartFlowError,
-    CreateCodeOkResult,
-    CreateNewCodeForDeviceOkResult,
-    CreateNewCodeForDeviceRestartFlowError,
-    CreateNewCodeForDeviceUserInputCodeAlreadyUsedError,
-    DeleteUserInfoOkResult,
-    DeleteUserInfoUnknownUserIdError,
-    RecipeInterface,
-    RevokeAllCodesOkResult,
-    RevokeCodeOkResult,
-    UpdateUserEmailAlreadyExistsError,
-    UpdateUserOkResult,
-    UpdateUserPhoneNumberAlreadyExistsError,
-    UpdateUserUnknownUserIdError,
-)
-
-
-class RecipeImplementation(RecipeInterface):
-    def __init__(self, querier: Querier):
-        super().__init__()
-        self.querier = querier
-
-    async def create_code(
-        self,
-        email: Union[None, str],
-        phone_number: Union[None, str],
-        user_input_code: Union[None, str],
-        tenant_id: str,
-        user_context: Dict[str, Any],
-    ) -> CreateCodeOkResult:
-        data: Dict[str, Any] = {}
-        if user_input_code is not None:
-            data = {**data, "userInputCode": user_input_code}
-        if email is not None:
-            data = {**data, "email": email}
-        if phone_number is not None:
-            data = {**data, "phoneNumber": phone_number}
-        result = await self.querier.send_post_request(
-            NormalisedURLPath(f"{tenant_id}/recipe/signinup/code"),
-            data,
-            user_context=user_context,
-        )
-        return CreateCodeOkResult(
-            pre_auth_session_id=result["preAuthSessionId"],
-            code_id=result["codeId"],
-            device_id=result["deviceId"],
-            user_input_code=result["userInputCode"],
-            link_code=result["linkCode"],
-            time_created=result["timeCreated"],
-            code_life_time=result["codeLifetime"],
-        )
-
-    async def create_new_code_for_device(
-        self,
-        device_id: str,
-        user_input_code: Union[str, None],
-        tenant_id: str,
-        user_context: Dict[str, Any],
-    ) -> Union[
-        CreateNewCodeForDeviceOkResult,
-        CreateNewCodeForDeviceRestartFlowError,
-        CreateNewCodeForDeviceUserInputCodeAlreadyUsedError,
-    ]:
-        data = {"deviceId": device_id}
-        if user_input_code is not None:
-            data = {**data, "userInputCode": user_input_code}
-        result = await self.querier.send_post_request(
-            NormalisedURLPath(f"{tenant_id}/recipe/signinup/code"),
-            data,
-            user_context=user_context,
-        )
-        if result["status"] == "RESTART_FLOW_ERROR":
-            return CreateNewCodeForDeviceRestartFlowError()
-        if result["status"] == "USER_INPUT_CODE_ALREADY_USED_ERROR":
-            return CreateNewCodeForDeviceUserInputCodeAlreadyUsedError()
-        return CreateNewCodeForDeviceOkResult(
-            pre_auth_session_id=result["preAuthSessionId"],
-            code_id=result["codeId"],
-            device_id=result["deviceId"],
-            user_input_code=result["userInputCode"],
-            link_code=result["linkCode"],
-            code_life_time=result["codeLifetime"],
-            time_created=result["timeCreated"],
-        )
-
-    async def consume_code(
-        self,
-        pre_auth_session_id: str,
-        user_input_code: Union[str, None],
-        device_id: Union[str, None],
-        link_code: Union[str, None],
-        tenant_id: str,
-        user_context: Dict[str, Any],
-    ) -> Union[
-        ConsumeCodeOkResult,
-        ConsumeCodeIncorrectUserInputCodeError,
-        ConsumeCodeExpiredUserInputCodeError,
-        ConsumeCodeRestartFlowError,
-    ]:
-        data = {"preAuthSessionId": pre_auth_session_id}
-        if device_id is not None:
-            data = {**data, "deviceId": device_id, "userInputCode": user_input_code}
-        else:
-            data = {**data, "linkCode": link_code}
-        result = await self.querier.send_post_request(
-            NormalisedURLPath(f"{tenant_id}/recipe/signinup/code/consume"),
-            data,
-            user_context=user_context,
-        )
-        if result["status"] == "OK":
-            email = None
-            phone_number = None
-            if "email" in result["user"]:
-                email = result["user"]["email"]
-            if "phoneNumber" in result["user"]:
-                phone_number = result["user"]["phoneNumber"]
-            user = User(
-                user_id=result["user"]["id"],
-                email=email,
-                phone_number=phone_number,
-                time_joined=result["user"]["timeJoined"],
-                tenant_ids=result["user"]["tenantIds"],
-            )
-            return ConsumeCodeOkResult(result["createdNewUser"], user)
-        if result["status"] == "RESTART_FLOW_ERROR":
-            return ConsumeCodeRestartFlowError()
-        if result["status"] == "INCORRECT_USER_INPUT_CODE_ERROR":
-            return ConsumeCodeIncorrectUserInputCodeError(
-                failed_code_input_attempt_count=result["failedCodeInputAttemptCount"],
-                maximum_code_input_attempts=result["maximumCodeInputAttempts"],
-            )
-        return ConsumeCodeExpiredUserInputCodeError(
-            failed_code_input_attempt_count=result["failedCodeInputAttemptCount"],
-            maximum_code_input_attempts=result["maximumCodeInputAttempts"],
-        )
-
-    async def get_user_by_id(
-        self, user_id: str, user_context: Dict[str, Any]
-    ) -> Union[User, None]:
-        param = {"userId": user_id}
-        result = await self.querier.send_get_request(
-            NormalisedURLPath("/recipe/user"),
-            param,
-            user_context=user_context,
-        )
-        if result["status"] == "OK":
-            email = None
-            phone_number = None
-            if "email" in result["user"]:
-                email = result["user"]["email"]
-            if "phoneNumber" in result["user"]:
-                phone_number = result["user"]["phoneNumber"]
-            return User(
-                user_id=result["user"]["id"],
-                email=email,
-                phone_number=phone_number,
-                time_joined=result["user"]["timeJoined"],
-                tenant_ids=result["user"]["tenantIds"],
-            )
-        return None
-
-    async def get_user_by_email(
-        self, email: str, tenant_id: str, user_context: Dict[str, Any]
-    ) -> Union[User, None]:
-        param = {"email": email}
-        result = await self.querier.send_get_request(
-            NormalisedURLPath(f"{tenant_id}/recipe/user"),
-            param,
-            user_context=user_context,
-        )
-        if result["status"] == "OK":
-            email_resp = None
-            phone_number_resp = None
-            if "email" in result["user"]:
-                email_resp = result["user"]["email"]
-            if "phoneNumber" in result["user"]:
-                phone_number_resp = result["user"]["phoneNumber"]
-            return User(
-                user_id=result["user"]["id"],
-                email=email_resp,
-                phone_number=phone_number_resp,
-                tenant_ids=result["user"]["tenantIds"],
-                time_joined=result["user"]["timeJoined"],
-            )
-        return None
-
-    async def get_user_by_phone_number(
-        self, phone_number: str, tenant_id: str, user_context: Dict[str, Any]
-    ) -> Union[User, None]:
-        param = {"phoneNumber": phone_number}
-        result = await self.querier.send_get_request(
-            NormalisedURLPath(f"{tenant_id}/recipe/user"),
-            param,
-            user_context=user_context,
-        )
-        if result["status"] == "OK":
-            email_resp = None
-            phone_number_resp = None
-            if "email" in result["user"]:
-                email_resp = result["user"]["email"]
-            if "phoneNumber" in result["user"]:
-                phone_number_resp = result["user"]["phoneNumber"]
-            return User(
-                user_id=result["user"]["id"],
-                email=email_resp,
-                phone_number=phone_number_resp,
-                time_joined=result["user"]["timeJoined"],
-                tenant_ids=result["user"]["tenantIds"],
-            )
-        return None
-
-    async def update_user(
-        self,
-        user_id: str,
-        email: Union[str, None],
-        phone_number: Union[str, None],
-        user_context: Dict[str, Any],
-    ) -> Union[
-        UpdateUserOkResult,
-        UpdateUserUnknownUserIdError,
-        UpdateUserEmailAlreadyExistsError,
-        UpdateUserPhoneNumberAlreadyExistsError,
-    ]:
-        data = {"userId": user_id}
-        if email is not None:
-            data = {**data, "email": email}
-        if phone_number is not None:
-            data = {**data, "phoneNumber": phone_number}
-        result = await self.querier.send_put_request(
-            NormalisedURLPath("/recipe/user"),
-            data,
-            user_context=user_context,
-        )
-        if result["status"] == "OK":
-            return UpdateUserOkResult()
-        if result["status"] == "UNKNOWN_USER_ID_ERROR":
-            return UpdateUserUnknownUserIdError()
-        if result["status"] == "EMAIL_ALREADY_EXISTS_ERROR":
-            return UpdateUserEmailAlreadyExistsError()
-        return UpdateUserPhoneNumberAlreadyExistsError()
-
-    async def delete_email_for_user(
-        self, user_id: str, user_context: Dict[str, Any]
-    ) -> Union[DeleteUserInfoOkResult, DeleteUserInfoUnknownUserIdError]:
-        data = {"userId": user_id, "email": None}
-        result = await self.querier.send_put_request(
-            NormalisedURLPath("/recipe/user"),
-            data,
-            user_context=user_context,
-        )
-        if result["status"] == "OK":
-            return DeleteUserInfoOkResult()
-        if result.get("EMAIL_ALREADY_EXISTS_ERROR"):
-            raise Exception("Should never come here")
-        if result.get("PHONE_NUMBER_ALREADY_EXISTS_ERROR"):
-            raise Exception("Should never come here")
-        return DeleteUserInfoUnknownUserIdError()
-
-    async def delete_phone_number_for_user(
-        self, user_id: str, user_context: Dict[str, Any]
-    ) -> Union[DeleteUserInfoOkResult, DeleteUserInfoUnknownUserIdError]:
-        data = {"userId": user_id, "phoneNumber": None}
-        result = await self.querier.send_put_request(
-            NormalisedURLPath("/recipe/user"),
-            data,
-            user_context=user_context,
-        )
-        if result["status"] == "OK":
-            return DeleteUserInfoOkResult()
-        if result.get("EMAIL_ALREADY_EXISTS_ERROR"):
-            raise Exception("Should never come here")
-        if result.get("PHONE_NUMBER_ALREADY_EXISTS_ERROR"):
-            raise Exception("Should never come here")
-        return DeleteUserInfoUnknownUserIdError()
-
-    async def revoke_all_codes(
-        self,
-        email: Union[str, None],
-        phone_number: Union[str, None],
-        tenant_id: str,
-        user_context: Dict[str, Any],
-    ) -> RevokeAllCodesOkResult:
-        data: Dict[str, Any] = {}
-        if email is not None:
-            data = {**data, "email": email}
-        if phone_number is not None:
-            data = {**data, "email": phone_number}
-        await self.querier.send_post_request(
-            NormalisedURLPath(f"{tenant_id}/recipe/signinup/codes/remove"),
-            data,
-            user_context=user_context,
-        )
-        return RevokeAllCodesOkResult()
-
-    async def revoke_code(
-        self, code_id: str, tenant_id: str, user_context: Dict[str, Any]
-    ) -> RevokeCodeOkResult:
-        data = {"codeId": code_id}
-        await self.querier.send_post_request(
-            NormalisedURLPath(f"{tenant_id}/recipe/signinup/code/remove"),
-            data,
-            user_context=user_context,
-        )
-        return RevokeCodeOkResult()
-
-    async def list_codes_by_email(
-        self, email: str, tenant_id: str, user_context: Dict[str, Any]
-    ) -> List[DeviceType]:
-        param = {"email": email}
-        result = await self.querier.send_get_request(
-            NormalisedURLPath(f"{tenant_id}/recipe/signinup/codes"),
-            param,
-            user_context=user_context,
-        )
-        devices: List[DeviceType] = []
-        if "devices" in result:
-            for device in result["devices"]:
-                codes: List[DeviceCode] = []
-                if "code" in device:
-                    for code in device:
-                        codes.append(
-                            DeviceCode(
-                                code_id=code["codeId"],
-                                time_created=code["timeCreated"],
-                                code_life_time=code["codeLifetime"],
-                            )
-                        )
-                email_resp = None
-                phone_number_resp = None
-                if "email" in device:
-                    email_resp = device["email"]
-                if "phoneNumber" in device:
-                    phone_number_resp = device["phoneNumber"]
-                devices.append(
-                    DeviceType(
-                        pre_auth_session_id=device["preAuthSessionId"],
-                        failed_code_input_attempt_count=device[
-                            "failedCodeInputAttemptCount"
-                        ],
-                        codes=codes,
-                        email=email_resp,
-                        phone_number=phone_number_resp,
-                    )
-                )
-        return devices
-
-    async def list_codes_by_phone_number(
-        self, phone_number: str, tenant_id: str, user_context: Dict[str, Any]
-    ) -> List[DeviceType]:
-        param = {"phoneNumber": phone_number}
-        result = await self.querier.send_get_request(
-            NormalisedURLPath(f"{tenant_id}/recipe/signinup/codes"),
-            param,
-            user_context=user_context,
-        )
-        devices: List[DeviceType] = []
-        if "devices" in result:
-            for device in result["devices"]:
-                codes: List[DeviceCode] = []
-                if "code" in device:
-                    for code in device:
-                        codes.append(
-                            DeviceCode(
-                                code_id=code["codeId"],
-                                time_created=code["timeCreated"],
-                                code_life_time=code["codeLifetime"],
-                            )
-                        )
-                email_resp = None
-                phone_number_resp = None
-                if "email" in device:
-                    email_resp = device["email"]
-                if "phoneNumber" in device:
-                    phone_number_resp = device["phoneNumber"]
-                devices.append(
-                    DeviceType(
-                        pre_auth_session_id=device["preAuthSessionId"],
-                        failed_code_input_attempt_count=device[
-                            "failedCodeInputAttemptCount"
-                        ],
-                        codes=codes,
-                        email=email_resp,
-                        phone_number=phone_number_resp,
-                    )
-                )
-        return devices
-
-    async def list_codes_by_device_id(
-        self, device_id: str, tenant_id: str, user_context: Dict[str, Any]
-    ) -> Union[DeviceType, None]:
-        param = {"deviceId": device_id}
-        result = await self.querier.send_get_request(
-            NormalisedURLPath(f"{tenant_id}/recipe/signinup/codes"),
-            param,
-            user_context=user_context,
-        )
-        if "devices" in result and len(result["devices"]) == 1:
-            codes: List[DeviceCode] = []
-            if "code" in result["devices"][0]:
-                for code in result["devices"][0]:
-                    codes.append(
-                        DeviceCode(
-                            code_id=code["codeId"],
-                            time_created=code["timeCreated"],
-                            code_life_time=code["codeLifetime"],
-                        )
-                    )
-            email = None
-            phone_number = None
-            if "email" in result["devices"][0]:
-                email = result["devices"][0]["email"]
-            if "phoneNumber" in result["devices"][0]:
-                phone_number = result["devices"][0]["phoneNumber"]
-            return DeviceType(
-                pre_auth_session_id=result["devices"][0]["preAuthSessionId"],
-                failed_code_input_attempt_count=result["devices"][0][
-                    "failedCodeInputAttemptCount"
-                ],
-                codes=codes,
-                email=email,
-                phone_number=phone_number,
-            )
-        return None
-
-    async def list_codes_by_pre_auth_session_id(
-        self, pre_auth_session_id: str, tenant_id: str, user_context: Dict[str, Any]
-    ) -> Union[DeviceType, None]:
-        param = {"preAuthSessionId": pre_auth_session_id}
-        result = await self.querier.send_get_request(
-            NormalisedURLPath(f"{tenant_id}/recipe/signinup/codes"),
-            param,
-            user_context=user_context,
-        )
-        if "devices" in result and len(result["devices"]) == 1:
-            codes: List[DeviceCode] = []
-            if "code" in result["devices"][0]:
-                for code in result["devices"][0]:
-                    codes.append(
-                        DeviceCode(
-                            code_id=code["codeId"],
-                            time_created=code["timeCreated"],
-                            code_life_time=code["codeLifetime"],
-                        )
-                    )
-            email = None
-            phone_number = None
-            if "email" in result["devices"][0]:
-                email = result["devices"][0]["email"]
-            if "phoneNumber" in result["devices"][0]:
-                phone_number = result["devices"][0]["phoneNumber"]
-            return DeviceType(
-                pre_auth_session_id=result["devices"][0]["preAuthSessionId"],
-                failed_code_input_attempt_count=result["devices"][0][
-                    "failedCodeInputAttemptCount"
-                ],
-                codes=codes,
-                email=email,
-                phone_number=phone_number,
-            )
-        return None
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class RecipeImplementation -(querier: Querier) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class RecipeImplementation(RecipeInterface):
-    def __init__(self, querier: Querier):
-        super().__init__()
-        self.querier = querier
-
-    async def create_code(
-        self,
-        email: Union[None, str],
-        phone_number: Union[None, str],
-        user_input_code: Union[None, str],
-        tenant_id: str,
-        user_context: Dict[str, Any],
-    ) -> CreateCodeOkResult:
-        data: Dict[str, Any] = {}
-        if user_input_code is not None:
-            data = {**data, "userInputCode": user_input_code}
-        if email is not None:
-            data = {**data, "email": email}
-        if phone_number is not None:
-            data = {**data, "phoneNumber": phone_number}
-        result = await self.querier.send_post_request(
-            NormalisedURLPath(f"{tenant_id}/recipe/signinup/code"),
-            data,
-            user_context=user_context,
-        )
-        return CreateCodeOkResult(
-            pre_auth_session_id=result["preAuthSessionId"],
-            code_id=result["codeId"],
-            device_id=result["deviceId"],
-            user_input_code=result["userInputCode"],
-            link_code=result["linkCode"],
-            time_created=result["timeCreated"],
-            code_life_time=result["codeLifetime"],
-        )
-
-    async def create_new_code_for_device(
-        self,
-        device_id: str,
-        user_input_code: Union[str, None],
-        tenant_id: str,
-        user_context: Dict[str, Any],
-    ) -> Union[
-        CreateNewCodeForDeviceOkResult,
-        CreateNewCodeForDeviceRestartFlowError,
-        CreateNewCodeForDeviceUserInputCodeAlreadyUsedError,
-    ]:
-        data = {"deviceId": device_id}
-        if user_input_code is not None:
-            data = {**data, "userInputCode": user_input_code}
-        result = await self.querier.send_post_request(
-            NormalisedURLPath(f"{tenant_id}/recipe/signinup/code"),
-            data,
-            user_context=user_context,
-        )
-        if result["status"] == "RESTART_FLOW_ERROR":
-            return CreateNewCodeForDeviceRestartFlowError()
-        if result["status"] == "USER_INPUT_CODE_ALREADY_USED_ERROR":
-            return CreateNewCodeForDeviceUserInputCodeAlreadyUsedError()
-        return CreateNewCodeForDeviceOkResult(
-            pre_auth_session_id=result["preAuthSessionId"],
-            code_id=result["codeId"],
-            device_id=result["deviceId"],
-            user_input_code=result["userInputCode"],
-            link_code=result["linkCode"],
-            code_life_time=result["codeLifetime"],
-            time_created=result["timeCreated"],
-        )
-
-    async def consume_code(
-        self,
-        pre_auth_session_id: str,
-        user_input_code: Union[str, None],
-        device_id: Union[str, None],
-        link_code: Union[str, None],
-        tenant_id: str,
-        user_context: Dict[str, Any],
-    ) -> Union[
-        ConsumeCodeOkResult,
-        ConsumeCodeIncorrectUserInputCodeError,
-        ConsumeCodeExpiredUserInputCodeError,
-        ConsumeCodeRestartFlowError,
-    ]:
-        data = {"preAuthSessionId": pre_auth_session_id}
-        if device_id is not None:
-            data = {**data, "deviceId": device_id, "userInputCode": user_input_code}
-        else:
-            data = {**data, "linkCode": link_code}
-        result = await self.querier.send_post_request(
-            NormalisedURLPath(f"{tenant_id}/recipe/signinup/code/consume"),
-            data,
-            user_context=user_context,
-        )
-        if result["status"] == "OK":
-            email = None
-            phone_number = None
-            if "email" in result["user"]:
-                email = result["user"]["email"]
-            if "phoneNumber" in result["user"]:
-                phone_number = result["user"]["phoneNumber"]
-            user = User(
-                user_id=result["user"]["id"],
-                email=email,
-                phone_number=phone_number,
-                time_joined=result["user"]["timeJoined"],
-                tenant_ids=result["user"]["tenantIds"],
-            )
-            return ConsumeCodeOkResult(result["createdNewUser"], user)
-        if result["status"] == "RESTART_FLOW_ERROR":
-            return ConsumeCodeRestartFlowError()
-        if result["status"] == "INCORRECT_USER_INPUT_CODE_ERROR":
-            return ConsumeCodeIncorrectUserInputCodeError(
-                failed_code_input_attempt_count=result["failedCodeInputAttemptCount"],
-                maximum_code_input_attempts=result["maximumCodeInputAttempts"],
-            )
-        return ConsumeCodeExpiredUserInputCodeError(
-            failed_code_input_attempt_count=result["failedCodeInputAttemptCount"],
-            maximum_code_input_attempts=result["maximumCodeInputAttempts"],
-        )
-
-    async def get_user_by_id(
-        self, user_id: str, user_context: Dict[str, Any]
-    ) -> Union[User, None]:
-        param = {"userId": user_id}
-        result = await self.querier.send_get_request(
-            NormalisedURLPath("/recipe/user"),
-            param,
-            user_context=user_context,
-        )
-        if result["status"] == "OK":
-            email = None
-            phone_number = None
-            if "email" in result["user"]:
-                email = result["user"]["email"]
-            if "phoneNumber" in result["user"]:
-                phone_number = result["user"]["phoneNumber"]
-            return User(
-                user_id=result["user"]["id"],
-                email=email,
-                phone_number=phone_number,
-                time_joined=result["user"]["timeJoined"],
-                tenant_ids=result["user"]["tenantIds"],
-            )
-        return None
-
-    async def get_user_by_email(
-        self, email: str, tenant_id: str, user_context: Dict[str, Any]
-    ) -> Union[User, None]:
-        param = {"email": email}
-        result = await self.querier.send_get_request(
-            NormalisedURLPath(f"{tenant_id}/recipe/user"),
-            param,
-            user_context=user_context,
-        )
-        if result["status"] == "OK":
-            email_resp = None
-            phone_number_resp = None
-            if "email" in result["user"]:
-                email_resp = result["user"]["email"]
-            if "phoneNumber" in result["user"]:
-                phone_number_resp = result["user"]["phoneNumber"]
-            return User(
-                user_id=result["user"]["id"],
-                email=email_resp,
-                phone_number=phone_number_resp,
-                tenant_ids=result["user"]["tenantIds"],
-                time_joined=result["user"]["timeJoined"],
-            )
-        return None
-
-    async def get_user_by_phone_number(
-        self, phone_number: str, tenant_id: str, user_context: Dict[str, Any]
-    ) -> Union[User, None]:
-        param = {"phoneNumber": phone_number}
-        result = await self.querier.send_get_request(
-            NormalisedURLPath(f"{tenant_id}/recipe/user"),
-            param,
-            user_context=user_context,
-        )
-        if result["status"] == "OK":
-            email_resp = None
-            phone_number_resp = None
-            if "email" in result["user"]:
-                email_resp = result["user"]["email"]
-            if "phoneNumber" in result["user"]:
-                phone_number_resp = result["user"]["phoneNumber"]
-            return User(
-                user_id=result["user"]["id"],
-                email=email_resp,
-                phone_number=phone_number_resp,
-                time_joined=result["user"]["timeJoined"],
-                tenant_ids=result["user"]["tenantIds"],
-            )
-        return None
-
-    async def update_user(
-        self,
-        user_id: str,
-        email: Union[str, None],
-        phone_number: Union[str, None],
-        user_context: Dict[str, Any],
-    ) -> Union[
-        UpdateUserOkResult,
-        UpdateUserUnknownUserIdError,
-        UpdateUserEmailAlreadyExistsError,
-        UpdateUserPhoneNumberAlreadyExistsError,
-    ]:
-        data = {"userId": user_id}
-        if email is not None:
-            data = {**data, "email": email}
-        if phone_number is not None:
-            data = {**data, "phoneNumber": phone_number}
-        result = await self.querier.send_put_request(
-            NormalisedURLPath("/recipe/user"),
-            data,
-            user_context=user_context,
-        )
-        if result["status"] == "OK":
-            return UpdateUserOkResult()
-        if result["status"] == "UNKNOWN_USER_ID_ERROR":
-            return UpdateUserUnknownUserIdError()
-        if result["status"] == "EMAIL_ALREADY_EXISTS_ERROR":
-            return UpdateUserEmailAlreadyExistsError()
-        return UpdateUserPhoneNumberAlreadyExistsError()
-
-    async def delete_email_for_user(
-        self, user_id: str, user_context: Dict[str, Any]
-    ) -> Union[DeleteUserInfoOkResult, DeleteUserInfoUnknownUserIdError]:
-        data = {"userId": user_id, "email": None}
-        result = await self.querier.send_put_request(
-            NormalisedURLPath("/recipe/user"),
-            data,
-            user_context=user_context,
-        )
-        if result["status"] == "OK":
-            return DeleteUserInfoOkResult()
-        if result.get("EMAIL_ALREADY_EXISTS_ERROR"):
-            raise Exception("Should never come here")
-        if result.get("PHONE_NUMBER_ALREADY_EXISTS_ERROR"):
-            raise Exception("Should never come here")
-        return DeleteUserInfoUnknownUserIdError()
-
-    async def delete_phone_number_for_user(
-        self, user_id: str, user_context: Dict[str, Any]
-    ) -> Union[DeleteUserInfoOkResult, DeleteUserInfoUnknownUserIdError]:
-        data = {"userId": user_id, "phoneNumber": None}
-        result = await self.querier.send_put_request(
-            NormalisedURLPath("/recipe/user"),
-            data,
-            user_context=user_context,
-        )
-        if result["status"] == "OK":
-            return DeleteUserInfoOkResult()
-        if result.get("EMAIL_ALREADY_EXISTS_ERROR"):
-            raise Exception("Should never come here")
-        if result.get("PHONE_NUMBER_ALREADY_EXISTS_ERROR"):
-            raise Exception("Should never come here")
-        return DeleteUserInfoUnknownUserIdError()
-
-    async def revoke_all_codes(
-        self,
-        email: Union[str, None],
-        phone_number: Union[str, None],
-        tenant_id: str,
-        user_context: Dict[str, Any],
-    ) -> RevokeAllCodesOkResult:
-        data: Dict[str, Any] = {}
-        if email is not None:
-            data = {**data, "email": email}
-        if phone_number is not None:
-            data = {**data, "email": phone_number}
-        await self.querier.send_post_request(
-            NormalisedURLPath(f"{tenant_id}/recipe/signinup/codes/remove"),
-            data,
-            user_context=user_context,
-        )
-        return RevokeAllCodesOkResult()
-
-    async def revoke_code(
-        self, code_id: str, tenant_id: str, user_context: Dict[str, Any]
-    ) -> RevokeCodeOkResult:
-        data = {"codeId": code_id}
-        await self.querier.send_post_request(
-            NormalisedURLPath(f"{tenant_id}/recipe/signinup/code/remove"),
-            data,
-            user_context=user_context,
-        )
-        return RevokeCodeOkResult()
-
-    async def list_codes_by_email(
-        self, email: str, tenant_id: str, user_context: Dict[str, Any]
-    ) -> List[DeviceType]:
-        param = {"email": email}
-        result = await self.querier.send_get_request(
-            NormalisedURLPath(f"{tenant_id}/recipe/signinup/codes"),
-            param,
-            user_context=user_context,
-        )
-        devices: List[DeviceType] = []
-        if "devices" in result:
-            for device in result["devices"]:
-                codes: List[DeviceCode] = []
-                if "code" in device:
-                    for code in device:
-                        codes.append(
-                            DeviceCode(
-                                code_id=code["codeId"],
-                                time_created=code["timeCreated"],
-                                code_life_time=code["codeLifetime"],
-                            )
-                        )
-                email_resp = None
-                phone_number_resp = None
-                if "email" in device:
-                    email_resp = device["email"]
-                if "phoneNumber" in device:
-                    phone_number_resp = device["phoneNumber"]
-                devices.append(
-                    DeviceType(
-                        pre_auth_session_id=device["preAuthSessionId"],
-                        failed_code_input_attempt_count=device[
-                            "failedCodeInputAttemptCount"
-                        ],
-                        codes=codes,
-                        email=email_resp,
-                        phone_number=phone_number_resp,
-                    )
-                )
-        return devices
-
-    async def list_codes_by_phone_number(
-        self, phone_number: str, tenant_id: str, user_context: Dict[str, Any]
-    ) -> List[DeviceType]:
-        param = {"phoneNumber": phone_number}
-        result = await self.querier.send_get_request(
-            NormalisedURLPath(f"{tenant_id}/recipe/signinup/codes"),
-            param,
-            user_context=user_context,
-        )
-        devices: List[DeviceType] = []
-        if "devices" in result:
-            for device in result["devices"]:
-                codes: List[DeviceCode] = []
-                if "code" in device:
-                    for code in device:
-                        codes.append(
-                            DeviceCode(
-                                code_id=code["codeId"],
-                                time_created=code["timeCreated"],
-                                code_life_time=code["codeLifetime"],
-                            )
-                        )
-                email_resp = None
-                phone_number_resp = None
-                if "email" in device:
-                    email_resp = device["email"]
-                if "phoneNumber" in device:
-                    phone_number_resp = device["phoneNumber"]
-                devices.append(
-                    DeviceType(
-                        pre_auth_session_id=device["preAuthSessionId"],
-                        failed_code_input_attempt_count=device[
-                            "failedCodeInputAttemptCount"
-                        ],
-                        codes=codes,
-                        email=email_resp,
-                        phone_number=phone_number_resp,
-                    )
-                )
-        return devices
-
-    async def list_codes_by_device_id(
-        self, device_id: str, tenant_id: str, user_context: Dict[str, Any]
-    ) -> Union[DeviceType, None]:
-        param = {"deviceId": device_id}
-        result = await self.querier.send_get_request(
-            NormalisedURLPath(f"{tenant_id}/recipe/signinup/codes"),
-            param,
-            user_context=user_context,
-        )
-        if "devices" in result and len(result["devices"]) == 1:
-            codes: List[DeviceCode] = []
-            if "code" in result["devices"][0]:
-                for code in result["devices"][0]:
-                    codes.append(
-                        DeviceCode(
-                            code_id=code["codeId"],
-                            time_created=code["timeCreated"],
-                            code_life_time=code["codeLifetime"],
-                        )
-                    )
-            email = None
-            phone_number = None
-            if "email" in result["devices"][0]:
-                email = result["devices"][0]["email"]
-            if "phoneNumber" in result["devices"][0]:
-                phone_number = result["devices"][0]["phoneNumber"]
-            return DeviceType(
-                pre_auth_session_id=result["devices"][0]["preAuthSessionId"],
-                failed_code_input_attempt_count=result["devices"][0][
-                    "failedCodeInputAttemptCount"
-                ],
-                codes=codes,
-                email=email,
-                phone_number=phone_number,
-            )
-        return None
-
-    async def list_codes_by_pre_auth_session_id(
-        self, pre_auth_session_id: str, tenant_id: str, user_context: Dict[str, Any]
-    ) -> Union[DeviceType, None]:
-        param = {"preAuthSessionId": pre_auth_session_id}
-        result = await self.querier.send_get_request(
-            NormalisedURLPath(f"{tenant_id}/recipe/signinup/codes"),
-            param,
-            user_context=user_context,
-        )
-        if "devices" in result and len(result["devices"]) == 1:
-            codes: List[DeviceCode] = []
-            if "code" in result["devices"][0]:
-                for code in result["devices"][0]:
-                    codes.append(
-                        DeviceCode(
-                            code_id=code["codeId"],
-                            time_created=code["timeCreated"],
-                            code_life_time=code["codeLifetime"],
-                        )
-                    )
-            email = None
-            phone_number = None
-            if "email" in result["devices"][0]:
-                email = result["devices"][0]["email"]
-            if "phoneNumber" in result["devices"][0]:
-                phone_number = result["devices"][0]["phoneNumber"]
-            return DeviceType(
-                pre_auth_session_id=result["devices"][0]["preAuthSessionId"],
-                failed_code_input_attempt_count=result["devices"][0][
-                    "failedCodeInputAttemptCount"
-                ],
-                codes=codes,
-                email=email,
-                phone_number=phone_number,
-            )
-        return None
-
-

Ancestors

- -

Methods

-
-
-async def consume_code(self, pre_auth_session_id: str, user_input_code: Union[str, None], device_id: Union[str, None], link_code: Union[str, None], tenant_id: str, user_context: Dict[str, Any]) ‑> Union[ConsumeCodeOkResultConsumeCodeIncorrectUserInputCodeErrorConsumeCodeExpiredUserInputCodeErrorConsumeCodeRestartFlowError] -
-
-
-
- -Expand source code - -
async def consume_code(
-    self,
-    pre_auth_session_id: str,
-    user_input_code: Union[str, None],
-    device_id: Union[str, None],
-    link_code: Union[str, None],
-    tenant_id: str,
-    user_context: Dict[str, Any],
-) -> Union[
-    ConsumeCodeOkResult,
-    ConsumeCodeIncorrectUserInputCodeError,
-    ConsumeCodeExpiredUserInputCodeError,
-    ConsumeCodeRestartFlowError,
-]:
-    data = {"preAuthSessionId": pre_auth_session_id}
-    if device_id is not None:
-        data = {**data, "deviceId": device_id, "userInputCode": user_input_code}
-    else:
-        data = {**data, "linkCode": link_code}
-    result = await self.querier.send_post_request(
-        NormalisedURLPath(f"{tenant_id}/recipe/signinup/code/consume"),
-        data,
-        user_context=user_context,
-    )
-    if result["status"] == "OK":
-        email = None
-        phone_number = None
-        if "email" in result["user"]:
-            email = result["user"]["email"]
-        if "phoneNumber" in result["user"]:
-            phone_number = result["user"]["phoneNumber"]
-        user = User(
-            user_id=result["user"]["id"],
-            email=email,
-            phone_number=phone_number,
-            time_joined=result["user"]["timeJoined"],
-            tenant_ids=result["user"]["tenantIds"],
-        )
-        return ConsumeCodeOkResult(result["createdNewUser"], user)
-    if result["status"] == "RESTART_FLOW_ERROR":
-        return ConsumeCodeRestartFlowError()
-    if result["status"] == "INCORRECT_USER_INPUT_CODE_ERROR":
-        return ConsumeCodeIncorrectUserInputCodeError(
-            failed_code_input_attempt_count=result["failedCodeInputAttemptCount"],
-            maximum_code_input_attempts=result["maximumCodeInputAttempts"],
-        )
-    return ConsumeCodeExpiredUserInputCodeError(
-        failed_code_input_attempt_count=result["failedCodeInputAttemptCount"],
-        maximum_code_input_attempts=result["maximumCodeInputAttempts"],
-    )
-
-
-
-async def create_code(self, email: Union[None, str], phone_number: Union[None, str], user_input_code: Union[None, str], tenant_id: str, user_context: Dict[str, Any]) ‑> CreateCodeOkResult -
-
-
-
- -Expand source code - -
async def create_code(
-    self,
-    email: Union[None, str],
-    phone_number: Union[None, str],
-    user_input_code: Union[None, str],
-    tenant_id: str,
-    user_context: Dict[str, Any],
-) -> CreateCodeOkResult:
-    data: Dict[str, Any] = {}
-    if user_input_code is not None:
-        data = {**data, "userInputCode": user_input_code}
-    if email is not None:
-        data = {**data, "email": email}
-    if phone_number is not None:
-        data = {**data, "phoneNumber": phone_number}
-    result = await self.querier.send_post_request(
-        NormalisedURLPath(f"{tenant_id}/recipe/signinup/code"),
-        data,
-        user_context=user_context,
-    )
-    return CreateCodeOkResult(
-        pre_auth_session_id=result["preAuthSessionId"],
-        code_id=result["codeId"],
-        device_id=result["deviceId"],
-        user_input_code=result["userInputCode"],
-        link_code=result["linkCode"],
-        time_created=result["timeCreated"],
-        code_life_time=result["codeLifetime"],
-    )
-
-
-
-async def create_new_code_for_device(self, device_id: str, user_input_code: Union[str, None], tenant_id: str, user_context: Dict[str, Any]) ‑> Union[CreateNewCodeForDeviceOkResultCreateNewCodeForDeviceRestartFlowErrorCreateNewCodeForDeviceUserInputCodeAlreadyUsedError] -
-
-
-
- -Expand source code - -
async def create_new_code_for_device(
-    self,
-    device_id: str,
-    user_input_code: Union[str, None],
-    tenant_id: str,
-    user_context: Dict[str, Any],
-) -> Union[
-    CreateNewCodeForDeviceOkResult,
-    CreateNewCodeForDeviceRestartFlowError,
-    CreateNewCodeForDeviceUserInputCodeAlreadyUsedError,
-]:
-    data = {"deviceId": device_id}
-    if user_input_code is not None:
-        data = {**data, "userInputCode": user_input_code}
-    result = await self.querier.send_post_request(
-        NormalisedURLPath(f"{tenant_id}/recipe/signinup/code"),
-        data,
-        user_context=user_context,
-    )
-    if result["status"] == "RESTART_FLOW_ERROR":
-        return CreateNewCodeForDeviceRestartFlowError()
-    if result["status"] == "USER_INPUT_CODE_ALREADY_USED_ERROR":
-        return CreateNewCodeForDeviceUserInputCodeAlreadyUsedError()
-    return CreateNewCodeForDeviceOkResult(
-        pre_auth_session_id=result["preAuthSessionId"],
-        code_id=result["codeId"],
-        device_id=result["deviceId"],
-        user_input_code=result["userInputCode"],
-        link_code=result["linkCode"],
-        code_life_time=result["codeLifetime"],
-        time_created=result["timeCreated"],
-    )
-
-
-
-async def delete_email_for_user(self, user_id: str, user_context: Dict[str, Any]) ‑> Union[DeleteUserInfoOkResultDeleteUserInfoUnknownUserIdError] -
-
-
-
- -Expand source code - -
async def delete_email_for_user(
-    self, user_id: str, user_context: Dict[str, Any]
-) -> Union[DeleteUserInfoOkResult, DeleteUserInfoUnknownUserIdError]:
-    data = {"userId": user_id, "email": None}
-    result = await self.querier.send_put_request(
-        NormalisedURLPath("/recipe/user"),
-        data,
-        user_context=user_context,
-    )
-    if result["status"] == "OK":
-        return DeleteUserInfoOkResult()
-    if result.get("EMAIL_ALREADY_EXISTS_ERROR"):
-        raise Exception("Should never come here")
-    if result.get("PHONE_NUMBER_ALREADY_EXISTS_ERROR"):
-        raise Exception("Should never come here")
-    return DeleteUserInfoUnknownUserIdError()
-
-
-
-async def delete_phone_number_for_user(self, user_id: str, user_context: Dict[str, Any]) ‑> Union[DeleteUserInfoOkResultDeleteUserInfoUnknownUserIdError] -
-
-
-
- -Expand source code - -
async def delete_phone_number_for_user(
-    self, user_id: str, user_context: Dict[str, Any]
-) -> Union[DeleteUserInfoOkResult, DeleteUserInfoUnknownUserIdError]:
-    data = {"userId": user_id, "phoneNumber": None}
-    result = await self.querier.send_put_request(
-        NormalisedURLPath("/recipe/user"),
-        data,
-        user_context=user_context,
-    )
-    if result["status"] == "OK":
-        return DeleteUserInfoOkResult()
-    if result.get("EMAIL_ALREADY_EXISTS_ERROR"):
-        raise Exception("Should never come here")
-    if result.get("PHONE_NUMBER_ALREADY_EXISTS_ERROR"):
-        raise Exception("Should never come here")
-    return DeleteUserInfoUnknownUserIdError()
-
-
-
-async def get_user_by_email(self, email: str, tenant_id: str, user_context: Dict[str, Any]) ‑> Optional[User] -
-
-
-
- -Expand source code - -
async def get_user_by_email(
-    self, email: str, tenant_id: str, user_context: Dict[str, Any]
-) -> Union[User, None]:
-    param = {"email": email}
-    result = await self.querier.send_get_request(
-        NormalisedURLPath(f"{tenant_id}/recipe/user"),
-        param,
-        user_context=user_context,
-    )
-    if result["status"] == "OK":
-        email_resp = None
-        phone_number_resp = None
-        if "email" in result["user"]:
-            email_resp = result["user"]["email"]
-        if "phoneNumber" in result["user"]:
-            phone_number_resp = result["user"]["phoneNumber"]
-        return User(
-            user_id=result["user"]["id"],
-            email=email_resp,
-            phone_number=phone_number_resp,
-            tenant_ids=result["user"]["tenantIds"],
-            time_joined=result["user"]["timeJoined"],
-        )
-    return None
-
-
-
-async def get_user_by_id(self, user_id: str, user_context: Dict[str, Any]) ‑> Optional[User] -
-
-
-
- -Expand source code - -
async def get_user_by_id(
-    self, user_id: str, user_context: Dict[str, Any]
-) -> Union[User, None]:
-    param = {"userId": user_id}
-    result = await self.querier.send_get_request(
-        NormalisedURLPath("/recipe/user"),
-        param,
-        user_context=user_context,
-    )
-    if result["status"] == "OK":
-        email = None
-        phone_number = None
-        if "email" in result["user"]:
-            email = result["user"]["email"]
-        if "phoneNumber" in result["user"]:
-            phone_number = result["user"]["phoneNumber"]
-        return User(
-            user_id=result["user"]["id"],
-            email=email,
-            phone_number=phone_number,
-            time_joined=result["user"]["timeJoined"],
-            tenant_ids=result["user"]["tenantIds"],
-        )
-    return None
-
-
-
-async def get_user_by_phone_number(self, phone_number: str, tenant_id: str, user_context: Dict[str, Any]) ‑> Optional[User] -
-
-
-
- -Expand source code - -
async def get_user_by_phone_number(
-    self, phone_number: str, tenant_id: str, user_context: Dict[str, Any]
-) -> Union[User, None]:
-    param = {"phoneNumber": phone_number}
-    result = await self.querier.send_get_request(
-        NormalisedURLPath(f"{tenant_id}/recipe/user"),
-        param,
-        user_context=user_context,
-    )
-    if result["status"] == "OK":
-        email_resp = None
-        phone_number_resp = None
-        if "email" in result["user"]:
-            email_resp = result["user"]["email"]
-        if "phoneNumber" in result["user"]:
-            phone_number_resp = result["user"]["phoneNumber"]
-        return User(
-            user_id=result["user"]["id"],
-            email=email_resp,
-            phone_number=phone_number_resp,
-            time_joined=result["user"]["timeJoined"],
-            tenant_ids=result["user"]["tenantIds"],
-        )
-    return None
-
-
-
-async def list_codes_by_device_id(self, device_id: str, tenant_id: str, user_context: Dict[str, Any]) ‑> Optional[DeviceType] -
-
-
-
- -Expand source code - -
async def list_codes_by_device_id(
-    self, device_id: str, tenant_id: str, user_context: Dict[str, Any]
-) -> Union[DeviceType, None]:
-    param = {"deviceId": device_id}
-    result = await self.querier.send_get_request(
-        NormalisedURLPath(f"{tenant_id}/recipe/signinup/codes"),
-        param,
-        user_context=user_context,
-    )
-    if "devices" in result and len(result["devices"]) == 1:
-        codes: List[DeviceCode] = []
-        if "code" in result["devices"][0]:
-            for code in result["devices"][0]:
-                codes.append(
-                    DeviceCode(
-                        code_id=code["codeId"],
-                        time_created=code["timeCreated"],
-                        code_life_time=code["codeLifetime"],
-                    )
-                )
-        email = None
-        phone_number = None
-        if "email" in result["devices"][0]:
-            email = result["devices"][0]["email"]
-        if "phoneNumber" in result["devices"][0]:
-            phone_number = result["devices"][0]["phoneNumber"]
-        return DeviceType(
-            pre_auth_session_id=result["devices"][0]["preAuthSessionId"],
-            failed_code_input_attempt_count=result["devices"][0][
-                "failedCodeInputAttemptCount"
-            ],
-            codes=codes,
-            email=email,
-            phone_number=phone_number,
-        )
-    return None
-
-
-
-async def list_codes_by_email(self, email: str, tenant_id: str, user_context: Dict[str, Any]) ‑> List[DeviceType] -
-
-
-
- -Expand source code - -
async def list_codes_by_email(
-    self, email: str, tenant_id: str, user_context: Dict[str, Any]
-) -> List[DeviceType]:
-    param = {"email": email}
-    result = await self.querier.send_get_request(
-        NormalisedURLPath(f"{tenant_id}/recipe/signinup/codes"),
-        param,
-        user_context=user_context,
-    )
-    devices: List[DeviceType] = []
-    if "devices" in result:
-        for device in result["devices"]:
-            codes: List[DeviceCode] = []
-            if "code" in device:
-                for code in device:
-                    codes.append(
-                        DeviceCode(
-                            code_id=code["codeId"],
-                            time_created=code["timeCreated"],
-                            code_life_time=code["codeLifetime"],
-                        )
-                    )
-            email_resp = None
-            phone_number_resp = None
-            if "email" in device:
-                email_resp = device["email"]
-            if "phoneNumber" in device:
-                phone_number_resp = device["phoneNumber"]
-            devices.append(
-                DeviceType(
-                    pre_auth_session_id=device["preAuthSessionId"],
-                    failed_code_input_attempt_count=device[
-                        "failedCodeInputAttemptCount"
-                    ],
-                    codes=codes,
-                    email=email_resp,
-                    phone_number=phone_number_resp,
-                )
-            )
-    return devices
-
-
-
-async def list_codes_by_phone_number(self, phone_number: str, tenant_id: str, user_context: Dict[str, Any]) ‑> List[DeviceType] -
-
-
-
- -Expand source code - -
async def list_codes_by_phone_number(
-    self, phone_number: str, tenant_id: str, user_context: Dict[str, Any]
-) -> List[DeviceType]:
-    param = {"phoneNumber": phone_number}
-    result = await self.querier.send_get_request(
-        NormalisedURLPath(f"{tenant_id}/recipe/signinup/codes"),
-        param,
-        user_context=user_context,
-    )
-    devices: List[DeviceType] = []
-    if "devices" in result:
-        for device in result["devices"]:
-            codes: List[DeviceCode] = []
-            if "code" in device:
-                for code in device:
-                    codes.append(
-                        DeviceCode(
-                            code_id=code["codeId"],
-                            time_created=code["timeCreated"],
-                            code_life_time=code["codeLifetime"],
-                        )
-                    )
-            email_resp = None
-            phone_number_resp = None
-            if "email" in device:
-                email_resp = device["email"]
-            if "phoneNumber" in device:
-                phone_number_resp = device["phoneNumber"]
-            devices.append(
-                DeviceType(
-                    pre_auth_session_id=device["preAuthSessionId"],
-                    failed_code_input_attempt_count=device[
-                        "failedCodeInputAttemptCount"
-                    ],
-                    codes=codes,
-                    email=email_resp,
-                    phone_number=phone_number_resp,
-                )
-            )
-    return devices
-
-
-
-async def list_codes_by_pre_auth_session_id(self, pre_auth_session_id: str, tenant_id: str, user_context: Dict[str, Any]) ‑> Optional[DeviceType] -
-
-
-
- -Expand source code - -
async def list_codes_by_pre_auth_session_id(
-    self, pre_auth_session_id: str, tenant_id: str, user_context: Dict[str, Any]
-) -> Union[DeviceType, None]:
-    param = {"preAuthSessionId": pre_auth_session_id}
-    result = await self.querier.send_get_request(
-        NormalisedURLPath(f"{tenant_id}/recipe/signinup/codes"),
-        param,
-        user_context=user_context,
-    )
-    if "devices" in result and len(result["devices"]) == 1:
-        codes: List[DeviceCode] = []
-        if "code" in result["devices"][0]:
-            for code in result["devices"][0]:
-                codes.append(
-                    DeviceCode(
-                        code_id=code["codeId"],
-                        time_created=code["timeCreated"],
-                        code_life_time=code["codeLifetime"],
-                    )
-                )
-        email = None
-        phone_number = None
-        if "email" in result["devices"][0]:
-            email = result["devices"][0]["email"]
-        if "phoneNumber" in result["devices"][0]:
-            phone_number = result["devices"][0]["phoneNumber"]
-        return DeviceType(
-            pre_auth_session_id=result["devices"][0]["preAuthSessionId"],
-            failed_code_input_attempt_count=result["devices"][0][
-                "failedCodeInputAttemptCount"
-            ],
-            codes=codes,
-            email=email,
-            phone_number=phone_number,
-        )
-    return None
-
-
-
-async def revoke_all_codes(self, email: Union[str, None], phone_number: Union[str, None], tenant_id: str, user_context: Dict[str, Any]) ‑> RevokeAllCodesOkResult -
-
-
-
- -Expand source code - -
async def revoke_all_codes(
-    self,
-    email: Union[str, None],
-    phone_number: Union[str, None],
-    tenant_id: str,
-    user_context: Dict[str, Any],
-) -> RevokeAllCodesOkResult:
-    data: Dict[str, Any] = {}
-    if email is not None:
-        data = {**data, "email": email}
-    if phone_number is not None:
-        data = {**data, "email": phone_number}
-    await self.querier.send_post_request(
-        NormalisedURLPath(f"{tenant_id}/recipe/signinup/codes/remove"),
-        data,
-        user_context=user_context,
-    )
-    return RevokeAllCodesOkResult()
-
-
-
-async def revoke_code(self, code_id: str, tenant_id: str, user_context: Dict[str, Any]) ‑> RevokeCodeOkResult -
-
-
-
- -Expand source code - -
async def revoke_code(
-    self, code_id: str, tenant_id: str, user_context: Dict[str, Any]
-) -> RevokeCodeOkResult:
-    data = {"codeId": code_id}
-    await self.querier.send_post_request(
-        NormalisedURLPath(f"{tenant_id}/recipe/signinup/code/remove"),
-        data,
-        user_context=user_context,
-    )
-    return RevokeCodeOkResult()
-
-
-
-async def update_user(self, user_id: str, email: Union[str, None], phone_number: Union[str, None], user_context: Dict[str, Any]) ‑> Union[UpdateUserOkResultUpdateUserUnknownUserIdErrorUpdateUserEmailAlreadyExistsErrorUpdateUserPhoneNumberAlreadyExistsError] -
-
-
-
- -Expand source code - -
async def update_user(
-    self,
-    user_id: str,
-    email: Union[str, None],
-    phone_number: Union[str, None],
-    user_context: Dict[str, Any],
-) -> Union[
-    UpdateUserOkResult,
-    UpdateUserUnknownUserIdError,
-    UpdateUserEmailAlreadyExistsError,
-    UpdateUserPhoneNumberAlreadyExistsError,
-]:
-    data = {"userId": user_id}
-    if email is not None:
-        data = {**data, "email": email}
-    if phone_number is not None:
-        data = {**data, "phoneNumber": phone_number}
-    result = await self.querier.send_put_request(
-        NormalisedURLPath("/recipe/user"),
-        data,
-        user_context=user_context,
-    )
-    if result["status"] == "OK":
-        return UpdateUserOkResult()
-    if result["status"] == "UNKNOWN_USER_ID_ERROR":
-        return UpdateUserUnknownUserIdError()
-    if result["status"] == "EMAIL_ALREADY_EXISTS_ERROR":
-        return UpdateUserEmailAlreadyExistsError()
-    return UpdateUserPhoneNumberAlreadyExistsError()
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/passwordless/smsdelivery/index.html b/html/supertokens_python/recipe/passwordless/smsdelivery/index.html deleted file mode 100644 index b5d096612..000000000 --- a/html/supertokens_python/recipe/passwordless/smsdelivery/index.html +++ /dev/null @@ -1,83 +0,0 @@ - - - - - - -supertokens_python.recipe.passwordless.smsdelivery API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.passwordless.smsdelivery

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-
-
-

Sub-modules

-
-
supertokens_python.recipe.passwordless.smsdelivery.services
-
-
-
-
-
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/passwordless/smsdelivery/services/backward_compatibility/index.html b/html/supertokens_python/recipe/passwordless/smsdelivery/services/backward_compatibility/index.html deleted file mode 100644 index 822c8815e..000000000 --- a/html/supertokens_python/recipe/passwordless/smsdelivery/services/backward_compatibility/index.html +++ /dev/null @@ -1,305 +0,0 @@ - - - - - - -supertokens_python.recipe.passwordless.smsdelivery.services.backward_compatibility API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.passwordless.smsdelivery.services.backward_compatibility

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-import json
-from os import environ
-from typing import Any, Dict
-
-from httpx import AsyncClient, HTTPStatusError, Response
-from supertokens_python.ingredients.smsdelivery.services.supertokens import (
-    SUPERTOKENS_SMS_SERVICE_URL,
-)
-from supertokens_python.ingredients.smsdelivery.types import SMSDeliveryInterface
-from supertokens_python.logger import log_debug_message
-from supertokens_python.supertokens import AppInfo
-from supertokens_python.utils import handle_httpx_client_exceptions
-
-from ....types import PasswordlessLoginSMSTemplateVars
-
-
-async def create_and_send_sms_using_supertokens_service(
-    app_info: AppInfo, input_: PasswordlessLoginSMSTemplateVars
-):
-    if ("SUPERTOKENS_ENV" in environ) and (environ["SUPERTOKENS_ENV"] == "testing"):
-        return
-
-    sms_input_json = {
-        "appName": app_info.app_name,
-        "type": "PASSWORDLESS_LOGIN",
-        "phoneNumber": input_.phone_number,
-        "codeLifetime": input_.code_life_time,
-    }
-    if input_.user_input_code:
-        sms_input_json["userInputCode"] = input_.user_input_code
-    if input_.url_with_link_code:
-        sms_input_json["urlWithLinkCode"] = input_.url_with_link_code
-
-    try:
-        async with AsyncClient(timeout=30.0) as client:
-            res = await client.post(  # type: ignore
-                SUPERTOKENS_SMS_SERVICE_URL,
-                json={
-                    "smsInput": sms_input_json,
-                },
-                headers={"api-version": "0"},
-            )
-            res.raise_for_status()
-            log_debug_message("Passwordless login SMS sent to %s", input_.phone_number)
-            return
-    except Exception as e:
-        log_debug_message("Error sending passwordless login SMS")
-        handle_httpx_client_exceptions(e)
-
-        if isinstance(e, HTTPStatusError):  # type: ignore
-            res: Response = e.response  # type: ignore
-            if res.status_code != 429:  # type: ignore (429 == Too many requests)
-                data = res.json()
-                if "err" in data:
-                    raise Exception(data["err"])
-                if data:
-                    raise Exception(json.dumps(data))
-                if data is None:
-                    raise e
-            else:
-                pass  # Reach Point (1)
-        else:
-            log_debug_message("Error: %s", str(e))
-            raise e
-
-    # Point (1): Reached only when we get HTTPStatusError with e.response.status_code == 429
-    print(
-        "Free daily SMS quota reached. If you want to use SuperTokens to send SMS, please sign up on supertokens.com to get your SMS API key, else you can also define your own method by overriding the service. For now, we are logging it below:"
-    )
-    print("SMS content:\n", json.dumps(input_.__dict__, indent=2))
-
-
-class BackwardCompatibilityService(
-    SMSDeliveryInterface[PasswordlessLoginSMSTemplateVars]
-):
-    def __init__(
-        self,
-        app_info: AppInfo,
-    ) -> None:
-        self.app_info = app_info
-
-    async def send_sms(
-        self,
-        template_vars: PasswordlessLoginSMSTemplateVars,
-        user_context: Dict[str, Any],
-    ) -> None:
-        await create_and_send_sms_using_supertokens_service(
-            self.app_info, template_vars
-        )  # Note: intentionally not using try-except (unlike other recipes)
-
-
-
-
-
-
-
-

Functions

-
-
-async def create_and_send_sms_using_supertokens_service(app_info: AppInfo, input_: CreateAndSendCustomTextMessageParameters) -
-
-
-
- -Expand source code - -
async def create_and_send_sms_using_supertokens_service(
-    app_info: AppInfo, input_: PasswordlessLoginSMSTemplateVars
-):
-    if ("SUPERTOKENS_ENV" in environ) and (environ["SUPERTOKENS_ENV"] == "testing"):
-        return
-
-    sms_input_json = {
-        "appName": app_info.app_name,
-        "type": "PASSWORDLESS_LOGIN",
-        "phoneNumber": input_.phone_number,
-        "codeLifetime": input_.code_life_time,
-    }
-    if input_.user_input_code:
-        sms_input_json["userInputCode"] = input_.user_input_code
-    if input_.url_with_link_code:
-        sms_input_json["urlWithLinkCode"] = input_.url_with_link_code
-
-    try:
-        async with AsyncClient(timeout=30.0) as client:
-            res = await client.post(  # type: ignore
-                SUPERTOKENS_SMS_SERVICE_URL,
-                json={
-                    "smsInput": sms_input_json,
-                },
-                headers={"api-version": "0"},
-            )
-            res.raise_for_status()
-            log_debug_message("Passwordless login SMS sent to %s", input_.phone_number)
-            return
-    except Exception as e:
-        log_debug_message("Error sending passwordless login SMS")
-        handle_httpx_client_exceptions(e)
-
-        if isinstance(e, HTTPStatusError):  # type: ignore
-            res: Response = e.response  # type: ignore
-            if res.status_code != 429:  # type: ignore (429 == Too many requests)
-                data = res.json()
-                if "err" in data:
-                    raise Exception(data["err"])
-                if data:
-                    raise Exception(json.dumps(data))
-                if data is None:
-                    raise e
-            else:
-                pass  # Reach Point (1)
-        else:
-            log_debug_message("Error: %s", str(e))
-            raise e
-
-    # Point (1): Reached only when we get HTTPStatusError with e.response.status_code == 429
-    print(
-        "Free daily SMS quota reached. If you want to use SuperTokens to send SMS, please sign up on supertokens.com to get your SMS API key, else you can also define your own method by overriding the service. For now, we are logging it below:"
-    )
-    print("SMS content:\n", json.dumps(input_.__dict__, indent=2))
-
-
-
-
-
-

Classes

-
-
-class BackwardCompatibilityService -(app_info: AppInfo) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class BackwardCompatibilityService(
-    SMSDeliveryInterface[PasswordlessLoginSMSTemplateVars]
-):
-    def __init__(
-        self,
-        app_info: AppInfo,
-    ) -> None:
-        self.app_info = app_info
-
-    async def send_sms(
-        self,
-        template_vars: PasswordlessLoginSMSTemplateVars,
-        user_context: Dict[str, Any],
-    ) -> None:
-        await create_and_send_sms_using_supertokens_service(
-            self.app_info, template_vars
-        )  # Note: intentionally not using try-except (unlike other recipes)
-
-

Ancestors

- -

Methods

-
-
-async def send_sms(self, template_vars: CreateAndSendCustomTextMessageParameters, user_context: Dict[str, Any]) ‑> None -
-
-
-
- -Expand source code - -
async def send_sms(
-    self,
-    template_vars: PasswordlessLoginSMSTemplateVars,
-    user_context: Dict[str, Any],
-) -> None:
-    await create_and_send_sms_using_supertokens_service(
-        self.app_info, template_vars
-    )  # Note: intentionally not using try-except (unlike other recipes)
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/passwordless/smsdelivery/services/index.html b/html/supertokens_python/recipe/passwordless/smsdelivery/services/index.html deleted file mode 100644 index 284b41a10..000000000 --- a/html/supertokens_python/recipe/passwordless/smsdelivery/services/index.html +++ /dev/null @@ -1,98 +0,0 @@ - - - - - - -supertokens_python.recipe.passwordless.smsdelivery.services API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.passwordless.smsdelivery.services

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from . import supertokens, twilio
-
-SuperTokensSMSService = supertokens.SuperTokensSMSService
-TwilioService = twilio.TwilioService
-
-
-
-

Sub-modules

-
-
supertokens_python.recipe.passwordless.smsdelivery.services.backward_compatibility
-
-
-
-
supertokens_python.recipe.passwordless.smsdelivery.services.supertokens
-
-
-
-
supertokens_python.recipe.passwordless.smsdelivery.services.twilio
-
-
-
-
-
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/passwordless/smsdelivery/services/supertokens/index.html b/html/supertokens_python/recipe/passwordless/smsdelivery/services/supertokens/index.html deleted file mode 100644 index 5ffaccf49..000000000 --- a/html/supertokens_python/recipe/passwordless/smsdelivery/services/supertokens/index.html +++ /dev/null @@ -1,237 +0,0 @@ - - - - - - -supertokens_python.recipe.passwordless.smsdelivery.services.supertokens API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.passwordless.smsdelivery.services.supertokens

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-
-from typing import Any, Dict
-
-from httpx import AsyncClient
-from supertokens_python.ingredients.smsdelivery.services.supertokens import (
-    SUPERTOKENS_SMS_SERVICE_URL,
-)
-from supertokens_python.ingredients.smsdelivery.types import SMSDeliveryInterface
-from supertokens_python.logger import log_debug_message
-from supertokens_python.supertokens import Supertokens
-from supertokens_python.utils import handle_httpx_client_exceptions
-
-from ....types import PasswordlessLoginSMSTemplateVars
-
-
-class SuperTokensSMSService(SMSDeliveryInterface[PasswordlessLoginSMSTemplateVars]):
-    def __init__(self, api_key: str) -> None:
-        self.api_key = api_key
-
-    async def send_sms(
-        self,
-        template_vars: PasswordlessLoginSMSTemplateVars,
-        user_context: Dict[str, Any],
-    ) -> None:
-        supertokens = Supertokens.get_instance()
-        app_name = supertokens.app_info.app_name
-
-        sms_input = {
-            "type": "PASSWORDLESS_LOGIN",
-            "phoneNumber": template_vars.phone_number,
-            "codeLifetime": template_vars.code_life_time,
-            "appName": app_name,
-        }
-        if template_vars.url_with_link_code:
-            sms_input["urlWithLinkCode"] = template_vars.url_with_link_code
-        if template_vars.user_input_code:
-            sms_input["userInputCode"] = template_vars.user_input_code
-        try:
-            async with AsyncClient(timeout=30.0) as client:
-                await client.post(  # type: ignore
-                    SUPERTOKENS_SMS_SERVICE_URL,
-                    json={
-                        "apiKey": self.api_key,
-                        "smsInput": sms_input,
-                    },
-                    headers={"api-version": "0"},
-                )
-        except Exception as e:
-            log_debug_message("Error sending passwordless login SMS")
-            handle_httpx_client_exceptions(e, sms_input)
-            raise e
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class SuperTokensSMSService -(api_key: str) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class SuperTokensSMSService(SMSDeliveryInterface[PasswordlessLoginSMSTemplateVars]):
-    def __init__(self, api_key: str) -> None:
-        self.api_key = api_key
-
-    async def send_sms(
-        self,
-        template_vars: PasswordlessLoginSMSTemplateVars,
-        user_context: Dict[str, Any],
-    ) -> None:
-        supertokens = Supertokens.get_instance()
-        app_name = supertokens.app_info.app_name
-
-        sms_input = {
-            "type": "PASSWORDLESS_LOGIN",
-            "phoneNumber": template_vars.phone_number,
-            "codeLifetime": template_vars.code_life_time,
-            "appName": app_name,
-        }
-        if template_vars.url_with_link_code:
-            sms_input["urlWithLinkCode"] = template_vars.url_with_link_code
-        if template_vars.user_input_code:
-            sms_input["userInputCode"] = template_vars.user_input_code
-        try:
-            async with AsyncClient(timeout=30.0) as client:
-                await client.post(  # type: ignore
-                    SUPERTOKENS_SMS_SERVICE_URL,
-                    json={
-                        "apiKey": self.api_key,
-                        "smsInput": sms_input,
-                    },
-                    headers={"api-version": "0"},
-                )
-        except Exception as e:
-            log_debug_message("Error sending passwordless login SMS")
-            handle_httpx_client_exceptions(e, sms_input)
-            raise e
-
-

Ancestors

- -

Methods

-
-
-async def send_sms(self, template_vars: CreateAndSendCustomTextMessageParameters, user_context: Dict[str, Any]) ‑> None -
-
-
-
- -Expand source code - -
async def send_sms(
-    self,
-    template_vars: PasswordlessLoginSMSTemplateVars,
-    user_context: Dict[str, Any],
-) -> None:
-    supertokens = Supertokens.get_instance()
-    app_name = supertokens.app_info.app_name
-
-    sms_input = {
-        "type": "PASSWORDLESS_LOGIN",
-        "phoneNumber": template_vars.phone_number,
-        "codeLifetime": template_vars.code_life_time,
-        "appName": app_name,
-    }
-    if template_vars.url_with_link_code:
-        sms_input["urlWithLinkCode"] = template_vars.url_with_link_code
-    if template_vars.user_input_code:
-        sms_input["userInputCode"] = template_vars.user_input_code
-    try:
-        async with AsyncClient(timeout=30.0) as client:
-            await client.post(  # type: ignore
-                SUPERTOKENS_SMS_SERVICE_URL,
-                json={
-                    "apiKey": self.api_key,
-                    "smsInput": sms_input,
-                },
-                headers={"api-version": "0"},
-            )
-    except Exception as e:
-        log_debug_message("Error sending passwordless login SMS")
-        handle_httpx_client_exceptions(e, sms_input)
-        raise e
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/passwordless/smsdelivery/services/twilio/index.html b/html/supertokens_python/recipe/passwordless/smsdelivery/services/twilio/index.html deleted file mode 100644 index 976653810..000000000 --- a/html/supertokens_python/recipe/passwordless/smsdelivery/services/twilio/index.html +++ /dev/null @@ -1,244 +0,0 @@ - - - - - - -supertokens_python.recipe.passwordless.smsdelivery.services.twilio API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.passwordless.smsdelivery.services.twilio

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from __future__ import annotations
-
-from typing import Any, Dict, Callable, Union, TypeVar
-
-from supertokens_python.ingredients.smsdelivery.services.twilio import (
-    normalize_twilio_settings,
-)
-from supertokens_python.ingredients.smsdelivery.types import (
-    SMSDeliveryInterface,
-    TwilioServiceInterface,
-    TwilioSettings,
-)
-from supertokens_python.recipe.passwordless.types import (
-    PasswordlessLoginSMSTemplateVars,
-)
-
-from twilio.rest import Client  # type: ignore
-
-from .service_implementation import ServiceImplementation
-
-_T = TypeVar("_T")
-
-
-class TwilioService(SMSDeliveryInterface[PasswordlessLoginSMSTemplateVars]):
-    service_implementation: TwilioServiceInterface[PasswordlessLoginSMSTemplateVars]
-
-    def __init__(
-        self,
-        twilio_settings: TwilioSettings,
-        override: Union[
-            Callable[[TwilioServiceInterface[_T]], TwilioServiceInterface[_T]], None
-        ] = None,
-    ) -> None:
-        self.config = normalize_twilio_settings(twilio_settings)
-        otps = twilio_settings.opts if twilio_settings.opts else {}
-        self.twilio_client = Client(  # type: ignore
-            twilio_settings.account_sid, twilio_settings.auth_token, **otps
-        )
-        oi = ServiceImplementation(self.twilio_client)  # type: ignore
-        self.service_implementation = oi if override is None else override(oi)
-
-    async def send_sms(
-        self,
-        template_vars: PasswordlessLoginSMSTemplateVars,
-        user_context: Dict[str, Any],
-    ) -> None:
-        content = await self.service_implementation.get_content(
-            template_vars, user_context
-        )
-        await self.service_implementation.send_raw_sms(
-            content,
-            user_context,
-            from_=self.config.from_,
-            messaging_service_sid=self.config.messaging_service_sid,
-        )
-
-
-
-

Sub-modules

-
-
supertokens_python.recipe.passwordless.smsdelivery.services.twilio.passwordless_login
-
-
-
-
supertokens_python.recipe.passwordless.smsdelivery.services.twilio.service_implementation
-
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class TwilioService -(twilio_settings: TwilioSettings, override: Union[Callable[[TwilioServiceInterface[_T]], TwilioServiceInterface[_T]], None] = None) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class TwilioService(SMSDeliveryInterface[PasswordlessLoginSMSTemplateVars]):
-    service_implementation: TwilioServiceInterface[PasswordlessLoginSMSTemplateVars]
-
-    def __init__(
-        self,
-        twilio_settings: TwilioSettings,
-        override: Union[
-            Callable[[TwilioServiceInterface[_T]], TwilioServiceInterface[_T]], None
-        ] = None,
-    ) -> None:
-        self.config = normalize_twilio_settings(twilio_settings)
-        otps = twilio_settings.opts if twilio_settings.opts else {}
-        self.twilio_client = Client(  # type: ignore
-            twilio_settings.account_sid, twilio_settings.auth_token, **otps
-        )
-        oi = ServiceImplementation(self.twilio_client)  # type: ignore
-        self.service_implementation = oi if override is None else override(oi)
-
-    async def send_sms(
-        self,
-        template_vars: PasswordlessLoginSMSTemplateVars,
-        user_context: Dict[str, Any],
-    ) -> None:
-        content = await self.service_implementation.get_content(
-            template_vars, user_context
-        )
-        await self.service_implementation.send_raw_sms(
-            content,
-            user_context,
-            from_=self.config.from_,
-            messaging_service_sid=self.config.messaging_service_sid,
-        )
-
-

Ancestors

- -

Class variables

-
-
var service_implementationTwilioServiceInterface[CreateAndSendCustomTextMessageParameters]
-
-
-
-
-

Methods

-
-
-async def send_sms(self, template_vars: PasswordlessLoginSMSTemplateVars, user_context: Dict[str, Any]) ‑> None -
-
-
-
- -Expand source code - -
async def send_sms(
-    self,
-    template_vars: PasswordlessLoginSMSTemplateVars,
-    user_context: Dict[str, Any],
-) -> None:
-    content = await self.service_implementation.get_content(
-        template_vars, user_context
-    )
-    await self.service_implementation.send_raw_sms(
-        content,
-        user_context,
-        from_=self.config.from_,
-        messaging_service_sid=self.config.messaging_service_sid,
-    )
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/passwordless/smsdelivery/services/twilio/passwordless_login.html b/html/supertokens_python/recipe/passwordless/smsdelivery/services/twilio/passwordless_login.html deleted file mode 100644 index f503ab765..000000000 --- a/html/supertokens_python/recipe/passwordless/smsdelivery/services/twilio/passwordless_login.html +++ /dev/null @@ -1,212 +0,0 @@ - - - - - - -supertokens_python.recipe.passwordless.smsdelivery.services.twilio.passwordless_login API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.passwordless.smsdelivery.services.twilio.passwordless_login

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from string import Template
-from textwrap import dedent
-from typing import TYPE_CHECKING, Union
-
-from supertokens_python.ingredients.smsdelivery.types import SMSContent
-from supertokens_python.supertokens import Supertokens
-from supertokens_python.utils import humanize_time
-
-if TYPE_CHECKING:
-    from supertokens_python.recipe.passwordless.types import (
-        PasswordlessLoginSMSTemplateVars,
-    )
-
-
-def pless_sms_content(input_: PasswordlessLoginSMSTemplateVars) -> SMSContent:
-    supertokens = Supertokens.get_instance()
-    app_name = supertokens.app_info.app_name
-    code_lifetime = humanize_time(input_.code_life_time)
-    body = get_pless_sms_body(
-        app_name, code_lifetime, input_.url_with_link_code, input_.user_input_code
-    )
-    return SMSContent(body, input_.phone_number)
-
-
-def get_pless_sms_body(
-    app_name: str,
-    code_lifetime: str,
-    url_with_link_code: Union[str, None] = None,
-    user_input_code: Union[str, None] = None,
-) -> str:
-    if (url_with_link_code is not None) and (user_input_code is not None):
-        sms_template = dedent(
-            """\
-        OTP to login is ${otp} for ${appname}
-
-        Or click ${magicLink} to login.
-
-        This is valid for ${time}."""
-        )
-    elif url_with_link_code is not None:
-        sms_template = dedent(
-            """\
-        Click ${magicLink} to login to ${appname}
-
-        This is valid for ${time}."""
-        )
-    elif user_input_code is not None:
-        sms_template = dedent(
-            """\
-        OTP to login is ${otp} for ${appname}
-
-        This is valid for ${time}."""
-        )
-    else:
-        raise Exception("This should never be thrown.")
-
-    return Template(sms_template).substitute(
-        appname=app_name,
-        magicLink=url_with_link_code,
-        otp=user_input_code,
-        time=code_lifetime,
-    )
-
-
-
-
-
-
-
-

Functions

-
-
-def get_pless_sms_body(app_name: str, code_lifetime: str, url_with_link_code: Union[str, None] = None, user_input_code: Union[str, None] = None) ‑> str -
-
-
-
- -Expand source code - -
def get_pless_sms_body(
-    app_name: str,
-    code_lifetime: str,
-    url_with_link_code: Union[str, None] = None,
-    user_input_code: Union[str, None] = None,
-) -> str:
-    if (url_with_link_code is not None) and (user_input_code is not None):
-        sms_template = dedent(
-            """\
-        OTP to login is ${otp} for ${appname}
-
-        Or click ${magicLink} to login.
-
-        This is valid for ${time}."""
-        )
-    elif url_with_link_code is not None:
-        sms_template = dedent(
-            """\
-        Click ${magicLink} to login to ${appname}
-
-        This is valid for ${time}."""
-        )
-    elif user_input_code is not None:
-        sms_template = dedent(
-            """\
-        OTP to login is ${otp} for ${appname}
-
-        This is valid for ${time}."""
-        )
-    else:
-        raise Exception("This should never be thrown.")
-
-    return Template(sms_template).substitute(
-        appname=app_name,
-        magicLink=url_with_link_code,
-        otp=user_input_code,
-        time=code_lifetime,
-    )
-
-
-
-def pless_sms_content(input_: PasswordlessLoginSMSTemplateVars) ‑> SMSContent -
-
-
-
- -Expand source code - -
def pless_sms_content(input_: PasswordlessLoginSMSTemplateVars) -> SMSContent:
-    supertokens = Supertokens.get_instance()
-    app_name = supertokens.app_info.app_name
-    code_lifetime = humanize_time(input_.code_life_time)
-    body = get_pless_sms_body(
-        app_name, code_lifetime, input_.url_with_link_code, input_.user_input_code
-    )
-    return SMSContent(body, input_.phone_number)
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/passwordless/smsdelivery/services/twilio/service_implementation.html b/html/supertokens_python/recipe/passwordless/smsdelivery/services/twilio/service_implementation.html deleted file mode 100644 index 468c1aeb4..000000000 --- a/html/supertokens_python/recipe/passwordless/smsdelivery/services/twilio/service_implementation.html +++ /dev/null @@ -1,228 +0,0 @@ - - - - - - -supertokens_python.recipe.passwordless.smsdelivery.services.twilio.service_implementation API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.passwordless.smsdelivery.services.twilio.service_implementation

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from __future__ import annotations
-
-from typing import Any, Dict, Union
-
-from supertokens_python.ingredients.smsdelivery.types import (
-    SMSContent,
-    TwilioServiceInterface,
-)
-from supertokens_python.recipe.passwordless.smsdelivery.services.twilio.passwordless_login import (
-    pless_sms_content,
-)
-from supertokens_python.recipe.passwordless.types import (
-    PasswordlessLoginSMSTemplateVars,
-)
-
-
-class ServiceImplementation(TwilioServiceInterface[PasswordlessLoginSMSTemplateVars]):
-    async def send_raw_sms(
-        self,
-        content: SMSContent,
-        user_context: Dict[str, Any],
-        from_: Union[str, None] = None,
-        messaging_service_sid: Union[str, None] = None,
-    ) -> None:
-        if from_:
-            self.twilio_client.messages.create(  # type: ignore
-                to=content.to_phone,
-                body=content.body,
-                from_=from_,
-            )
-        else:
-            self.twilio_client.messages.create(  # type: ignore
-                to=content.to_phone,
-                body=content.body,
-                messaging_service_sid=messaging_service_sid,
-            )
-
-    async def get_content(
-        self,
-        template_vars: PasswordlessLoginSMSTemplateVars,
-        user_context: Dict[str, Any],
-    ) -> SMSContent:
-        _ = user_context
-        return pless_sms_content(template_vars)
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class ServiceImplementation -(twilio_client: twilio.rest.Client) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class ServiceImplementation(TwilioServiceInterface[PasswordlessLoginSMSTemplateVars]):
-    async def send_raw_sms(
-        self,
-        content: SMSContent,
-        user_context: Dict[str, Any],
-        from_: Union[str, None] = None,
-        messaging_service_sid: Union[str, None] = None,
-    ) -> None:
-        if from_:
-            self.twilio_client.messages.create(  # type: ignore
-                to=content.to_phone,
-                body=content.body,
-                from_=from_,
-            )
-        else:
-            self.twilio_client.messages.create(  # type: ignore
-                to=content.to_phone,
-                body=content.body,
-                messaging_service_sid=messaging_service_sid,
-            )
-
-    async def get_content(
-        self,
-        template_vars: PasswordlessLoginSMSTemplateVars,
-        user_context: Dict[str, Any],
-    ) -> SMSContent:
-        _ = user_context
-        return pless_sms_content(template_vars)
-
-

Ancestors

- -

Methods

-
-
-async def get_content(self, template_vars: PasswordlessLoginSMSTemplateVars, user_context: Dict[str, Any]) ‑> SMSContent -
-
-
-
- -Expand source code - -
async def get_content(
-    self,
-    template_vars: PasswordlessLoginSMSTemplateVars,
-    user_context: Dict[str, Any],
-) -> SMSContent:
-    _ = user_context
-    return pless_sms_content(template_vars)
-
-
-
-async def send_raw_sms(self, content: SMSContent, user_context: Dict[str, Any], from_: Union[str, None] = None, messaging_service_sid: Union[str, None] = None) ‑> None -
-
-
-
- -Expand source code - -
async def send_raw_sms(
-    self,
-    content: SMSContent,
-    user_context: Dict[str, Any],
-    from_: Union[str, None] = None,
-    messaging_service_sid: Union[str, None] = None,
-) -> None:
-    if from_:
-        self.twilio_client.messages.create(  # type: ignore
-            to=content.to_phone,
-            body=content.body,
-            from_=from_,
-        )
-    else:
-        self.twilio_client.messages.create(  # type: ignore
-            to=content.to_phone,
-            body=content.body,
-            messaging_service_sid=messaging_service_sid,
-        )
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/passwordless/syncio/index.html b/html/supertokens_python/recipe/passwordless/syncio/index.html deleted file mode 100644 index ad19877d3..000000000 --- a/html/supertokens_python/recipe/passwordless/syncio/index.html +++ /dev/null @@ -1,770 +0,0 @@ - - - - - - -supertokens_python.recipe.passwordless.syncio API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.passwordless.syncio

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from typing import Any, Dict, List, Union
-
-from supertokens_python.async_to_sync_wrapper import sync
-from supertokens_python.recipe.passwordless import asyncio
-from supertokens_python.recipe.passwordless.interfaces import (
-    ConsumeCodeExpiredUserInputCodeError,
-    ConsumeCodeIncorrectUserInputCodeError,
-    ConsumeCodeOkResult,
-    ConsumeCodeRestartFlowError,
-    CreateCodeOkResult,
-    CreateNewCodeForDeviceOkResult,
-    CreateNewCodeForDeviceRestartFlowError,
-    CreateNewCodeForDeviceUserInputCodeAlreadyUsedError,
-    DeleteUserInfoOkResult,
-    DeleteUserInfoUnknownUserIdError,
-    RevokeAllCodesOkResult,
-    RevokeCodeOkResult,
-    UpdateUserEmailAlreadyExistsError,
-    UpdateUserOkResult,
-    UpdateUserPhoneNumberAlreadyExistsError,
-    UpdateUserUnknownUserIdError,
-)
-from supertokens_python.recipe.passwordless.types import (
-    DeviceType,
-    PasswordlessLoginEmailTemplateVars,
-    PasswordlessLoginSMSTemplateVars,
-    User,
-)
-
-
-def create_code(
-    tenant_id: str,
-    email: Union[None, str] = None,
-    phone_number: Union[None, str] = None,
-    user_input_code: Union[None, str] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> CreateCodeOkResult:
-    return sync(
-        asyncio.create_code(
-            tenant_id,
-            email=email,
-            phone_number=phone_number,
-            user_input_code=user_input_code,
-            user_context=user_context,
-        )
-    )
-
-
-def create_new_code_for_device(
-    tenant_id: str,
-    device_id: str,
-    user_input_code: Union[str, None] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> Union[
-    CreateNewCodeForDeviceOkResult,
-    CreateNewCodeForDeviceRestartFlowError,
-    CreateNewCodeForDeviceUserInputCodeAlreadyUsedError,
-]:
-    return sync(
-        asyncio.create_new_code_for_device(
-            tenant_id,
-            device_id=device_id,
-            user_input_code=user_input_code,
-            user_context=user_context,
-        )
-    )
-
-
-def consume_code(
-    tenant_id: str,
-    pre_auth_session_id: str,
-    user_input_code: Union[str, None] = None,
-    device_id: Union[str, None] = None,
-    link_code: Union[str, None] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> Union[
-    ConsumeCodeOkResult,
-    ConsumeCodeIncorrectUserInputCodeError,
-    ConsumeCodeExpiredUserInputCodeError,
-    ConsumeCodeRestartFlowError,
-]:
-    return sync(
-        asyncio.consume_code(
-            tenant_id,
-            pre_auth_session_id=pre_auth_session_id,
-            user_input_code=user_input_code,
-            device_id=device_id,
-            link_code=link_code,
-            user_context=user_context,
-        )
-    )
-
-
-def get_user_by_id(
-    user_id: str, user_context: Union[None, Dict[str, Any]] = None
-) -> Union[User, None]:
-    return sync(asyncio.get_user_by_id(user_id=user_id, user_context=user_context))
-
-
-def get_user_by_email(
-    tenant_id: str, email: str, user_context: Union[None, Dict[str, Any]] = None
-) -> Union[User, None]:
-    return sync(
-        asyncio.get_user_by_email(tenant_id, email=email, user_context=user_context)
-    )
-
-
-def get_user_by_phone_number(
-    tenant_id: str,
-    phone_number: str,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> Union[User, None]:
-    return sync(
-        asyncio.get_user_by_phone_number(
-            tenant_id=tenant_id, phone_number=phone_number, user_context=user_context
-        )
-    )
-
-
-def update_user(
-    user_id: str,
-    email: Union[str, None] = None,
-    phone_number: Union[str, None] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> Union[
-    UpdateUserOkResult,
-    UpdateUserUnknownUserIdError,
-    UpdateUserEmailAlreadyExistsError,
-    UpdateUserPhoneNumberAlreadyExistsError,
-]:
-    return sync(
-        asyncio.update_user(
-            user_id=user_id,
-            email=email,
-            phone_number=phone_number,
-            user_context=user_context,
-        )
-    )
-
-
-def delete_email_for_user(
-    user_id: str, user_context: Union[None, Dict[str, Any]] = None
-) -> Union[DeleteUserInfoOkResult, DeleteUserInfoUnknownUserIdError]:
-    return sync(
-        asyncio.delete_email_for_user(user_id=user_id, user_context=user_context)
-    )
-
-
-def delete_phone_number_for_user(
-    user_id: str, user_context: Union[None, Dict[str, Any]] = None
-) -> Union[DeleteUserInfoOkResult, DeleteUserInfoUnknownUserIdError]:
-    return sync(
-        asyncio.delete_phone_number_for_user(user_id=user_id, user_context=user_context)
-    )
-
-
-def revoke_all_codes(
-    tenant_id: str,
-    email: Union[str, None] = None,
-    phone_number: Union[str, None] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> RevokeAllCodesOkResult:
-    return sync(
-        asyncio.revoke_all_codes(
-            tenant_id, email=email, phone_number=phone_number, user_context=user_context
-        )
-    )
-
-
-def revoke_code(
-    tenant_id: str, code_id: str, user_context: Union[None, Dict[str, Any]] = None
-) -> RevokeCodeOkResult:
-    return sync(
-        asyncio.revoke_code(tenant_id, code_id=code_id, user_context=user_context)
-    )
-
-
-def list_codes_by_email(
-    tenant_id: str, email: str, user_context: Union[None, Dict[str, Any]] = None
-) -> List[DeviceType]:
-    return sync(
-        asyncio.list_codes_by_email(tenant_id, email=email, user_context=user_context)
-    )
-
-
-def list_codes_by_phone_number(
-    tenant_id: str, phone_number: str, user_context: Union[None, Dict[str, Any]] = None
-) -> List[DeviceType]:
-    return sync(
-        asyncio.list_codes_by_phone_number(
-            tenant_id, phone_number=phone_number, user_context=user_context
-        )
-    )
-
-
-def list_codes_by_device_id(
-    tenant_id: str,
-    device_id: str,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> Union[DeviceType, None]:
-    return sync(
-        asyncio.list_codes_by_device_id(
-            tenant_id=tenant_id, device_id=device_id, user_context=user_context
-        )
-    )
-
-
-def list_codes_by_pre_auth_session_id(
-    tenant_id: str,
-    pre_auth_session_id: str,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> Union[DeviceType, None]:
-    return sync(
-        asyncio.list_codes_by_pre_auth_session_id(
-            tenant_id=tenant_id,
-            pre_auth_session_id=pre_auth_session_id,
-            user_context=user_context,
-        )
-    )
-
-
-def create_magic_link(
-    tenant_id: str,
-    email: Union[str, None],
-    phone_number: Union[str, None],
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> str:
-    return sync(
-        asyncio.create_magic_link(
-            tenant_id=tenant_id,
-            email=email,
-            phone_number=phone_number,
-            user_context=user_context,
-        )
-    )
-
-
-def signinup(
-    tenant_id: str,
-    email: Union[str, None],
-    phone_number: Union[str, None],
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> ConsumeCodeOkResult:
-    return sync(
-        asyncio.signinup(
-            tenant_id=tenant_id,
-            email=email,
-            phone_number=phone_number,
-            user_context=user_context,
-        )
-    )
-
-
-def send_email(
-    input_: PasswordlessLoginEmailTemplateVars,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> None:
-    return sync(asyncio.send_email(input_, user_context))
-
-
-def send_sms(
-    input_: PasswordlessLoginSMSTemplateVars,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> None:
-    return sync(asyncio.send_sms(input_, user_context))
-
-
-
-
-
-
-
-

Functions

-
-
-def consume_code(tenant_id: str, pre_auth_session_id: str, user_input_code: Optional[str] = None, device_id: Optional[str] = None, link_code: Optional[str] = None, user_context: Optional[Dict[str, Any]] = None) ‑> Union[ConsumeCodeOkResultConsumeCodeIncorrectUserInputCodeErrorConsumeCodeExpiredUserInputCodeErrorConsumeCodeRestartFlowError] -
-
-
-
- -Expand source code - -
def consume_code(
-    tenant_id: str,
-    pre_auth_session_id: str,
-    user_input_code: Union[str, None] = None,
-    device_id: Union[str, None] = None,
-    link_code: Union[str, None] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> Union[
-    ConsumeCodeOkResult,
-    ConsumeCodeIncorrectUserInputCodeError,
-    ConsumeCodeExpiredUserInputCodeError,
-    ConsumeCodeRestartFlowError,
-]:
-    return sync(
-        asyncio.consume_code(
-            tenant_id,
-            pre_auth_session_id=pre_auth_session_id,
-            user_input_code=user_input_code,
-            device_id=device_id,
-            link_code=link_code,
-            user_context=user_context,
-        )
-    )
-
-
-
-def create_code(tenant_id: str, email: Optional[str] = None, phone_number: Optional[str] = None, user_input_code: Optional[str] = None, user_context: Optional[Dict[str, Any]] = None) ‑> CreateCodeOkResult -
-
-
-
- -Expand source code - -
def create_code(
-    tenant_id: str,
-    email: Union[None, str] = None,
-    phone_number: Union[None, str] = None,
-    user_input_code: Union[None, str] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> CreateCodeOkResult:
-    return sync(
-        asyncio.create_code(
-            tenant_id,
-            email=email,
-            phone_number=phone_number,
-            user_input_code=user_input_code,
-            user_context=user_context,
-        )
-    )
-
-
- -
-
-
- -Expand source code - -
def create_magic_link(
-    tenant_id: str,
-    email: Union[str, None],
-    phone_number: Union[str, None],
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> str:
-    return sync(
-        asyncio.create_magic_link(
-            tenant_id=tenant_id,
-            email=email,
-            phone_number=phone_number,
-            user_context=user_context,
-        )
-    )
-
-
-
-def create_new_code_for_device(tenant_id: str, device_id: str, user_input_code: Optional[str] = None, user_context: Optional[Dict[str, Any]] = None) ‑> Union[CreateNewCodeForDeviceOkResultCreateNewCodeForDeviceRestartFlowErrorCreateNewCodeForDeviceUserInputCodeAlreadyUsedError] -
-
-
-
- -Expand source code - -
def create_new_code_for_device(
-    tenant_id: str,
-    device_id: str,
-    user_input_code: Union[str, None] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> Union[
-    CreateNewCodeForDeviceOkResult,
-    CreateNewCodeForDeviceRestartFlowError,
-    CreateNewCodeForDeviceUserInputCodeAlreadyUsedError,
-]:
-    return sync(
-        asyncio.create_new_code_for_device(
-            tenant_id,
-            device_id=device_id,
-            user_input_code=user_input_code,
-            user_context=user_context,
-        )
-    )
-
-
-
-def delete_email_for_user(user_id: str, user_context: Optional[Dict[str, Any]] = None) ‑> Union[DeleteUserInfoOkResultDeleteUserInfoUnknownUserIdError] -
-
-
-
- -Expand source code - -
def delete_email_for_user(
-    user_id: str, user_context: Union[None, Dict[str, Any]] = None
-) -> Union[DeleteUserInfoOkResult, DeleteUserInfoUnknownUserIdError]:
-    return sync(
-        asyncio.delete_email_for_user(user_id=user_id, user_context=user_context)
-    )
-
-
-
-def delete_phone_number_for_user(user_id: str, user_context: Optional[Dict[str, Any]] = None) ‑> Union[DeleteUserInfoOkResultDeleteUserInfoUnknownUserIdError] -
-
-
-
- -Expand source code - -
def delete_phone_number_for_user(
-    user_id: str, user_context: Union[None, Dict[str, Any]] = None
-) -> Union[DeleteUserInfoOkResult, DeleteUserInfoUnknownUserIdError]:
-    return sync(
-        asyncio.delete_phone_number_for_user(user_id=user_id, user_context=user_context)
-    )
-
-
-
-def get_user_by_email(tenant_id: str, email: str, user_context: Optional[Dict[str, Any]] = None) ‑> Optional[User] -
-
-
-
- -Expand source code - -
def get_user_by_email(
-    tenant_id: str, email: str, user_context: Union[None, Dict[str, Any]] = None
-) -> Union[User, None]:
-    return sync(
-        asyncio.get_user_by_email(tenant_id, email=email, user_context=user_context)
-    )
-
-
-
-def get_user_by_id(user_id: str, user_context: Optional[Dict[str, Any]] = None) ‑> Optional[User] -
-
-
-
- -Expand source code - -
def get_user_by_id(
-    user_id: str, user_context: Union[None, Dict[str, Any]] = None
-) -> Union[User, None]:
-    return sync(asyncio.get_user_by_id(user_id=user_id, user_context=user_context))
-
-
-
-def get_user_by_phone_number(tenant_id: str, phone_number: str, user_context: Optional[Dict[str, Any]] = None) ‑> Optional[User] -
-
-
-
- -Expand source code - -
def get_user_by_phone_number(
-    tenant_id: str,
-    phone_number: str,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> Union[User, None]:
-    return sync(
-        asyncio.get_user_by_phone_number(
-            tenant_id=tenant_id, phone_number=phone_number, user_context=user_context
-        )
-    )
-
-
-
-def list_codes_by_device_id(tenant_id: str, device_id: str, user_context: Optional[Dict[str, Any]] = None) ‑> Optional[DeviceType] -
-
-
-
- -Expand source code - -
def list_codes_by_device_id(
-    tenant_id: str,
-    device_id: str,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> Union[DeviceType, None]:
-    return sync(
-        asyncio.list_codes_by_device_id(
-            tenant_id=tenant_id, device_id=device_id, user_context=user_context
-        )
-    )
-
-
-
-def list_codes_by_email(tenant_id: str, email: str, user_context: Optional[Dict[str, Any]] = None) ‑> List[DeviceType] -
-
-
-
- -Expand source code - -
def list_codes_by_email(
-    tenant_id: str, email: str, user_context: Union[None, Dict[str, Any]] = None
-) -> List[DeviceType]:
-    return sync(
-        asyncio.list_codes_by_email(tenant_id, email=email, user_context=user_context)
-    )
-
-
-
-def list_codes_by_phone_number(tenant_id: str, phone_number: str, user_context: Optional[Dict[str, Any]] = None) ‑> List[DeviceType] -
-
-
-
- -Expand source code - -
def list_codes_by_phone_number(
-    tenant_id: str, phone_number: str, user_context: Union[None, Dict[str, Any]] = None
-) -> List[DeviceType]:
-    return sync(
-        asyncio.list_codes_by_phone_number(
-            tenant_id, phone_number=phone_number, user_context=user_context
-        )
-    )
-
-
-
-def list_codes_by_pre_auth_session_id(tenant_id: str, pre_auth_session_id: str, user_context: Optional[Dict[str, Any]] = None) ‑> Optional[DeviceType] -
-
-
-
- -Expand source code - -
def list_codes_by_pre_auth_session_id(
-    tenant_id: str,
-    pre_auth_session_id: str,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> Union[DeviceType, None]:
-    return sync(
-        asyncio.list_codes_by_pre_auth_session_id(
-            tenant_id=tenant_id,
-            pre_auth_session_id=pre_auth_session_id,
-            user_context=user_context,
-        )
-    )
-
-
-
-def revoke_all_codes(tenant_id: str, email: Optional[str] = None, phone_number: Optional[str] = None, user_context: Optional[Dict[str, Any]] = None) ‑> RevokeAllCodesOkResult -
-
-
-
- -Expand source code - -
def revoke_all_codes(
-    tenant_id: str,
-    email: Union[str, None] = None,
-    phone_number: Union[str, None] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> RevokeAllCodesOkResult:
-    return sync(
-        asyncio.revoke_all_codes(
-            tenant_id, email=email, phone_number=phone_number, user_context=user_context
-        )
-    )
-
-
-
-def revoke_code(tenant_id: str, code_id: str, user_context: Optional[Dict[str, Any]] = None) ‑> RevokeCodeOkResult -
-
-
-
- -Expand source code - -
def revoke_code(
-    tenant_id: str, code_id: str, user_context: Union[None, Dict[str, Any]] = None
-) -> RevokeCodeOkResult:
-    return sync(
-        asyncio.revoke_code(tenant_id, code_id=code_id, user_context=user_context)
-    )
-
-
-
-def send_email(input_: CreateAndSendCustomEmailParameters, user_context: Optional[Dict[str, Any]] = None) ‑> None -
-
-
-
- -Expand source code - -
def send_email(
-    input_: PasswordlessLoginEmailTemplateVars,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> None:
-    return sync(asyncio.send_email(input_, user_context))
-
-
-
-def send_sms(input_: CreateAndSendCustomTextMessageParameters, user_context: Optional[Dict[str, Any]] = None) ‑> None -
-
-
-
- -Expand source code - -
def send_sms(
-    input_: PasswordlessLoginSMSTemplateVars,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> None:
-    return sync(asyncio.send_sms(input_, user_context))
-
-
-
-def signinup(tenant_id: str, email: Optional[str], phone_number: Optional[str], user_context: Optional[Dict[str, Any]] = None) ‑> ConsumeCodeOkResult -
-
-
-
- -Expand source code - -
def signinup(
-    tenant_id: str,
-    email: Union[str, None],
-    phone_number: Union[str, None],
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> ConsumeCodeOkResult:
-    return sync(
-        asyncio.signinup(
-            tenant_id=tenant_id,
-            email=email,
-            phone_number=phone_number,
-            user_context=user_context,
-        )
-    )
-
-
-
-def update_user(user_id: str, email: Optional[str] = None, phone_number: Optional[str] = None, user_context: Optional[Dict[str, Any]] = None) ‑> Union[UpdateUserOkResultUpdateUserUnknownUserIdErrorUpdateUserEmailAlreadyExistsErrorUpdateUserPhoneNumberAlreadyExistsError] -
-
-
-
- -Expand source code - -
def update_user(
-    user_id: str,
-    email: Union[str, None] = None,
-    phone_number: Union[str, None] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> Union[
-    UpdateUserOkResult,
-    UpdateUserUnknownUserIdError,
-    UpdateUserEmailAlreadyExistsError,
-    UpdateUserPhoneNumberAlreadyExistsError,
-]:
-    return sync(
-        asyncio.update_user(
-            user_id=user_id,
-            email=email,
-            phone_number=phone_number,
-            user_context=user_context,
-        )
-    )
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/passwordless/types.html b/html/supertokens_python/recipe/passwordless/types.html deleted file mode 100644 index cea370957..000000000 --- a/html/supertokens_python/recipe/passwordless/types.html +++ /dev/null @@ -1,499 +0,0 @@ - - - - - - -supertokens_python.recipe.passwordless.types API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.passwordless.types

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from typing import List, Union
-
-from supertokens_python.ingredients.emaildelivery import EmailDeliveryIngredient
-from supertokens_python.ingredients.emaildelivery.types import (
-    SMTPServiceInterface,
-    EmailDeliveryInterface,
-)
-from supertokens_python.ingredients.smsdelivery import SMSDeliveryIngredient
-from supertokens_python.ingredients.smsdelivery.types import (
-    TwilioServiceInterface,
-    SMSDeliveryInterface,
-)
-
-
-class User:
-    def __init__(
-        self,
-        user_id: str,
-        email: Union[str, None],
-        phone_number: Union[str, None],
-        time_joined: int,
-        tenant_ids: List[str],
-    ):
-        self.user_id = user_id
-        self.email = email
-        self.phone_number = phone_number
-        self.time_joined = time_joined
-        self.tenant_ids = tenant_ids
-
-    def __eq__(self, other: object) -> bool:
-        return (
-            isinstance(other, self.__class__)
-            and self.user_id == other.user_id
-            and self.email == other.email
-            and self.phone_number == other.phone_number
-            and self.time_joined == other.time_joined
-            and self.tenant_ids == other.tenant_ids
-        )
-
-
-class DeviceCode:
-    def __init__(self, code_id: str, time_created: str, code_life_time: int):
-        self.code_id = code_id
-        self.time_created = time_created
-        self.code_life_time = code_life_time
-
-
-class DeviceType:
-    def __init__(
-        self,
-        pre_auth_session_id: str,
-        failed_code_input_attempt_count: int,
-        codes: List[DeviceCode],
-        email: Union[str, None] = None,
-        phone_number: Union[str, None] = None,
-    ):
-        self.pre_auth_session_id = pre_auth_session_id
-        self.failed_code_input_attempt_count = failed_code_input_attempt_count
-        self.codes = codes
-        self.email = email
-        self.phone_number = phone_number
-
-
-class CreateAndSendCustomEmailParameters:
-    def __init__(
-        self,
-        tenant_id: str,
-        code_life_time: int,
-        pre_auth_session_id: str,
-        email: str,
-        user_input_code: Union[str, None] = None,
-        url_with_link_code: Union[str, None] = None,
-    ):
-        self.email = email
-        self.code_life_time = code_life_time
-        self.pre_auth_session_id = pre_auth_session_id
-        self.user_input_code = user_input_code
-        self.url_with_link_code = url_with_link_code
-        self.tenant_id = tenant_id
-
-
-PasswordlessLoginEmailTemplateVars = CreateAndSendCustomEmailParameters
-
-
-class CreateAndSendCustomTextMessageParameters:
-    def __init__(
-        self,
-        tenant_id: str,
-        code_life_time: int,
-        pre_auth_session_id: str,
-        phone_number: str,
-        user_input_code: Union[str, None] = None,
-        url_with_link_code: Union[str, None] = None,
-    ):
-        self.code_life_time = code_life_time
-        self.pre_auth_session_id = pre_auth_session_id
-        self.phone_number = phone_number
-        self.user_input_code = user_input_code
-        self.url_with_link_code = url_with_link_code
-        self.tenant_id = tenant_id
-
-
-PasswordlessLoginSMSTemplateVars = CreateAndSendCustomTextMessageParameters
-
-
-# Export:
-EmailTemplateVars = PasswordlessLoginEmailTemplateVars
-SMSTemplateVars = PasswordlessLoginSMSTemplateVars
-
-SMTPOverrideInput = SMTPServiceInterface[EmailTemplateVars]
-TwilioOverrideInput = TwilioServiceInterface[SMSTemplateVars]
-
-EmailDeliveryOverrideInput = EmailDeliveryInterface[EmailTemplateVars]
-SMSDeliveryOverrideInput = SMSDeliveryInterface[SMSTemplateVars]
-
-
-class PasswordlessIngredients:
-    def __init__(
-        self,
-        email_delivery: Union[EmailDeliveryIngredient[EmailTemplateVars], None] = None,
-        sms_delivery: Union[SMSDeliveryIngredient[SMSTemplateVars], None] = None,
-    ):
-        self.email_delivery = email_delivery
-        self.sms_delivery = sms_delivery
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class CreateAndSendCustomEmailParameters -(tenant_id: str, code_life_time: int, pre_auth_session_id: str, email: str, user_input_code: Optional[str] = None, url_with_link_code: Optional[str] = None) -
-
-
-
- -Expand source code - -
class CreateAndSendCustomEmailParameters:
-    def __init__(
-        self,
-        tenant_id: str,
-        code_life_time: int,
-        pre_auth_session_id: str,
-        email: str,
-        user_input_code: Union[str, None] = None,
-        url_with_link_code: Union[str, None] = None,
-    ):
-        self.email = email
-        self.code_life_time = code_life_time
-        self.pre_auth_session_id = pre_auth_session_id
-        self.user_input_code = user_input_code
-        self.url_with_link_code = url_with_link_code
-        self.tenant_id = tenant_id
-
-
-
-class PasswordlessLoginEmailTemplateVars -(tenant_id: str, code_life_time: int, pre_auth_session_id: str, email: str, user_input_code: Optional[str] = None, url_with_link_code: Optional[str] = None) -
-
-
-
- -Expand source code - -
class CreateAndSendCustomEmailParameters:
-    def __init__(
-        self,
-        tenant_id: str,
-        code_life_time: int,
-        pre_auth_session_id: str,
-        email: str,
-        user_input_code: Union[str, None] = None,
-        url_with_link_code: Union[str, None] = None,
-    ):
-        self.email = email
-        self.code_life_time = code_life_time
-        self.pre_auth_session_id = pre_auth_session_id
-        self.user_input_code = user_input_code
-        self.url_with_link_code = url_with_link_code
-        self.tenant_id = tenant_id
-
-
-
-class EmailTemplateVars -(tenant_id: str, code_life_time: int, pre_auth_session_id: str, email: str, user_input_code: Optional[str] = None, url_with_link_code: Optional[str] = None) -
-
-
-
- -Expand source code - -
class CreateAndSendCustomEmailParameters:
-    def __init__(
-        self,
-        tenant_id: str,
-        code_life_time: int,
-        pre_auth_session_id: str,
-        email: str,
-        user_input_code: Union[str, None] = None,
-        url_with_link_code: Union[str, None] = None,
-    ):
-        self.email = email
-        self.code_life_time = code_life_time
-        self.pre_auth_session_id = pre_auth_session_id
-        self.user_input_code = user_input_code
-        self.url_with_link_code = url_with_link_code
-        self.tenant_id = tenant_id
-
-
-
-class CreateAndSendCustomTextMessageParameters -(tenant_id: str, code_life_time: int, pre_auth_session_id: str, phone_number: str, user_input_code: Optional[str] = None, url_with_link_code: Optional[str] = None) -
-
-
-
- -Expand source code - -
class CreateAndSendCustomTextMessageParameters:
-    def __init__(
-        self,
-        tenant_id: str,
-        code_life_time: int,
-        pre_auth_session_id: str,
-        phone_number: str,
-        user_input_code: Union[str, None] = None,
-        url_with_link_code: Union[str, None] = None,
-    ):
-        self.code_life_time = code_life_time
-        self.pre_auth_session_id = pre_auth_session_id
-        self.phone_number = phone_number
-        self.user_input_code = user_input_code
-        self.url_with_link_code = url_with_link_code
-        self.tenant_id = tenant_id
-
-
-
-class PasswordlessLoginSMSTemplateVars -(tenant_id: str, code_life_time: int, pre_auth_session_id: str, phone_number: str, user_input_code: Optional[str] = None, url_with_link_code: Optional[str] = None) -
-
-
-
- -Expand source code - -
class CreateAndSendCustomTextMessageParameters:
-    def __init__(
-        self,
-        tenant_id: str,
-        code_life_time: int,
-        pre_auth_session_id: str,
-        phone_number: str,
-        user_input_code: Union[str, None] = None,
-        url_with_link_code: Union[str, None] = None,
-    ):
-        self.code_life_time = code_life_time
-        self.pre_auth_session_id = pre_auth_session_id
-        self.phone_number = phone_number
-        self.user_input_code = user_input_code
-        self.url_with_link_code = url_with_link_code
-        self.tenant_id = tenant_id
-
-
-
-class SMSTemplateVars -(tenant_id: str, code_life_time: int, pre_auth_session_id: str, phone_number: str, user_input_code: Optional[str] = None, url_with_link_code: Optional[str] = None) -
-
-
-
- -Expand source code - -
class CreateAndSendCustomTextMessageParameters:
-    def __init__(
-        self,
-        tenant_id: str,
-        code_life_time: int,
-        pre_auth_session_id: str,
-        phone_number: str,
-        user_input_code: Union[str, None] = None,
-        url_with_link_code: Union[str, None] = None,
-    ):
-        self.code_life_time = code_life_time
-        self.pre_auth_session_id = pre_auth_session_id
-        self.phone_number = phone_number
-        self.user_input_code = user_input_code
-        self.url_with_link_code = url_with_link_code
-        self.tenant_id = tenant_id
-
-
-
-class DeviceCode -(code_id: str, time_created: str, code_life_time: int) -
-
-
-
- -Expand source code - -
class DeviceCode:
-    def __init__(self, code_id: str, time_created: str, code_life_time: int):
-        self.code_id = code_id
-        self.time_created = time_created
-        self.code_life_time = code_life_time
-
-
-
-class DeviceType -(pre_auth_session_id: str, failed_code_input_attempt_count: int, codes: List[DeviceCode], email: Optional[str] = None, phone_number: Optional[str] = None) -
-
-
-
- -Expand source code - -
class DeviceType:
-    def __init__(
-        self,
-        pre_auth_session_id: str,
-        failed_code_input_attempt_count: int,
-        codes: List[DeviceCode],
-        email: Union[str, None] = None,
-        phone_number: Union[str, None] = None,
-    ):
-        self.pre_auth_session_id = pre_auth_session_id
-        self.failed_code_input_attempt_count = failed_code_input_attempt_count
-        self.codes = codes
-        self.email = email
-        self.phone_number = phone_number
-
-
-
-class PasswordlessIngredients -(email_delivery: Optional[EmailDeliveryIngredient[CreateAndSendCustomEmailParameters]] = None, sms_delivery: Optional[SMSDeliveryIngredient[CreateAndSendCustomTextMessageParameters]] = None) -
-
-
-
- -Expand source code - -
class PasswordlessIngredients:
-    def __init__(
-        self,
-        email_delivery: Union[EmailDeliveryIngredient[EmailTemplateVars], None] = None,
-        sms_delivery: Union[SMSDeliveryIngredient[SMSTemplateVars], None] = None,
-    ):
-        self.email_delivery = email_delivery
-        self.sms_delivery = sms_delivery
-
-
-
-class User -(user_id: str, email: Optional[str], phone_number: Optional[str], time_joined: int, tenant_ids: List[str]) -
-
-
-
- -Expand source code - -
class User:
-    def __init__(
-        self,
-        user_id: str,
-        email: Union[str, None],
-        phone_number: Union[str, None],
-        time_joined: int,
-        tenant_ids: List[str],
-    ):
-        self.user_id = user_id
-        self.email = email
-        self.phone_number = phone_number
-        self.time_joined = time_joined
-        self.tenant_ids = tenant_ids
-
-    def __eq__(self, other: object) -> bool:
-        return (
-            isinstance(other, self.__class__)
-            and self.user_id == other.user_id
-            and self.email == other.email
-            and self.phone_number == other.phone_number
-            and self.time_joined == other.time_joined
-            and self.tenant_ids == other.tenant_ids
-        )
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/passwordless/utils.html b/html/supertokens_python/recipe/passwordless/utils.html deleted file mode 100644 index 1d3a65ab8..000000000 --- a/html/supertokens_python/recipe/passwordless/utils.html +++ /dev/null @@ -1,657 +0,0 @@ - - - - - - -supertokens_python.recipe.passwordless.utils API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.passwordless.utils

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from __future__ import annotations
-
-from abc import ABC
-from typing import TYPE_CHECKING, Any, Awaitable, Callable, Dict, Union
-
-from supertokens_python.ingredients.emaildelivery.types import (
-    EmailDeliveryConfig,
-    EmailDeliveryConfigWithService,
-)
-from supertokens_python.ingredients.smsdelivery.types import (
-    SMSDeliveryConfig,
-    SMSDeliveryConfigWithService,
-)
-from supertokens_python.recipe.passwordless.types import (
-    PasswordlessLoginSMSTemplateVars,
-)
-from typing_extensions import Literal
-
-if TYPE_CHECKING:
-    from .interfaces import (
-        APIInterface,
-        RecipeInterface,
-        PasswordlessLoginEmailTemplateVars,
-    )
-    from supertokens_python import AppInfo
-
-from re import fullmatch
-
-from phonenumbers import is_valid_number, parse  # type: ignore
-from supertokens_python.recipe.passwordless.emaildelivery.services.backward_compatibility import (
-    BackwardCompatibilityService,
-)
-from supertokens_python.recipe.passwordless.smsdelivery.services.backward_compatibility import (
-    BackwardCompatibilityService as SMSBackwardCompatibilityService,
-)
-
-
-async def default_validate_phone_number(value: str, _tenant_id: str):
-    try:
-        parsed_phone_number: Any = parse(value, None)
-        if not is_valid_number(parsed_phone_number):
-            return "Phone number is invalid"
-    except Exception:
-        return "Phone number is invalid"
-
-
-async def default_validate_email(value: str, _tenant_id: str):
-    pattern = r"^(([^<>()\[\]\\.,;:\s@\"]+(\.[^<>()\[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$"
-    if fullmatch(pattern, value) is None:
-        return "Email is invalid"
-
-
-class OverrideConfig:
-    def __init__(
-        self,
-        functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None,
-        apis: Union[Callable[[APIInterface], APIInterface], None] = None,
-    ):
-        self.functions = functions
-        self.apis = apis
-
-
-class ContactConfig(ABC):
-    def __init__(self, contact_method: Literal["PHONE", "EMAIL", "EMAIL_OR_PHONE"]):
-        self.contact_method = contact_method
-
-
-class ContactPhoneOnlyConfig(ContactConfig):
-    def __init__(
-        self,
-        validate_phone_number: Union[
-            Callable[[str, str], Awaitable[Union[str, None]]], None
-        ] = None,
-    ):
-        super().__init__("PHONE")
-
-        if validate_phone_number is None:
-            self.validate_phone_number = default_validate_phone_number
-        else:
-            self.validate_phone_number = validate_phone_number
-
-
-class ContactEmailOnlyConfig(ContactConfig):
-    def __init__(
-        self,
-        validate_email_address: Union[
-            Callable[[str, str], Awaitable[Union[str, None]]], None
-        ] = None,
-    ):
-        super().__init__("EMAIL")
-
-        if validate_email_address is None:
-            self.validate_email_address = default_validate_email
-        else:
-            self.validate_email_address = validate_email_address
-
-
-class ContactEmailOrPhoneConfig(ContactConfig):
-    def __init__(
-        self,
-        validate_email_address: Union[
-            Callable[[str, str], Awaitable[Union[str, None]]], None
-        ] = None,
-        validate_phone_number: Union[
-            Callable[[str, str], Awaitable[Union[str, None]]], None
-        ] = None,
-    ):
-        super().__init__("EMAIL_OR_PHONE")
-
-        if validate_email_address is None:
-            self.validate_email_address = default_validate_email
-        else:
-            self.validate_email_address = validate_email_address
-
-        if validate_phone_number is None:
-            self.validate_phone_number = default_validate_phone_number
-        else:
-            self.validate_phone_number = validate_phone_number
-
-
-class PhoneOrEmailInput:
-    def __init__(self, phone_number: Union[str, None], email: Union[str, None]):
-        self.phone_number = phone_number
-        self.email = email
-
-
-class PasswordlessConfig:
-    def __init__(
-        self,
-        contact_config: ContactConfig,
-        override: OverrideConfig,
-        flow_type: Literal[
-            "USER_INPUT_CODE", "MAGIC_LINK", "USER_INPUT_CODE_AND_MAGIC_LINK"
-        ],
-        get_email_delivery_config: Callable[
-            [], EmailDeliveryConfigWithService[PasswordlessLoginEmailTemplateVars]
-        ],
-        get_sms_delivery_config: Callable[
-            [], SMSDeliveryConfigWithService[PasswordlessLoginSMSTemplateVars]
-        ],
-        get_custom_user_input_code: Union[
-            Callable[[str, Dict[str, Any]], Awaitable[str]], None
-        ] = None,
-    ):
-        self.contact_config = contact_config
-        self.override = override
-        self.flow_type: Literal[
-            "USER_INPUT_CODE", "MAGIC_LINK", "USER_INPUT_CODE_AND_MAGIC_LINK"
-        ] = flow_type
-        self.get_custom_user_input_code = get_custom_user_input_code
-        self.get_email_delivery_config = get_email_delivery_config
-        self.get_sms_delivery_config = get_sms_delivery_config
-
-
-def validate_and_normalise_user_input(
-    app_info: AppInfo,
-    contact_config: ContactConfig,
-    flow_type: Literal[
-        "USER_INPUT_CODE", "MAGIC_LINK", "USER_INPUT_CODE_AND_MAGIC_LINK"
-    ],
-    override: Union[OverrideConfig, None] = None,
-    get_custom_user_input_code: Union[
-        Callable[[str, Dict[str, Any]], Awaitable[str]], None
-    ] = None,
-    email_delivery: Union[
-        EmailDeliveryConfig[PasswordlessLoginEmailTemplateVars], None
-    ] = None,
-    sms_delivery: Union[
-        SMSDeliveryConfig[PasswordlessLoginSMSTemplateVars], None
-    ] = None,
-) -> PasswordlessConfig:
-
-    if override is None:
-        override = OverrideConfig()
-
-    def get_email_delivery_config() -> EmailDeliveryConfigWithService[
-        PasswordlessLoginEmailTemplateVars
-    ]:
-        email_service = email_delivery.service if email_delivery is not None else None
-
-        if email_service is None:
-            email_service = BackwardCompatibilityService(app_info)
-
-        if email_delivery is not None and email_delivery.override is not None:
-            override = email_delivery.override
-        else:
-            override = None
-
-        return EmailDeliveryConfigWithService(email_service, override=override)
-
-    def get_sms_delivery_config() -> SMSDeliveryConfigWithService[
-        PasswordlessLoginSMSTemplateVars
-    ]:
-        sms_service = sms_delivery.service if sms_delivery is not None else None
-
-        if sms_service is None:
-            sms_service = SMSBackwardCompatibilityService(app_info)
-
-        if sms_delivery is not None and sms_delivery.override is not None:
-            override = sms_delivery.override
-        else:
-            override = None
-
-        return SMSDeliveryConfigWithService(sms_service, override=override)
-
-    if not isinstance(contact_config, ContactConfig):  # type: ignore user might not have linter enabled
-        raise ValueError("contact_config must be of type ContactConfig")
-
-    if flow_type not in [
-        "USER_INPUT_CODE",
-        "MAGIC_LINK",
-        "USER_INPUT_CODE_AND_MAGIC_LINK",
-    ]:
-        raise ValueError(
-            "flow_type must be one of USER_INPUT_CODE, MAGIC_LINK, USER_INPUT_CODE_AND_MAGIC_LINK"
-        )
-
-    if not isinstance(override, OverrideConfig):  # type: ignore user might not have linter enabled
-        raise ValueError("override must be of type OverrideConfig")
-
-    return PasswordlessConfig(
-        contact_config=contact_config,
-        override=OverrideConfig(functions=override.functions, apis=override.apis),
-        flow_type=flow_type,
-        get_email_delivery_config=get_email_delivery_config,
-        get_sms_delivery_config=get_sms_delivery_config,
-        get_custom_user_input_code=get_custom_user_input_code,
-    )
-
-
-
-
-
-
-
-

Functions

-
-
-async def default_validate_email(value: str, _tenant_id: str) -
-
-
-
- -Expand source code - -
async def default_validate_email(value: str, _tenant_id: str):
-    pattern = r"^(([^<>()\[\]\\.,;:\s@\"]+(\.[^<>()\[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$"
-    if fullmatch(pattern, value) is None:
-        return "Email is invalid"
-
-
-
-async def default_validate_phone_number(value: str, _tenant_id: str) -
-
-
-
- -Expand source code - -
async def default_validate_phone_number(value: str, _tenant_id: str):
-    try:
-        parsed_phone_number: Any = parse(value, None)
-        if not is_valid_number(parsed_phone_number):
-            return "Phone number is invalid"
-    except Exception:
-        return "Phone number is invalid"
-
-
-
-def validate_and_normalise_user_input(app_info: AppInfo, contact_config: ContactConfig, flow_type: "Literal['USER_INPUT_CODE', 'MAGIC_LINK', 'USER_INPUT_CODE_AND_MAGIC_LINK']", override: Union[OverrideConfig, None] = None, get_custom_user_input_code: Union[Callable[[str, Dict[str, Any]], Awaitable[str]], None] = None, email_delivery: Union[EmailDeliveryConfig[PasswordlessLoginEmailTemplateVars], None] = None, sms_delivery: Union[SMSDeliveryConfig[PasswordlessLoginSMSTemplateVars], None] = None) ‑> PasswordlessConfig -
-
-
-
- -Expand source code - -
def validate_and_normalise_user_input(
-    app_info: AppInfo,
-    contact_config: ContactConfig,
-    flow_type: Literal[
-        "USER_INPUT_CODE", "MAGIC_LINK", "USER_INPUT_CODE_AND_MAGIC_LINK"
-    ],
-    override: Union[OverrideConfig, None] = None,
-    get_custom_user_input_code: Union[
-        Callable[[str, Dict[str, Any]], Awaitable[str]], None
-    ] = None,
-    email_delivery: Union[
-        EmailDeliveryConfig[PasswordlessLoginEmailTemplateVars], None
-    ] = None,
-    sms_delivery: Union[
-        SMSDeliveryConfig[PasswordlessLoginSMSTemplateVars], None
-    ] = None,
-) -> PasswordlessConfig:
-
-    if override is None:
-        override = OverrideConfig()
-
-    def get_email_delivery_config() -> EmailDeliveryConfigWithService[
-        PasswordlessLoginEmailTemplateVars
-    ]:
-        email_service = email_delivery.service if email_delivery is not None else None
-
-        if email_service is None:
-            email_service = BackwardCompatibilityService(app_info)
-
-        if email_delivery is not None and email_delivery.override is not None:
-            override = email_delivery.override
-        else:
-            override = None
-
-        return EmailDeliveryConfigWithService(email_service, override=override)
-
-    def get_sms_delivery_config() -> SMSDeliveryConfigWithService[
-        PasswordlessLoginSMSTemplateVars
-    ]:
-        sms_service = sms_delivery.service if sms_delivery is not None else None
-
-        if sms_service is None:
-            sms_service = SMSBackwardCompatibilityService(app_info)
-
-        if sms_delivery is not None and sms_delivery.override is not None:
-            override = sms_delivery.override
-        else:
-            override = None
-
-        return SMSDeliveryConfigWithService(sms_service, override=override)
-
-    if not isinstance(contact_config, ContactConfig):  # type: ignore user might not have linter enabled
-        raise ValueError("contact_config must be of type ContactConfig")
-
-    if flow_type not in [
-        "USER_INPUT_CODE",
-        "MAGIC_LINK",
-        "USER_INPUT_CODE_AND_MAGIC_LINK",
-    ]:
-        raise ValueError(
-            "flow_type must be one of USER_INPUT_CODE, MAGIC_LINK, USER_INPUT_CODE_AND_MAGIC_LINK"
-        )
-
-    if not isinstance(override, OverrideConfig):  # type: ignore user might not have linter enabled
-        raise ValueError("override must be of type OverrideConfig")
-
-    return PasswordlessConfig(
-        contact_config=contact_config,
-        override=OverrideConfig(functions=override.functions, apis=override.apis),
-        flow_type=flow_type,
-        get_email_delivery_config=get_email_delivery_config,
-        get_sms_delivery_config=get_sms_delivery_config,
-        get_custom_user_input_code=get_custom_user_input_code,
-    )
-
-
-
-
-
-

Classes

-
-
-class ContactConfig -(contact_method: "Literal['PHONE', 'EMAIL', 'EMAIL_OR_PHONE']") -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class ContactConfig(ABC):
-    def __init__(self, contact_method: Literal["PHONE", "EMAIL", "EMAIL_OR_PHONE"]):
-        self.contact_method = contact_method
-
-

Ancestors

-
    -
  • abc.ABC
  • -
-

Subclasses

- -
-
-class ContactEmailOnlyConfig -(validate_email_address: Union[Callable[[str, str], Awaitable[Union[str, None]]], None] = None) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class ContactEmailOnlyConfig(ContactConfig):
-    def __init__(
-        self,
-        validate_email_address: Union[
-            Callable[[str, str], Awaitable[Union[str, None]]], None
-        ] = None,
-    ):
-        super().__init__("EMAIL")
-
-        if validate_email_address is None:
-            self.validate_email_address = default_validate_email
-        else:
-            self.validate_email_address = validate_email_address
-
-

Ancestors

- -
-
-class ContactEmailOrPhoneConfig -(validate_email_address: Union[Callable[[str, str], Awaitable[Union[str, None]]], None] = None, validate_phone_number: Union[Callable[[str, str], Awaitable[Union[str, None]]], None] = None) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class ContactEmailOrPhoneConfig(ContactConfig):
-    def __init__(
-        self,
-        validate_email_address: Union[
-            Callable[[str, str], Awaitable[Union[str, None]]], None
-        ] = None,
-        validate_phone_number: Union[
-            Callable[[str, str], Awaitable[Union[str, None]]], None
-        ] = None,
-    ):
-        super().__init__("EMAIL_OR_PHONE")
-
-        if validate_email_address is None:
-            self.validate_email_address = default_validate_email
-        else:
-            self.validate_email_address = validate_email_address
-
-        if validate_phone_number is None:
-            self.validate_phone_number = default_validate_phone_number
-        else:
-            self.validate_phone_number = validate_phone_number
-
-

Ancestors

- -
-
-class ContactPhoneOnlyConfig -(validate_phone_number: Union[Callable[[str, str], Awaitable[Union[str, None]]], None] = None) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class ContactPhoneOnlyConfig(ContactConfig):
-    def __init__(
-        self,
-        validate_phone_number: Union[
-            Callable[[str, str], Awaitable[Union[str, None]]], None
-        ] = None,
-    ):
-        super().__init__("PHONE")
-
-        if validate_phone_number is None:
-            self.validate_phone_number = default_validate_phone_number
-        else:
-            self.validate_phone_number = validate_phone_number
-
-

Ancestors

- -
-
-class OverrideConfig -(functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None, apis: Union[Callable[[APIInterface], APIInterface], None] = None) -
-
-
-
- -Expand source code - -
class OverrideConfig:
-    def __init__(
-        self,
-        functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None,
-        apis: Union[Callable[[APIInterface], APIInterface], None] = None,
-    ):
-        self.functions = functions
-        self.apis = apis
-
-
-
-class PasswordlessConfig -(contact_config: ContactConfig, override: OverrideConfig, flow_type: "Literal['USER_INPUT_CODE', 'MAGIC_LINK', 'USER_INPUT_CODE_AND_MAGIC_LINK']", get_email_delivery_config: Callable[[], EmailDeliveryConfigWithService[PasswordlessLoginEmailTemplateVars]], get_sms_delivery_config: Callable[[], SMSDeliveryConfigWithService[PasswordlessLoginSMSTemplateVars]], get_custom_user_input_code: Union[Callable[[str, Dict[str, Any]], Awaitable[str]], None] = None) -
-
-
-
- -Expand source code - -
class PasswordlessConfig:
-    def __init__(
-        self,
-        contact_config: ContactConfig,
-        override: OverrideConfig,
-        flow_type: Literal[
-            "USER_INPUT_CODE", "MAGIC_LINK", "USER_INPUT_CODE_AND_MAGIC_LINK"
-        ],
-        get_email_delivery_config: Callable[
-            [], EmailDeliveryConfigWithService[PasswordlessLoginEmailTemplateVars]
-        ],
-        get_sms_delivery_config: Callable[
-            [], SMSDeliveryConfigWithService[PasswordlessLoginSMSTemplateVars]
-        ],
-        get_custom_user_input_code: Union[
-            Callable[[str, Dict[str, Any]], Awaitable[str]], None
-        ] = None,
-    ):
-        self.contact_config = contact_config
-        self.override = override
-        self.flow_type: Literal[
-            "USER_INPUT_CODE", "MAGIC_LINK", "USER_INPUT_CODE_AND_MAGIC_LINK"
-        ] = flow_type
-        self.get_custom_user_input_code = get_custom_user_input_code
-        self.get_email_delivery_config = get_email_delivery_config
-        self.get_sms_delivery_config = get_sms_delivery_config
-
-
-
-class PhoneOrEmailInput -(phone_number: Union[str, None], email: Union[str, None]) -
-
-
-
- -Expand source code - -
class PhoneOrEmailInput:
-    def __init__(self, phone_number: Union[str, None], email: Union[str, None]):
-        self.phone_number = phone_number
-        self.email = email
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/session/access_token.html b/html/supertokens_python/recipe/session/access_token.html deleted file mode 100644 index d44536fcd..000000000 --- a/html/supertokens_python/recipe/session/access_token.html +++ /dev/null @@ -1,428 +0,0 @@ - - - - - - -supertokens_python.recipe.session.access_token API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.session.access_token

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from typing import Any, Dict, Optional, Union
-
-import jwt
-from jwt.exceptions import DecodeError
-
-from supertokens_python.logger import log_debug_message
-from supertokens_python.recipe.session.utils import SessionConfig
-from supertokens_python.utils import get_timestamp_ms
-
-from .exceptions import raise_try_refresh_token_exception
-from .jwt import ParsedJWTInfo
-
-from supertokens_python.recipe.multitenancy.constants import DEFAULT_TENANT_ID
-
-
-def sanitize_string(s: Any) -> Union[str, None]:
-    if s == "":
-        return s
-
-    if not isinstance(s, str):
-        return None
-
-    return s.strip()
-
-
-def sanitize_number(n: Any) -> Union[Union[int, float], None]:
-    if isinstance(n, (int, float)):
-        return n
-
-    return None
-
-
-from supertokens_python.recipe.session.jwks import get_latest_keys
-
-
-def get_info_from_access_token(
-    config: SessionConfig,
-    jwt_info: ParsedJWTInfo,
-    do_anti_csrf_check: bool,
-):
-    try:
-        payload: Optional[Dict[str, Any]] = None
-        decode_algo = (
-            jwt_info.parsed_header["alg"]
-            if jwt_info.parsed_header is not None
-            else "RS256"
-        )
-
-        if jwt_info.version >= 3:
-            matching_keys = get_latest_keys(config, jwt_info.kid)
-            payload = jwt.decode(  # type: ignore
-                jwt_info.raw_token_string,
-                matching_keys[0].key,  # type: ignore
-                algorithms=[decode_algo],
-                options={"verify_signature": True, "verify_exp": True},
-            )
-        else:
-            # It won't have kid. So we'll have to try the token against all the keys from all the jwk_clients
-            # If any of them work, we'll use that payload
-            for k in get_latest_keys(config):
-                try:
-                    payload = jwt.decode(  # type: ignore
-                        jwt_info.raw_token_string,
-                        k.key,  # type: ignore
-                        algorithms=[decode_algo],
-                        options={"verify_signature": True, "verify_exp": True},
-                    )
-                    break
-                except DecodeError:
-                    pass
-
-        if payload is None:
-            raise DecodeError("Could not decode the token")
-
-        validate_access_token_structure(payload, jwt_info.version)
-
-        if jwt_info.version == 2:
-            user_id = sanitize_string(payload.get("userId"))
-            expiry_time = sanitize_number(payload.get("expiryTime"))
-            time_created = sanitize_number(payload.get("timeCreated"))
-            user_data = payload.get("userData")
-        else:
-            user_id = sanitize_string(payload.get("sub"))
-            expiry_time = sanitize_number(payload.get("exp", 0) * 1000)
-            time_created = sanitize_number(payload.get("iat", 0) * 1000)
-            user_data = payload
-
-        session_handle = sanitize_string(payload.get("sessionHandle"))
-        refresh_token_hash_1 = sanitize_string(payload.get("refreshTokenHash1"))
-        parent_refresh_token_hash_1 = sanitize_string(
-            payload.get("parentRefreshTokenHash1")
-        )
-        anti_csrf_token = sanitize_string(payload.get("antiCsrfToken"))
-        tenant_id = DEFAULT_TENANT_ID
-
-        if jwt_info.version >= 4:
-            tenant_id = sanitize_string(payload.get("tId"))
-
-        if anti_csrf_token is None and do_anti_csrf_check:
-            raise Exception("Access token does not contain the anti-csrf token")
-
-        assert isinstance(expiry_time, (float, int))
-
-        if expiry_time < get_timestamp_ms():
-            raise Exception("Access token expired")
-
-        return {
-            "sessionHandle": session_handle,
-            "userId": user_id,
-            "refreshTokenHash1": refresh_token_hash_1,
-            "parentRefreshTokenHash1": parent_refresh_token_hash_1,
-            "userData": user_data,
-            "antiCsrfToken": anti_csrf_token,
-            "expiryTime": expiry_time,
-            "timeCreated": time_created,
-            "tenantId": tenant_id,
-        }
-    except Exception as e:
-        log_debug_message(
-            "getInfoFromAccessToken: Returning TRY_REFRESH_TOKEN because access token validation failed - %s",
-            e,
-        )
-        raise_try_refresh_token_exception(e)
-
-
-def validate_access_token_structure(payload: Dict[str, Any], version: int) -> None:
-    if version >= 3:
-        if (
-            not isinstance(payload.get("sub"), str)
-            or not isinstance(payload.get("exp"), (int, float))
-            or not isinstance(payload.get("iat"), (int, float))
-            or not isinstance(payload.get("sessionHandle"), str)
-            or not isinstance(payload.get("refreshTokenHash1"), str)
-        ):
-            log_debug_message(
-                "validateAccessTokenStructure: Access token is using version >= 3"
-            )
-            # The error message below will be logged by the error handler that translates this into a TRY_REFRESH_TOKEN_ERROR
-            raise Exception(
-                "Access token does not contain all the information. Maybe the structure has changed?"
-            )
-
-        if version >= 4:
-            if not isinstance(payload.get("tId"), str):
-                raise Exception(
-                    "Access token does not contain all the information. Maybe the structure has changed?"
-                )
-
-    elif (
-        not isinstance(payload.get("sessionHandle"), str)
-        or payload.get("userData") is None
-        or not isinstance(payload.get("refreshTokenHash1"), str)
-        or not isinstance(payload.get("expiryTime"), (float, int))
-        or not isinstance(payload.get("timeCreated"), (float, int))
-    ):
-        log_debug_message(
-            "validateAccessTokenStructure: Access token is using version < 3"
-        )
-        # The error message below will be logged by the error handler that translates this into a TRY_REFRESH_TOKEN_ERROR
-        raise Exception(
-            "Access token does not contain all the information. Maybe the structure has changed?"
-        )
-
-
-
-
-
-
-
-

Functions

-
-
-def get_info_from_access_token(config: SessionConfig, jwt_info: ParsedJWTInfo, do_anti_csrf_check: bool) -
-
-
-
- -Expand source code - -
def get_info_from_access_token(
-    config: SessionConfig,
-    jwt_info: ParsedJWTInfo,
-    do_anti_csrf_check: bool,
-):
-    try:
-        payload: Optional[Dict[str, Any]] = None
-        decode_algo = (
-            jwt_info.parsed_header["alg"]
-            if jwt_info.parsed_header is not None
-            else "RS256"
-        )
-
-        if jwt_info.version >= 3:
-            matching_keys = get_latest_keys(config, jwt_info.kid)
-            payload = jwt.decode(  # type: ignore
-                jwt_info.raw_token_string,
-                matching_keys[0].key,  # type: ignore
-                algorithms=[decode_algo],
-                options={"verify_signature": True, "verify_exp": True},
-            )
-        else:
-            # It won't have kid. So we'll have to try the token against all the keys from all the jwk_clients
-            # If any of them work, we'll use that payload
-            for k in get_latest_keys(config):
-                try:
-                    payload = jwt.decode(  # type: ignore
-                        jwt_info.raw_token_string,
-                        k.key,  # type: ignore
-                        algorithms=[decode_algo],
-                        options={"verify_signature": True, "verify_exp": True},
-                    )
-                    break
-                except DecodeError:
-                    pass
-
-        if payload is None:
-            raise DecodeError("Could not decode the token")
-
-        validate_access_token_structure(payload, jwt_info.version)
-
-        if jwt_info.version == 2:
-            user_id = sanitize_string(payload.get("userId"))
-            expiry_time = sanitize_number(payload.get("expiryTime"))
-            time_created = sanitize_number(payload.get("timeCreated"))
-            user_data = payload.get("userData")
-        else:
-            user_id = sanitize_string(payload.get("sub"))
-            expiry_time = sanitize_number(payload.get("exp", 0) * 1000)
-            time_created = sanitize_number(payload.get("iat", 0) * 1000)
-            user_data = payload
-
-        session_handle = sanitize_string(payload.get("sessionHandle"))
-        refresh_token_hash_1 = sanitize_string(payload.get("refreshTokenHash1"))
-        parent_refresh_token_hash_1 = sanitize_string(
-            payload.get("parentRefreshTokenHash1")
-        )
-        anti_csrf_token = sanitize_string(payload.get("antiCsrfToken"))
-        tenant_id = DEFAULT_TENANT_ID
-
-        if jwt_info.version >= 4:
-            tenant_id = sanitize_string(payload.get("tId"))
-
-        if anti_csrf_token is None and do_anti_csrf_check:
-            raise Exception("Access token does not contain the anti-csrf token")
-
-        assert isinstance(expiry_time, (float, int))
-
-        if expiry_time < get_timestamp_ms():
-            raise Exception("Access token expired")
-
-        return {
-            "sessionHandle": session_handle,
-            "userId": user_id,
-            "refreshTokenHash1": refresh_token_hash_1,
-            "parentRefreshTokenHash1": parent_refresh_token_hash_1,
-            "userData": user_data,
-            "antiCsrfToken": anti_csrf_token,
-            "expiryTime": expiry_time,
-            "timeCreated": time_created,
-            "tenantId": tenant_id,
-        }
-    except Exception as e:
-        log_debug_message(
-            "getInfoFromAccessToken: Returning TRY_REFRESH_TOKEN because access token validation failed - %s",
-            e,
-        )
-        raise_try_refresh_token_exception(e)
-
-
-
-def sanitize_number(n: Any) ‑> Union[int, float, ForwardRef(None)] -
-
-
-
- -Expand source code - -
def sanitize_number(n: Any) -> Union[Union[int, float], None]:
-    if isinstance(n, (int, float)):
-        return n
-
-    return None
-
-
-
-def sanitize_string(s: Any) ‑> Optional[str] -
-
-
-
- -Expand source code - -
def sanitize_string(s: Any) -> Union[str, None]:
-    if s == "":
-        return s
-
-    if not isinstance(s, str):
-        return None
-
-    return s.strip()
-
-
-
-def validate_access_token_structure(payload: Dict[str, Any], version: int) ‑> None -
-
-
-
- -Expand source code - -
def validate_access_token_structure(payload: Dict[str, Any], version: int) -> None:
-    if version >= 3:
-        if (
-            not isinstance(payload.get("sub"), str)
-            or not isinstance(payload.get("exp"), (int, float))
-            or not isinstance(payload.get("iat"), (int, float))
-            or not isinstance(payload.get("sessionHandle"), str)
-            or not isinstance(payload.get("refreshTokenHash1"), str)
-        ):
-            log_debug_message(
-                "validateAccessTokenStructure: Access token is using version >= 3"
-            )
-            # The error message below will be logged by the error handler that translates this into a TRY_REFRESH_TOKEN_ERROR
-            raise Exception(
-                "Access token does not contain all the information. Maybe the structure has changed?"
-            )
-
-        if version >= 4:
-            if not isinstance(payload.get("tId"), str):
-                raise Exception(
-                    "Access token does not contain all the information. Maybe the structure has changed?"
-                )
-
-    elif (
-        not isinstance(payload.get("sessionHandle"), str)
-        or payload.get("userData") is None
-        or not isinstance(payload.get("refreshTokenHash1"), str)
-        or not isinstance(payload.get("expiryTime"), (float, int))
-        or not isinstance(payload.get("timeCreated"), (float, int))
-    ):
-        log_debug_message(
-            "validateAccessTokenStructure: Access token is using version < 3"
-        )
-        # The error message below will be logged by the error handler that translates this into a TRY_REFRESH_TOKEN_ERROR
-        raise Exception(
-            "Access token does not contain all the information. Maybe the structure has changed?"
-        )
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/session/api/implementation.html b/html/supertokens_python/recipe/session/api/implementation.html deleted file mode 100644 index 73168803a..000000000 --- a/html/supertokens_python/recipe/session/api/implementation.html +++ /dev/null @@ -1,344 +0,0 @@ - - - - - - -supertokens_python.recipe.session.api.implementation API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.session.api.implementation

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from typing import TYPE_CHECKING, Callable, List, Optional, Union
-
-from supertokens_python.normalised_url_path import NormalisedURLPath
-from supertokens_python.recipe.session.interfaces import (
-    APIInterface,
-    SessionClaimValidator,
-    SignOutOkayResponse,
-)
-from supertokens_python.types import MaybeAwaitable
-from supertokens_python.utils import normalise_http_method
-
-if TYPE_CHECKING:
-    from supertokens_python.recipe.session.interfaces import APIOptions
-    from ..interfaces import SessionContainer
-
-from typing import Any, Dict
-
-from ..session_request_functions import (
-    get_session_from_request,
-    refresh_session_in_request,
-)
-
-
-class APIImplementation(APIInterface):
-    async def refresh_post(
-        self, api_options: APIOptions, user_context: Dict[str, Any]
-    ) -> SessionContainer:
-        return await refresh_session_in_request(
-            api_options.request,
-            user_context,
-            api_options.config,
-            api_options.recipe_implementation,
-        )
-
-    async def signout_post(
-        self,
-        session: Optional[SessionContainer],
-        api_options: APIOptions,
-        user_context: Dict[str, Any],
-    ) -> SignOutOkayResponse:
-        if session is not None:
-            await session.revoke_session(user_context)
-        return SignOutOkayResponse()
-
-    async def verify_session(
-        self,
-        api_options: APIOptions,
-        anti_csrf_check: Union[bool, None],
-        session_required: bool,
-        check_database: bool,
-        override_global_claim_validators: Optional[
-            Callable[
-                [List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
-                MaybeAwaitable[List[SessionClaimValidator]],
-            ]
-        ],
-        user_context: Dict[str, Any],
-    ) -> Union[SessionContainer, None]:
-        method = normalise_http_method(api_options.request.method())
-        if method in ("options", "trace"):
-            if session_required:
-                raise Exception(f"verify_session cannot be used with {method} method")
-            return None
-        incoming_path = NormalisedURLPath(api_options.request.get_path())
-        refresh_token_path = api_options.config.refresh_token_path
-
-        if incoming_path.equals(refresh_token_path) and method == "post":
-            return await refresh_session_in_request(
-                api_options.request,
-                user_context,
-                api_options.config,
-                api_options.recipe_implementation,
-            )
-
-        return await get_session_from_request(
-            api_options.request,
-            api_options.config,
-            api_options.recipe_implementation,
-            session_required=session_required,
-            anti_csrf_check=anti_csrf_check,
-            check_database=check_database,
-            override_global_claim_validators=override_global_claim_validators,
-            user_context=user_context,
-        )
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class APIImplementation -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class APIImplementation(APIInterface):
-    async def refresh_post(
-        self, api_options: APIOptions, user_context: Dict[str, Any]
-    ) -> SessionContainer:
-        return await refresh_session_in_request(
-            api_options.request,
-            user_context,
-            api_options.config,
-            api_options.recipe_implementation,
-        )
-
-    async def signout_post(
-        self,
-        session: Optional[SessionContainer],
-        api_options: APIOptions,
-        user_context: Dict[str, Any],
-    ) -> SignOutOkayResponse:
-        if session is not None:
-            await session.revoke_session(user_context)
-        return SignOutOkayResponse()
-
-    async def verify_session(
-        self,
-        api_options: APIOptions,
-        anti_csrf_check: Union[bool, None],
-        session_required: bool,
-        check_database: bool,
-        override_global_claim_validators: Optional[
-            Callable[
-                [List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
-                MaybeAwaitable[List[SessionClaimValidator]],
-            ]
-        ],
-        user_context: Dict[str, Any],
-    ) -> Union[SessionContainer, None]:
-        method = normalise_http_method(api_options.request.method())
-        if method in ("options", "trace"):
-            if session_required:
-                raise Exception(f"verify_session cannot be used with {method} method")
-            return None
-        incoming_path = NormalisedURLPath(api_options.request.get_path())
-        refresh_token_path = api_options.config.refresh_token_path
-
-        if incoming_path.equals(refresh_token_path) and method == "post":
-            return await refresh_session_in_request(
-                api_options.request,
-                user_context,
-                api_options.config,
-                api_options.recipe_implementation,
-            )
-
-        return await get_session_from_request(
-            api_options.request,
-            api_options.config,
-            api_options.recipe_implementation,
-            session_required=session_required,
-            anti_csrf_check=anti_csrf_check,
-            check_database=check_database,
-            override_global_claim_validators=override_global_claim_validators,
-            user_context=user_context,
-        )
-
-

Ancestors

- -

Methods

-
-
-async def refresh_post(self, api_options: APIOptions, user_context: Dict[str, Any]) ‑> SessionContainer -
-
-
-
- -Expand source code - -
async def refresh_post(
-    self, api_options: APIOptions, user_context: Dict[str, Any]
-) -> SessionContainer:
-    return await refresh_session_in_request(
-        api_options.request,
-        user_context,
-        api_options.config,
-        api_options.recipe_implementation,
-    )
-
-
-
-async def signout_post(self, session: Optional[SessionContainer], api_options: APIOptions, user_context: Dict[str, Any]) ‑> SignOutOkayResponse -
-
-
-
- -Expand source code - -
async def signout_post(
-    self,
-    session: Optional[SessionContainer],
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-) -> SignOutOkayResponse:
-    if session is not None:
-        await session.revoke_session(user_context)
-    return SignOutOkayResponse()
-
-
-
-async def verify_session(self, api_options: APIOptions, anti_csrf_check: Union[bool, None], session_required: bool, check_database: bool, override_global_claim_validators: Optional[Callable[[List[SessionClaimValidator], SessionContainer, Dict[str, Any]], MaybeAwaitable[List[SessionClaimValidator]]]], user_context: Dict[str, Any]) ‑> Union[SessionContainer, None] -
-
-
-
- -Expand source code - -
async def verify_session(
-    self,
-    api_options: APIOptions,
-    anti_csrf_check: Union[bool, None],
-    session_required: bool,
-    check_database: bool,
-    override_global_claim_validators: Optional[
-        Callable[
-            [List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
-            MaybeAwaitable[List[SessionClaimValidator]],
-        ]
-    ],
-    user_context: Dict[str, Any],
-) -> Union[SessionContainer, None]:
-    method = normalise_http_method(api_options.request.method())
-    if method in ("options", "trace"):
-        if session_required:
-            raise Exception(f"verify_session cannot be used with {method} method")
-        return None
-    incoming_path = NormalisedURLPath(api_options.request.get_path())
-    refresh_token_path = api_options.config.refresh_token_path
-
-    if incoming_path.equals(refresh_token_path) and method == "post":
-        return await refresh_session_in_request(
-            api_options.request,
-            user_context,
-            api_options.config,
-            api_options.recipe_implementation,
-        )
-
-    return await get_session_from_request(
-        api_options.request,
-        api_options.config,
-        api_options.recipe_implementation,
-        session_required=session_required,
-        anti_csrf_check=anti_csrf_check,
-        check_database=check_database,
-        override_global_claim_validators=override_global_claim_validators,
-        user_context=user_context,
-    )
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/session/api/index.html b/html/supertokens_python/recipe/session/api/index.html deleted file mode 100644 index f8c809cfb..000000000 --- a/html/supertokens_python/recipe/session/api/index.html +++ /dev/null @@ -1,95 +0,0 @@ - - - - - - -supertokens_python.recipe.session.api API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.session.api

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from .refresh import handle_refresh_api  # type: ignore
-from .signout import handle_signout_api  # type: ignore
-
-
-
-

Sub-modules

-
-
supertokens_python.recipe.session.api.implementation
-
-
-
-
supertokens_python.recipe.session.api.refresh
-
-
-
-
supertokens_python.recipe.session.api.signout
-
-
-
-
-
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/session/api/refresh.html b/html/supertokens_python/recipe/session/api/refresh.html deleted file mode 100644 index 621365eb0..000000000 --- a/html/supertokens_python/recipe/session/api/refresh.html +++ /dev/null @@ -1,130 +0,0 @@ - - - - - - -supertokens_python.recipe.session.api.refresh API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.session.api.refresh

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from typing import TYPE_CHECKING, Any, Dict
-
-if TYPE_CHECKING:
-    from supertokens_python.recipe.session.interfaces import APIInterface, APIOptions
-
-from supertokens_python.utils import send_200_response
-
-
-async def handle_refresh_api(
-    api_implementation: APIInterface,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-):
-    if (
-        api_implementation.disable_refresh_post
-        or api_implementation.refresh_post is None
-    ):
-        return None
-
-    await api_implementation.refresh_post(api_options, user_context)
-    if api_options.response is None:
-        raise Exception("Should never come here")
-    return send_200_response({}, api_options.response)
-
-
-
-
-
-
-
-

Functions

-
-
-async def handle_refresh_api(api_implementation: APIInterface, api_options: APIOptions, user_context: Dict[str, Any]) -
-
-
-
- -Expand source code - -
async def handle_refresh_api(
-    api_implementation: APIInterface,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-):
-    if (
-        api_implementation.disable_refresh_post
-        or api_implementation.refresh_post is None
-    ):
-        return None
-
-    await api_implementation.refresh_post(api_options, user_context)
-    if api_options.response is None:
-        raise Exception("Should never come here")
-    return send_200_response({}, api_options.response)
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/session/api/signout.html b/html/supertokens_python/recipe/session/api/signout.html deleted file mode 100644 index aa44282dc..000000000 --- a/html/supertokens_python/recipe/session/api/signout.html +++ /dev/null @@ -1,155 +0,0 @@ - - - - - - -supertokens_python.recipe.session.api.signout API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.session.api.signout

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from typing import TYPE_CHECKING, Any, Dict
-
-from supertokens_python.recipe.session.session_request_functions import (
-    get_session_from_request,
-)
-
-if TYPE_CHECKING:
-    from supertokens_python.recipe.session.interfaces import (
-        APIInterface,
-        APIOptions,
-    )
-
-from supertokens_python.utils import send_200_response
-
-
-async def handle_signout_api(
-    api_implementation: APIInterface,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-):
-    if (
-        api_implementation.disable_signout_post
-        or api_implementation.signout_post is None
-    ):
-        return None
-
-    session = await get_session_from_request(
-        api_options.request,
-        api_options.config,
-        api_options.recipe_implementation,
-        session_required=True,
-        override_global_claim_validators=lambda _, __, ___: [],
-        user_context=user_context,
-    )
-
-    response = await api_implementation.signout_post(session, api_options, user_context)
-    if api_options.response is None:
-        raise Exception("Should never come here")
-    return send_200_response(response.to_json(), api_options.response)
-
-
-
-
-
-
-
-

Functions

-
-
-async def handle_signout_api(api_implementation: APIInterface, api_options: APIOptions, user_context: Dict[str, Any]) -
-
-
-
- -Expand source code - -
async def handle_signout_api(
-    api_implementation: APIInterface,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-):
-    if (
-        api_implementation.disable_signout_post
-        or api_implementation.signout_post is None
-    ):
-        return None
-
-    session = await get_session_from_request(
-        api_options.request,
-        api_options.config,
-        api_options.recipe_implementation,
-        session_required=True,
-        override_global_claim_validators=lambda _, __, ___: [],
-        user_context=user_context,
-    )
-
-    response = await api_implementation.signout_post(session, api_options, user_context)
-    if api_options.response is None:
-        raise Exception("Should never come here")
-    return send_200_response(response.to_json(), api_options.response)
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/session/asyncio/index.html b/html/supertokens_python/recipe/session/asyncio/index.html deleted file mode 100644 index 4b08c49d3..000000000 --- a/html/supertokens_python/recipe/session/asyncio/index.html +++ /dev/null @@ -1,1330 +0,0 @@ - - - - - - -supertokens_python.recipe.session.asyncio API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.session.asyncio

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-from typing import Any, Callable, Dict, List, Optional, TypeVar, Union
-
-from supertokens_python.recipe.openid.interfaces import (
-    GetOpenIdDiscoveryConfigurationResult,
-)
-from supertokens_python.recipe.session.interfaces import (
-    ClaimsValidationResult,
-    GetClaimValueOkResult,
-    JSONObject,
-    SessionClaim,
-    SessionClaimValidator,
-    SessionContainer,
-    SessionDoesNotExistError,
-    SessionInformationResult,
-)
-from supertokens_python.recipe.session.recipe import SessionRecipe
-from supertokens_python.types import MaybeAwaitable
-from supertokens_python.utils import FRAMEWORKS, resolve
-
-from ...jwt.interfaces import (
-    CreateJwtOkResult,
-    CreateJwtResultUnsupportedAlgorithm,
-    GetJWKSResult,
-)
-from ..session_request_functions import (
-    create_new_session_in_request,
-    get_session_from_request,
-    refresh_session_in_request,
-)
-from ..constants import protected_props
-from ..utils import get_required_claim_validators
-
-from supertokens_python.recipe.multitenancy.constants import DEFAULT_TENANT_ID
-
-_T = TypeVar("_T")
-
-
-async def create_new_session(
-    request: Any,
-    tenant_id: str,
-    user_id: str,
-    access_token_payload: Union[Dict[str, Any], None] = None,
-    session_data_in_database: Union[Dict[str, Any], None] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> SessionContainer:
-    if user_context is None:
-        user_context = {}
-    if session_data_in_database is None:
-        session_data_in_database = {}
-    if access_token_payload is None:
-        access_token_payload = {}
-
-    recipe_instance = SessionRecipe.get_instance()
-    config = recipe_instance.config
-    app_info = recipe_instance.app_info
-
-    return await create_new_session_in_request(
-        request,
-        user_context,
-        recipe_instance,
-        access_token_payload,
-        user_id,
-        config,
-        app_info,
-        session_data_in_database,
-        tenant_id,
-    )
-
-
-async def create_new_session_without_request_response(
-    tenant_id: str,
-    user_id: str,
-    access_token_payload: Union[Dict[str, Any], None] = None,
-    session_data_in_database: Union[Dict[str, Any], None] = None,
-    disable_anti_csrf: bool = False,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> SessionContainer:
-    if user_context is None:
-        user_context = {}
-    if session_data_in_database is None:
-        session_data_in_database = {}
-    if access_token_payload is None:
-        access_token_payload = {}
-
-    claims_added_by_other_recipes = (
-        SessionRecipe.get_instance().get_claims_added_by_other_recipes()
-    )
-    app_info = SessionRecipe.get_instance().app_info
-    issuer = (
-        app_info.api_domain.get_as_string_dangerous()
-        + app_info.api_base_path.get_as_string_dangerous()
-    )
-
-    final_access_token_payload = {**access_token_payload, "iss": issuer}
-
-    for prop in protected_props:
-        if prop in final_access_token_payload:
-            del final_access_token_payload[prop]
-
-    for claim in claims_added_by_other_recipes:
-        update = await claim.build(user_id, tenant_id, user_context)
-        final_access_token_payload = {**final_access_token_payload, **update}
-
-    return await SessionRecipe.get_instance().recipe_implementation.create_new_session(
-        user_id,
-        final_access_token_payload,
-        session_data_in_database,
-        disable_anti_csrf,
-        tenant_id,
-        user_context=user_context,
-    )
-
-
-async def validate_claims_for_session_handle(
-    session_handle: str,
-    override_global_claim_validators: Optional[
-        Callable[
-            [
-                List[SessionClaimValidator],
-                SessionInformationResult,
-                Dict[str, Any],
-            ],
-            MaybeAwaitable[List[SessionClaimValidator]],
-        ]
-    ] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> Union[SessionDoesNotExistError, ClaimsValidationResult]:
-    if user_context is None:
-        user_context = {}
-
-    recipe_impl = SessionRecipe.get_instance().recipe_implementation
-    session_info = await recipe_impl.get_session_information(
-        session_handle, user_context
-    )
-
-    if session_info is None:
-        return SessionDoesNotExistError()
-
-    claim_validators_added_by_other_recipes = (
-        SessionRecipe.get_instance().get_claim_validators_added_by_other_recipes()
-    )
-    global_claim_validators = await resolve(
-        recipe_impl.get_global_claim_validators(
-            session_info.tenant_id,
-            session_info.user_id,
-            claim_validators_added_by_other_recipes,
-            user_context,
-        )
-    )
-
-    if override_global_claim_validators is not None:
-        claim_validators = await resolve(
-            override_global_claim_validators(
-                global_claim_validators, session_info, user_context
-            )
-        )
-    else:
-        claim_validators = global_claim_validators
-
-    claim_validation_res = await recipe_impl.validate_claims(
-        session_info.user_id,
-        session_info.custom_claims_in_access_token_payload,
-        claim_validators,
-        user_context,
-    )
-
-    if claim_validation_res.access_token_payload_update is not None:
-        updated = await recipe_impl.merge_into_access_token_payload(
-            session_handle,
-            claim_validation_res.access_token_payload_update,
-            user_context,
-        )
-        if not updated:
-            return SessionDoesNotExistError()
-
-    return ClaimsValidationResult(claim_validation_res.invalid_claims)
-
-
-async def validate_claims_in_jwt_payload(
-    tenant_id: str,
-    user_id: str,
-    jwt_payload: JSONObject,
-    override_global_claim_validators: Optional[
-        Callable[
-            [
-                List[SessionClaimValidator],
-                str,
-                Dict[str, Any],
-            ],
-            MaybeAwaitable[List[SessionClaimValidator]],
-        ]
-    ] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-):
-    if user_context is None:
-        user_context = {}
-
-    recipe_impl = SessionRecipe.get_instance().recipe_implementation
-
-    claim_validators_added_by_other_recipes = (
-        SessionRecipe.get_instance().get_claim_validators_added_by_other_recipes()
-    )
-    global_claim_validators = await resolve(
-        recipe_impl.get_global_claim_validators(
-            tenant_id,
-            user_id,
-            claim_validators_added_by_other_recipes,
-            user_context,
-        )
-    )
-
-    if override_global_claim_validators is not None:
-        claim_validators = await resolve(
-            override_global_claim_validators(
-                global_claim_validators, user_id, user_context
-            )
-        )
-    else:
-        claim_validators = global_claim_validators
-
-    return await recipe_impl.validate_claims_in_jwt_payload(
-        user_id, jwt_payload, claim_validators, user_context
-    )
-
-
-async def fetch_and_set_claim(
-    session_handle: str,
-    claim: SessionClaim[Any],
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> bool:
-    if user_context is None:
-        user_context = {}
-    return await SessionRecipe.get_instance().recipe_implementation.fetch_and_set_claim(
-        session_handle, claim, user_context
-    )
-
-
-async def get_claim_value(
-    session_handle: str,
-    claim: SessionClaim[_T],
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> Union[SessionDoesNotExistError, GetClaimValueOkResult[_T]]:
-    if user_context is None:
-        user_context = {}
-    return await SessionRecipe.get_instance().recipe_implementation.get_claim_value(
-        session_handle, claim, user_context
-    )
-
-
-async def set_claim_value(
-    session_handle: str,
-    claim: SessionClaim[_T],
-    value: _T,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> bool:
-    if user_context is None:
-        user_context = {}
-    return await SessionRecipe.get_instance().recipe_implementation.set_claim_value(
-        session_handle, claim, value, user_context
-    )
-
-
-async def remove_claim(
-    session_handle: str,
-    claim: SessionClaim[Any],
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> bool:
-    if user_context is None:
-        user_context = {}
-    return await SessionRecipe.get_instance().recipe_implementation.remove_claim(
-        session_handle, claim, user_context
-    )
-
-
-async def get_session(
-    request: Any,
-    session_required: Optional[bool] = None,
-    anti_csrf_check: Optional[bool] = None,
-    check_database: Optional[bool] = None,
-    override_global_claim_validators: Optional[
-        Callable[
-            [List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
-            MaybeAwaitable[List[SessionClaimValidator]],
-        ]
-    ] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> Union[SessionContainer, None]:
-    if user_context is None:
-        user_context = {}
-
-    if session_required is None:
-        session_required = True
-
-    recipe_instance = SessionRecipe.get_instance()
-    recipe_interface_impl = recipe_instance.recipe_implementation
-    config = recipe_instance.config
-
-    return await get_session_from_request(
-        request,
-        config,
-        recipe_interface_impl,
-        session_required=session_required,
-        anti_csrf_check=anti_csrf_check,
-        check_database=check_database,
-        override_global_claim_validators=override_global_claim_validators,
-        user_context=user_context,
-    )
-
-
-async def get_session_without_request_response(
-    access_token: str,
-    anti_csrf_token: Optional[str] = None,
-    anti_csrf_check: Optional[bool] = None,
-    session_required: Optional[bool] = None,
-    check_database: Optional[bool] = None,
-    override_global_claim_validators: Optional[
-        Callable[
-            [List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
-            MaybeAwaitable[List[SessionClaimValidator]],
-        ]
-    ] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> Optional[SessionContainer]:
-    """Tries to validate an access token and build a Session object from it.
-
-    Notes about anti-csrf checking:
-    - if the `antiCsrf` is set to VIA_HEADER in the Session recipe config you have to handle anti-csrf checking before calling this function and set antiCsrfCheck to false in the options.
-    - you can disable anti-csrf checks by setting antiCsrf to NONE in the Session recipe config. We only recommend this if you are always getting the access-token from the Authorization header.
-    - if the antiCsrf check fails the returned status will be TRY_REFRESH_TOKEN_ERROR
-
-    Args:
-    - access_token: The access token extracted from the authorization header or cookies
-    - anti_csrf_token: The anti-csrf token extracted from the authorization header or cookies. Can be undefined if antiCsrfCheck is false
-    - anti_csrf_check: If true, anti-csrf checking will be done. If false, it will be skipped. Default behaviour is to check.
-    - session_required: If true, throws an error if the session does not exist. Default is True.
-    - check_database: If true, the session will be checked in the database. If false, it will be skipped. Default behaviour is to skip.
-    - override_global_claim_validators: Alter the
-    - user_context: user context
-
-    Results:
-    - OK: The session was successfully validated, including claim validation
-    - CLAIM_VALIDATION_ERROR: While the access token is valid, one or more claim validators have failed. Our frontend SDKs expect a 403 response the contents matching the value returned from this function.
-    - TRY_REFRESH_TOKEN_ERROR: This means, that the access token structure was valid, but it didn't pass validation for some reason and the user should call the refresh API.
-        You can send a 401 response to trigger this behaviour if you are using our frontend SDKs
-    - UNAUTHORISED: This means that the access token likely doesn't belong to a SuperTokens session. If this is unexpected, it's best handled by sending a 401 response.
-    """
-    if user_context is None:
-        user_context = {}
-
-    if session_required is None:
-        session_required = True
-
-    recipe_interface_impl = SessionRecipe.get_instance().recipe_implementation
-
-    session = await recipe_interface_impl.get_session(
-        access_token,
-        anti_csrf_token,
-        anti_csrf_check,
-        session_required,
-        check_database,
-        override_global_claim_validators,
-        user_context,
-    )
-
-    if session is not None:
-        claim_validators = await get_required_claim_validators(
-            session, override_global_claim_validators, user_context
-        )
-        await session.assert_claims(claim_validators, user_context)
-
-    return session
-
-
-async def refresh_session(
-    request: Any,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> SessionContainer:
-    if user_context is None:
-        user_context = {}
-
-    if not hasattr(request, "wrapper_used") or not request.wrapper_used:
-        request = FRAMEWORKS[
-            SessionRecipe.get_instance().app_info.framework
-        ].wrap_request(request)
-
-    recipe_instance = SessionRecipe.get_instance()
-    config = recipe_instance.config
-    recipe_interface_impl = recipe_instance.recipe_implementation
-
-    return await refresh_session_in_request(
-        request,
-        user_context,
-        config,
-        recipe_interface_impl,
-    )
-
-
-async def refresh_session_without_request_response(
-    refresh_token: str,
-    disable_anti_csrf: bool = False,
-    anti_csrf_token: Optional[str] = None,
-    user_context: Optional[Dict[str, Any]] = None,
-) -> SessionContainer:
-    if user_context is None:
-        user_context = {}
-
-    return await SessionRecipe.get_instance().recipe_implementation.refresh_session(
-        refresh_token, anti_csrf_token, disable_anti_csrf, user_context
-    )
-
-
-async def revoke_session(
-    session_handle: str, user_context: Union[None, Dict[str, Any]] = None
-) -> bool:
-    if user_context is None:
-        user_context = {}
-    return await SessionRecipe.get_instance().recipe_implementation.revoke_session(
-        session_handle, user_context
-    )
-
-
-async def revoke_all_sessions_for_user(
-    user_id: str,
-    tenant_id: Optional[str] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> List[str]:
-    if user_context is None:
-        user_context = {}
-    return await SessionRecipe.get_instance().recipe_implementation.revoke_all_sessions_for_user(
-        user_id, tenant_id or DEFAULT_TENANT_ID, tenant_id is None, user_context
-    )
-
-
-async def get_all_session_handles_for_user(
-    user_id: str,
-    tenant_id: Optional[str] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> List[str]:
-    if user_context is None:
-        user_context = {}
-    return await SessionRecipe.get_instance().recipe_implementation.get_all_session_handles_for_user(
-        user_id, tenant_id or DEFAULT_TENANT_ID, tenant_id is None, user_context
-    )
-
-
-async def revoke_multiple_sessions(
-    session_handles: List[str], user_context: Union[None, Dict[str, Any]] = None
-) -> List[str]:
-    if user_context is None:
-        user_context = {}
-    return await SessionRecipe.get_instance().recipe_implementation.revoke_multiple_sessions(
-        session_handles, user_context
-    )
-
-
-async def get_session_information(
-    session_handle: str, user_context: Union[None, Dict[str, Any]] = None
-) -> Union[SessionInformationResult, None]:
-    if user_context is None:
-        user_context = {}
-    return await SessionRecipe.get_instance().recipe_implementation.get_session_information(
-        session_handle, user_context
-    )
-
-
-async def update_session_data_in_database(
-    session_handle: str,
-    new_session_data: Dict[str, Any],
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> bool:
-    if user_context is None:
-        user_context = {}
-    return await SessionRecipe.get_instance().recipe_implementation.update_session_data_in_database(
-        session_handle, new_session_data, user_context
-    )
-
-
-async def merge_into_access_token_payload(
-    session_handle: str,
-    new_access_token_payload: Dict[str, Any],
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> bool:
-    if user_context is None:
-        user_context = {}
-
-    return await SessionRecipe.get_instance().recipe_implementation.merge_into_access_token_payload(
-        session_handle, new_access_token_payload, user_context
-    )
-
-
-async def create_jwt(
-    payload: Dict[str, Any],
-    validity_seconds: Optional[int] = None,
-    use_static_signing_key: Optional[bool] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> Union[CreateJwtOkResult, CreateJwtResultUnsupportedAlgorithm]:
-    if user_context is None:
-        user_context = {}
-    openid_recipe = SessionRecipe.get_instance().openid_recipe
-
-    return await openid_recipe.recipe_implementation.create_jwt(
-        payload, validity_seconds, use_static_signing_key, user_context
-    )
-
-
-async def get_jwks(user_context: Union[None, Dict[str, Any]] = None) -> GetJWKSResult:
-    if user_context is None:
-        user_context = {}
-    openid_recipe = SessionRecipe.get_instance().openid_recipe
-    return await openid_recipe.recipe_implementation.get_jwks(user_context)
-
-
-async def get_open_id_discovery_configuration(
-    user_context: Union[None, Dict[str, Any]] = None
-) -> GetOpenIdDiscoveryConfigurationResult:
-    if user_context is None:
-        user_context = {}
-    openid_recipe = SessionRecipe.get_instance().openid_recipe
-
-    return (
-        await openid_recipe.recipe_implementation.get_open_id_discovery_configuration(
-            user_context
-        )
-    )
-
-
-
-
-
-
-
-

Functions

-
-
-async def create_jwt(payload: Dict[str, Any], validity_seconds: Optional[int] = None, use_static_signing_key: Optional[bool] = None, user_context: Union[None, Dict[str, Any]] = None) ‑> Union[CreateJwtOkResultCreateJwtResultUnsupportedAlgorithm] -
-
-
-
- -Expand source code - -
async def create_jwt(
-    payload: Dict[str, Any],
-    validity_seconds: Optional[int] = None,
-    use_static_signing_key: Optional[bool] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> Union[CreateJwtOkResult, CreateJwtResultUnsupportedAlgorithm]:
-    if user_context is None:
-        user_context = {}
-    openid_recipe = SessionRecipe.get_instance().openid_recipe
-
-    return await openid_recipe.recipe_implementation.create_jwt(
-        payload, validity_seconds, use_static_signing_key, user_context
-    )
-
-
-
-async def create_new_session(request: Any, tenant_id: str, user_id: str, access_token_payload: Union[Dict[str, Any], None] = None, session_data_in_database: Union[Dict[str, Any], None] = None, user_context: Union[None, Dict[str, Any]] = None) ‑> SessionContainer -
-
-
-
- -Expand source code - -
async def create_new_session(
-    request: Any,
-    tenant_id: str,
-    user_id: str,
-    access_token_payload: Union[Dict[str, Any], None] = None,
-    session_data_in_database: Union[Dict[str, Any], None] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> SessionContainer:
-    if user_context is None:
-        user_context = {}
-    if session_data_in_database is None:
-        session_data_in_database = {}
-    if access_token_payload is None:
-        access_token_payload = {}
-
-    recipe_instance = SessionRecipe.get_instance()
-    config = recipe_instance.config
-    app_info = recipe_instance.app_info
-
-    return await create_new_session_in_request(
-        request,
-        user_context,
-        recipe_instance,
-        access_token_payload,
-        user_id,
-        config,
-        app_info,
-        session_data_in_database,
-        tenant_id,
-    )
-
-
-
-async def create_new_session_without_request_response(tenant_id: str, user_id: str, access_token_payload: Union[Dict[str, Any], None] = None, session_data_in_database: Union[Dict[str, Any], None] = None, disable_anti_csrf: bool = False, user_context: Union[None, Dict[str, Any]] = None) ‑> SessionContainer -
-
-
-
- -Expand source code - -
async def create_new_session_without_request_response(
-    tenant_id: str,
-    user_id: str,
-    access_token_payload: Union[Dict[str, Any], None] = None,
-    session_data_in_database: Union[Dict[str, Any], None] = None,
-    disable_anti_csrf: bool = False,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> SessionContainer:
-    if user_context is None:
-        user_context = {}
-    if session_data_in_database is None:
-        session_data_in_database = {}
-    if access_token_payload is None:
-        access_token_payload = {}
-
-    claims_added_by_other_recipes = (
-        SessionRecipe.get_instance().get_claims_added_by_other_recipes()
-    )
-    app_info = SessionRecipe.get_instance().app_info
-    issuer = (
-        app_info.api_domain.get_as_string_dangerous()
-        + app_info.api_base_path.get_as_string_dangerous()
-    )
-
-    final_access_token_payload = {**access_token_payload, "iss": issuer}
-
-    for prop in protected_props:
-        if prop in final_access_token_payload:
-            del final_access_token_payload[prop]
-
-    for claim in claims_added_by_other_recipes:
-        update = await claim.build(user_id, tenant_id, user_context)
-        final_access_token_payload = {**final_access_token_payload, **update}
-
-    return await SessionRecipe.get_instance().recipe_implementation.create_new_session(
-        user_id,
-        final_access_token_payload,
-        session_data_in_database,
-        disable_anti_csrf,
-        tenant_id,
-        user_context=user_context,
-    )
-
-
-
-async def fetch_and_set_claim(session_handle: str, claim: SessionClaim[Any], user_context: Union[None, Dict[str, Any]] = None) ‑> bool -
-
-
-
- -Expand source code - -
async def fetch_and_set_claim(
-    session_handle: str,
-    claim: SessionClaim[Any],
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> bool:
-    if user_context is None:
-        user_context = {}
-    return await SessionRecipe.get_instance().recipe_implementation.fetch_and_set_claim(
-        session_handle, claim, user_context
-    )
-
-
-
-async def get_all_session_handles_for_user(user_id: str, tenant_id: Optional[str] = None, user_context: Union[None, Dict[str, Any]] = None) ‑> List[str] -
-
-
-
- -Expand source code - -
async def get_all_session_handles_for_user(
-    user_id: str,
-    tenant_id: Optional[str] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> List[str]:
-    if user_context is None:
-        user_context = {}
-    return await SessionRecipe.get_instance().recipe_implementation.get_all_session_handles_for_user(
-        user_id, tenant_id or DEFAULT_TENANT_ID, tenant_id is None, user_context
-    )
-
-
-
-async def get_claim_value(session_handle: str, claim: SessionClaim[_T], user_context: Union[None, Dict[str, Any]] = None) ‑> Union[SessionDoesNotExistErrorGetClaimValueOkResult[~_T]] -
-
-
-
- -Expand source code - -
async def get_claim_value(
-    session_handle: str,
-    claim: SessionClaim[_T],
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> Union[SessionDoesNotExistError, GetClaimValueOkResult[_T]]:
-    if user_context is None:
-        user_context = {}
-    return await SessionRecipe.get_instance().recipe_implementation.get_claim_value(
-        session_handle, claim, user_context
-    )
-
-
-
-async def get_jwks(user_context: Union[None, Dict[str, Any]] = None) ‑> GetJWKSResult -
-
-
-
- -Expand source code - -
async def get_jwks(user_context: Union[None, Dict[str, Any]] = None) -> GetJWKSResult:
-    if user_context is None:
-        user_context = {}
-    openid_recipe = SessionRecipe.get_instance().openid_recipe
-    return await openid_recipe.recipe_implementation.get_jwks(user_context)
-
-
-
-async def get_open_id_discovery_configuration(user_context: Union[None, Dict[str, Any]] = None) ‑> GetOpenIdDiscoveryConfigurationResult -
-
-
-
- -Expand source code - -
async def get_open_id_discovery_configuration(
-    user_context: Union[None, Dict[str, Any]] = None
-) -> GetOpenIdDiscoveryConfigurationResult:
-    if user_context is None:
-        user_context = {}
-    openid_recipe = SessionRecipe.get_instance().openid_recipe
-
-    return (
-        await openid_recipe.recipe_implementation.get_open_id_discovery_configuration(
-            user_context
-        )
-    )
-
-
-
-async def get_session(request: Any, session_required: Optional[bool] = None, anti_csrf_check: Optional[bool] = None, check_database: Optional[bool] = None, override_global_claim_validators: Optional[Callable[[List[SessionClaimValidator], SessionContainer, Dict[str, Any]], MaybeAwaitable[List[SessionClaimValidator]]]] = None, user_context: Union[None, Dict[str, Any]] = None) ‑> Optional[SessionContainer] -
-
-
-
- -Expand source code - -
async def get_session(
-    request: Any,
-    session_required: Optional[bool] = None,
-    anti_csrf_check: Optional[bool] = None,
-    check_database: Optional[bool] = None,
-    override_global_claim_validators: Optional[
-        Callable[
-            [List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
-            MaybeAwaitable[List[SessionClaimValidator]],
-        ]
-    ] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> Union[SessionContainer, None]:
-    if user_context is None:
-        user_context = {}
-
-    if session_required is None:
-        session_required = True
-
-    recipe_instance = SessionRecipe.get_instance()
-    recipe_interface_impl = recipe_instance.recipe_implementation
-    config = recipe_instance.config
-
-    return await get_session_from_request(
-        request,
-        config,
-        recipe_interface_impl,
-        session_required=session_required,
-        anti_csrf_check=anti_csrf_check,
-        check_database=check_database,
-        override_global_claim_validators=override_global_claim_validators,
-        user_context=user_context,
-    )
-
-
-
-async def get_session_information(session_handle: str, user_context: Union[None, Dict[str, Any]] = None) ‑> Optional[SessionInformationResult] -
-
-
-
- -Expand source code - -
async def get_session_information(
-    session_handle: str, user_context: Union[None, Dict[str, Any]] = None
-) -> Union[SessionInformationResult, None]:
-    if user_context is None:
-        user_context = {}
-    return await SessionRecipe.get_instance().recipe_implementation.get_session_information(
-        session_handle, user_context
-    )
-
-
-
-async def get_session_without_request_response(access_token: str, anti_csrf_token: Optional[str] = None, anti_csrf_check: Optional[bool] = None, session_required: Optional[bool] = None, check_database: Optional[bool] = None, override_global_claim_validators: Optional[Callable[[List[SessionClaimValidator], SessionContainer, Dict[str, Any]], MaybeAwaitable[List[SessionClaimValidator]]]] = None, user_context: Union[None, Dict[str, Any]] = None) ‑> Optional[SessionContainer] -
-
-

Tries to validate an access token and build a Session object from it.

-

Notes about anti-csrf checking: -- if the antiCsrf is set to VIA_HEADER in the Session recipe config you have to handle anti-csrf checking before calling this function and set antiCsrfCheck to false in the options. -- you can disable anti-csrf checks by setting antiCsrf to NONE in the Session recipe config. We only recommend this if you are always getting the access-token from the Authorization header. -- if the antiCsrf check fails the returned status will be TRY_REFRESH_TOKEN_ERROR

-

Args: -- access_token: The access token extracted from the authorization header or cookies -- anti_csrf_token: The anti-csrf token extracted from the authorization header or cookies. Can be undefined if antiCsrfCheck is false -- anti_csrf_check: If true, anti-csrf checking will be done. If false, it will be skipped. Default behaviour is to check. -- session_required: If true, throws an error if the session does not exist. Default is True. -- check_database: If true, the session will be checked in the database. If false, it will be skipped. Default behaviour is to skip. -- override_global_claim_validators: Alter the -- user_context: user context

-

Results: -- OK: The session was successfully validated, including claim validation -- CLAIM_VALIDATION_ERROR: While the access token is valid, one or more claim validators have failed. Our frontend SDKs expect a 403 response the contents matching the value returned from this function. -- TRY_REFRESH_TOKEN_ERROR: This means, that the access token structure was valid, but it didn't pass validation for some reason and the user should call the refresh API. -You can send a 401 response to trigger this behaviour if you are using our frontend SDKs -- UNAUTHORISED: This means that the access token likely doesn't belong to a SuperTokens session. If this is unexpected, it's best handled by sending a 401 response.

-
- -Expand source code - -
async def get_session_without_request_response(
-    access_token: str,
-    anti_csrf_token: Optional[str] = None,
-    anti_csrf_check: Optional[bool] = None,
-    session_required: Optional[bool] = None,
-    check_database: Optional[bool] = None,
-    override_global_claim_validators: Optional[
-        Callable[
-            [List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
-            MaybeAwaitable[List[SessionClaimValidator]],
-        ]
-    ] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> Optional[SessionContainer]:
-    """Tries to validate an access token and build a Session object from it.
-
-    Notes about anti-csrf checking:
-    - if the `antiCsrf` is set to VIA_HEADER in the Session recipe config you have to handle anti-csrf checking before calling this function and set antiCsrfCheck to false in the options.
-    - you can disable anti-csrf checks by setting antiCsrf to NONE in the Session recipe config. We only recommend this if you are always getting the access-token from the Authorization header.
-    - if the antiCsrf check fails the returned status will be TRY_REFRESH_TOKEN_ERROR
-
-    Args:
-    - access_token: The access token extracted from the authorization header or cookies
-    - anti_csrf_token: The anti-csrf token extracted from the authorization header or cookies. Can be undefined if antiCsrfCheck is false
-    - anti_csrf_check: If true, anti-csrf checking will be done. If false, it will be skipped. Default behaviour is to check.
-    - session_required: If true, throws an error if the session does not exist. Default is True.
-    - check_database: If true, the session will be checked in the database. If false, it will be skipped. Default behaviour is to skip.
-    - override_global_claim_validators: Alter the
-    - user_context: user context
-
-    Results:
-    - OK: The session was successfully validated, including claim validation
-    - CLAIM_VALIDATION_ERROR: While the access token is valid, one or more claim validators have failed. Our frontend SDKs expect a 403 response the contents matching the value returned from this function.
-    - TRY_REFRESH_TOKEN_ERROR: This means, that the access token structure was valid, but it didn't pass validation for some reason and the user should call the refresh API.
-        You can send a 401 response to trigger this behaviour if you are using our frontend SDKs
-    - UNAUTHORISED: This means that the access token likely doesn't belong to a SuperTokens session. If this is unexpected, it's best handled by sending a 401 response.
-    """
-    if user_context is None:
-        user_context = {}
-
-    if session_required is None:
-        session_required = True
-
-    recipe_interface_impl = SessionRecipe.get_instance().recipe_implementation
-
-    session = await recipe_interface_impl.get_session(
-        access_token,
-        anti_csrf_token,
-        anti_csrf_check,
-        session_required,
-        check_database,
-        override_global_claim_validators,
-        user_context,
-    )
-
-    if session is not None:
-        claim_validators = await get_required_claim_validators(
-            session, override_global_claim_validators, user_context
-        )
-        await session.assert_claims(claim_validators, user_context)
-
-    return session
-
-
-
-async def merge_into_access_token_payload(session_handle: str, new_access_token_payload: Dict[str, Any], user_context: Union[None, Dict[str, Any]] = None) ‑> bool -
-
-
-
- -Expand source code - -
async def merge_into_access_token_payload(
-    session_handle: str,
-    new_access_token_payload: Dict[str, Any],
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> bool:
-    if user_context is None:
-        user_context = {}
-
-    return await SessionRecipe.get_instance().recipe_implementation.merge_into_access_token_payload(
-        session_handle, new_access_token_payload, user_context
-    )
-
-
-
-async def refresh_session(request: Any, user_context: Union[None, Dict[str, Any]] = None) ‑> SessionContainer -
-
-
-
- -Expand source code - -
async def refresh_session(
-    request: Any,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> SessionContainer:
-    if user_context is None:
-        user_context = {}
-
-    if not hasattr(request, "wrapper_used") or not request.wrapper_used:
-        request = FRAMEWORKS[
-            SessionRecipe.get_instance().app_info.framework
-        ].wrap_request(request)
-
-    recipe_instance = SessionRecipe.get_instance()
-    config = recipe_instance.config
-    recipe_interface_impl = recipe_instance.recipe_implementation
-
-    return await refresh_session_in_request(
-        request,
-        user_context,
-        config,
-        recipe_interface_impl,
-    )
-
-
-
-async def refresh_session_without_request_response(refresh_token: str, disable_anti_csrf: bool = False, anti_csrf_token: Optional[str] = None, user_context: Optional[Dict[str, Any]] = None) ‑> SessionContainer -
-
-
-
- -Expand source code - -
async def refresh_session_without_request_response(
-    refresh_token: str,
-    disable_anti_csrf: bool = False,
-    anti_csrf_token: Optional[str] = None,
-    user_context: Optional[Dict[str, Any]] = None,
-) -> SessionContainer:
-    if user_context is None:
-        user_context = {}
-
-    return await SessionRecipe.get_instance().recipe_implementation.refresh_session(
-        refresh_token, anti_csrf_token, disable_anti_csrf, user_context
-    )
-
-
-
-async def remove_claim(session_handle: str, claim: SessionClaim[Any], user_context: Union[None, Dict[str, Any]] = None) ‑> bool -
-
-
-
- -Expand source code - -
async def remove_claim(
-    session_handle: str,
-    claim: SessionClaim[Any],
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> bool:
-    if user_context is None:
-        user_context = {}
-    return await SessionRecipe.get_instance().recipe_implementation.remove_claim(
-        session_handle, claim, user_context
-    )
-
-
-
-async def revoke_all_sessions_for_user(user_id: str, tenant_id: Optional[str] = None, user_context: Union[None, Dict[str, Any]] = None) ‑> List[str] -
-
-
-
- -Expand source code - -
async def revoke_all_sessions_for_user(
-    user_id: str,
-    tenant_id: Optional[str] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> List[str]:
-    if user_context is None:
-        user_context = {}
-    return await SessionRecipe.get_instance().recipe_implementation.revoke_all_sessions_for_user(
-        user_id, tenant_id or DEFAULT_TENANT_ID, tenant_id is None, user_context
-    )
-
-
-
-async def revoke_multiple_sessions(session_handles: List[str], user_context: Union[None, Dict[str, Any]] = None) ‑> List[str] -
-
-
-
- -Expand source code - -
async def revoke_multiple_sessions(
-    session_handles: List[str], user_context: Union[None, Dict[str, Any]] = None
-) -> List[str]:
-    if user_context is None:
-        user_context = {}
-    return await SessionRecipe.get_instance().recipe_implementation.revoke_multiple_sessions(
-        session_handles, user_context
-    )
-
-
-
-async def revoke_session(session_handle: str, user_context: Union[None, Dict[str, Any]] = None) ‑> bool -
-
-
-
- -Expand source code - -
async def revoke_session(
-    session_handle: str, user_context: Union[None, Dict[str, Any]] = None
-) -> bool:
-    if user_context is None:
-        user_context = {}
-    return await SessionRecipe.get_instance().recipe_implementation.revoke_session(
-        session_handle, user_context
-    )
-
-
-
-async def set_claim_value(session_handle: str, claim: SessionClaim[_T], value: _T, user_context: Union[None, Dict[str, Any]] = None) ‑> bool -
-
-
-
- -Expand source code - -
async def set_claim_value(
-    session_handle: str,
-    claim: SessionClaim[_T],
-    value: _T,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> bool:
-    if user_context is None:
-        user_context = {}
-    return await SessionRecipe.get_instance().recipe_implementation.set_claim_value(
-        session_handle, claim, value, user_context
-    )
-
-
-
-async def update_session_data_in_database(session_handle: str, new_session_data: Dict[str, Any], user_context: Union[None, Dict[str, Any]] = None) ‑> bool -
-
-
-
- -Expand source code - -
async def update_session_data_in_database(
-    session_handle: str,
-    new_session_data: Dict[str, Any],
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> bool:
-    if user_context is None:
-        user_context = {}
-    return await SessionRecipe.get_instance().recipe_implementation.update_session_data_in_database(
-        session_handle, new_session_data, user_context
-    )
-
-
-
-async def validate_claims_for_session_handle(session_handle: str, override_global_claim_validators: Optional[Callable[[List[SessionClaimValidator], SessionInformationResult, Dict[str, Any]], MaybeAwaitable[List[SessionClaimValidator]]]] = None, user_context: Union[None, Dict[str, Any]] = None) ‑> Union[SessionDoesNotExistErrorClaimsValidationResult] -
-
-
-
- -Expand source code - -
async def validate_claims_for_session_handle(
-    session_handle: str,
-    override_global_claim_validators: Optional[
-        Callable[
-            [
-                List[SessionClaimValidator],
-                SessionInformationResult,
-                Dict[str, Any],
-            ],
-            MaybeAwaitable[List[SessionClaimValidator]],
-        ]
-    ] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> Union[SessionDoesNotExistError, ClaimsValidationResult]:
-    if user_context is None:
-        user_context = {}
-
-    recipe_impl = SessionRecipe.get_instance().recipe_implementation
-    session_info = await recipe_impl.get_session_information(
-        session_handle, user_context
-    )
-
-    if session_info is None:
-        return SessionDoesNotExistError()
-
-    claim_validators_added_by_other_recipes = (
-        SessionRecipe.get_instance().get_claim_validators_added_by_other_recipes()
-    )
-    global_claim_validators = await resolve(
-        recipe_impl.get_global_claim_validators(
-            session_info.tenant_id,
-            session_info.user_id,
-            claim_validators_added_by_other_recipes,
-            user_context,
-        )
-    )
-
-    if override_global_claim_validators is not None:
-        claim_validators = await resolve(
-            override_global_claim_validators(
-                global_claim_validators, session_info, user_context
-            )
-        )
-    else:
-        claim_validators = global_claim_validators
-
-    claim_validation_res = await recipe_impl.validate_claims(
-        session_info.user_id,
-        session_info.custom_claims_in_access_token_payload,
-        claim_validators,
-        user_context,
-    )
-
-    if claim_validation_res.access_token_payload_update is not None:
-        updated = await recipe_impl.merge_into_access_token_payload(
-            session_handle,
-            claim_validation_res.access_token_payload_update,
-            user_context,
-        )
-        if not updated:
-            return SessionDoesNotExistError()
-
-    return ClaimsValidationResult(claim_validation_res.invalid_claims)
-
-
-
-async def validate_claims_in_jwt_payload(tenant_id: str, user_id: str, jwt_payload: JSONObject, override_global_claim_validators: Optional[Callable[[List[SessionClaimValidator], str, Dict[str, Any]], MaybeAwaitable[List[SessionClaimValidator]]]] = None, user_context: Union[None, Dict[str, Any]] = None) -
-
-
-
- -Expand source code - -
async def validate_claims_in_jwt_payload(
-    tenant_id: str,
-    user_id: str,
-    jwt_payload: JSONObject,
-    override_global_claim_validators: Optional[
-        Callable[
-            [
-                List[SessionClaimValidator],
-                str,
-                Dict[str, Any],
-            ],
-            MaybeAwaitable[List[SessionClaimValidator]],
-        ]
-    ] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-):
-    if user_context is None:
-        user_context = {}
-
-    recipe_impl = SessionRecipe.get_instance().recipe_implementation
-
-    claim_validators_added_by_other_recipes = (
-        SessionRecipe.get_instance().get_claim_validators_added_by_other_recipes()
-    )
-    global_claim_validators = await resolve(
-        recipe_impl.get_global_claim_validators(
-            tenant_id,
-            user_id,
-            claim_validators_added_by_other_recipes,
-            user_context,
-        )
-    )
-
-    if override_global_claim_validators is not None:
-        claim_validators = await resolve(
-            override_global_claim_validators(
-                global_claim_validators, user_id, user_context
-            )
-        )
-    else:
-        claim_validators = global_claim_validators
-
-    return await recipe_impl.validate_claims_in_jwt_payload(
-        user_id, jwt_payload, claim_validators, user_context
-    )
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/session/claim_base_classes/boolean_claim.html b/html/supertokens_python/recipe/session/claim_base_classes/boolean_claim.html deleted file mode 100644 index b7613f194..000000000 --- a/html/supertokens_python/recipe/session/claim_base_classes/boolean_claim.html +++ /dev/null @@ -1,245 +0,0 @@ - - - - - - -supertokens_python.recipe.session.claim_base_classes.boolean_claim API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.session.claim_base_classes.boolean_claim

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from typing import Any, Callable, Dict, Optional
-
-from supertokens_python.types import MaybeAwaitable
-
-from .primitive_claim import PrimitiveClaim, PrimitiveClaimValidators
-
-
-class BooleanClaimValidators(PrimitiveClaimValidators[bool]):
-    def is_true(self, max_age: Optional[int], id_: Optional[str] = None):
-        return self.has_value(True, max_age, id_)
-
-    def is_false(self, max_age: Optional[int], id_: Optional[str] = None):
-        return self.has_value(False, max_age, id_)
-
-
-class BooleanClaim(PrimitiveClaim[bool]):
-    def __init__(
-        self,
-        key: str,
-        fetch_value: Callable[
-            [str, str, Dict[str, Any]],
-            MaybeAwaitable[Optional[bool]],
-        ],
-        default_max_age_in_sec: Optional[int] = None,
-    ):
-        super().__init__(key, fetch_value, default_max_age_in_sec)
-        self.validators = BooleanClaimValidators(
-            claim=self, default_max_age_in_sec=default_max_age_in_sec
-        )
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class BooleanClaim -(key: str, fetch_value: Callable[[str, str, Dict[str, Any]], Union[Awaitable[Optional[bool]], bool, ForwardRef(None)]], default_max_age_in_sec: Optional[int] = None) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-

Args

-
-
key
-
The key to use when storing the claim in the payload.
-
fetch_value
-
a method that fetches the current value of this claim for the user. -A None return value signifies that we don't want to update the claim payload and or the claim value is -not present in the database. For example, this can happen with a second factor auth claim, where we -don't want to add the claim to the session automatically
-
-
- -Expand source code - -
class BooleanClaim(PrimitiveClaim[bool]):
-    def __init__(
-        self,
-        key: str,
-        fetch_value: Callable[
-            [str, str, Dict[str, Any]],
-            MaybeAwaitable[Optional[bool]],
-        ],
-        default_max_age_in_sec: Optional[int] = None,
-    ):
-        super().__init__(key, fetch_value, default_max_age_in_sec)
-        self.validators = BooleanClaimValidators(
-            claim=self, default_max_age_in_sec=default_max_age_in_sec
-        )
-
-

Ancestors

- -

Subclasses

- -

Inherited members

- -
-
-class BooleanClaimValidators -(claim: SessionClaim[~Primitive], default_max_age_in_sec: Optional[int]) -
-
-

Abstract base class for generic types.

-

A generic type is typically declared by inheriting from -this class parameterized with one or more type variables. -For example, a generic mapping type might be defined as::

-

class Mapping(Generic[KT, VT]): -def getitem(self, key: KT) -> VT: -… -# Etc.

-

This class can then be used as follows::

-

def lookup_name(mapping: Mapping[KT, VT], key: KT, default: VT) -> VT: -try: -return mapping[key] -except KeyError: -return default

-
- -Expand source code - -
class BooleanClaimValidators(PrimitiveClaimValidators[bool]):
-    def is_true(self, max_age: Optional[int], id_: Optional[str] = None):
-        return self.has_value(True, max_age, id_)
-
-    def is_false(self, max_age: Optional[int], id_: Optional[str] = None):
-        return self.has_value(False, max_age, id_)
-
-

Ancestors

- -

Subclasses

- -

Methods

-
-
-def is_false(self, max_age: Optional[int], id_: Optional[str] = None) -
-
-
-
- -Expand source code - -
def is_false(self, max_age: Optional[int], id_: Optional[str] = None):
-    return self.has_value(False, max_age, id_)
-
-
-
-def is_true(self, max_age: Optional[int], id_: Optional[str] = None) -
-
-
-
- -Expand source code - -
def is_true(self, max_age: Optional[int], id_: Optional[str] = None):
-    return self.has_value(True, max_age, id_)
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/session/claim_base_classes/index.html b/html/supertokens_python/recipe/session/claim_base_classes/index.html deleted file mode 100644 index 71e9251c6..000000000 --- a/html/supertokens_python/recipe/session/claim_base_classes/index.html +++ /dev/null @@ -1,93 +0,0 @@ - - - - - - -supertokens_python.recipe.session.claim_base_classes API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.session.claim_base_classes

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-
-
-

Sub-modules

-
-
supertokens_python.recipe.session.claim_base_classes.boolean_claim
-
-
-
-
supertokens_python.recipe.session.claim_base_classes.primitive_array_claim
-
-
-
-
supertokens_python.recipe.session.claim_base_classes.primitive_claim
-
-
-
-
-
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/session/claim_base_classes/primitive_array_claim.html b/html/supertokens_python/recipe/session/claim_base_classes/primitive_array_claim.html deleted file mode 100644 index 6a5230983..000000000 --- a/html/supertokens_python/recipe/session/claim_base_classes/primitive_array_claim.html +++ /dev/null @@ -1,1158 +0,0 @@ - - - - - - -supertokens_python.recipe.session.claim_base_classes.primitive_array_claim API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.session.claim_base_classes.primitive_array_claim

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from typing import Any, Callable, Dict, Optional, TypeVar, Union, Generic, List
-
-from supertokens_python.types import MaybeAwaitable
-from supertokens_python.utils import get_timestamp_ms
-
-from ..interfaces import (
-    JSONObject,
-    JSONPrimitive,
-    SessionClaim,
-    SessionClaimValidator,
-    ClaimValidationResult,
-    JSONPrimitiveList,
-)
-
-
-Primitive = TypeVar("Primitive", bound=JSONPrimitive)
-PrimitiveList = TypeVar("PrimitiveList", bound=JSONPrimitiveList)
-
-_T = TypeVar("_T")
-
-
-class SCVMixin(SessionClaimValidator, Generic[_T]):
-    def __init__(
-        self,
-        id_: str,
-        claim: SessionClaim[PrimitiveList],
-        val: _T,
-        max_age_in_sec: Optional[int] = None,
-    ):
-        super().__init__(id_)
-        self.claim: SessionClaim[PrimitiveList] = claim  # TODO:PrimitiveArrayClaim
-        self.val = val
-        self.max_age_in_sec = max_age_in_sec
-
-    def should_refetch(
-        self,
-        payload: JSONObject,
-        user_context: Dict[str, Any],
-    ) -> bool:
-        claim = self.claim
-
-        return (claim.get_value_from_payload(payload, user_context) is None) or (
-            self.max_age_in_sec is not None
-            and (
-                payload[claim.key]["t"]
-                < get_timestamp_ms() - self.max_age_in_sec * 1000
-            )
-        )
-
-    async def _validate(
-        self,
-        payload: JSONObject,
-        user_context: Dict[str, Any],
-        is_include: bool,
-        is_include_any: bool = False,
-    ):
-        val = self.val
-        max_age_in_sec = self.max_age_in_sec
-
-        expected_key = "expectedToInclude" if is_include else "expectedToNotInclude"
-        if is_include_any:
-            expected_key = "expectedToIncludeAtLeastOneOf"
-
-        assert isinstance(self.claim, PrimitiveArrayClaim)
-        claim_val = self.claim.get_value_from_payload(payload, user_context)
-
-        if claim_val is None:
-            return ClaimValidationResult(
-                is_valid=False,
-                reason={
-                    "message": "value does not exist",
-                    expected_key: val,
-                    "actualValue": claim_val,
-                },
-            )
-
-        last_refetch_time = self.claim.get_last_refetch_time(payload, user_context)
-        assert last_refetch_time is not None
-        age_in_sec = (get_timestamp_ms() - last_refetch_time) / 1000
-        if max_age_in_sec is not None and age_in_sec > max_age_in_sec:
-            return ClaimValidationResult(
-                is_valid=False,
-                reason={
-                    "message": "expired",
-                    "ageInSeconds": age_in_sec,
-                    "maxAgeInSeconds": max_age_in_sec,
-                },
-            )
-
-        # Doing this to ensure same code in the upcoming steps irrespective of
-        # whether self.val is Primitive or PrimitiveList
-        vals: List[JSONPrimitive] = (
-            val if isinstance(val, list) else [val]
-        )  # pyright: reportGeneralTypeIssues=false
-
-        claim_val_set = set(claim_val)
-        if is_include and not is_include_any:
-            for v in vals:
-                if v not in claim_val_set:
-                    return ClaimValidationResult(
-                        is_valid=False,
-                        reason={
-                            "message": "wrong value",
-                            expected_key: val,
-                            # other SDKs return the item itself
-                            "actualValue": claim_val,
-                        },
-                    )
-        else:
-            for v in vals:
-                if v in claim_val_set:
-                    if is_include_any:
-                        return ClaimValidationResult(is_valid=True)
-
-                    return ClaimValidationResult(
-                        is_valid=False,
-                        reason={
-                            "message": "wrong value",
-                            expected_key: val,
-                            # other SDKs return the item itself
-                            "actualValue": claim_val,
-                        },
-                    )
-
-            if is_include_any:
-                return ClaimValidationResult(
-                    is_valid=False,
-                    reason={
-                        "message": "wrong value",
-                        expected_key: val,
-                        # other SDKs return the item itself
-                        "actualValue": claim_val,
-                    },
-                )
-
-        return ClaimValidationResult(is_valid=True)
-
-
-class IncludesSCV(SCVMixin[Primitive]):
-    async def validate(
-        self,
-        payload: JSONObject,
-        user_context: Dict[str, Any],
-    ):
-        return await self._validate(payload, user_context, is_include=True)
-
-
-class ExcludesSCV(SCVMixin[Primitive]):
-    async def validate(
-        self,
-        payload: JSONObject,
-        user_context: Dict[str, Any],
-    ):
-        return await self._validate(payload, user_context, is_include=False)
-
-
-class IncludesAllSCV(SCVMixin[PrimitiveList]):
-    async def validate(
-        self,
-        payload: JSONObject,
-        user_context: Dict[str, Any],
-    ):
-        return await self._validate(payload, user_context, is_include=True)
-
-
-class IncludesAnySCV(SCVMixin[PrimitiveList]):
-    async def validate(
-        self,
-        payload: JSONObject,
-        user_context: Dict[str, Any],
-    ):
-        return await self._validate(
-            payload, user_context, is_include=True, is_include_any=True
-        )
-
-
-class ExcludesAllSCV(SCVMixin[PrimitiveList]):
-    async def validate(
-        self,
-        payload: JSONObject,
-        user_context: Dict[str, Any],
-    ):
-        return await self._validate(payload, user_context, is_include=False)
-
-
-class PrimitiveArrayClaimValidators(Generic[PrimitiveList]):
-    def __init__(
-        self,
-        claim: SessionClaim[PrimitiveList],
-        default_max_age_in_sec: Optional[int] = None,
-    ) -> None:
-        self.claim = claim
-        self.default_max_age_in_sec = default_max_age_in_sec
-
-    def includes(  # pyright: ignore[reportInvalidTypeVarUse]
-        self,
-        val: Primitive,  # pyright: ignore[reportInvalidTypeVarUse]
-        max_age_in_seconds: Optional[int] = None,
-        id_: Union[str, None] = None,
-    ) -> SessionClaimValidator:
-        max_age_in_sec = max_age_in_seconds or self.default_max_age_in_sec
-        return IncludesSCV(
-            (id_ or self.claim.key), self.claim, val=val, max_age_in_sec=max_age_in_sec
-        )
-
-    def excludes(  # pyright: ignore[reportInvalidTypeVarUse]
-        self,
-        val: Primitive,  # pyright: ignore[reportInvalidTypeVarUse]
-        max_age_in_seconds: Optional[int] = None,
-        id_: Union[str, None] = None,
-    ) -> SessionClaimValidator:
-        max_age_in_sec = max_age_in_seconds or self.default_max_age_in_sec
-        return ExcludesSCV(
-            (id_ or self.claim.key), self.claim, val=val, max_age_in_sec=max_age_in_sec
-        )
-
-    def includes_all(
-        self,
-        val: PrimitiveList,
-        max_age_in_seconds: Optional[int] = None,
-        id_: Union[str, None] = None,
-    ) -> SessionClaimValidator:
-        max_age_in_sec = max_age_in_seconds or self.default_max_age_in_sec
-        return IncludesAllSCV(
-            (id_ or self.claim.key), self.claim, val=val, max_age_in_sec=max_age_in_sec
-        )
-
-    def includes_any(
-        self,
-        val: PrimitiveList,
-        max_age_in_seconds: Optional[int] = None,
-        id_: Union[str, None] = None,
-    ) -> SessionClaimValidator:
-        max_age_in_sec = max_age_in_seconds or self.default_max_age_in_sec
-        return IncludesAnySCV(
-            (id_ or self.claim.key), self.claim, val=val, max_age_in_sec=max_age_in_sec
-        )
-
-    def excludes_all(
-        self,
-        val: PrimitiveList,
-        max_age_in_seconds: Optional[int] = None,
-        id_: Union[str, None] = None,
-    ) -> SessionClaimValidator:
-        max_age_in_sec = max_age_in_seconds or self.default_max_age_in_sec
-        return ExcludesAllSCV(
-            (id_ or self.claim.key), self.claim, val=val, max_age_in_sec=max_age_in_sec
-        )
-
-
-class PrimitiveArrayClaim(SessionClaim[PrimitiveList], Generic[PrimitiveList]):
-    def __init__(
-        self,
-        key: str,
-        fetch_value: Callable[
-            [str, str, Dict[str, Any]],
-            MaybeAwaitable[Optional[PrimitiveList]],
-        ],
-        default_max_age_in_sec: Optional[int] = None,
-    ) -> None:
-        super().__init__(key, fetch_value)
-
-        claim = self
-        self.validators = PrimitiveArrayClaimValidators(claim, default_max_age_in_sec)
-
-    def add_to_payload_(
-        self,
-        payload: Dict[str, Any],
-        value: PrimitiveList,
-        user_context: Union[Dict[str, Any], None] = None,
-    ) -> JSONObject:
-        payload[self.key] = {"v": value, "t": get_timestamp_ms()}
-        _ = user_context
-
-        return payload
-
-    def remove_from_payload_by_merge_(
-        self, payload: JSONObject, user_context: Optional[Dict[str, Any]] = None
-    ) -> JSONObject:
-        payload[self.key] = None
-        return payload
-
-    def remove_from_payload(
-        self, payload: JSONObject, user_context: Optional[Dict[str, Any]] = None
-    ) -> JSONObject:
-        del payload[self.key]
-        return payload
-
-    def get_value_from_payload(
-        self, payload: JSONObject, user_context: Union[Dict[str, Any], None] = None
-    ) -> Union[PrimitiveList, None]:
-        _ = user_context
-
-        return payload.get(self.key, {}).get("v")
-
-    def get_last_refetch_time(
-        self, payload: JSONObject, user_context: Union[Dict[str, Any], None] = None
-    ) -> Union[int, None]:
-        _ = user_context
-
-        return payload.get(self.key, {}).get("t")
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class ExcludesAllSCV -(id_: str, claim: SessionClaim[~PrimitiveList], val: ~_T, max_age_in_sec: Optional[int] = None) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class ExcludesAllSCV(SCVMixin[PrimitiveList]):
-    async def validate(
-        self,
-        payload: JSONObject,
-        user_context: Dict[str, Any],
-    ):
-        return await self._validate(payload, user_context, is_include=False)
-
-

Ancestors

- -

Methods

-
-
-async def validate(self, payload: Dict[str, Any], user_context: Dict[str, Any]) -
-
-
-
- -Expand source code - -
async def validate(
-    self,
-    payload: JSONObject,
-    user_context: Dict[str, Any],
-):
-    return await self._validate(payload, user_context, is_include=False)
-
-
-
-
-
-class ExcludesSCV -(id_: str, claim: SessionClaim[~PrimitiveList], val: ~_T, max_age_in_sec: Optional[int] = None) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class ExcludesSCV(SCVMixin[Primitive]):
-    async def validate(
-        self,
-        payload: JSONObject,
-        user_context: Dict[str, Any],
-    ):
-        return await self._validate(payload, user_context, is_include=False)
-
-

Ancestors

- -

Methods

-
-
-async def validate(self, payload: Dict[str, Any], user_context: Dict[str, Any]) -
-
-
-
- -Expand source code - -
async def validate(
-    self,
-    payload: JSONObject,
-    user_context: Dict[str, Any],
-):
-    return await self._validate(payload, user_context, is_include=False)
-
-
-
-
-
-class IncludesAllSCV -(id_: str, claim: SessionClaim[~PrimitiveList], val: ~_T, max_age_in_sec: Optional[int] = None) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class IncludesAllSCV(SCVMixin[PrimitiveList]):
-    async def validate(
-        self,
-        payload: JSONObject,
-        user_context: Dict[str, Any],
-    ):
-        return await self._validate(payload, user_context, is_include=True)
-
-

Ancestors

- -

Methods

-
-
-async def validate(self, payload: Dict[str, Any], user_context: Dict[str, Any]) -
-
-
-
- -Expand source code - -
async def validate(
-    self,
-    payload: JSONObject,
-    user_context: Dict[str, Any],
-):
-    return await self._validate(payload, user_context, is_include=True)
-
-
-
-
-
-class IncludesAnySCV -(id_: str, claim: SessionClaim[~PrimitiveList], val: ~_T, max_age_in_sec: Optional[int] = None) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class IncludesAnySCV(SCVMixin[PrimitiveList]):
-    async def validate(
-        self,
-        payload: JSONObject,
-        user_context: Dict[str, Any],
-    ):
-        return await self._validate(
-            payload, user_context, is_include=True, is_include_any=True
-        )
-
-

Ancestors

- -

Methods

-
-
-async def validate(self, payload: Dict[str, Any], user_context: Dict[str, Any]) -
-
-
-
- -Expand source code - -
async def validate(
-    self,
-    payload: JSONObject,
-    user_context: Dict[str, Any],
-):
-    return await self._validate(
-        payload, user_context, is_include=True, is_include_any=True
-    )
-
-
-
-
-
-class IncludesSCV -(id_: str, claim: SessionClaim[~PrimitiveList], val: ~_T, max_age_in_sec: Optional[int] = None) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class IncludesSCV(SCVMixin[Primitive]):
-    async def validate(
-        self,
-        payload: JSONObject,
-        user_context: Dict[str, Any],
-    ):
-        return await self._validate(payload, user_context, is_include=True)
-
-

Ancestors

- -

Methods

-
-
-async def validate(self, payload: Dict[str, Any], user_context: Dict[str, Any]) -
-
-
-
- -Expand source code - -
async def validate(
-    self,
-    payload: JSONObject,
-    user_context: Dict[str, Any],
-):
-    return await self._validate(payload, user_context, is_include=True)
-
-
-
-
-
-class PrimitiveArrayClaim -(key: str, fetch_value: Callable[[str, str, Dict[str, Any]], Union[Awaitable[Optional[~PrimitiveList]], ~PrimitiveList, ForwardRef(None)]], default_max_age_in_sec: Optional[int] = None) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-

Args

-
-
key
-
The key to use when storing the claim in the payload.
-
fetch_value
-
a method that fetches the current value of this claim for the user. -A None return value signifies that we don't want to update the claim payload and or the claim value is -not present in the database. For example, this can happen with a second factor auth claim, where we -don't want to add the claim to the session automatically
-
-
- -Expand source code - -
class PrimitiveArrayClaim(SessionClaim[PrimitiveList], Generic[PrimitiveList]):
-    def __init__(
-        self,
-        key: str,
-        fetch_value: Callable[
-            [str, str, Dict[str, Any]],
-            MaybeAwaitable[Optional[PrimitiveList]],
-        ],
-        default_max_age_in_sec: Optional[int] = None,
-    ) -> None:
-        super().__init__(key, fetch_value)
-
-        claim = self
-        self.validators = PrimitiveArrayClaimValidators(claim, default_max_age_in_sec)
-
-    def add_to_payload_(
-        self,
-        payload: Dict[str, Any],
-        value: PrimitiveList,
-        user_context: Union[Dict[str, Any], None] = None,
-    ) -> JSONObject:
-        payload[self.key] = {"v": value, "t": get_timestamp_ms()}
-        _ = user_context
-
-        return payload
-
-    def remove_from_payload_by_merge_(
-        self, payload: JSONObject, user_context: Optional[Dict[str, Any]] = None
-    ) -> JSONObject:
-        payload[self.key] = None
-        return payload
-
-    def remove_from_payload(
-        self, payload: JSONObject, user_context: Optional[Dict[str, Any]] = None
-    ) -> JSONObject:
-        del payload[self.key]
-        return payload
-
-    def get_value_from_payload(
-        self, payload: JSONObject, user_context: Union[Dict[str, Any], None] = None
-    ) -> Union[PrimitiveList, None]:
-        _ = user_context
-
-        return payload.get(self.key, {}).get("v")
-
-    def get_last_refetch_time(
-        self, payload: JSONObject, user_context: Union[Dict[str, Any], None] = None
-    ) -> Union[int, None]:
-        _ = user_context
-
-        return payload.get(self.key, {}).get("t")
-
-

Ancestors

- -

Subclasses

- -

Methods

-
-
-def get_last_refetch_time(self, payload: Dict[str, Any], user_context: Optional[Dict[str, Any]] = None) ‑> Optional[int] -
-
-
-
- -Expand source code - -
def get_last_refetch_time(
-    self, payload: JSONObject, user_context: Union[Dict[str, Any], None] = None
-) -> Union[int, None]:
-    _ = user_context
-
-    return payload.get(self.key, {}).get("t")
-
-
-
-

Inherited members

- -
-
-class PrimitiveArrayClaimValidators -(claim: SessionClaim[~PrimitiveList], default_max_age_in_sec: Optional[int] = None) -
-
-

Abstract base class for generic types.

-

A generic type is typically declared by inheriting from -this class parameterized with one or more type variables. -For example, a generic mapping type might be defined as::

-

class Mapping(Generic[KT, VT]): -def getitem(self, key: KT) -> VT: -… -# Etc.

-

This class can then be used as follows::

-

def lookup_name(mapping: Mapping[KT, VT], key: KT, default: VT) -> VT: -try: -return mapping[key] -except KeyError: -return default

-
- -Expand source code - -
class PrimitiveArrayClaimValidators(Generic[PrimitiveList]):
-    def __init__(
-        self,
-        claim: SessionClaim[PrimitiveList],
-        default_max_age_in_sec: Optional[int] = None,
-    ) -> None:
-        self.claim = claim
-        self.default_max_age_in_sec = default_max_age_in_sec
-
-    def includes(  # pyright: ignore[reportInvalidTypeVarUse]
-        self,
-        val: Primitive,  # pyright: ignore[reportInvalidTypeVarUse]
-        max_age_in_seconds: Optional[int] = None,
-        id_: Union[str, None] = None,
-    ) -> SessionClaimValidator:
-        max_age_in_sec = max_age_in_seconds or self.default_max_age_in_sec
-        return IncludesSCV(
-            (id_ or self.claim.key), self.claim, val=val, max_age_in_sec=max_age_in_sec
-        )
-
-    def excludes(  # pyright: ignore[reportInvalidTypeVarUse]
-        self,
-        val: Primitive,  # pyright: ignore[reportInvalidTypeVarUse]
-        max_age_in_seconds: Optional[int] = None,
-        id_: Union[str, None] = None,
-    ) -> SessionClaimValidator:
-        max_age_in_sec = max_age_in_seconds or self.default_max_age_in_sec
-        return ExcludesSCV(
-            (id_ or self.claim.key), self.claim, val=val, max_age_in_sec=max_age_in_sec
-        )
-
-    def includes_all(
-        self,
-        val: PrimitiveList,
-        max_age_in_seconds: Optional[int] = None,
-        id_: Union[str, None] = None,
-    ) -> SessionClaimValidator:
-        max_age_in_sec = max_age_in_seconds or self.default_max_age_in_sec
-        return IncludesAllSCV(
-            (id_ or self.claim.key), self.claim, val=val, max_age_in_sec=max_age_in_sec
-        )
-
-    def includes_any(
-        self,
-        val: PrimitiveList,
-        max_age_in_seconds: Optional[int] = None,
-        id_: Union[str, None] = None,
-    ) -> SessionClaimValidator:
-        max_age_in_sec = max_age_in_seconds or self.default_max_age_in_sec
-        return IncludesAnySCV(
-            (id_ or self.claim.key), self.claim, val=val, max_age_in_sec=max_age_in_sec
-        )
-
-    def excludes_all(
-        self,
-        val: PrimitiveList,
-        max_age_in_seconds: Optional[int] = None,
-        id_: Union[str, None] = None,
-    ) -> SessionClaimValidator:
-        max_age_in_sec = max_age_in_seconds or self.default_max_age_in_sec
-        return ExcludesAllSCV(
-            (id_ or self.claim.key), self.claim, val=val, max_age_in_sec=max_age_in_sec
-        )
-
-

Ancestors

-
    -
  • typing.Generic
  • -
-

Methods

-
-
-def excludes(self, val: ~Primitive, max_age_in_seconds: Optional[int] = None, id_: Optional[str] = None) ‑> SessionClaimValidator -
-
-
-
- -Expand source code - -
def excludes(  # pyright: ignore[reportInvalidTypeVarUse]
-    self,
-    val: Primitive,  # pyright: ignore[reportInvalidTypeVarUse]
-    max_age_in_seconds: Optional[int] = None,
-    id_: Union[str, None] = None,
-) -> SessionClaimValidator:
-    max_age_in_sec = max_age_in_seconds or self.default_max_age_in_sec
-    return ExcludesSCV(
-        (id_ or self.claim.key), self.claim, val=val, max_age_in_sec=max_age_in_sec
-    )
-
-
-
-def excludes_all(self, val: ~PrimitiveList, max_age_in_seconds: Optional[int] = None, id_: Optional[str] = None) ‑> SessionClaimValidator -
-
-
-
- -Expand source code - -
def excludes_all(
-    self,
-    val: PrimitiveList,
-    max_age_in_seconds: Optional[int] = None,
-    id_: Union[str, None] = None,
-) -> SessionClaimValidator:
-    max_age_in_sec = max_age_in_seconds or self.default_max_age_in_sec
-    return ExcludesAllSCV(
-        (id_ or self.claim.key), self.claim, val=val, max_age_in_sec=max_age_in_sec
-    )
-
-
-
-def includes(self, val: ~Primitive, max_age_in_seconds: Optional[int] = None, id_: Optional[str] = None) ‑> SessionClaimValidator -
-
-
-
- -Expand source code - -
def includes(  # pyright: ignore[reportInvalidTypeVarUse]
-    self,
-    val: Primitive,  # pyright: ignore[reportInvalidTypeVarUse]
-    max_age_in_seconds: Optional[int] = None,
-    id_: Union[str, None] = None,
-) -> SessionClaimValidator:
-    max_age_in_sec = max_age_in_seconds or self.default_max_age_in_sec
-    return IncludesSCV(
-        (id_ or self.claim.key), self.claim, val=val, max_age_in_sec=max_age_in_sec
-    )
-
-
-
-def includes_all(self, val: ~PrimitiveList, max_age_in_seconds: Optional[int] = None, id_: Optional[str] = None) ‑> SessionClaimValidator -
-
-
-
- -Expand source code - -
def includes_all(
-    self,
-    val: PrimitiveList,
-    max_age_in_seconds: Optional[int] = None,
-    id_: Union[str, None] = None,
-) -> SessionClaimValidator:
-    max_age_in_sec = max_age_in_seconds or self.default_max_age_in_sec
-    return IncludesAllSCV(
-        (id_ or self.claim.key), self.claim, val=val, max_age_in_sec=max_age_in_sec
-    )
-
-
-
-def includes_any(self, val: ~PrimitiveList, max_age_in_seconds: Optional[int] = None, id_: Optional[str] = None) ‑> SessionClaimValidator -
-
-
-
- -Expand source code - -
def includes_any(
-    self,
-    val: PrimitiveList,
-    max_age_in_seconds: Optional[int] = None,
-    id_: Union[str, None] = None,
-) -> SessionClaimValidator:
-    max_age_in_sec = max_age_in_seconds or self.default_max_age_in_sec
-    return IncludesAnySCV(
-        (id_ or self.claim.key), self.claim, val=val, max_age_in_sec=max_age_in_sec
-    )
-
-
-
-
-
-class SCVMixin -(id_: str, claim: SessionClaim[~PrimitiveList], val: ~_T, max_age_in_sec: Optional[int] = None) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class SCVMixin(SessionClaimValidator, Generic[_T]):
-    def __init__(
-        self,
-        id_: str,
-        claim: SessionClaim[PrimitiveList],
-        val: _T,
-        max_age_in_sec: Optional[int] = None,
-    ):
-        super().__init__(id_)
-        self.claim: SessionClaim[PrimitiveList] = claim  # TODO:PrimitiveArrayClaim
-        self.val = val
-        self.max_age_in_sec = max_age_in_sec
-
-    def should_refetch(
-        self,
-        payload: JSONObject,
-        user_context: Dict[str, Any],
-    ) -> bool:
-        claim = self.claim
-
-        return (claim.get_value_from_payload(payload, user_context) is None) or (
-            self.max_age_in_sec is not None
-            and (
-                payload[claim.key]["t"]
-                < get_timestamp_ms() - self.max_age_in_sec * 1000
-            )
-        )
-
-    async def _validate(
-        self,
-        payload: JSONObject,
-        user_context: Dict[str, Any],
-        is_include: bool,
-        is_include_any: bool = False,
-    ):
-        val = self.val
-        max_age_in_sec = self.max_age_in_sec
-
-        expected_key = "expectedToInclude" if is_include else "expectedToNotInclude"
-        if is_include_any:
-            expected_key = "expectedToIncludeAtLeastOneOf"
-
-        assert isinstance(self.claim, PrimitiveArrayClaim)
-        claim_val = self.claim.get_value_from_payload(payload, user_context)
-
-        if claim_val is None:
-            return ClaimValidationResult(
-                is_valid=False,
-                reason={
-                    "message": "value does not exist",
-                    expected_key: val,
-                    "actualValue": claim_val,
-                },
-            )
-
-        last_refetch_time = self.claim.get_last_refetch_time(payload, user_context)
-        assert last_refetch_time is not None
-        age_in_sec = (get_timestamp_ms() - last_refetch_time) / 1000
-        if max_age_in_sec is not None and age_in_sec > max_age_in_sec:
-            return ClaimValidationResult(
-                is_valid=False,
-                reason={
-                    "message": "expired",
-                    "ageInSeconds": age_in_sec,
-                    "maxAgeInSeconds": max_age_in_sec,
-                },
-            )
-
-        # Doing this to ensure same code in the upcoming steps irrespective of
-        # whether self.val is Primitive or PrimitiveList
-        vals: List[JSONPrimitive] = (
-            val if isinstance(val, list) else [val]
-        )  # pyright: reportGeneralTypeIssues=false
-
-        claim_val_set = set(claim_val)
-        if is_include and not is_include_any:
-            for v in vals:
-                if v not in claim_val_set:
-                    return ClaimValidationResult(
-                        is_valid=False,
-                        reason={
-                            "message": "wrong value",
-                            expected_key: val,
-                            # other SDKs return the item itself
-                            "actualValue": claim_val,
-                        },
-                    )
-        else:
-            for v in vals:
-                if v in claim_val_set:
-                    if is_include_any:
-                        return ClaimValidationResult(is_valid=True)
-
-                    return ClaimValidationResult(
-                        is_valid=False,
-                        reason={
-                            "message": "wrong value",
-                            expected_key: val,
-                            # other SDKs return the item itself
-                            "actualValue": claim_val,
-                        },
-                    )
-
-            if is_include_any:
-                return ClaimValidationResult(
-                    is_valid=False,
-                    reason={
-                        "message": "wrong value",
-                        expected_key: val,
-                        # other SDKs return the item itself
-                        "actualValue": claim_val,
-                    },
-                )
-
-        return ClaimValidationResult(is_valid=True)
-
-

Ancestors

- -

Subclasses

- -

Methods

-
-
-def should_refetch(self, payload: Dict[str, Any], user_context: Dict[str, Any]) ‑> bool -
-
-
-
- -Expand source code - -
def should_refetch(
-    self,
-    payload: JSONObject,
-    user_context: Dict[str, Any],
-) -> bool:
-    claim = self.claim
-
-    return (claim.get_value_from_payload(payload, user_context) is None) or (
-        self.max_age_in_sec is not None
-        and (
-            payload[claim.key]["t"]
-            < get_timestamp_ms() - self.max_age_in_sec * 1000
-        )
-    )
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/session/claim_base_classes/primitive_claim.html b/html/supertokens_python/recipe/session/claim_base_classes/primitive_claim.html deleted file mode 100644 index a580cbf7e..000000000 --- a/html/supertokens_python/recipe/session/claim_base_classes/primitive_claim.html +++ /dev/null @@ -1,636 +0,0 @@ - - - - - - -supertokens_python.recipe.session.claim_base_classes.primitive_claim API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.session.claim_base_classes.primitive_claim

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from typing import Any, Callable, Dict, Generic, Optional, TypeVar, Union
-
-from supertokens_python.types import MaybeAwaitable
-from supertokens_python.utils import get_timestamp_ms
-
-from ..interfaces import (
-    ClaimValidationResult,
-    JSONObject,
-    JSONPrimitive,
-    SessionClaim,
-    SessionClaimValidator,
-)
-
-Primitive = TypeVar("Primitive", bound=JSONPrimitive)
-
-
-class HasValueSCV(SessionClaimValidator):
-    def __init__(
-        self,
-        id_: str,
-        claim: SessionClaim[Primitive],
-        val: Primitive,
-        max_age_in_sec: Optional[int] = None,
-    ):
-        super().__init__(id_)
-        self.claim: SessionClaim[Primitive] = claim  # to fix the type for pyright
-        self.val = val
-        self.max_age_in_sec = max_age_in_sec
-
-    def should_refetch(
-        self,
-        payload: JSONObject,
-        user_context: Dict[str, Any],
-    ) -> bool:
-        max_age_in_sec = self.max_age_in_sec
-
-        # (claim value is None) OR (value has expired)
-        return (self.claim.get_value_from_payload(payload, user_context) is None) or (
-            (max_age_in_sec is not None)
-            and (
-                payload[self.claim.key]["t"]
-                < (get_timestamp_ms() - max_age_in_sec * 1000)
-            )
-        )
-
-    async def validate(
-        self,
-        payload: JSONObject,
-        user_context: Dict[str, Any],
-    ):
-        val = self.val
-        max_age_in_sec = self.max_age_in_sec
-
-        claim_val: JSONPrimitive = self.claim.get_value_from_payload(
-            payload, user_context
-        )
-        if claim_val is None:
-            return ClaimValidationResult(
-                is_valid=False,
-                reason={
-                    "message": "value does not exist",
-                    "expectedValue": val,
-                    "actualValue": claim_val,
-                },
-            )
-
-        if max_age_in_sec is not None:
-            assert isinstance(self.claim, PrimitiveClaim)
-            last_refetch_time = self.claim.get_last_refetch_time(payload, user_context)
-            assert last_refetch_time is not None
-            age_in_sec = (get_timestamp_ms() - last_refetch_time) / 1000
-            if age_in_sec > max_age_in_sec:
-                return ClaimValidationResult(
-                    is_valid=False,
-                    reason={
-                        "message": "expired",
-                        "ageInSeconds": age_in_sec,
-                        "maxAgeInSeconds": max_age_in_sec,
-                    },
-                )
-
-        if claim_val != val:
-            return ClaimValidationResult(
-                is_valid=False,
-                reason={
-                    "message": "wrong value",
-                    "expectedValue": val,
-                    "actualValue": claim_val,
-                },
-            )
-
-        return ClaimValidationResult(is_valid=True)
-
-
-class PrimitiveClaimValidators(Generic[Primitive]):
-    def __init__(
-        self,
-        claim: SessionClaim[Primitive],
-        default_max_age_in_sec: Optional[int],
-    ) -> None:
-        self.claim = claim
-        self.default_max_age_in_sec = default_max_age_in_sec
-
-    def has_value(
-        self,
-        val: Primitive,
-        max_age_in_sec: Optional[int] = None,
-        id_: Optional[str] = None,
-    ) -> SessionClaimValidator:
-        max_age_in_sec = max_age_in_sec or self.default_max_age_in_sec
-        return HasValueSCV(
-            (id_ or self.claim.key), self.claim, val=val, max_age_in_sec=max_age_in_sec
-        )
-
-
-class PrimitiveClaim(SessionClaim[Primitive]):
-    def __init__(
-        self,
-        key: str,
-        fetch_value: Callable[
-            [str, str, Dict[str, Any]],
-            MaybeAwaitable[Optional[Primitive]],
-        ],
-        default_max_age_in_sec: Optional[int] = None,
-    ) -> None:
-        super().__init__(key, fetch_value)
-
-        claim = self
-        self.validators = PrimitiveClaimValidators(claim, default_max_age_in_sec)
-
-    def add_to_payload_(
-        self,
-        payload: Dict[str, Any],
-        value: Primitive,
-        user_context: Union[Dict[str, Any], None] = None,
-    ) -> JSONObject:
-        payload[self.key] = {"v": value, "t": get_timestamp_ms()}
-        _ = user_context
-
-        return payload
-
-    def remove_from_payload_by_merge_(
-        self, payload: JSONObject, user_context: Optional[Dict[str, Any]] = None
-    ) -> JSONObject:
-        payload[self.key] = None
-        return payload
-
-    def remove_from_payload(
-        self, payload: JSONObject, user_context: Optional[Dict[str, Any]] = None
-    ) -> JSONObject:
-        del payload[self.key]
-        return payload
-
-    def get_value_from_payload(
-        self, payload: JSONObject, user_context: Union[Dict[str, Any], None] = None
-    ) -> Union[Primitive, None]:
-        _ = user_context
-
-        return payload.get(self.key, {}).get("v")
-
-    def get_last_refetch_time(
-        self, payload: JSONObject, user_context: Union[Dict[str, Any], None] = None
-    ) -> Union[int, None]:
-        _ = user_context
-
-        return payload.get(self.key, {}).get("t")
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class HasValueSCV -(id_: str, claim: SessionClaim[~Primitive], val: ~Primitive, max_age_in_sec: Optional[int] = None) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class HasValueSCV(SessionClaimValidator):
-    def __init__(
-        self,
-        id_: str,
-        claim: SessionClaim[Primitive],
-        val: Primitive,
-        max_age_in_sec: Optional[int] = None,
-    ):
-        super().__init__(id_)
-        self.claim: SessionClaim[Primitive] = claim  # to fix the type for pyright
-        self.val = val
-        self.max_age_in_sec = max_age_in_sec
-
-    def should_refetch(
-        self,
-        payload: JSONObject,
-        user_context: Dict[str, Any],
-    ) -> bool:
-        max_age_in_sec = self.max_age_in_sec
-
-        # (claim value is None) OR (value has expired)
-        return (self.claim.get_value_from_payload(payload, user_context) is None) or (
-            (max_age_in_sec is not None)
-            and (
-                payload[self.claim.key]["t"]
-                < (get_timestamp_ms() - max_age_in_sec * 1000)
-            )
-        )
-
-    async def validate(
-        self,
-        payload: JSONObject,
-        user_context: Dict[str, Any],
-    ):
-        val = self.val
-        max_age_in_sec = self.max_age_in_sec
-
-        claim_val: JSONPrimitive = self.claim.get_value_from_payload(
-            payload, user_context
-        )
-        if claim_val is None:
-            return ClaimValidationResult(
-                is_valid=False,
-                reason={
-                    "message": "value does not exist",
-                    "expectedValue": val,
-                    "actualValue": claim_val,
-                },
-            )
-
-        if max_age_in_sec is not None:
-            assert isinstance(self.claim, PrimitiveClaim)
-            last_refetch_time = self.claim.get_last_refetch_time(payload, user_context)
-            assert last_refetch_time is not None
-            age_in_sec = (get_timestamp_ms() - last_refetch_time) / 1000
-            if age_in_sec > max_age_in_sec:
-                return ClaimValidationResult(
-                    is_valid=False,
-                    reason={
-                        "message": "expired",
-                        "ageInSeconds": age_in_sec,
-                        "maxAgeInSeconds": max_age_in_sec,
-                    },
-                )
-
-        if claim_val != val:
-            return ClaimValidationResult(
-                is_valid=False,
-                reason={
-                    "message": "wrong value",
-                    "expectedValue": val,
-                    "actualValue": claim_val,
-                },
-            )
-
-        return ClaimValidationResult(is_valid=True)
-
-

Ancestors

- -

Methods

-
-
-def should_refetch(self, payload: Dict[str, Any], user_context: Dict[str, Any]) ‑> bool -
-
-
-
- -Expand source code - -
def should_refetch(
-    self,
-    payload: JSONObject,
-    user_context: Dict[str, Any],
-) -> bool:
-    max_age_in_sec = self.max_age_in_sec
-
-    # (claim value is None) OR (value has expired)
-    return (self.claim.get_value_from_payload(payload, user_context) is None) or (
-        (max_age_in_sec is not None)
-        and (
-            payload[self.claim.key]["t"]
-            < (get_timestamp_ms() - max_age_in_sec * 1000)
-        )
-    )
-
-
-
-async def validate(self, payload: Dict[str, Any], user_context: Dict[str, Any]) -
-
-
-
- -Expand source code - -
async def validate(
-    self,
-    payload: JSONObject,
-    user_context: Dict[str, Any],
-):
-    val = self.val
-    max_age_in_sec = self.max_age_in_sec
-
-    claim_val: JSONPrimitive = self.claim.get_value_from_payload(
-        payload, user_context
-    )
-    if claim_val is None:
-        return ClaimValidationResult(
-            is_valid=False,
-            reason={
-                "message": "value does not exist",
-                "expectedValue": val,
-                "actualValue": claim_val,
-            },
-        )
-
-    if max_age_in_sec is not None:
-        assert isinstance(self.claim, PrimitiveClaim)
-        last_refetch_time = self.claim.get_last_refetch_time(payload, user_context)
-        assert last_refetch_time is not None
-        age_in_sec = (get_timestamp_ms() - last_refetch_time) / 1000
-        if age_in_sec > max_age_in_sec:
-            return ClaimValidationResult(
-                is_valid=False,
-                reason={
-                    "message": "expired",
-                    "ageInSeconds": age_in_sec,
-                    "maxAgeInSeconds": max_age_in_sec,
-                },
-            )
-
-    if claim_val != val:
-        return ClaimValidationResult(
-            is_valid=False,
-            reason={
-                "message": "wrong value",
-                "expectedValue": val,
-                "actualValue": claim_val,
-            },
-        )
-
-    return ClaimValidationResult(is_valid=True)
-
-
-
-
-
-class PrimitiveClaim -(key: str, fetch_value: Callable[[str, str, Dict[str, Any]], Union[Awaitable[Optional[~Primitive]], ~Primitive, ForwardRef(None)]], default_max_age_in_sec: Optional[int] = None) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-

Args

-
-
key
-
The key to use when storing the claim in the payload.
-
fetch_value
-
a method that fetches the current value of this claim for the user. -A None return value signifies that we don't want to update the claim payload and or the claim value is -not present in the database. For example, this can happen with a second factor auth claim, where we -don't want to add the claim to the session automatically
-
-
- -Expand source code - -
class PrimitiveClaim(SessionClaim[Primitive]):
-    def __init__(
-        self,
-        key: str,
-        fetch_value: Callable[
-            [str, str, Dict[str, Any]],
-            MaybeAwaitable[Optional[Primitive]],
-        ],
-        default_max_age_in_sec: Optional[int] = None,
-    ) -> None:
-        super().__init__(key, fetch_value)
-
-        claim = self
-        self.validators = PrimitiveClaimValidators(claim, default_max_age_in_sec)
-
-    def add_to_payload_(
-        self,
-        payload: Dict[str, Any],
-        value: Primitive,
-        user_context: Union[Dict[str, Any], None] = None,
-    ) -> JSONObject:
-        payload[self.key] = {"v": value, "t": get_timestamp_ms()}
-        _ = user_context
-
-        return payload
-
-    def remove_from_payload_by_merge_(
-        self, payload: JSONObject, user_context: Optional[Dict[str, Any]] = None
-    ) -> JSONObject:
-        payload[self.key] = None
-        return payload
-
-    def remove_from_payload(
-        self, payload: JSONObject, user_context: Optional[Dict[str, Any]] = None
-    ) -> JSONObject:
-        del payload[self.key]
-        return payload
-
-    def get_value_from_payload(
-        self, payload: JSONObject, user_context: Union[Dict[str, Any], None] = None
-    ) -> Union[Primitive, None]:
-        _ = user_context
-
-        return payload.get(self.key, {}).get("v")
-
-    def get_last_refetch_time(
-        self, payload: JSONObject, user_context: Union[Dict[str, Any], None] = None
-    ) -> Union[int, None]:
-        _ = user_context
-
-        return payload.get(self.key, {}).get("t")
-
-

Ancestors

- -

Subclasses

- -

Methods

-
-
-def get_last_refetch_time(self, payload: Dict[str, Any], user_context: Optional[Dict[str, Any]] = None) ‑> Optional[int] -
-
-
-
- -Expand source code - -
def get_last_refetch_time(
-    self, payload: JSONObject, user_context: Union[Dict[str, Any], None] = None
-) -> Union[int, None]:
-    _ = user_context
-
-    return payload.get(self.key, {}).get("t")
-
-
-
-

Inherited members

- -
-
-class PrimitiveClaimValidators -(claim: SessionClaim[~Primitive], default_max_age_in_sec: Optional[int]) -
-
-

Abstract base class for generic types.

-

A generic type is typically declared by inheriting from -this class parameterized with one or more type variables. -For example, a generic mapping type might be defined as::

-

class Mapping(Generic[KT, VT]): -def getitem(self, key: KT) -> VT: -… -# Etc.

-

This class can then be used as follows::

-

def lookup_name(mapping: Mapping[KT, VT], key: KT, default: VT) -> VT: -try: -return mapping[key] -except KeyError: -return default

-
- -Expand source code - -
class PrimitiveClaimValidators(Generic[Primitive]):
-    def __init__(
-        self,
-        claim: SessionClaim[Primitive],
-        default_max_age_in_sec: Optional[int],
-    ) -> None:
-        self.claim = claim
-        self.default_max_age_in_sec = default_max_age_in_sec
-
-    def has_value(
-        self,
-        val: Primitive,
-        max_age_in_sec: Optional[int] = None,
-        id_: Optional[str] = None,
-    ) -> SessionClaimValidator:
-        max_age_in_sec = max_age_in_sec or self.default_max_age_in_sec
-        return HasValueSCV(
-            (id_ or self.claim.key), self.claim, val=val, max_age_in_sec=max_age_in_sec
-        )
-
-

Ancestors

-
    -
  • typing.Generic
  • -
-

Subclasses

- -

Methods

-
-
-def has_value(self, val: ~Primitive, max_age_in_sec: Optional[int] = None, id_: Optional[str] = None) ‑> SessionClaimValidator -
-
-
-
- -Expand source code - -
def has_value(
-    self,
-    val: Primitive,
-    max_age_in_sec: Optional[int] = None,
-    id_: Optional[str] = None,
-) -> SessionClaimValidator:
-    max_age_in_sec = max_age_in_sec or self.default_max_age_in_sec
-    return HasValueSCV(
-        (id_ or self.claim.key), self.claim, val=val, max_age_in_sec=max_age_in_sec
-    )
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/session/claims.html b/html/supertokens_python/recipe/session/claims.html deleted file mode 100644 index 7d732a9a4..000000000 --- a/html/supertokens_python/recipe/session/claims.html +++ /dev/null @@ -1,79 +0,0 @@ - - - - - - -supertokens_python.recipe.session.claims API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.session.claims

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from . import interfaces
-from .claim_base_classes import boolean_claim, primitive_claim, primitive_array_claim
-
-SessionClaim = interfaces.SessionClaim
-BooleanClaim = boolean_claim.BooleanClaim
-PrimitiveClaim = primitive_claim.PrimitiveClaim
-PrimitiveArrayClaim = primitive_array_claim.PrimitiveArrayClaim
-
-
-
-
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/session/constants.html b/html/supertokens_python/recipe/session/constants.html deleted file mode 100644 index 283484403..000000000 --- a/html/supertokens_python/recipe/session/constants.html +++ /dev/null @@ -1,104 +0,0 @@ - - - - - - -supertokens_python.recipe.session.constants API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.session.constants

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from typing import TYPE_CHECKING, List
-
-if TYPE_CHECKING:
-    from .utils import TokenTransferMethod
-
-SESSION_REFRESH = "/session/refresh"
-SIGNOUT = "/signout"
-ACCESS_TOKEN_COOKIE_KEY = "sAccessToken"
-REFRESH_TOKEN_COOKIE_KEY = "sRefreshToken"
-FRONT_TOKEN_HEADER_SET_KEY = "front-token"
-ANTI_CSRF_HEADER_KEY = "anti-csrf"
-RID_HEADER_KEY = "rid"
-AUTH_MODE_HEADER_KEY = "st-auth-mode"
-AUTHORIZATION_HEADER_KEY = "authorization"
-ACCESS_TOKEN_HEADER_KEY = "st-access-token"
-REFRESH_TOKEN_HEADER_KEY = "st-refresh-token"
-ACCESS_CONTROL_EXPOSE_HEADERS = "Access-Control-Expose-Headers"
-
-available_token_transfer_methods: List[TokenTransferMethod] = ["cookie", "header"]
-
-protected_props = [
-    "sub",
-    "iat",
-    "exp",
-    "sessionHandle",
-    "parentRefreshTokenHash1",
-    "refreshTokenHash1",
-    "antiCsrfToken",
-    "rsub",
-    "tId",
-]
-
-
-
-
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/session/cookie_and_header.html b/html/supertokens_python/recipe/session/cookie_and_header.html deleted file mode 100644 index b52ce8139..000000000 --- a/html/supertokens_python/recipe/session/cookie_and_header.html +++ /dev/null @@ -1,1061 +0,0 @@ - - - - - - -supertokens_python.recipe.session.cookie_and_header API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.session.cookie_and_header

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from typing import TYPE_CHECKING, List, Optional
-from urllib.parse import quote, unquote
-
-from typing_extensions import Literal
-
-from supertokens_python.recipe.session.exceptions import (
-    raise_clear_duplicate_session_cookies_exception,
-)
-from supertokens_python.recipe.session.interfaces import ResponseMutator
-
-from .constants import (
-    ACCESS_CONTROL_EXPOSE_HEADERS,
-    ACCESS_TOKEN_COOKIE_KEY,
-    ACCESS_TOKEN_HEADER_KEY,
-    ANTI_CSRF_HEADER_KEY,
-    AUTH_MODE_HEADER_KEY,
-    AUTHORIZATION_HEADER_KEY,
-    FRONT_TOKEN_HEADER_SET_KEY,
-    REFRESH_TOKEN_COOKIE_KEY,
-    REFRESH_TOKEN_HEADER_KEY,
-    RID_HEADER_KEY,
-    available_token_transfer_methods,
-)
-from ...logger import log_debug_message
-from supertokens_python.constants import ONE_YEAR_IN_MS
-
-if TYPE_CHECKING:
-    from supertokens_python.framework.request import BaseRequest
-    from supertokens_python.framework.response import BaseResponse
-    from .recipe import SessionRecipe
-    from .utils import (
-        TokenTransferMethod,
-        TokenType,
-        SessionConfig,
-    )
-
-from json import dumps
-from typing import Any, Dict
-
-from supertokens_python.utils import get_header, utf_base64encode, get_timestamp_ms
-
-
-def build_front_token(
-    user_id: str, at_expiry: int, access_token_payload: Optional[Dict[str, Any]] = None
-):
-    if access_token_payload is None:
-        access_token_payload = {}
-    token_info = {"uid": user_id, "ate": at_expiry, "up": access_token_payload}
-    return utf_base64encode(
-        dumps(token_info, separators=(",", ":"), sort_keys=True), urlsafe=False
-    )
-
-
-def _set_front_token_in_headers(
-    response: BaseResponse,
-    front_token: str,
-):
-    set_header(response, FRONT_TOKEN_HEADER_SET_KEY, front_token, False)
-    set_header(
-        response, ACCESS_CONTROL_EXPOSE_HEADERS, FRONT_TOKEN_HEADER_SET_KEY, True
-    )
-
-
-def get_cors_allowed_headers():
-    return [
-        ANTI_CSRF_HEADER_KEY,
-        RID_HEADER_KEY,
-        AUTHORIZATION_HEADER_KEY,
-        AUTH_MODE_HEADER_KEY,
-    ]
-
-
-def set_header(response: BaseResponse, key: str, value: str, allow_duplicate: bool):
-    if allow_duplicate:
-        old_value = response.get_header(key)
-        if old_value is None:
-            response.set_header(key, value)
-        else:
-            response.set_header(key, old_value + "," + value)
-    else:
-        response.set_header(key, value)
-
-
-def remove_header(response: BaseResponse, key: str):
-    if response.get_header(key) is not None:
-        response.remove_header(key)
-
-
-def get_cookie(request: BaseRequest, key: str):
-    cookie_val = request.get_cookie(key)
-    if cookie_val is None:
-        return None
-    return unquote(cookie_val)
-
-
-def _set_cookie(
-    response: BaseResponse,
-    config: SessionConfig,
-    key: str,
-    value: str,
-    expires: int,
-    path_type: Literal["refresh_token_path", "access_token_path"],
-    request: BaseRequest,
-    domain: Optional[str],
-    user_context: Dict[str, Any],
-):
-    secure = config.cookie_secure
-    same_site = config.get_cookie_same_site(request, user_context)
-    path = ""
-    if path_type == "refresh_token_path":
-        path = config.refresh_token_path.get_as_string_dangerous()
-    elif path_type == "access_token_path":
-        path = "/"
-    http_only = True
-    response.set_cookie(
-        key=key,
-        value=quote(value, encoding="utf-8"),
-        expires=expires,
-        path=path,
-        domain=domain,
-        secure=secure,
-        httponly=http_only,
-        samesite=same_site,
-    )
-
-
-def set_cookie_response_mutator(
-    config: SessionConfig,
-    key: str,
-    value: str,
-    expires: int,
-    path_type: Literal["refresh_token_path", "access_token_path"],
-    request: BaseRequest,
-    domain: Optional[str] = None,
-):
-    domain = domain if domain is not None else config.cookie_domain
-
-    def mutator(response: BaseResponse, user_context: Dict[str, Any]):
-        return _set_cookie(
-            response,
-            config,
-            key,
-            value,
-            expires,
-            path_type,
-            request,
-            domain,
-            user_context,
-        )
-
-    return mutator
-
-
-def _attach_anti_csrf_header(response: BaseResponse, value: str):
-    set_header(response, ANTI_CSRF_HEADER_KEY, value, False)
-    set_header(response, ACCESS_CONTROL_EXPOSE_HEADERS, ANTI_CSRF_HEADER_KEY, True)
-
-
-def anti_csrf_response_mutator(value: str):
-    def mutator(
-        response: BaseResponse,
-        _: Dict[str, Any],
-    ):
-        return _attach_anti_csrf_header(response, value)
-
-    return mutator
-
-
-def get_anti_csrf_header(request: BaseRequest):
-    return get_header(request, ANTI_CSRF_HEADER_KEY)
-
-
-def get_rid_header(request: BaseRequest):
-    return get_header(request, RID_HEADER_KEY)
-
-
-def clear_session_from_all_token_transfer_methods(
-    response: BaseResponse,
-    recipe: SessionRecipe,
-    request: BaseRequest,
-    user_context: Dict[str, Any],
-):
-    # We are clearing the session in all transfermethods to be sure to override cookies in case they have been already added to the response.
-    # This is done to handle the following use-case:
-    # If the app overrides signInPOST to check the ban status of the user after the original implementation and throwing an UNAUTHORISED error
-    # In this case: the SDK has attached cookies to the response, but none was sent with the request
-    # We can't know which to clear since we can't reliably query or remove the set-cookie header added to the response (causes issues in some frameworks, i.e.: hapi)
-    # The safe solution in this case is to overwrite all the response cookies/headers with an empty value, which is what we are doing here.
-    for transfer_method in available_token_transfer_methods:
-        _clear_session(response, recipe.config, transfer_method, request, user_context)
-
-
-def clear_session_mutator(
-    config: SessionConfig,
-    transfer_method: TokenTransferMethod,
-    request: BaseRequest,
-):
-    def mutator(
-        response: BaseResponse,
-        user_context: Dict[str, Any],
-    ):
-        return _clear_session(response, config, transfer_method, request, user_context)
-
-    return mutator
-
-
-def _clear_session(
-    response: BaseResponse,
-    config: SessionConfig,
-    transfer_method: TokenTransferMethod,
-    request: BaseRequest,
-    user_context: Dict[str, Any],
-):
-    # If we can be specific about which transferMethod we want to clear, there is no reason to clear the other ones
-    token_types: List[TokenType] = ["access", "refresh"]
-    for token_type in token_types:
-        _set_token(
-            response, config, token_type, "", 0, transfer_method, request, user_context
-        )
-
-    remove_header(
-        response, ANTI_CSRF_HEADER_KEY
-    )  # This can be added multiple times in some cases, but that should be OK
-    set_header(response, FRONT_TOKEN_HEADER_SET_KEY, "remove", False)
-    set_header(
-        response, ACCESS_CONTROL_EXPOSE_HEADERS, FRONT_TOKEN_HEADER_SET_KEY, True
-    )
-
-
-def clear_session_response_mutator(
-    config: SessionConfig,
-    transfer_method: TokenTransferMethod,
-    request: BaseRequest,
-):
-    def mutator(
-        response: BaseResponse,
-        user_context: Dict[str, Any],
-    ):
-        return _clear_session(response, config, transfer_method, request, user_context)
-
-    return mutator
-
-
-def get_cookie_name_from_token_type(token_type: TokenType):
-    if token_type == "access":
-        return ACCESS_TOKEN_COOKIE_KEY
-    if token_type == "refresh":
-        return REFRESH_TOKEN_COOKIE_KEY
-    raise Exception("Unknown token type, should never happen")
-
-
-def get_response_header_name_for_token_type(token_type: TokenType):
-    if token_type == "access":
-        return ACCESS_TOKEN_HEADER_KEY
-    if token_type == "refresh":
-        return REFRESH_TOKEN_HEADER_KEY
-    raise Exception("Unknown token type, should never happen")
-
-
-def get_token(
-    request: BaseRequest,
-    token_type: TokenType,
-    transfer_method: TokenTransferMethod,
-) -> Optional[str]:
-    if transfer_method == "cookie":
-        # Note: Don't use request.get_cookie() as it won't apply unquote() func
-        return get_cookie(request, get_cookie_name_from_token_type(token_type))
-    if transfer_method == "header":
-        value = request.get_header(AUTHORIZATION_HEADER_KEY)
-        if value is None or not value.startswith("Bearer "):
-            return None
-
-        return value[len("Bearer ") :].strip()
-
-    raise Exception("Should never happen: Unknown transferMethod: " + transfer_method)
-
-
-def _set_token(
-    response: BaseResponse,
-    config: SessionConfig,
-    token_type: TokenType,
-    value: str,
-    expires: int,
-    transfer_method: TokenTransferMethod,
-    request: BaseRequest,
-    user_context: Dict[str, Any],
-):
-    log_debug_message("Setting %s token as %s", token_type, transfer_method)
-    if transfer_method == "cookie":
-        _set_cookie(
-            response,
-            config,
-            get_cookie_name_from_token_type(token_type),
-            value,
-            expires,
-            "refresh_token_path" if token_type == "refresh" else "access_token_path",
-            request,
-            config.cookie_domain,
-            user_context,
-        )
-    elif transfer_method == "header":
-        set_token_in_header(
-            response,
-            get_response_header_name_for_token_type(token_type),
-            value,
-        )
-
-
-def token_response_mutator(
-    config: SessionConfig,
-    token_type: TokenType,
-    value: str,
-    expires: int,
-    transfer_method: TokenTransferMethod,
-    request: BaseRequest,
-):
-    def mutator(
-        response: BaseResponse,
-        user_context: Dict[str, Any],
-    ):
-        _set_token(
-            response,
-            config,
-            token_type,
-            value,
-            expires,
-            transfer_method,
-            request,
-            user_context,
-        )
-
-    return mutator
-
-
-def set_token_in_header(response: BaseResponse, name: str, value: str):
-    set_header(response, name, value, allow_duplicate=False)
-    set_header(response, ACCESS_CONTROL_EXPOSE_HEADERS, name, allow_duplicate=True)
-
-
-def access_token_mutator(
-    access_token: str,
-    front_token: str,
-    config: SessionConfig,
-    transfer_method: TokenTransferMethod,
-    request: BaseRequest,
-):
-    def mutator(
-        response: BaseResponse,
-        user_context: Dict[str, Any],
-    ):
-        _set_access_token_in_response(
-            response,
-            access_token,
-            front_token,
-            config,
-            transfer_method,
-            request,
-            user_context,
-        )
-
-    return mutator
-
-
-def _set_access_token_in_response(
-    res: BaseResponse,
-    access_token: str,
-    front_token: str,
-    config: SessionConfig,
-    transfer_method: TokenTransferMethod,
-    request: BaseRequest,
-    user_context: Dict[str, Any],
-):
-    _set_front_token_in_headers(res, front_token)
-    _set_token(
-        res,
-        config,
-        "access",
-        access_token,
-        # We set the expiration to 1 year, because we can't really access the expiration of the refresh token everywhere we are setting it.
-        # This should be safe to do, since this is only the validity of the cookie (set here or on the frontend) but we check the expiration of the JWT anyway.
-        # Even if the token is expired the presence of the token indicates that the user could have a valid refresh
-        # Some browsers now cap the maximum expiry at 400 days, so we set it to 1 year, which should suffice.
-        get_timestamp_ms() + ONE_YEAR_IN_MS,
-        transfer_method,
-        request,
-        user_context,
-    )
-
-    if (
-        config.expose_access_token_to_frontend_in_cookie_based_auth
-        and transfer_method == "cookie"
-    ):
-        _set_token(
-            res,
-            config,
-            "access",
-            access_token,
-            get_timestamp_ms() + ONE_YEAR_IN_MS,
-            "header",
-            request,
-            user_context,
-        )
-
-
-# This function addresses an edge case where changing the cookie_domain config on the server can
-# lead to session integrity issues. For instance, if the API server URL is 'api.example.com'
-# with a cookie domain of '.example.com', and the server updates the cookie domain to 'api.example.com',
-# the client may retain cookies with both '.example.com' and 'api.example.com' domains.
-
-# Consequently, if the server chooses the older cookie, session invalidation occurs, potentially
-# resulting in an infinite refresh loop. To fix this, users are asked to specify "older_cookie_domain" in
-# the config.
-
-
-# This function checks for multiple cookies with the same name and clears the cookies for the older domain.
-def clear_session_cookies_from_older_cookie_domain(
-    request: BaseRequest, config: SessionConfig, user_context: Dict[str, Any]
-):
-    allowed_transfer_method = config.get_token_transfer_method(
-        request, False, user_context
-    )
-    # If the transfer method is 'header', there's no need to clear cookies immediately, even if there are multiple in the request.
-    if allowed_transfer_method == "header":
-        return
-
-    did_clear_cookies = False
-    response_mutators: List[ResponseMutator] = []
-
-    token_types: List[TokenType] = ["access", "refresh"]
-    for token_type in token_types:
-        if has_multiple_cookies_for_token_type(request, token_type):
-            # If a request has multiple session cookies and 'older_cookie_domain' is
-            # unset, we can't identify the correct cookie for refreshing the session.
-            # Using the wrong cookie can cause an infinite refresh loop. To avoid this,
-            # we throw a 500 error asking the user to set 'older_cookie_domain''.
-            if config.older_cookie_domain is None:
-                raise Exception(
-                    "The request contains multiple session cookies. This may happen if you've changed the 'cookie_domain' setting in your configuration. To clear tokens from the previous domain, set 'older_cookie_domain' in your config."
-                )
-
-            log_debug_message(
-                "Clearing duplicate %s cookie with domain %s",
-                token_type,
-                config.cookie_domain,
-            )
-            response_mutators.append(
-                set_cookie_response_mutator(
-                    config,
-                    get_cookie_name_from_token_type(token_type),
-                    "",
-                    0,
-                    (
-                        "refresh_token_path"
-                        if token_type == "refresh"
-                        else "access_token_path"
-                    ),
-                    request,
-                    domain=config.older_cookie_domain,
-                )
-            )
-            did_clear_cookies = True
-    if did_clear_cookies:
-        raise_clear_duplicate_session_cookies_exception(
-            "The request contains multiple session cookies. We are clearing the cookie from older_cookie_domain. Session will be refreshed in the next refresh call.",
-            response_mutators=response_mutators,
-        )
-
-
-def has_multiple_cookies_for_token_type(
-    request: BaseRequest, token_type: TokenType
-) -> bool:
-    cookie_string = request.get_header("cookie")
-    if cookie_string is None:
-        return False
-
-    cookies = _parse_cookie_string_from_request_header_allow_duplicates(cookie_string)
-    cookie_name = get_cookie_name_from_token_type(token_type)
-    return cookie_name in cookies and len(cookies[cookie_name]) > 1
-
-
-def _parse_cookie_string_from_request_header_allow_duplicates(
-    cookie_string: str,
-) -> Dict[str, List[str]]:
-    cookies: Dict[str, List[str]] = {}
-    cookie_pairs = cookie_string.split(";")
-    for cookie_pair in cookie_pairs:
-        name_value = cookie_pair.split("=")
-        if len(name_value) != 2:
-            continue
-        name, value = unquote(name_value[0].strip()), unquote(name_value[1].strip())
-        if name in cookies:
-            cookies[name].append(value)
-        else:
-            cookies[name] = [value]
-    return cookies
-
-
-
-
-
-
-
-

Functions

-
-
-def access_token_mutator(access_token: str, front_token: str, config: SessionConfig, transfer_method: TokenTransferMethod, request: BaseRequest) -
-
-
-
- -Expand source code - -
def access_token_mutator(
-    access_token: str,
-    front_token: str,
-    config: SessionConfig,
-    transfer_method: TokenTransferMethod,
-    request: BaseRequest,
-):
-    def mutator(
-        response: BaseResponse,
-        user_context: Dict[str, Any],
-    ):
-        _set_access_token_in_response(
-            response,
-            access_token,
-            front_token,
-            config,
-            transfer_method,
-            request,
-            user_context,
-        )
-
-    return mutator
-
-
-
-def anti_csrf_response_mutator(value: str) -
-
-
-
- -Expand source code - -
def anti_csrf_response_mutator(value: str):
-    def mutator(
-        response: BaseResponse,
-        _: Dict[str, Any],
-    ):
-        return _attach_anti_csrf_header(response, value)
-
-    return mutator
-
-
-
-def build_front_token(user_id: str, at_expiry: int, access_token_payload: Optional[Dict[str, Any]] = None) -
-
-
-
- -Expand source code - -
def build_front_token(
-    user_id: str, at_expiry: int, access_token_payload: Optional[Dict[str, Any]] = None
-):
-    if access_token_payload is None:
-        access_token_payload = {}
-    token_info = {"uid": user_id, "ate": at_expiry, "up": access_token_payload}
-    return utf_base64encode(
-        dumps(token_info, separators=(",", ":"), sort_keys=True), urlsafe=False
-    )
-
-
- -
-
-
- -Expand source code - -
def clear_session_cookies_from_older_cookie_domain(
-    request: BaseRequest, config: SessionConfig, user_context: Dict[str, Any]
-):
-    allowed_transfer_method = config.get_token_transfer_method(
-        request, False, user_context
-    )
-    # If the transfer method is 'header', there's no need to clear cookies immediately, even if there are multiple in the request.
-    if allowed_transfer_method == "header":
-        return
-
-    did_clear_cookies = False
-    response_mutators: List[ResponseMutator] = []
-
-    token_types: List[TokenType] = ["access", "refresh"]
-    for token_type in token_types:
-        if has_multiple_cookies_for_token_type(request, token_type):
-            # If a request has multiple session cookies and 'older_cookie_domain' is
-            # unset, we can't identify the correct cookie for refreshing the session.
-            # Using the wrong cookie can cause an infinite refresh loop. To avoid this,
-            # we throw a 500 error asking the user to set 'older_cookie_domain''.
-            if config.older_cookie_domain is None:
-                raise Exception(
-                    "The request contains multiple session cookies. This may happen if you've changed the 'cookie_domain' setting in your configuration. To clear tokens from the previous domain, set 'older_cookie_domain' in your config."
-                )
-
-            log_debug_message(
-                "Clearing duplicate %s cookie with domain %s",
-                token_type,
-                config.cookie_domain,
-            )
-            response_mutators.append(
-                set_cookie_response_mutator(
-                    config,
-                    get_cookie_name_from_token_type(token_type),
-                    "",
-                    0,
-                    (
-                        "refresh_token_path"
-                        if token_type == "refresh"
-                        else "access_token_path"
-                    ),
-                    request,
-                    domain=config.older_cookie_domain,
-                )
-            )
-            did_clear_cookies = True
-    if did_clear_cookies:
-        raise_clear_duplicate_session_cookies_exception(
-            "The request contains multiple session cookies. We are clearing the cookie from older_cookie_domain. Session will be refreshed in the next refresh call.",
-            response_mutators=response_mutators,
-        )
-
-
-
-def clear_session_from_all_token_transfer_methods(response: BaseResponse, recipe: SessionRecipe, request: BaseRequest, user_context: Dict[str, Any]) -
-
-
-
- -Expand source code - -
def clear_session_from_all_token_transfer_methods(
-    response: BaseResponse,
-    recipe: SessionRecipe,
-    request: BaseRequest,
-    user_context: Dict[str, Any],
-):
-    # We are clearing the session in all transfermethods to be sure to override cookies in case they have been already added to the response.
-    # This is done to handle the following use-case:
-    # If the app overrides signInPOST to check the ban status of the user after the original implementation and throwing an UNAUTHORISED error
-    # In this case: the SDK has attached cookies to the response, but none was sent with the request
-    # We can't know which to clear since we can't reliably query or remove the set-cookie header added to the response (causes issues in some frameworks, i.e.: hapi)
-    # The safe solution in this case is to overwrite all the response cookies/headers with an empty value, which is what we are doing here.
-    for transfer_method in available_token_transfer_methods:
-        _clear_session(response, recipe.config, transfer_method, request, user_context)
-
-
-
-def clear_session_mutator(config: SessionConfig, transfer_method: TokenTransferMethod, request: BaseRequest) -
-
-
-
- -Expand source code - -
def clear_session_mutator(
-    config: SessionConfig,
-    transfer_method: TokenTransferMethod,
-    request: BaseRequest,
-):
-    def mutator(
-        response: BaseResponse,
-        user_context: Dict[str, Any],
-    ):
-        return _clear_session(response, config, transfer_method, request, user_context)
-
-    return mutator
-
-
-
-def clear_session_response_mutator(config: SessionConfig, transfer_method: TokenTransferMethod, request: BaseRequest) -
-
-
-
- -Expand source code - -
def clear_session_response_mutator(
-    config: SessionConfig,
-    transfer_method: TokenTransferMethod,
-    request: BaseRequest,
-):
-    def mutator(
-        response: BaseResponse,
-        user_context: Dict[str, Any],
-    ):
-        return _clear_session(response, config, transfer_method, request, user_context)
-
-    return mutator
-
-
-
-def get_anti_csrf_header(request: BaseRequest) -
-
-
-
- -Expand source code - -
def get_anti_csrf_header(request: BaseRequest):
-    return get_header(request, ANTI_CSRF_HEADER_KEY)
-
-
- -
-
-
- -Expand source code - -
def get_cookie(request: BaseRequest, key: str):
-    cookie_val = request.get_cookie(key)
-    if cookie_val is None:
-        return None
-    return unquote(cookie_val)
-
-
- -
-
-
- -Expand source code - -
def get_cookie_name_from_token_type(token_type: TokenType):
-    if token_type == "access":
-        return ACCESS_TOKEN_COOKIE_KEY
-    if token_type == "refresh":
-        return REFRESH_TOKEN_COOKIE_KEY
-    raise Exception("Unknown token type, should never happen")
-
-
-
-def get_cors_allowed_headers() -
-
-
-
- -Expand source code - -
def get_cors_allowed_headers():
-    return [
-        ANTI_CSRF_HEADER_KEY,
-        RID_HEADER_KEY,
-        AUTHORIZATION_HEADER_KEY,
-        AUTH_MODE_HEADER_KEY,
-    ]
-
-
-
-def get_response_header_name_for_token_type(token_type: TokenType) -
-
-
-
- -Expand source code - -
def get_response_header_name_for_token_type(token_type: TokenType):
-    if token_type == "access":
-        return ACCESS_TOKEN_HEADER_KEY
-    if token_type == "refresh":
-        return REFRESH_TOKEN_HEADER_KEY
-    raise Exception("Unknown token type, should never happen")
-
-
-
-def get_rid_header(request: BaseRequest) -
-
-
-
- -Expand source code - -
def get_rid_header(request: BaseRequest):
-    return get_header(request, RID_HEADER_KEY)
-
-
-
-def get_token(request: BaseRequest, token_type: TokenType, transfer_method: TokenTransferMethod) ‑> Optional[str] -
-
-
-
- -Expand source code - -
def get_token(
-    request: BaseRequest,
-    token_type: TokenType,
-    transfer_method: TokenTransferMethod,
-) -> Optional[str]:
-    if transfer_method == "cookie":
-        # Note: Don't use request.get_cookie() as it won't apply unquote() func
-        return get_cookie(request, get_cookie_name_from_token_type(token_type))
-    if transfer_method == "header":
-        value = request.get_header(AUTHORIZATION_HEADER_KEY)
-        if value is None or not value.startswith("Bearer "):
-            return None
-
-        return value[len("Bearer ") :].strip()
-
-    raise Exception("Should never happen: Unknown transferMethod: " + transfer_method)
-
-
-
-def has_multiple_cookies_for_token_type(request: BaseRequest, token_type: TokenType) ‑> bool -
-
-
-
- -Expand source code - -
def has_multiple_cookies_for_token_type(
-    request: BaseRequest, token_type: TokenType
-) -> bool:
-    cookie_string = request.get_header("cookie")
-    if cookie_string is None:
-        return False
-
-    cookies = _parse_cookie_string_from_request_header_allow_duplicates(cookie_string)
-    cookie_name = get_cookie_name_from_token_type(token_type)
-    return cookie_name in cookies and len(cookies[cookie_name]) > 1
-
-
-
-def remove_header(response: BaseResponse, key: str) -
-
-
-
- -Expand source code - -
def remove_header(response: BaseResponse, key: str):
-    if response.get_header(key) is not None:
-        response.remove_header(key)
-
-
- -
-
-
- -Expand source code - -
def set_cookie_response_mutator(
-    config: SessionConfig,
-    key: str,
-    value: str,
-    expires: int,
-    path_type: Literal["refresh_token_path", "access_token_path"],
-    request: BaseRequest,
-    domain: Optional[str] = None,
-):
-    domain = domain if domain is not None else config.cookie_domain
-
-    def mutator(response: BaseResponse, user_context: Dict[str, Any]):
-        return _set_cookie(
-            response,
-            config,
-            key,
-            value,
-            expires,
-            path_type,
-            request,
-            domain,
-            user_context,
-        )
-
-    return mutator
-
-
-
-def set_header(response: BaseResponse, key: str, value: str, allow_duplicate: bool) -
-
-
-
- -Expand source code - -
def set_header(response: BaseResponse, key: str, value: str, allow_duplicate: bool):
-    if allow_duplicate:
-        old_value = response.get_header(key)
-        if old_value is None:
-            response.set_header(key, value)
-        else:
-            response.set_header(key, old_value + "," + value)
-    else:
-        response.set_header(key, value)
-
-
-
-def set_token_in_header(response: BaseResponse, name: str, value: str) -
-
-
-
- -Expand source code - -
def set_token_in_header(response: BaseResponse, name: str, value: str):
-    set_header(response, name, value, allow_duplicate=False)
-    set_header(response, ACCESS_CONTROL_EXPOSE_HEADERS, name, allow_duplicate=True)
-
-
-
-def token_response_mutator(config: SessionConfig, token_type: TokenType, value: str, expires: int, transfer_method: TokenTransferMethod, request: BaseRequest) -
-
-
-
- -Expand source code - -
def token_response_mutator(
-    config: SessionConfig,
-    token_type: TokenType,
-    value: str,
-    expires: int,
-    transfer_method: TokenTransferMethod,
-    request: BaseRequest,
-):
-    def mutator(
-        response: BaseResponse,
-        user_context: Dict[str, Any],
-    ):
-        _set_token(
-            response,
-            config,
-            token_type,
-            value,
-            expires,
-            transfer_method,
-            request,
-            user_context,
-        )
-
-    return mutator
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/session/exceptions.html b/html/supertokens_python/recipe/session/exceptions.html deleted file mode 100644 index 83f9bfd56..000000000 --- a/html/supertokens_python/recipe/session/exceptions.html +++ /dev/null @@ -1,470 +0,0 @@ - - - - - - -supertokens_python.recipe.session.exceptions API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.session.exceptions

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from typing import TYPE_CHECKING, Any, Dict, List, NoReturn, Optional, Union
-
-from supertokens_python.exceptions import SuperTokensError
-
-if TYPE_CHECKING:
-    from .interfaces import ResponseMutator
-
-
-def raise_token_theft_exception(user_id: str, session_handle: str) -> NoReturn:
-    raise TokenTheftError(user_id, session_handle)
-
-
-def raise_try_refresh_token_exception(ex: Union[str, Exception]) -> NoReturn:
-    if isinstance(ex, SuperTokensError):
-        raise ex
-
-    raise TryRefreshTokenError(ex) from None
-
-
-def raise_unauthorised_exception(
-    msg: str,
-    clear_tokens: bool = True,
-    response_mutators: Optional[List[ResponseMutator]] = None,
-) -> NoReturn:
-    err = UnauthorisedError(msg, clear_tokens)
-
-    if response_mutators is not None:
-        err.response_mutators.extend(response_mutators)
-
-    raise err
-
-
-def raise_clear_duplicate_session_cookies_exception(
-    msg: str, response_mutators: List[ResponseMutator]
-) -> NoReturn:
-    err = ClearDuplicateSessionCookiesError(msg)
-    err.response_mutators.extend(response_mutators)
-    raise err
-
-
-class SuperTokensSessionError(SuperTokensError):
-    def __init__(self, *args: Any, **kwargs: Any) -> None:
-        super().__init__(*args, **kwargs)
-        self.response_mutators: List[ResponseMutator] = []
-
-
-class TokenTheftError(SuperTokensSessionError):
-    def __init__(self, user_id: str, session_handle: str):
-        super().__init__("token theft detected")
-        self.user_id = user_id
-        self.session_handle = session_handle
-
-
-class UnauthorisedError(SuperTokensSessionError):
-    def __init__(self, msg: str, clear_tokens: bool = True):
-        super().__init__(msg)
-        self.clear_tokens = clear_tokens
-
-
-class TryRefreshTokenError(SuperTokensSessionError):
-    pass
-
-
-class InvalidClaimsError(SuperTokensSessionError):
-    def __init__(self, msg: str, payload: List[ClaimValidationError]):
-        super().__init__(msg)
-        self.payload = payload
-
-
-class ClaimValidationError:
-    def __init__(self, id_: str, reason: Optional[Dict[str, Any]]):
-        self.id = id_
-        self.reason = reason
-
-    def to_json(self):
-        result: Dict[str, Any] = {"id": self.id}
-        if self.reason is not None:
-            result["reason"] = self.reason
-
-        return result
-
-
-def raise_invalid_claims_exception(msg: str, payload: List[ClaimValidationError]):
-    raise InvalidClaimsError(msg, payload)
-
-
-class ClearDuplicateSessionCookiesError(SuperTokensSessionError):
-    pass
-
-
-
-
-
-
-
-

Functions

-
-
-def raise_clear_duplicate_session_cookies_exception(msg: str, response_mutators: List[ResponseMutator]) ‑> NoReturn -
-
-
-
- -Expand source code - -
def raise_clear_duplicate_session_cookies_exception(
-    msg: str, response_mutators: List[ResponseMutator]
-) -> NoReturn:
-    err = ClearDuplicateSessionCookiesError(msg)
-    err.response_mutators.extend(response_mutators)
-    raise err
-
-
-
-def raise_invalid_claims_exception(msg: str, payload: List[ClaimValidationError]) -
-
-
-
- -Expand source code - -
def raise_invalid_claims_exception(msg: str, payload: List[ClaimValidationError]):
-    raise InvalidClaimsError(msg, payload)
-
-
-
-def raise_token_theft_exception(user_id: str, session_handle: str) ‑> NoReturn -
-
-
-
- -Expand source code - -
def raise_token_theft_exception(user_id: str, session_handle: str) -> NoReturn:
-    raise TokenTheftError(user_id, session_handle)
-
-
-
-def raise_try_refresh_token_exception(ex: Union[str, Exception]) ‑> NoReturn -
-
-
-
- -Expand source code - -
def raise_try_refresh_token_exception(ex: Union[str, Exception]) -> NoReturn:
-    if isinstance(ex, SuperTokensError):
-        raise ex
-
-    raise TryRefreshTokenError(ex) from None
-
-
-
-def raise_unauthorised_exception(msg: str, clear_tokens: bool = True, response_mutators: Optional[List[ResponseMutator]] = None) ‑> NoReturn -
-
-
-
- -Expand source code - -
def raise_unauthorised_exception(
-    msg: str,
-    clear_tokens: bool = True,
-    response_mutators: Optional[List[ResponseMutator]] = None,
-) -> NoReturn:
-    err = UnauthorisedError(msg, clear_tokens)
-
-    if response_mutators is not None:
-        err.response_mutators.extend(response_mutators)
-
-    raise err
-
-
-
-
-
-

Classes

-
-
-class ClaimValidationError -(id_: str, reason: Optional[Dict[str, Any]]) -
-
-
-
- -Expand source code - -
class ClaimValidationError:
-    def __init__(self, id_: str, reason: Optional[Dict[str, Any]]):
-        self.id = id_
-        self.reason = reason
-
-    def to_json(self):
-        result: Dict[str, Any] = {"id": self.id}
-        if self.reason is not None:
-            result["reason"] = self.reason
-
-        return result
-
-

Methods

-
-
-def to_json(self) -
-
-
-
- -Expand source code - -
def to_json(self):
-    result: Dict[str, Any] = {"id": self.id}
-    if self.reason is not None:
-        result["reason"] = self.reason
-
-    return result
-
-
-
-
-
-class ClearDuplicateSessionCookiesError -(*args: Any, **kwargs: Any) -
-
-

Common base class for all non-exit exceptions.

-
- -Expand source code - -
class ClearDuplicateSessionCookiesError(SuperTokensSessionError):
-    pass
-
-

Ancestors

- -
-
-class InvalidClaimsError -(msg: str, payload: List[ClaimValidationError]) -
-
-

Common base class for all non-exit exceptions.

-
- -Expand source code - -
class InvalidClaimsError(SuperTokensSessionError):
-    def __init__(self, msg: str, payload: List[ClaimValidationError]):
-        super().__init__(msg)
-        self.payload = payload
-
-

Ancestors

- -
-
-class SuperTokensSessionError -(*args: Any, **kwargs: Any) -
-
-

Common base class for all non-exit exceptions.

-
- -Expand source code - -
class SuperTokensSessionError(SuperTokensError):
-    def __init__(self, *args: Any, **kwargs: Any) -> None:
-        super().__init__(*args, **kwargs)
-        self.response_mutators: List[ResponseMutator] = []
-
-

Ancestors

- -

Subclasses

- -
-
-class TokenTheftError -(user_id: str, session_handle: str) -
-
-

Common base class for all non-exit exceptions.

-
- -Expand source code - -
class TokenTheftError(SuperTokensSessionError):
-    def __init__(self, user_id: str, session_handle: str):
-        super().__init__("token theft detected")
-        self.user_id = user_id
-        self.session_handle = session_handle
-
-

Ancestors

- -
-
-class TryRefreshTokenError -(*args: Any, **kwargs: Any) -
-
-

Common base class for all non-exit exceptions.

-
- -Expand source code - -
class TryRefreshTokenError(SuperTokensSessionError):
-    pass
-
-

Ancestors

- -
-
-class UnauthorisedError -(msg: str, clear_tokens: bool = True) -
-
-

Common base class for all non-exit exceptions.

-
- -Expand source code - -
class UnauthorisedError(SuperTokensSessionError):
-    def __init__(self, msg: str, clear_tokens: bool = True):
-        super().__init__(msg)
-        self.clear_tokens = clear_tokens
-
-

Ancestors

- -
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/session/framework/django/asyncio/index.html b/html/supertokens_python/recipe/session/framework/django/asyncio/index.html deleted file mode 100644 index 394e376b1..000000000 --- a/html/supertokens_python/recipe/session/framework/django/asyncio/index.html +++ /dev/null @@ -1,223 +0,0 @@ - - - - - - -supertokens_python.recipe.session.framework.django.asyncio API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.session.framework.django.asyncio

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from functools import wraps
-from typing import Any, Callable, Dict, List, Optional, TypeVar, Union, cast
-
-from supertokens_python import Supertokens
-from supertokens_python.exceptions import SuperTokensError
-from supertokens_python.framework.django.django_request import DjangoRequest
-from supertokens_python.framework.django.django_response import DjangoResponse
-from supertokens_python.recipe.session import SessionContainer, SessionRecipe
-from supertokens_python.recipe.session.interfaces import SessionClaimValidator
-from supertokens_python.utils import set_request_in_user_context_if_not_defined
-from supertokens_python.types import MaybeAwaitable
-
-_T = TypeVar("_T", bound=Callable[..., Any])
-
-
-def verify_session(
-    anti_csrf_check: Union[bool, None] = None,
-    session_required: bool = True,
-    check_database: bool = False,
-    override_global_claim_validators: Optional[
-        Callable[
-            [List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
-            MaybeAwaitable[List[SessionClaimValidator]],
-        ]
-    ] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> Callable[[_T], _T]:
-    _ = user_context
-
-    def session_verify(f: _T) -> _T:
-        from django.http import HttpRequest
-
-        @wraps(f)
-        async def wrapped_function(request: HttpRequest, *args: Any, **kwargs: Any):
-            nonlocal user_context
-            from django.http import JsonResponse
-
-            baseRequest = DjangoRequest(request)
-            try:
-                user_context = set_request_in_user_context_if_not_defined(
-                    user_context, baseRequest
-                )
-
-                recipe = SessionRecipe.get_instance()
-                session = await recipe.verify_session(
-                    baseRequest,
-                    anti_csrf_check,
-                    session_required,
-                    check_database,
-                    override_global_claim_validators,
-                    user_context,
-                )
-                if session is None:
-                    if session_required:
-                        raise Exception("Should never come here")
-                    baseRequest.set_session_as_none()
-                else:
-                    baseRequest.set_session(session)
-                return await f(baseRequest.request, *args, **kwargs)
-            except SuperTokensError as e:
-                response = DjangoResponse(JsonResponse({}))
-                user_context = set_request_in_user_context_if_not_defined(
-                    user_context, baseRequest
-                )
-                result = await Supertokens.get_instance().handle_supertokens_error(
-                    DjangoRequest(request), e, response, user_context
-                )
-                if isinstance(result, DjangoResponse):
-                    return result.response
-                raise Exception("Should never come here")
-
-        return cast(_T, wrapped_function)
-
-    return session_verify
-
-
-
-
-
-
-
-

Functions

-
-
-def verify_session(anti_csrf_check: Optional[bool] = None, session_required: bool = True, check_database: bool = False, override_global_claim_validators: Optional[Callable[[List[SessionClaimValidator], SessionContainer, Dict[str, Any]], Union[Awaitable[List[SessionClaimValidator]], List[SessionClaimValidator]]]] = None, user_context: Optional[Dict[str, Any]] = None) ‑> Callable[[~_T], ~_T] -
-
-
-
- -Expand source code - -
def verify_session(
-    anti_csrf_check: Union[bool, None] = None,
-    session_required: bool = True,
-    check_database: bool = False,
-    override_global_claim_validators: Optional[
-        Callable[
-            [List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
-            MaybeAwaitable[List[SessionClaimValidator]],
-        ]
-    ] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> Callable[[_T], _T]:
-    _ = user_context
-
-    def session_verify(f: _T) -> _T:
-        from django.http import HttpRequest
-
-        @wraps(f)
-        async def wrapped_function(request: HttpRequest, *args: Any, **kwargs: Any):
-            nonlocal user_context
-            from django.http import JsonResponse
-
-            baseRequest = DjangoRequest(request)
-            try:
-                user_context = set_request_in_user_context_if_not_defined(
-                    user_context, baseRequest
-                )
-
-                recipe = SessionRecipe.get_instance()
-                session = await recipe.verify_session(
-                    baseRequest,
-                    anti_csrf_check,
-                    session_required,
-                    check_database,
-                    override_global_claim_validators,
-                    user_context,
-                )
-                if session is None:
-                    if session_required:
-                        raise Exception("Should never come here")
-                    baseRequest.set_session_as_none()
-                else:
-                    baseRequest.set_session(session)
-                return await f(baseRequest.request, *args, **kwargs)
-            except SuperTokensError as e:
-                response = DjangoResponse(JsonResponse({}))
-                user_context = set_request_in_user_context_if_not_defined(
-                    user_context, baseRequest
-                )
-                result = await Supertokens.get_instance().handle_supertokens_error(
-                    DjangoRequest(request), e, response, user_context
-                )
-                if isinstance(result, DjangoResponse):
-                    return result.response
-                raise Exception("Should never come here")
-
-        return cast(_T, wrapped_function)
-
-    return session_verify
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/session/framework/django/index.html b/html/supertokens_python/recipe/session/framework/django/index.html deleted file mode 100644 index e83af2d25..000000000 --- a/html/supertokens_python/recipe/session/framework/django/index.html +++ /dev/null @@ -1,88 +0,0 @@ - - - - - - -supertokens_python.recipe.session.framework.django API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.session.framework.django

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-
-
-

Sub-modules

-
-
supertokens_python.recipe.session.framework.django.asyncio
-
-
-
-
supertokens_python.recipe.session.framework.django.syncio
-
-
-
-
-
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/session/framework/django/syncio/index.html b/html/supertokens_python/recipe/session/framework/django/syncio/index.html deleted file mode 100644 index 978a855d5..000000000 --- a/html/supertokens_python/recipe/session/framework/django/syncio/index.html +++ /dev/null @@ -1,232 +0,0 @@ - - - - - - -supertokens_python.recipe.session.framework.django.syncio API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.session.framework.django.syncio

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from functools import wraps
-from typing import Any, Callable, Dict, TypeVar, Union, cast, List, Optional
-
-from supertokens_python import Supertokens
-from supertokens_python.async_to_sync_wrapper import sync
-from supertokens_python.exceptions import SuperTokensError
-from supertokens_python.framework.django.django_request import DjangoRequest
-from supertokens_python.framework.django.django_response import DjangoResponse
-from supertokens_python.recipe.session import SessionRecipe, SessionContainer
-from supertokens_python.recipe.session.interfaces import SessionClaimValidator
-from supertokens_python.utils import set_request_in_user_context_if_not_defined
-from supertokens_python.types import MaybeAwaitable
-
-_T = TypeVar("_T", bound=Callable[..., Any])
-
-
-def verify_session(
-    anti_csrf_check: Union[bool, None] = None,
-    session_required: bool = True,
-    check_database: bool = False,
-    override_global_claim_validators: Optional[
-        Callable[
-            [List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
-            MaybeAwaitable[List[SessionClaimValidator]],
-        ]
-    ] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> Callable[[_T], _T]:
-    _ = user_context
-
-    def session_verify(f: _T) -> _T:
-        from django.http import HttpRequest
-
-        @wraps(f)
-        def wrapped_function(request: HttpRequest, *args: Any, **kwargs: Any):
-            nonlocal user_context
-            from django.http import JsonResponse
-
-            baseRequest = DjangoRequest(request)
-            try:
-                user_context = set_request_in_user_context_if_not_defined(
-                    user_context, baseRequest
-                )
-
-                recipe = SessionRecipe.get_instance()
-                session = sync(
-                    recipe.verify_session(
-                        baseRequest,
-                        anti_csrf_check,
-                        session_required,
-                        check_database,
-                        override_global_claim_validators,
-                        user_context,
-                    )
-                )
-                if session is None:
-                    if session_required:
-                        raise Exception("Should never come here")
-                    baseRequest.set_session_as_none()
-                else:
-                    baseRequest.set_session(session)
-                return f(baseRequest.request, *args, **kwargs)
-            except SuperTokensError as e:
-                response = DjangoResponse(JsonResponse({}))
-                user_context = set_request_in_user_context_if_not_defined(
-                    user_context, baseRequest
-                )
-                result = sync(
-                    Supertokens.get_instance().handle_supertokens_error(
-                        DjangoRequest(request), e, response, user_context
-                    )
-                )
-                if isinstance(result, DjangoResponse):
-                    return result.response
-                raise Exception("Should never come here")
-
-        return cast(_T, wrapped_function)
-
-    return session_verify
-
-
-
-
-
-
-
-

Functions

-
-
-def verify_session(anti_csrf_check: Optional[bool] = None, session_required: bool = True, check_database: bool = False, override_global_claim_validators: Optional[Callable[[List[SessionClaimValidator], SessionContainer, Dict[str, Any]], Union[Awaitable[List[SessionClaimValidator]], List[SessionClaimValidator]]]] = None, user_context: Optional[Dict[str, Any]] = None) ‑> Callable[[~_T], ~_T] -
-
-
-
- -Expand source code - -
def verify_session(
-    anti_csrf_check: Union[bool, None] = None,
-    session_required: bool = True,
-    check_database: bool = False,
-    override_global_claim_validators: Optional[
-        Callable[
-            [List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
-            MaybeAwaitable[List[SessionClaimValidator]],
-        ]
-    ] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> Callable[[_T], _T]:
-    _ = user_context
-
-    def session_verify(f: _T) -> _T:
-        from django.http import HttpRequest
-
-        @wraps(f)
-        def wrapped_function(request: HttpRequest, *args: Any, **kwargs: Any):
-            nonlocal user_context
-            from django.http import JsonResponse
-
-            baseRequest = DjangoRequest(request)
-            try:
-                user_context = set_request_in_user_context_if_not_defined(
-                    user_context, baseRequest
-                )
-
-                recipe = SessionRecipe.get_instance()
-                session = sync(
-                    recipe.verify_session(
-                        baseRequest,
-                        anti_csrf_check,
-                        session_required,
-                        check_database,
-                        override_global_claim_validators,
-                        user_context,
-                    )
-                )
-                if session is None:
-                    if session_required:
-                        raise Exception("Should never come here")
-                    baseRequest.set_session_as_none()
-                else:
-                    baseRequest.set_session(session)
-                return f(baseRequest.request, *args, **kwargs)
-            except SuperTokensError as e:
-                response = DjangoResponse(JsonResponse({}))
-                user_context = set_request_in_user_context_if_not_defined(
-                    user_context, baseRequest
-                )
-                result = sync(
-                    Supertokens.get_instance().handle_supertokens_error(
-                        DjangoRequest(request), e, response, user_context
-                    )
-                )
-                if isinstance(result, DjangoResponse):
-                    return result.response
-                raise Exception("Should never come here")
-
-        return cast(_T, wrapped_function)
-
-    return session_verify
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/session/framework/fastapi/index.html b/html/supertokens_python/recipe/session/framework/fastapi/index.html deleted file mode 100644 index f2453f7c2..000000000 --- a/html/supertokens_python/recipe/session/framework/fastapi/index.html +++ /dev/null @@ -1,240 +0,0 @@ - - - - - - -supertokens_python.recipe.session.framework.fastapi API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.session.framework.fastapi

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-import json
-
-from typing import Any, Callable, Coroutine, Dict, Union, List, Optional
-
-from supertokens_python import Supertokens
-from supertokens_python.framework.fastapi.fastapi_request import FastApiRequest
-from supertokens_python.framework.fastapi.fastapi_response import FastApiResponse
-from supertokens_python.recipe.session import SessionRecipe
-from supertokens_python.exceptions import SuperTokensError
-from supertokens_python.types import MaybeAwaitable
-from fastapi.responses import JSONResponse
-
-from ...interfaces import SessionContainer, SessionClaimValidator
-from supertokens_python.utils import (
-    set_request_in_user_context_if_not_defined,
-    default_user_context,
-)
-
-from fastapi import Request
-
-
-def verify_session(
-    anti_csrf_check: Union[bool, None] = None,
-    session_required: bool = True,
-    check_database: bool = False,
-    override_global_claim_validators: Optional[
-        Callable[
-            [List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
-            MaybeAwaitable[List[SessionClaimValidator]],
-        ]
-    ] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> Callable[..., Coroutine[Any, Any, Union[SessionContainer, None]]]:
-    _ = user_context
-
-    async def func(request: Request) -> Union[SessionContainer, None]:
-        nonlocal user_context
-        base_req = FastApiRequest(request)
-        user_context = set_request_in_user_context_if_not_defined(
-            user_context, base_req
-        )
-
-        recipe = SessionRecipe.get_instance()
-        session = await recipe.verify_session(
-            base_req,
-            anti_csrf_check,
-            session_required,
-            check_database,
-            override_global_claim_validators,
-            user_context,
-        )
-        if session is None:
-            if session_required:
-                raise Exception("Should never come here")
-            base_req.set_session_as_none()
-        else:
-            base_req.set_session(session)
-        return base_req.get_session()
-
-    return func
-
-
-async def session_exception_handler(
-    request: Request, exc: SuperTokensError
-) -> JSONResponse:
-    """FastAPI exceptional handler for errors raised by Supertokens SDK when not using middleware
-
-    Usage: `app.add_exception_handler(SuperTokensError, st_exception_handler)`
-    """
-    base_req = FastApiRequest(request)
-    base_res = FastApiResponse(JSONResponse())
-    user_context = default_user_context(base_req)
-    result = await Supertokens.get_instance().handle_supertokens_error(
-        base_req, exc, base_res, user_context
-    )
-    if isinstance(result, FastApiResponse):
-        body = json.loads(result.response.body)
-        return JSONResponse(body, status_code=result.response.status_code)
-
-    raise Exception("Should never come here")
-
-
-
-
-
-
-
-

Functions

-
-
-async def session_exception_handler(request: starlette.requests.Request, exc: SuperTokensError) ‑> starlette.responses.JSONResponse -
-
-

FastAPI exceptional handler for errors raised by Supertokens SDK when not using middleware

-

Usage: app.add_exception_handler(SuperTokensError, st_exception_handler)

-
- -Expand source code - -
async def session_exception_handler(
-    request: Request, exc: SuperTokensError
-) -> JSONResponse:
-    """FastAPI exceptional handler for errors raised by Supertokens SDK when not using middleware
-
-    Usage: `app.add_exception_handler(SuperTokensError, st_exception_handler)`
-    """
-    base_req = FastApiRequest(request)
-    base_res = FastApiResponse(JSONResponse())
-    user_context = default_user_context(base_req)
-    result = await Supertokens.get_instance().handle_supertokens_error(
-        base_req, exc, base_res, user_context
-    )
-    if isinstance(result, FastApiResponse):
-        body = json.loads(result.response.body)
-        return JSONResponse(body, status_code=result.response.status_code)
-
-    raise Exception("Should never come here")
-
-
-
-def verify_session(anti_csrf_check: Optional[bool] = None, session_required: bool = True, check_database: bool = False, override_global_claim_validators: Optional[Callable[[List[SessionClaimValidator], SessionContainer, Dict[str, Any]], Union[Awaitable[List[SessionClaimValidator]], List[SessionClaimValidator]]]] = None, user_context: Optional[Dict[str, Any]] = None) ‑> Callable[..., Coroutine[Any, Any, Optional[SessionContainer]]] -
-
-
-
- -Expand source code - -
def verify_session(
-    anti_csrf_check: Union[bool, None] = None,
-    session_required: bool = True,
-    check_database: bool = False,
-    override_global_claim_validators: Optional[
-        Callable[
-            [List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
-            MaybeAwaitable[List[SessionClaimValidator]],
-        ]
-    ] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> Callable[..., Coroutine[Any, Any, Union[SessionContainer, None]]]:
-    _ = user_context
-
-    async def func(request: Request) -> Union[SessionContainer, None]:
-        nonlocal user_context
-        base_req = FastApiRequest(request)
-        user_context = set_request_in_user_context_if_not_defined(
-            user_context, base_req
-        )
-
-        recipe = SessionRecipe.get_instance()
-        session = await recipe.verify_session(
-            base_req,
-            anti_csrf_check,
-            session_required,
-            check_database,
-            override_global_claim_validators,
-            user_context,
-        )
-        if session is None:
-            if session_required:
-                raise Exception("Should never come here")
-            base_req.set_session_as_none()
-        else:
-            base_req.set_session(session)
-        return base_req.get_session()
-
-    return func
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/session/framework/flask/index.html b/html/supertokens_python/recipe/session/framework/flask/index.html deleted file mode 100644 index 6c578e6d5..000000000 --- a/html/supertokens_python/recipe/session/framework/flask/index.html +++ /dev/null @@ -1,228 +0,0 @@ - - - - - - -supertokens_python.recipe.session.framework.flask API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.session.framework.flask

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from functools import wraps
-from typing import Any, Callable, Dict, TypeVar, Union, cast, List, Optional
-
-from supertokens_python import Supertokens
-from supertokens_python.async_to_sync_wrapper import sync
-from supertokens_python.framework.flask.flask_request import FlaskRequest
-from supertokens_python.framework.flask.flask_response import FlaskResponse
-from supertokens_python.recipe.session import SessionRecipe, SessionContainer
-from supertokens_python.exceptions import SuperTokensError
-from supertokens_python.recipe.session.interfaces import SessionClaimValidator
-from supertokens_python.utils import set_request_in_user_context_if_not_defined
-from supertokens_python.types import MaybeAwaitable
-
-_T = TypeVar("_T", bound=Callable[..., Any])
-
-
-def verify_session(
-    anti_csrf_check: Union[bool, None] = None,
-    session_required: bool = True,
-    check_database: bool = False,
-    override_global_claim_validators: Optional[
-        Callable[
-            [List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
-            MaybeAwaitable[List[SessionClaimValidator]],
-        ]
-    ] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> Callable[[_T], _T]:
-    _ = user_context
-
-    def session_verify(f: _T) -> _T:
-        @wraps(f)
-        def wrapped_function(*args: Any, **kwargs: Any):
-            nonlocal user_context
-            from flask import make_response, request
-
-            base_req = FlaskRequest(request)
-            user_context = set_request_in_user_context_if_not_defined(
-                user_context, base_req
-            )
-
-            recipe = SessionRecipe.get_instance()
-
-            try:
-                session = sync(
-                    recipe.verify_session(
-                        base_req,
-                        anti_csrf_check,
-                        session_required,
-                        check_database,
-                        override_global_claim_validators,
-                        user_context,
-                    )
-                )
-                if session is None:
-                    if session_required:
-                        raise Exception("Should never come here")
-                    base_req.set_session_as_none()
-                else:
-                    base_req.set_session(session)
-
-                response = f(*args, **kwargs)
-                return make_response(response) if response is not None else None
-            except SuperTokensError as e:
-                response = FlaskResponse(make_response())
-                result = sync(
-                    Supertokens.get_instance().handle_supertokens_error(
-                        base_req, e, response, user_context
-                    )
-                )
-                if isinstance(result, FlaskResponse):
-                    return result.response
-                raise Exception("Should never come here")
-
-        return cast(_T, wrapped_function)
-
-    return session_verify
-
-
-
-
-
-
-
-

Functions

-
-
-def verify_session(anti_csrf_check: Optional[bool] = None, session_required: bool = True, check_database: bool = False, override_global_claim_validators: Optional[Callable[[List[SessionClaimValidator], SessionContainer, Dict[str, Any]], Union[Awaitable[List[SessionClaimValidator]], List[SessionClaimValidator]]]] = None, user_context: Optional[Dict[str, Any]] = None) ‑> Callable[[~_T], ~_T] -
-
-
-
- -Expand source code - -
def verify_session(
-    anti_csrf_check: Union[bool, None] = None,
-    session_required: bool = True,
-    check_database: bool = False,
-    override_global_claim_validators: Optional[
-        Callable[
-            [List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
-            MaybeAwaitable[List[SessionClaimValidator]],
-        ]
-    ] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> Callable[[_T], _T]:
-    _ = user_context
-
-    def session_verify(f: _T) -> _T:
-        @wraps(f)
-        def wrapped_function(*args: Any, **kwargs: Any):
-            nonlocal user_context
-            from flask import make_response, request
-
-            base_req = FlaskRequest(request)
-            user_context = set_request_in_user_context_if_not_defined(
-                user_context, base_req
-            )
-
-            recipe = SessionRecipe.get_instance()
-
-            try:
-                session = sync(
-                    recipe.verify_session(
-                        base_req,
-                        anti_csrf_check,
-                        session_required,
-                        check_database,
-                        override_global_claim_validators,
-                        user_context,
-                    )
-                )
-                if session is None:
-                    if session_required:
-                        raise Exception("Should never come here")
-                    base_req.set_session_as_none()
-                else:
-                    base_req.set_session(session)
-
-                response = f(*args, **kwargs)
-                return make_response(response) if response is not None else None
-            except SuperTokensError as e:
-                response = FlaskResponse(make_response())
-                result = sync(
-                    Supertokens.get_instance().handle_supertokens_error(
-                        base_req, e, response, user_context
-                    )
-                )
-                if isinstance(result, FlaskResponse):
-                    return result.response
-                raise Exception("Should never come here")
-
-        return cast(_T, wrapped_function)
-
-    return session_verify
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/session/framework/index.html b/html/supertokens_python/recipe/session/framework/index.html deleted file mode 100644 index 5f690181d..000000000 --- a/html/supertokens_python/recipe/session/framework/index.html +++ /dev/null @@ -1,93 +0,0 @@ - - - - - - -supertokens_python.recipe.session.framework API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.session.framework

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-
-
-

Sub-modules

-
-
supertokens_python.recipe.session.framework.django
-
-
-
-
supertokens_python.recipe.session.framework.fastapi
-
-
-
-
supertokens_python.recipe.session.framework.flask
-
-
-
-
-
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/session/index.html b/html/supertokens_python/recipe/session/index.html deleted file mode 100644 index 022af4972..000000000 --- a/html/supertokens_python/recipe/session/index.html +++ /dev/null @@ -1,285 +0,0 @@ - - - - - - -supertokens_python.recipe.session API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.session

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from typing import TYPE_CHECKING, Any, Callable, Dict, Union
-
-from typing_extensions import Literal
-
-if TYPE_CHECKING:
-    from ...recipe_module import RecipeModule
-    from supertokens_python.supertokens import AppInfo, BaseRequest
-    from .utils import TokenTransferMethod
-
-from . import exceptions as ex
-from . import interfaces, utils
-from .recipe import SessionRecipe
-
-InputErrorHandlers = utils.InputErrorHandlers
-InputOverrideConfig = utils.InputOverrideConfig
-SessionContainer = interfaces.SessionContainer
-exceptions = ex
-
-
-def init(
-    cookie_domain: Union[str, None] = None,
-    older_cookie_domain: Union[str, None] = None,
-    cookie_secure: Union[bool, None] = None,
-    cookie_same_site: Union[Literal["lax", "none", "strict"], None] = None,
-    session_expired_status_code: Union[int, None] = None,
-    anti_csrf: Union[Literal["VIA_TOKEN", "VIA_CUSTOM_HEADER", "NONE"], None] = None,
-    get_token_transfer_method: Union[
-        Callable[
-            [BaseRequest, bool, Dict[str, Any]],
-            Union[TokenTransferMethod, Literal["any"]],
-        ],
-        None,
-    ] = None,
-    error_handlers: Union[InputErrorHandlers, None] = None,
-    override: Union[InputOverrideConfig, None] = None,
-    invalid_claim_status_code: Union[int, None] = None,
-    use_dynamic_access_token_signing_key: Union[bool, None] = None,
-    expose_access_token_to_frontend_in_cookie_based_auth: Union[bool, None] = None,
-    jwks_refresh_interval_sec: Union[int, None] = None,
-) -> Callable[[AppInfo], RecipeModule]:
-    return SessionRecipe.init(
-        cookie_domain,
-        older_cookie_domain,
-        cookie_secure,
-        cookie_same_site,
-        session_expired_status_code,
-        anti_csrf,
-        get_token_transfer_method,
-        error_handlers,
-        override,
-        invalid_claim_status_code,
-        use_dynamic_access_token_signing_key,
-        expose_access_token_to_frontend_in_cookie_based_auth,
-        jwks_refresh_interval_sec,
-    )
-
-
-
-

Sub-modules

-
-
supertokens_python.recipe.session.access_token
-
-
-
-
supertokens_python.recipe.session.api
-
-
-
-
supertokens_python.recipe.session.asyncio
-
-
-
-
supertokens_python.recipe.session.claim_base_classes
-
-
-
-
supertokens_python.recipe.session.claims
-
-
-
-
supertokens_python.recipe.session.constants
-
-
-
-
supertokens_python.recipe.session.cookie_and_header
-
-
-
-
supertokens_python.recipe.session.exceptions
-
-
-
-
supertokens_python.recipe.session.framework
-
-
-
-
supertokens_python.recipe.session.interfaces
-
-
-
-
supertokens_python.recipe.session.jwks
-
-
-
-
supertokens_python.recipe.session.jwt
-
-
-
-
supertokens_python.recipe.session.recipe
-
-
-
-
supertokens_python.recipe.session.recipe_implementation
-
-
-
-
supertokens_python.recipe.session.session_class
-
-
-
-
supertokens_python.recipe.session.session_functions
-
-
-
-
supertokens_python.recipe.session.session_request_functions
-
-
-
-
supertokens_python.recipe.session.syncio
-
-
-
-
supertokens_python.recipe.session.utils
-
-
-
-
-
-
-
-
-

Functions

-
-
-def init(cookie_domain: Union[str, None] = None, older_cookie_domain: Union[str, None] = None, cookie_secure: Union[bool, None] = None, cookie_same_site: "Union[Literal['lax', 'none', 'strict'], None]" = None, session_expired_status_code: Union[int, None] = None, anti_csrf: "Union[Literal['VIA_TOKEN', 'VIA_CUSTOM_HEADER', 'NONE'], None]" = None, get_token_transfer_method: "Union[Callable[[BaseRequest, bool, Dict[str, Any]], Union[TokenTransferMethod, Literal['any']]], None]" = None, error_handlers: Union[InputErrorHandlers, None] = None, override: Union[InputOverrideConfig, None] = None, invalid_claim_status_code: Union[int, None] = None, use_dynamic_access_token_signing_key: Union[bool, None] = None, expose_access_token_to_frontend_in_cookie_based_auth: Union[bool, None] = None, jwks_refresh_interval_sec: Union[int, None] = None) ‑> Callable[[AppInfo], RecipeModule] -
-
-
-
- -Expand source code - -
def init(
-    cookie_domain: Union[str, None] = None,
-    older_cookie_domain: Union[str, None] = None,
-    cookie_secure: Union[bool, None] = None,
-    cookie_same_site: Union[Literal["lax", "none", "strict"], None] = None,
-    session_expired_status_code: Union[int, None] = None,
-    anti_csrf: Union[Literal["VIA_TOKEN", "VIA_CUSTOM_HEADER", "NONE"], None] = None,
-    get_token_transfer_method: Union[
-        Callable[
-            [BaseRequest, bool, Dict[str, Any]],
-            Union[TokenTransferMethod, Literal["any"]],
-        ],
-        None,
-    ] = None,
-    error_handlers: Union[InputErrorHandlers, None] = None,
-    override: Union[InputOverrideConfig, None] = None,
-    invalid_claim_status_code: Union[int, None] = None,
-    use_dynamic_access_token_signing_key: Union[bool, None] = None,
-    expose_access_token_to_frontend_in_cookie_based_auth: Union[bool, None] = None,
-    jwks_refresh_interval_sec: Union[int, None] = None,
-) -> Callable[[AppInfo], RecipeModule]:
-    return SessionRecipe.init(
-        cookie_domain,
-        older_cookie_domain,
-        cookie_secure,
-        cookie_same_site,
-        session_expired_status_code,
-        anti_csrf,
-        get_token_transfer_method,
-        error_handlers,
-        override,
-        invalid_claim_status_code,
-        use_dynamic_access_token_signing_key,
-        expose_access_token_to_frontend_in_cookie_based_auth,
-        jwks_refresh_interval_sec,
-    )
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/session/interfaces.html b/html/supertokens_python/recipe/session/interfaces.html deleted file mode 100644 index f4d88a71a..000000000 --- a/html/supertokens_python/recipe/session/interfaces.html +++ /dev/null @@ -1,2903 +0,0 @@ - - - - - - -supertokens_python.recipe.session.interfaces API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.session.interfaces

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from abc import ABC, abstractmethod
-from typing import (
-    TYPE_CHECKING,
-    Any,
-    Callable,
-    Dict,
-    Generic,
-    List,
-    Optional,
-    TypeVar,
-    Union,
-)
-from typing_extensions import TypedDict
-
-from supertokens_python.async_to_sync_wrapper import sync
-from supertokens_python.types import APIResponse, GeneralErrorResponse, MaybeAwaitable
-
-from ...utils import resolve
-from .exceptions import ClaimValidationError
-from .utils import SessionConfig, TokenTransferMethod
-
-if TYPE_CHECKING:
-    from supertokens_python.framework import BaseRequest
-
-from supertokens_python.framework import BaseResponse
-
-
-class SessionObj:
-    def __init__(
-        self,
-        handle: str,
-        user_id: str,
-        user_data_in_jwt: Dict[str, Any],
-        tenant_id: str,
-    ):
-        self.handle = handle
-        self.user_id = user_id
-        self.user_data_in_jwt = user_data_in_jwt
-        self.tenant_id = tenant_id
-
-
-class AccessTokenObj:
-    def __init__(self, token: str, expiry: int, created_time: int):
-        self.token = token
-        self.expiry = expiry
-        self.created_time = created_time
-
-
-class RegenerateAccessTokenOkResult:
-    def __init__(self, session: SessionObj, access_token: Union[AccessTokenObj, None]):
-        self.session = session
-        self.access_token = access_token
-
-
-class SessionInformationResult:
-    def __init__(
-        self,
-        session_handle: str,
-        user_id: str,
-        session_data_in_database: Dict[str, Any],
-        expiry: int,
-        custom_claims_in_access_token_payload: Dict[str, Any],
-        time_created: int,
-        tenant_id: str,
-    ):
-        self.session_handle = session_handle
-        self.user_id = user_id
-        self.session_data_in_database = session_data_in_database
-        self.expiry = expiry
-        self.custom_claims_in_access_token_payload = (
-            custom_claims_in_access_token_payload
-        )
-        self.time_created = time_created
-        self.tenant_id = tenant_id
-
-
-class ReqResInfo:
-    def __init__(
-        self,
-        request: BaseRequest,
-        transfer_method: TokenTransferMethod,
-    ):
-        self.request = request
-        self.transfer_method = transfer_method
-
-
-_T = TypeVar("_T")
-JSONObject = Dict[str, Any]
-
-JSONPrimitive = Union[str, int, bool, None, Dict[str, Any]]
-JSONPrimitiveList = Union[
-    List[str], List[int], List[bool], List[None], List[Dict[str, Any]]
-]
-
-
-class SessionDoesNotExistError:
-    pass
-
-
-class GetClaimValueOkResult(Generic[_T]):
-    def __init__(self, value: Optional[_T]):
-        self.value = value
-
-
-class ClaimsValidationResult:
-    def __init__(
-        self,
-        invalid_claims: List[ClaimValidationError],
-        access_token_payload_update: Optional[Dict[str, Any]] = None,
-    ):
-        self.invalid_claims = invalid_claims
-        self.access_token_payload_update = access_token_payload_update
-
-
-class GetSessionTokensDangerouslyDict(TypedDict):
-    accessToken: str
-    accessAndFrontTokenUpdated: bool
-    refreshToken: Optional[str]
-    frontToken: str
-    antiCsrfToken: Optional[str]
-
-
-class RecipeInterface(ABC):  # pylint: disable=too-many-public-methods
-    def __init__(self):
-        pass
-
-    @abstractmethod
-    async def create_new_session(
-        self,
-        user_id: str,
-        access_token_payload: Optional[Dict[str, Any]],
-        session_data_in_database: Optional[Dict[str, Any]],
-        disable_anti_csrf: Optional[bool],
-        tenant_id: str,
-        user_context: Dict[str, Any],
-    ) -> SessionContainer:
-        pass
-
-    @abstractmethod
-    def get_global_claim_validators(
-        self,
-        tenant_id: str,
-        user_id: str,
-        claim_validators_added_by_other_recipes: List[SessionClaimValidator],
-        user_context: Dict[str, Any],
-    ) -> MaybeAwaitable[List[SessionClaimValidator]]:
-        pass
-
-    @abstractmethod
-    async def get_session(
-        self,
-        access_token: Optional[str],
-        anti_csrf_token: Optional[str] = None,
-        anti_csrf_check: Optional[bool] = None,
-        session_required: Optional[bool] = None,
-        check_database: Optional[bool] = None,
-        override_global_claim_validators: Optional[
-            Callable[
-                [List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
-                MaybeAwaitable[List[SessionClaimValidator]],
-            ]
-        ] = None,
-        user_context: Optional[Dict[str, Any]] = None,
-    ) -> Optional[SessionContainer]:
-        pass
-
-    @abstractmethod
-    async def validate_claims(
-        self,
-        user_id: str,
-        access_token_payload: Dict[str, Any],
-        claim_validators: List[SessionClaimValidator],
-        user_context: Dict[str, Any],
-    ) -> ClaimsValidationResult:
-        pass
-
-    @abstractmethod
-    async def validate_claims_in_jwt_payload(
-        self,
-        user_id: str,
-        jwt_payload: JSONObject,
-        claim_validators: List[SessionClaimValidator],
-        user_context: Dict[str, Any],
-    ) -> ClaimsValidationResult:
-        pass
-
-    @abstractmethod
-    async def refresh_session(
-        self,
-        refresh_token: str,
-        anti_csrf_token: Optional[str],
-        disable_anti_csrf: bool,
-        user_context: Dict[str, Any],
-    ) -> SessionContainer:
-        pass
-
-    @abstractmethod
-    async def revoke_session(
-        self, session_handle: str, user_context: Dict[str, Any]
-    ) -> bool:
-        pass
-
-    @abstractmethod
-    async def revoke_all_sessions_for_user(
-        self,
-        user_id: str,
-        tenant_id: str,
-        revoke_across_all_tenants: bool,
-        user_context: Dict[str, Any],
-    ) -> List[str]:
-        pass
-
-    @abstractmethod
-    async def get_all_session_handles_for_user(
-        self,
-        user_id: str,
-        tenant_id: str,
-        fetch_across_all_tenants: bool,
-        user_context: Dict[str, Any],
-    ) -> List[str]:
-        pass
-
-    @abstractmethod
-    async def revoke_multiple_sessions(
-        self, session_handles: List[str], user_context: Dict[str, Any]
-    ) -> List[str]:
-        pass
-
-    @abstractmethod
-    async def get_session_information(
-        self, session_handle: str, user_context: Dict[str, Any]
-    ) -> Union[SessionInformationResult, None]:
-        pass
-
-    @abstractmethod
-    async def update_session_data_in_database(
-        self,
-        session_handle: str,
-        new_session_data: Dict[str, Any],
-        user_context: Dict[str, Any],
-    ) -> bool:
-        pass
-
-    @abstractmethod
-    async def merge_into_access_token_payload(
-        self,
-        session_handle: str,
-        access_token_payload_update: JSONObject,
-        user_context: Dict[str, Any],
-    ) -> bool:
-        pass
-
-    @abstractmethod
-    async def fetch_and_set_claim(
-        self,
-        session_handle: str,
-        claim: SessionClaim[Any],
-        user_context: Dict[str, Any],
-    ) -> bool:
-        pass
-
-    @abstractmethod
-    async def set_claim_value(
-        self,
-        session_handle: str,
-        claim: SessionClaim[_T],
-        value: _T,
-        user_context: Dict[str, Any],
-    ) -> bool:
-        pass
-
-    @abstractmethod
-    async def get_claim_value(
-        self,
-        session_handle: str,
-        claim: SessionClaim[Any],
-        user_context: Dict[str, Any],
-    ) -> Union[SessionDoesNotExistError, GetClaimValueOkResult[Any]]:
-        pass
-
-    @abstractmethod
-    async def remove_claim(
-        self,
-        session_handle: str,
-        claim: SessionClaim[Any],
-        user_context: Dict[str, Any],
-    ) -> bool:
-        pass
-
-    @abstractmethod
-    async def regenerate_access_token(
-        self,
-        access_token: str,
-        new_access_token_payload: Union[Dict[str, Any], None],
-        user_context: Dict[str, Any],
-    ) -> Union[RegenerateAccessTokenOkResult, None]:
-        pass
-
-
-class SignOutOkayResponse(APIResponse):
-    def __init__(self):
-        self.status = "OK"
-
-    def to_json(self) -> Dict[str, Any]:
-        return {"status": self.status}
-
-
-class APIOptions:
-    def __init__(
-        self,
-        request: BaseRequest,
-        response: Optional[BaseResponse],
-        recipe_id: str,
-        config: SessionConfig,
-        recipe_implementation: RecipeInterface,
-    ):
-        self.request = request
-        self.response = response
-        self.recipe_id = recipe_id
-        self.config = config
-        self.recipe_implementation = recipe_implementation
-
-
-class APIInterface(ABC):
-    def __init__(self):
-        self.disable_refresh_post = False
-        self.disable_signout_post = False
-
-    # We do not add a GeneralErrorResponse response to this API
-    # since it's not something that is directly called by the user on the
-    # frontend anyway
-
-    @abstractmethod
-    async def refresh_post(
-        self, api_options: APIOptions, user_context: Dict[str, Any]
-    ) -> SessionContainer:
-        pass
-
-    @abstractmethod
-    async def signout_post(
-        self,
-        session: Optional[SessionContainer],
-        api_options: APIOptions,
-        user_context: Dict[str, Any],
-    ) -> Union[SignOutOkayResponse, GeneralErrorResponse]:
-        pass
-
-    @abstractmethod
-    async def verify_session(
-        self,
-        api_options: APIOptions,
-        anti_csrf_check: Union[bool, None],
-        session_required: bool,
-        check_database: bool,
-        override_global_claim_validators: Optional[
-            Callable[
-                [List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
-                MaybeAwaitable[List[SessionClaimValidator]],
-            ]
-        ],
-        user_context: Dict[str, Any],
-    ) -> Union[SessionContainer, None]:
-        pass
-
-
-ResponseMutator = Callable[[BaseResponse, Dict[str, Any]], None]
-
-
-class TokenInfo:
-    def __init__(self, token: str, expiry: int, created_time: int):
-        self.token = token
-        self.expiry = expiry
-        self.created_time = created_time
-
-
-class SessionContainer(ABC):  # pylint: disable=too-many-public-methods
-    def __init__(
-        self,
-        recipe_implementation: RecipeInterface,
-        config: SessionConfig,
-        access_token: str,
-        front_token: str,
-        refresh_token: Optional[TokenInfo],
-        anti_csrf_token: Optional[str],
-        session_handle: str,
-        user_id: str,
-        user_data_in_access_token: Optional[Dict[str, Any]],
-        req_res_info: Optional[ReqResInfo],
-        access_token_updated: bool,
-        tenant_id: str,
-    ):
-        self.recipe_implementation = recipe_implementation
-        self.config = config
-        self.access_token = access_token
-        self.front_token = front_token
-        self.refresh_token = refresh_token
-        self.anti_csrf_token = anti_csrf_token
-        self.session_handle = session_handle
-        self.user_id = user_id
-        self.user_data_in_access_token = user_data_in_access_token
-        self.req_res_info: Optional[ReqResInfo] = req_res_info
-        self.access_token_updated = access_token_updated
-        self.tenant_id = tenant_id
-
-        self.response_mutators: List[ResponseMutator] = []
-
-    @abstractmethod
-    async def revoke_session(
-        self, user_context: Optional[Dict[str, Any]] = None
-    ) -> None:
-        pass
-
-    @abstractmethod
-    async def get_session_data_from_database(
-        self, user_context: Optional[Dict[str, Any]] = None
-    ) -> Dict[str, Any]:
-        pass
-
-    @abstractmethod
-    async def update_session_data_in_database(
-        self,
-        new_session_data: Dict[str, Any],
-        user_context: Optional[Dict[str, Any]] = None,
-    ) -> None:
-        pass
-
-    @abstractmethod
-    async def attach_to_request_response(
-        self,
-        request: BaseRequest,
-        transfer_method: TokenTransferMethod,
-        user_context: Dict[str, Any],
-    ):
-        pass
-
-    @abstractmethod
-    async def merge_into_access_token_payload(
-        self,
-        access_token_payload_update: JSONObject,
-        user_context: Optional[Dict[str, Any]] = None,
-    ) -> None:
-        pass
-
-    @abstractmethod
-    def get_user_id(self, user_context: Optional[Dict[str, Any]] = None) -> str:
-        pass
-
-    @abstractmethod
-    def get_tenant_id(self, user_context: Optional[Dict[str, Any]] = None) -> str:
-        pass
-
-    @abstractmethod
-    def get_access_token_payload(
-        self, user_context: Optional[Dict[str, Any]] = None
-    ) -> Dict[str, Any]:
-        pass
-
-    @abstractmethod
-    def get_handle(self, user_context: Optional[Dict[str, Any]] = None) -> str:
-        pass
-
-    @abstractmethod
-    def get_all_session_tokens_dangerously(self) -> GetSessionTokensDangerouslyDict:
-        pass
-
-    @abstractmethod
-    def get_access_token(self, user_context: Optional[Dict[str, Any]] = None) -> str:
-        pass
-
-    @abstractmethod
-    async def get_time_created(
-        self, user_context: Optional[Dict[str, Any]] = None
-    ) -> int:
-        pass
-
-    @abstractmethod
-    async def get_expiry(self, user_context: Optional[Dict[str, Any]] = None) -> int:
-        pass
-
-    @abstractmethod
-    async def assert_claims(
-        self,
-        claim_validators: List[SessionClaimValidator],
-        user_context: Optional[Dict[str, Any]] = None,
-    ) -> None:
-        pass
-
-    @abstractmethod
-    async def fetch_and_set_claim(
-        self, claim: SessionClaim[Any], user_context: Optional[Dict[str, Any]] = None
-    ) -> None:
-        pass
-
-    @abstractmethod
-    async def set_claim_value(
-        self,
-        claim: SessionClaim[_T],
-        value: _T,
-        user_context: Optional[Dict[str, Any]] = None,
-    ) -> None:
-        pass
-
-    @abstractmethod
-    async def get_claim_value(
-        self, claim: SessionClaim[_T], user_context: Optional[Dict[str, Any]] = None
-    ) -> Union[_T, None]:
-        pass
-
-    @abstractmethod
-    async def remove_claim(
-        self,
-        claim: SessionClaim[Any],
-        user_context: Optional[Dict[str, Any]] = None,
-    ) -> None:
-        pass
-
-    def sync_get_expiry(self, user_context: Optional[Dict[str, Any]] = None) -> int:
-        return sync(self.get_expiry(user_context))
-
-    def sync_revoke_session(
-        self, user_context: Optional[Dict[str, Any]] = None
-    ) -> None:
-        return sync(self.revoke_session(user_context=user_context))
-
-    def sync_get_session_data_from_database(
-        self, user_context: Union[Dict[str, Any], None] = None
-    ) -> Dict[str, Any]:
-        return sync(self.get_session_data_from_database(user_context))
-
-    def sync_get_time_created(
-        self, user_context: Optional[Dict[str, Any]] = None
-    ) -> int:
-        return sync(self.get_time_created(user_context))
-
-    def sync_merge_into_access_token_payload(
-        self,
-        access_token_payload_update: Dict[str, Any],
-        user_context: Optional[Dict[str, Any]] = None,
-    ) -> None:
-        return sync(
-            self.merge_into_access_token_payload(
-                access_token_payload_update, user_context
-            )
-        )
-
-    def sync_update_session_data_in_database(
-        self,
-        new_session_data: Dict[str, Any],
-        user_context: Optional[Dict[str, Any]] = None,
-    ) -> None:
-        return sync(
-            self.update_session_data_in_database(new_session_data, user_context)
-        )
-
-    # Session claims sync functions:
-    def sync_assert_claims(
-        self,
-        claim_validators: List[SessionClaimValidator],
-        user_context: Optional[Dict[str, Any]] = None,
-    ) -> None:
-        return sync(self.assert_claims(claim_validators, user_context))
-
-    def sync_fetch_and_set_claim(
-        self, claim: SessionClaim[Any], user_context: Optional[Dict[str, Any]] = None
-    ) -> None:
-        return sync(self.fetch_and_set_claim(claim, user_context))
-
-    def sync_set_claim_value(
-        self,
-        claim: SessionClaim[_T],
-        value: _T,
-        user_context: Optional[Dict[str, Any]] = None,
-    ) -> None:
-        return sync(self.set_claim_value(claim, value, user_context))
-
-    def sync_get_claim_value(
-        self, claim: SessionClaim[_T], user_context: Optional[Dict[str, Any]] = None
-    ) -> Union[_T, None]:
-        return sync(self.get_claim_value(claim, user_context))
-
-    def sync_remove_claim(
-        self, claim: SessionClaim[Any], user_context: Optional[Dict[str, Any]] = None
-    ) -> None:
-        return sync(self.remove_claim(claim, user_context))
-
-    def sync_attach_to_request_response(
-        self,
-        request: BaseRequest,
-        token_transfer: TokenTransferMethod,
-        user_context: Dict[str, Any],
-    ) -> None:
-        return sync(
-            self.attach_to_request_response(request, token_transfer, user_context)
-        )
-
-    # This is there so that we can do session["..."] to access some of the members of this class
-    def __getitem__(self, item: str):
-        return getattr(self, item)
-
-
-class SessionClaim(ABC, Generic[_T]):
-    def __init__(
-        self,
-        key: str,
-        fetch_value: Callable[
-            [str, str, Dict[str, Any]],
-            MaybeAwaitable[Optional[_T]],
-        ],
-    ) -> None:
-        """
-        Args:
-            key: The key to use when storing the claim in the payload.
-            fetch_value: a method that fetches the current value of this claim for the user.
-                A None return value signifies that we don't want to update the claim payload and or the claim value is
-                not present in the database. For example, this can happen with a second factor auth claim, where we
-                don't want to add the claim to the session automatically
-        """
-        self.key = key
-        self.fetch_value = fetch_value
-
-    @abstractmethod
-    def add_to_payload_(
-        self,
-        payload: JSONObject,
-        value: _T,
-        user_context: Union[Dict[str, Any], None] = None,
-    ) -> JSONObject:
-        """Saves the provided value into the payload, by cloning and updating the entire object"""
-
-    @abstractmethod
-    def remove_from_payload_by_merge_(
-        self, payload: JSONObject, user_context: Optional[Dict[str, Any]] = None
-    ) -> JSONObject:
-        """Removes the claim from the payload by setting it to None, so merge_into_access_token_payload can clear it"""
-
-    @abstractmethod
-    def remove_from_payload(
-        self, payload: JSONObject, user_context: Optional[Dict[str, Any]] = None
-    ) -> JSONObject:
-        """Removes the claim from the payload, by cloning and updating the entire object."""
-
-    @abstractmethod
-    def get_value_from_payload(
-        self, payload: JSONObject, user_context: Union[Dict[str, Any], None] = None
-    ) -> Union[_T, None]:
-        """Gets the value of the claim stored in the payload"""
-
-    async def build(
-        self,
-        user_id: str,
-        tenant_id: str,
-        user_context: Optional[Dict[str, Any]] = None,
-    ) -> JSONObject:
-        if user_context is None:
-            user_context = {}
-
-        value = await resolve(self.fetch_value(user_id, tenant_id, user_context))
-
-        if value is None:
-            return {}
-
-        return self.add_to_payload_({}, value, user_context)
-
-
-class ClaimValidationResult:
-    def __init__(self, is_valid: bool, reason: Optional[Dict[str, Any]] = None):
-        self.is_valid = is_valid
-        self.reason = {} if is_valid else reason
-
-
-class SessionClaimValidator(ABC):
-    def __init__(
-        self,
-        id_: str,
-    ) -> None:
-        self.id = id_
-        self.claim: Optional[SessionClaim[Any]] = None
-
-    @abstractmethod
-    async def validate(
-        self, payload: JSONObject, user_context: Dict[str, Any]
-    ) -> ClaimValidationResult:
-        pass
-
-    def should_refetch(
-        self, payload: JSONObject, user_context: Dict[str, Any]
-    ) -> MaybeAwaitable[bool]:
-        raise NotImplementedError()
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class APIInterface -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class APIInterface(ABC):
-    def __init__(self):
-        self.disable_refresh_post = False
-        self.disable_signout_post = False
-
-    # We do not add a GeneralErrorResponse response to this API
-    # since it's not something that is directly called by the user on the
-    # frontend anyway
-
-    @abstractmethod
-    async def refresh_post(
-        self, api_options: APIOptions, user_context: Dict[str, Any]
-    ) -> SessionContainer:
-        pass
-
-    @abstractmethod
-    async def signout_post(
-        self,
-        session: Optional[SessionContainer],
-        api_options: APIOptions,
-        user_context: Dict[str, Any],
-    ) -> Union[SignOutOkayResponse, GeneralErrorResponse]:
-        pass
-
-    @abstractmethod
-    async def verify_session(
-        self,
-        api_options: APIOptions,
-        anti_csrf_check: Union[bool, None],
-        session_required: bool,
-        check_database: bool,
-        override_global_claim_validators: Optional[
-            Callable[
-                [List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
-                MaybeAwaitable[List[SessionClaimValidator]],
-            ]
-        ],
-        user_context: Dict[str, Any],
-    ) -> Union[SessionContainer, None]:
-        pass
-
-

Ancestors

-
    -
  • abc.ABC
  • -
-

Subclasses

- -

Methods

-
-
-async def refresh_post(self, api_options: APIOptions, user_context: Dict[str, Any]) ‑> SessionContainer -
-
-
-
- -Expand source code - -
@abstractmethod
-async def refresh_post(
-    self, api_options: APIOptions, user_context: Dict[str, Any]
-) -> SessionContainer:
-    pass
-
-
-
-async def signout_post(self, session: Optional[SessionContainer], api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[SignOutOkayResponseGeneralErrorResponse] -
-
-
-
- -Expand source code - -
@abstractmethod
-async def signout_post(
-    self,
-    session: Optional[SessionContainer],
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-) -> Union[SignOutOkayResponse, GeneralErrorResponse]:
-    pass
-
-
-
-async def verify_session(self, api_options: APIOptions, anti_csrf_check: Union[bool, None], session_required: bool, check_database: bool, override_global_claim_validators: Optional[Callable[[List[SessionClaimValidator], SessionContainer, Dict[str, Any]], MaybeAwaitable[List[SessionClaimValidator]]]], user_context: Dict[str, Any]) ‑> Optional[SessionContainer] -
-
-
-
- -Expand source code - -
@abstractmethod
-async def verify_session(
-    self,
-    api_options: APIOptions,
-    anti_csrf_check: Union[bool, None],
-    session_required: bool,
-    check_database: bool,
-    override_global_claim_validators: Optional[
-        Callable[
-            [List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
-            MaybeAwaitable[List[SessionClaimValidator]],
-        ]
-    ],
-    user_context: Dict[str, Any],
-) -> Union[SessionContainer, None]:
-    pass
-
-
-
-
-
-class APIOptions -(request: BaseRequest, response: Optional[BaseResponse], recipe_id: str, config: SessionConfig, recipe_implementation: RecipeInterface) -
-
-
-
- -Expand source code - -
class APIOptions:
-    def __init__(
-        self,
-        request: BaseRequest,
-        response: Optional[BaseResponse],
-        recipe_id: str,
-        config: SessionConfig,
-        recipe_implementation: RecipeInterface,
-    ):
-        self.request = request
-        self.response = response
-        self.recipe_id = recipe_id
-        self.config = config
-        self.recipe_implementation = recipe_implementation
-
-
-
-class AccessTokenObj -(token: str, expiry: int, created_time: int) -
-
-
-
- -Expand source code - -
class AccessTokenObj:
-    def __init__(self, token: str, expiry: int, created_time: int):
-        self.token = token
-        self.expiry = expiry
-        self.created_time = created_time
-
-
-
-class ClaimValidationResult -(is_valid: bool, reason: Optional[Dict[str, Any]] = None) -
-
-
-
- -Expand source code - -
class ClaimValidationResult:
-    def __init__(self, is_valid: bool, reason: Optional[Dict[str, Any]] = None):
-        self.is_valid = is_valid
-        self.reason = {} if is_valid else reason
-
-
-
-class ClaimsValidationResult -(invalid_claims: List[ClaimValidationError], access_token_payload_update: Optional[Dict[str, Any]] = None) -
-
-
-
- -Expand source code - -
class ClaimsValidationResult:
-    def __init__(
-        self,
-        invalid_claims: List[ClaimValidationError],
-        access_token_payload_update: Optional[Dict[str, Any]] = None,
-    ):
-        self.invalid_claims = invalid_claims
-        self.access_token_payload_update = access_token_payload_update
-
-
-
-class GetClaimValueOkResult -(value: Optional[_T]) -
-
-

Abstract base class for generic types.

-

A generic type is typically declared by inheriting from -this class parameterized with one or more type variables. -For example, a generic mapping type might be defined as::

-

class Mapping(Generic[KT, VT]): -def getitem(self, key: KT) -> VT: -… -# Etc.

-

This class can then be used as follows::

-

def lookup_name(mapping: Mapping[KT, VT], key: KT, default: VT) -> VT: -try: -return mapping[key] -except KeyError: -return default

-
- -Expand source code - -
class GetClaimValueOkResult(Generic[_T]):
-    def __init__(self, value: Optional[_T]):
-        self.value = value
-
-

Ancestors

-
    -
  • typing.Generic
  • -
-
-
-class GetSessionTokensDangerouslyDict -(*args, **kwargs) -
-
-

dict() -> new empty dictionary -dict(mapping) -> new dictionary initialized from a mapping object's -(key, value) pairs -dict(iterable) -> new dictionary initialized as if via: -d = {} -for k, v in iterable: -d[k] = v -dict(**kwargs) -> new dictionary initialized with the name=value pairs -in the keyword argument list. -For example: -dict(one=1, two=2)

-
- -Expand source code - -
class GetSessionTokensDangerouslyDict(TypedDict):
-    accessToken: str
-    accessAndFrontTokenUpdated: bool
-    refreshToken: Optional[str]
-    frontToken: str
-    antiCsrfToken: Optional[str]
-
-

Ancestors

-
    -
  • builtins.dict
  • -
-

Class variables

-
-
var accessAndFrontTokenUpdated : bool
-
-
-
-
var accessToken : str
-
-
-
-
var antiCsrfToken : Optional[str]
-
-
-
-
var frontToken : str
-
-
-
-
var refreshToken : Optional[str]
-
-
-
-
-
-
-class RecipeInterface -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class RecipeInterface(ABC):  # pylint: disable=too-many-public-methods
-    def __init__(self):
-        pass
-
-    @abstractmethod
-    async def create_new_session(
-        self,
-        user_id: str,
-        access_token_payload: Optional[Dict[str, Any]],
-        session_data_in_database: Optional[Dict[str, Any]],
-        disable_anti_csrf: Optional[bool],
-        tenant_id: str,
-        user_context: Dict[str, Any],
-    ) -> SessionContainer:
-        pass
-
-    @abstractmethod
-    def get_global_claim_validators(
-        self,
-        tenant_id: str,
-        user_id: str,
-        claim_validators_added_by_other_recipes: List[SessionClaimValidator],
-        user_context: Dict[str, Any],
-    ) -> MaybeAwaitable[List[SessionClaimValidator]]:
-        pass
-
-    @abstractmethod
-    async def get_session(
-        self,
-        access_token: Optional[str],
-        anti_csrf_token: Optional[str] = None,
-        anti_csrf_check: Optional[bool] = None,
-        session_required: Optional[bool] = None,
-        check_database: Optional[bool] = None,
-        override_global_claim_validators: Optional[
-            Callable[
-                [List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
-                MaybeAwaitable[List[SessionClaimValidator]],
-            ]
-        ] = None,
-        user_context: Optional[Dict[str, Any]] = None,
-    ) -> Optional[SessionContainer]:
-        pass
-
-    @abstractmethod
-    async def validate_claims(
-        self,
-        user_id: str,
-        access_token_payload: Dict[str, Any],
-        claim_validators: List[SessionClaimValidator],
-        user_context: Dict[str, Any],
-    ) -> ClaimsValidationResult:
-        pass
-
-    @abstractmethod
-    async def validate_claims_in_jwt_payload(
-        self,
-        user_id: str,
-        jwt_payload: JSONObject,
-        claim_validators: List[SessionClaimValidator],
-        user_context: Dict[str, Any],
-    ) -> ClaimsValidationResult:
-        pass
-
-    @abstractmethod
-    async def refresh_session(
-        self,
-        refresh_token: str,
-        anti_csrf_token: Optional[str],
-        disable_anti_csrf: bool,
-        user_context: Dict[str, Any],
-    ) -> SessionContainer:
-        pass
-
-    @abstractmethod
-    async def revoke_session(
-        self, session_handle: str, user_context: Dict[str, Any]
-    ) -> bool:
-        pass
-
-    @abstractmethod
-    async def revoke_all_sessions_for_user(
-        self,
-        user_id: str,
-        tenant_id: str,
-        revoke_across_all_tenants: bool,
-        user_context: Dict[str, Any],
-    ) -> List[str]:
-        pass
-
-    @abstractmethod
-    async def get_all_session_handles_for_user(
-        self,
-        user_id: str,
-        tenant_id: str,
-        fetch_across_all_tenants: bool,
-        user_context: Dict[str, Any],
-    ) -> List[str]:
-        pass
-
-    @abstractmethod
-    async def revoke_multiple_sessions(
-        self, session_handles: List[str], user_context: Dict[str, Any]
-    ) -> List[str]:
-        pass
-
-    @abstractmethod
-    async def get_session_information(
-        self, session_handle: str, user_context: Dict[str, Any]
-    ) -> Union[SessionInformationResult, None]:
-        pass
-
-    @abstractmethod
-    async def update_session_data_in_database(
-        self,
-        session_handle: str,
-        new_session_data: Dict[str, Any],
-        user_context: Dict[str, Any],
-    ) -> bool:
-        pass
-
-    @abstractmethod
-    async def merge_into_access_token_payload(
-        self,
-        session_handle: str,
-        access_token_payload_update: JSONObject,
-        user_context: Dict[str, Any],
-    ) -> bool:
-        pass
-
-    @abstractmethod
-    async def fetch_and_set_claim(
-        self,
-        session_handle: str,
-        claim: SessionClaim[Any],
-        user_context: Dict[str, Any],
-    ) -> bool:
-        pass
-
-    @abstractmethod
-    async def set_claim_value(
-        self,
-        session_handle: str,
-        claim: SessionClaim[_T],
-        value: _T,
-        user_context: Dict[str, Any],
-    ) -> bool:
-        pass
-
-    @abstractmethod
-    async def get_claim_value(
-        self,
-        session_handle: str,
-        claim: SessionClaim[Any],
-        user_context: Dict[str, Any],
-    ) -> Union[SessionDoesNotExistError, GetClaimValueOkResult[Any]]:
-        pass
-
-    @abstractmethod
-    async def remove_claim(
-        self,
-        session_handle: str,
-        claim: SessionClaim[Any],
-        user_context: Dict[str, Any],
-    ) -> bool:
-        pass
-
-    @abstractmethod
-    async def regenerate_access_token(
-        self,
-        access_token: str,
-        new_access_token_payload: Union[Dict[str, Any], None],
-        user_context: Dict[str, Any],
-    ) -> Union[RegenerateAccessTokenOkResult, None]:
-        pass
-
-

Ancestors

-
    -
  • abc.ABC
  • -
-

Subclasses

- -

Methods

-
-
-async def create_new_session(self, user_id: str, access_token_payload: Optional[Dict[str, Any]], session_data_in_database: Optional[Dict[str, Any]], disable_anti_csrf: Optional[bool], tenant_id: str, user_context: Dict[str, Any]) ‑> SessionContainer -
-
-
-
- -Expand source code - -
@abstractmethod
-async def create_new_session(
-    self,
-    user_id: str,
-    access_token_payload: Optional[Dict[str, Any]],
-    session_data_in_database: Optional[Dict[str, Any]],
-    disable_anti_csrf: Optional[bool],
-    tenant_id: str,
-    user_context: Dict[str, Any],
-) -> SessionContainer:
-    pass
-
-
-
-async def fetch_and_set_claim(self, session_handle: str, claim: SessionClaim[Any], user_context: Dict[str, Any]) ‑> bool -
-
-
-
- -Expand source code - -
@abstractmethod
-async def fetch_and_set_claim(
-    self,
-    session_handle: str,
-    claim: SessionClaim[Any],
-    user_context: Dict[str, Any],
-) -> bool:
-    pass
-
-
-
-async def get_all_session_handles_for_user(self, user_id: str, tenant_id: str, fetch_across_all_tenants: bool, user_context: Dict[str, Any]) ‑> List[str] -
-
-
-
- -Expand source code - -
@abstractmethod
-async def get_all_session_handles_for_user(
-    self,
-    user_id: str,
-    tenant_id: str,
-    fetch_across_all_tenants: bool,
-    user_context: Dict[str, Any],
-) -> List[str]:
-    pass
-
-
-
-async def get_claim_value(self, session_handle: str, claim: SessionClaim[Any], user_context: Dict[str, Any]) ‑> Union[SessionDoesNotExistErrorGetClaimValueOkResult[Any]] -
-
-
-
- -Expand source code - -
@abstractmethod
-async def get_claim_value(
-    self,
-    session_handle: str,
-    claim: SessionClaim[Any],
-    user_context: Dict[str, Any],
-) -> Union[SessionDoesNotExistError, GetClaimValueOkResult[Any]]:
-    pass
-
-
-
-def get_global_claim_validators(self, tenant_id: str, user_id: str, claim_validators_added_by_other_recipes: List[SessionClaimValidator], user_context: Dict[str, Any]) ‑> Union[Awaitable[List[SessionClaimValidator]], List[SessionClaimValidator]] -
-
-
-
- -Expand source code - -
@abstractmethod
-def get_global_claim_validators(
-    self,
-    tenant_id: str,
-    user_id: str,
-    claim_validators_added_by_other_recipes: List[SessionClaimValidator],
-    user_context: Dict[str, Any],
-) -> MaybeAwaitable[List[SessionClaimValidator]]:
-    pass
-
-
-
-async def get_session(self, access_token: Optional[str], anti_csrf_token: Optional[str] = None, anti_csrf_check: Optional[bool] = None, session_required: Optional[bool] = None, check_database: Optional[bool] = None, override_global_claim_validators: Optional[Callable[[List[SessionClaimValidator], SessionContainer, Dict[str, Any]], MaybeAwaitable[List[SessionClaimValidator]]]] = None, user_context: Optional[Dict[str, Any]] = None) ‑> Optional[SessionContainer] -
-
-
-
- -Expand source code - -
@abstractmethod
-async def get_session(
-    self,
-    access_token: Optional[str],
-    anti_csrf_token: Optional[str] = None,
-    anti_csrf_check: Optional[bool] = None,
-    session_required: Optional[bool] = None,
-    check_database: Optional[bool] = None,
-    override_global_claim_validators: Optional[
-        Callable[
-            [List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
-            MaybeAwaitable[List[SessionClaimValidator]],
-        ]
-    ] = None,
-    user_context: Optional[Dict[str, Any]] = None,
-) -> Optional[SessionContainer]:
-    pass
-
-
-
-async def get_session_information(self, session_handle: str, user_context: Dict[str, Any]) ‑> Optional[SessionInformationResult] -
-
-
-
- -Expand source code - -
@abstractmethod
-async def get_session_information(
-    self, session_handle: str, user_context: Dict[str, Any]
-) -> Union[SessionInformationResult, None]:
-    pass
-
-
-
-async def merge_into_access_token_payload(self, session_handle: str, access_token_payload_update: JSONObject, user_context: Dict[str, Any]) ‑> bool -
-
-
-
- -Expand source code - -
@abstractmethod
-async def merge_into_access_token_payload(
-    self,
-    session_handle: str,
-    access_token_payload_update: JSONObject,
-    user_context: Dict[str, Any],
-) -> bool:
-    pass
-
-
-
-async def refresh_session(self, refresh_token: str, anti_csrf_token: Optional[str], disable_anti_csrf: bool, user_context: Dict[str, Any]) ‑> SessionContainer -
-
-
-
- -Expand source code - -
@abstractmethod
-async def refresh_session(
-    self,
-    refresh_token: str,
-    anti_csrf_token: Optional[str],
-    disable_anti_csrf: bool,
-    user_context: Dict[str, Any],
-) -> SessionContainer:
-    pass
-
-
-
-async def regenerate_access_token(self, access_token: str, new_access_token_payload: Union[Dict[str, Any], None], user_context: Dict[str, Any]) ‑> Optional[RegenerateAccessTokenOkResult] -
-
-
-
- -Expand source code - -
@abstractmethod
-async def regenerate_access_token(
-    self,
-    access_token: str,
-    new_access_token_payload: Union[Dict[str, Any], None],
-    user_context: Dict[str, Any],
-) -> Union[RegenerateAccessTokenOkResult, None]:
-    pass
-
-
-
-async def remove_claim(self, session_handle: str, claim: SessionClaim[Any], user_context: Dict[str, Any]) ‑> bool -
-
-
-
- -Expand source code - -
@abstractmethod
-async def remove_claim(
-    self,
-    session_handle: str,
-    claim: SessionClaim[Any],
-    user_context: Dict[str, Any],
-) -> bool:
-    pass
-
-
-
-async def revoke_all_sessions_for_user(self, user_id: str, tenant_id: str, revoke_across_all_tenants: bool, user_context: Dict[str, Any]) ‑> List[str] -
-
-
-
- -Expand source code - -
@abstractmethod
-async def revoke_all_sessions_for_user(
-    self,
-    user_id: str,
-    tenant_id: str,
-    revoke_across_all_tenants: bool,
-    user_context: Dict[str, Any],
-) -> List[str]:
-    pass
-
-
-
-async def revoke_multiple_sessions(self, session_handles: List[str], user_context: Dict[str, Any]) ‑> List[str] -
-
-
-
- -Expand source code - -
@abstractmethod
-async def revoke_multiple_sessions(
-    self, session_handles: List[str], user_context: Dict[str, Any]
-) -> List[str]:
-    pass
-
-
-
-async def revoke_session(self, session_handle: str, user_context: Dict[str, Any]) ‑> bool -
-
-
-
- -Expand source code - -
@abstractmethod
-async def revoke_session(
-    self, session_handle: str, user_context: Dict[str, Any]
-) -> bool:
-    pass
-
-
-
-async def set_claim_value(self, session_handle: str, claim: SessionClaim[_T], value: _T, user_context: Dict[str, Any]) ‑> bool -
-
-
-
- -Expand source code - -
@abstractmethod
-async def set_claim_value(
-    self,
-    session_handle: str,
-    claim: SessionClaim[_T],
-    value: _T,
-    user_context: Dict[str, Any],
-) -> bool:
-    pass
-
-
-
-async def update_session_data_in_database(self, session_handle: str, new_session_data: Dict[str, Any], user_context: Dict[str, Any]) ‑> bool -
-
-
-
- -Expand source code - -
@abstractmethod
-async def update_session_data_in_database(
-    self,
-    session_handle: str,
-    new_session_data: Dict[str, Any],
-    user_context: Dict[str, Any],
-) -> bool:
-    pass
-
-
-
-async def validate_claims(self, user_id: str, access_token_payload: Dict[str, Any], claim_validators: List[SessionClaimValidator], user_context: Dict[str, Any]) ‑> ClaimsValidationResult -
-
-
-
- -Expand source code - -
@abstractmethod
-async def validate_claims(
-    self,
-    user_id: str,
-    access_token_payload: Dict[str, Any],
-    claim_validators: List[SessionClaimValidator],
-    user_context: Dict[str, Any],
-) -> ClaimsValidationResult:
-    pass
-
-
-
-async def validate_claims_in_jwt_payload(self, user_id: str, jwt_payload: JSONObject, claim_validators: List[SessionClaimValidator], user_context: Dict[str, Any]) ‑> ClaimsValidationResult -
-
-
-
- -Expand source code - -
@abstractmethod
-async def validate_claims_in_jwt_payload(
-    self,
-    user_id: str,
-    jwt_payload: JSONObject,
-    claim_validators: List[SessionClaimValidator],
-    user_context: Dict[str, Any],
-) -> ClaimsValidationResult:
-    pass
-
-
-
-
-
-class RegenerateAccessTokenOkResult -(session: SessionObj, access_token: Union[AccessTokenObj, None]) -
-
-
-
- -Expand source code - -
class RegenerateAccessTokenOkResult:
-    def __init__(self, session: SessionObj, access_token: Union[AccessTokenObj, None]):
-        self.session = session
-        self.access_token = access_token
-
-
-
-class ReqResInfo -(request: BaseRequest, transfer_method: TokenTransferMethod) -
-
-
-
- -Expand source code - -
class ReqResInfo:
-    def __init__(
-        self,
-        request: BaseRequest,
-        transfer_method: TokenTransferMethod,
-    ):
-        self.request = request
-        self.transfer_method = transfer_method
-
-
-
-class SessionClaim -(key: str, fetch_value: Callable[[str, str, Dict[str, Any]], MaybeAwaitable[Optional[_T]]]) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-

Args

-
-
key
-
The key to use when storing the claim in the payload.
-
fetch_value
-
a method that fetches the current value of this claim for the user. -A None return value signifies that we don't want to update the claim payload and or the claim value is -not present in the database. For example, this can happen with a second factor auth claim, where we -don't want to add the claim to the session automatically
-
-
- -Expand source code - -
class SessionClaim(ABC, Generic[_T]):
-    def __init__(
-        self,
-        key: str,
-        fetch_value: Callable[
-            [str, str, Dict[str, Any]],
-            MaybeAwaitable[Optional[_T]],
-        ],
-    ) -> None:
-        """
-        Args:
-            key: The key to use when storing the claim in the payload.
-            fetch_value: a method that fetches the current value of this claim for the user.
-                A None return value signifies that we don't want to update the claim payload and or the claim value is
-                not present in the database. For example, this can happen with a second factor auth claim, where we
-                don't want to add the claim to the session automatically
-        """
-        self.key = key
-        self.fetch_value = fetch_value
-
-    @abstractmethod
-    def add_to_payload_(
-        self,
-        payload: JSONObject,
-        value: _T,
-        user_context: Union[Dict[str, Any], None] = None,
-    ) -> JSONObject:
-        """Saves the provided value into the payload, by cloning and updating the entire object"""
-
-    @abstractmethod
-    def remove_from_payload_by_merge_(
-        self, payload: JSONObject, user_context: Optional[Dict[str, Any]] = None
-    ) -> JSONObject:
-        """Removes the claim from the payload by setting it to None, so merge_into_access_token_payload can clear it"""
-
-    @abstractmethod
-    def remove_from_payload(
-        self, payload: JSONObject, user_context: Optional[Dict[str, Any]] = None
-    ) -> JSONObject:
-        """Removes the claim from the payload, by cloning and updating the entire object."""
-
-    @abstractmethod
-    def get_value_from_payload(
-        self, payload: JSONObject, user_context: Union[Dict[str, Any], None] = None
-    ) -> Union[_T, None]:
-        """Gets the value of the claim stored in the payload"""
-
-    async def build(
-        self,
-        user_id: str,
-        tenant_id: str,
-        user_context: Optional[Dict[str, Any]] = None,
-    ) -> JSONObject:
-        if user_context is None:
-            user_context = {}
-
-        value = await resolve(self.fetch_value(user_id, tenant_id, user_context))
-
-        if value is None:
-            return {}
-
-        return self.add_to_payload_({}, value, user_context)
-
-

Ancestors

-
    -
  • abc.ABC
  • -
  • typing.Generic
  • -
-

Subclasses

- -

Methods

-
-
-def add_to_payload_(self, payload: JSONObject, value: _T, user_context: Union[Dict[str, Any], None] = None) ‑> Dict[str, Any] -
-
-

Saves the provided value into the payload, by cloning and updating the entire object

-
- -Expand source code - -
@abstractmethod
-def add_to_payload_(
-    self,
-    payload: JSONObject,
-    value: _T,
-    user_context: Union[Dict[str, Any], None] = None,
-) -> JSONObject:
-    """Saves the provided value into the payload, by cloning and updating the entire object"""
-
-
-
-async def build(self, user_id: str, tenant_id: str, user_context: Optional[Dict[str, Any]] = None) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
async def build(
-    self,
-    user_id: str,
-    tenant_id: str,
-    user_context: Optional[Dict[str, Any]] = None,
-) -> JSONObject:
-    if user_context is None:
-        user_context = {}
-
-    value = await resolve(self.fetch_value(user_id, tenant_id, user_context))
-
-    if value is None:
-        return {}
-
-    return self.add_to_payload_({}, value, user_context)
-
-
-
-def get_value_from_payload(self, payload: JSONObject, user_context: Union[Dict[str, Any], None] = None) ‑> Optional[~_T] -
-
-

Gets the value of the claim stored in the payload

-
- -Expand source code - -
@abstractmethod
-def get_value_from_payload(
-    self, payload: JSONObject, user_context: Union[Dict[str, Any], None] = None
-) -> Union[_T, None]:
-    """Gets the value of the claim stored in the payload"""
-
-
-
-def remove_from_payload(self, payload: JSONObject, user_context: Optional[Dict[str, Any]] = None) ‑> Dict[str, Any] -
-
-

Removes the claim from the payload, by cloning and updating the entire object.

-
- -Expand source code - -
@abstractmethod
-def remove_from_payload(
-    self, payload: JSONObject, user_context: Optional[Dict[str, Any]] = None
-) -> JSONObject:
-    """Removes the claim from the payload, by cloning and updating the entire object."""
-
-
-
-def remove_from_payload_by_merge_(self, payload: JSONObject, user_context: Optional[Dict[str, Any]] = None) ‑> Dict[str, Any] -
-
-

Removes the claim from the payload by setting it to None, so merge_into_access_token_payload can clear it

-
- -Expand source code - -
@abstractmethod
-def remove_from_payload_by_merge_(
-    self, payload: JSONObject, user_context: Optional[Dict[str, Any]] = None
-) -> JSONObject:
-    """Removes the claim from the payload by setting it to None, so merge_into_access_token_payload can clear it"""
-
-
-
-
-
-class SessionClaimValidator -(id_: str) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class SessionClaimValidator(ABC):
-    def __init__(
-        self,
-        id_: str,
-    ) -> None:
-        self.id = id_
-        self.claim: Optional[SessionClaim[Any]] = None
-
-    @abstractmethod
-    async def validate(
-        self, payload: JSONObject, user_context: Dict[str, Any]
-    ) -> ClaimValidationResult:
-        pass
-
-    def should_refetch(
-        self, payload: JSONObject, user_context: Dict[str, Any]
-    ) -> MaybeAwaitable[bool]:
-        raise NotImplementedError()
-
-

Ancestors

-
    -
  • abc.ABC
  • -
-

Subclasses

- -

Methods

-
-
-def should_refetch(self, payload: JSONObject, user_context: Dict[str, Any]) ‑> Union[Awaitable[bool], bool] -
-
-
-
- -Expand source code - -
def should_refetch(
-    self, payload: JSONObject, user_context: Dict[str, Any]
-) -> MaybeAwaitable[bool]:
-    raise NotImplementedError()
-
-
-
-async def validate(self, payload: JSONObject, user_context: Dict[str, Any]) ‑> ClaimValidationResult -
-
-
-
- -Expand source code - -
@abstractmethod
-async def validate(
-    self, payload: JSONObject, user_context: Dict[str, Any]
-) -> ClaimValidationResult:
-    pass
-
-
-
-
-
-class SessionContainer -(recipe_implementation: RecipeInterface, config: SessionConfig, access_token: str, front_token: str, refresh_token: Optional[TokenInfo], anti_csrf_token: Optional[str], session_handle: str, user_id: str, user_data_in_access_token: Optional[Dict[str, Any]], req_res_info: Optional[ReqResInfo], access_token_updated: bool, tenant_id: str) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class SessionContainer(ABC):  # pylint: disable=too-many-public-methods
-    def __init__(
-        self,
-        recipe_implementation: RecipeInterface,
-        config: SessionConfig,
-        access_token: str,
-        front_token: str,
-        refresh_token: Optional[TokenInfo],
-        anti_csrf_token: Optional[str],
-        session_handle: str,
-        user_id: str,
-        user_data_in_access_token: Optional[Dict[str, Any]],
-        req_res_info: Optional[ReqResInfo],
-        access_token_updated: bool,
-        tenant_id: str,
-    ):
-        self.recipe_implementation = recipe_implementation
-        self.config = config
-        self.access_token = access_token
-        self.front_token = front_token
-        self.refresh_token = refresh_token
-        self.anti_csrf_token = anti_csrf_token
-        self.session_handle = session_handle
-        self.user_id = user_id
-        self.user_data_in_access_token = user_data_in_access_token
-        self.req_res_info: Optional[ReqResInfo] = req_res_info
-        self.access_token_updated = access_token_updated
-        self.tenant_id = tenant_id
-
-        self.response_mutators: List[ResponseMutator] = []
-
-    @abstractmethod
-    async def revoke_session(
-        self, user_context: Optional[Dict[str, Any]] = None
-    ) -> None:
-        pass
-
-    @abstractmethod
-    async def get_session_data_from_database(
-        self, user_context: Optional[Dict[str, Any]] = None
-    ) -> Dict[str, Any]:
-        pass
-
-    @abstractmethod
-    async def update_session_data_in_database(
-        self,
-        new_session_data: Dict[str, Any],
-        user_context: Optional[Dict[str, Any]] = None,
-    ) -> None:
-        pass
-
-    @abstractmethod
-    async def attach_to_request_response(
-        self,
-        request: BaseRequest,
-        transfer_method: TokenTransferMethod,
-        user_context: Dict[str, Any],
-    ):
-        pass
-
-    @abstractmethod
-    async def merge_into_access_token_payload(
-        self,
-        access_token_payload_update: JSONObject,
-        user_context: Optional[Dict[str, Any]] = None,
-    ) -> None:
-        pass
-
-    @abstractmethod
-    def get_user_id(self, user_context: Optional[Dict[str, Any]] = None) -> str:
-        pass
-
-    @abstractmethod
-    def get_tenant_id(self, user_context: Optional[Dict[str, Any]] = None) -> str:
-        pass
-
-    @abstractmethod
-    def get_access_token_payload(
-        self, user_context: Optional[Dict[str, Any]] = None
-    ) -> Dict[str, Any]:
-        pass
-
-    @abstractmethod
-    def get_handle(self, user_context: Optional[Dict[str, Any]] = None) -> str:
-        pass
-
-    @abstractmethod
-    def get_all_session_tokens_dangerously(self) -> GetSessionTokensDangerouslyDict:
-        pass
-
-    @abstractmethod
-    def get_access_token(self, user_context: Optional[Dict[str, Any]] = None) -> str:
-        pass
-
-    @abstractmethod
-    async def get_time_created(
-        self, user_context: Optional[Dict[str, Any]] = None
-    ) -> int:
-        pass
-
-    @abstractmethod
-    async def get_expiry(self, user_context: Optional[Dict[str, Any]] = None) -> int:
-        pass
-
-    @abstractmethod
-    async def assert_claims(
-        self,
-        claim_validators: List[SessionClaimValidator],
-        user_context: Optional[Dict[str, Any]] = None,
-    ) -> None:
-        pass
-
-    @abstractmethod
-    async def fetch_and_set_claim(
-        self, claim: SessionClaim[Any], user_context: Optional[Dict[str, Any]] = None
-    ) -> None:
-        pass
-
-    @abstractmethod
-    async def set_claim_value(
-        self,
-        claim: SessionClaim[_T],
-        value: _T,
-        user_context: Optional[Dict[str, Any]] = None,
-    ) -> None:
-        pass
-
-    @abstractmethod
-    async def get_claim_value(
-        self, claim: SessionClaim[_T], user_context: Optional[Dict[str, Any]] = None
-    ) -> Union[_T, None]:
-        pass
-
-    @abstractmethod
-    async def remove_claim(
-        self,
-        claim: SessionClaim[Any],
-        user_context: Optional[Dict[str, Any]] = None,
-    ) -> None:
-        pass
-
-    def sync_get_expiry(self, user_context: Optional[Dict[str, Any]] = None) -> int:
-        return sync(self.get_expiry(user_context))
-
-    def sync_revoke_session(
-        self, user_context: Optional[Dict[str, Any]] = None
-    ) -> None:
-        return sync(self.revoke_session(user_context=user_context))
-
-    def sync_get_session_data_from_database(
-        self, user_context: Union[Dict[str, Any], None] = None
-    ) -> Dict[str, Any]:
-        return sync(self.get_session_data_from_database(user_context))
-
-    def sync_get_time_created(
-        self, user_context: Optional[Dict[str, Any]] = None
-    ) -> int:
-        return sync(self.get_time_created(user_context))
-
-    def sync_merge_into_access_token_payload(
-        self,
-        access_token_payload_update: Dict[str, Any],
-        user_context: Optional[Dict[str, Any]] = None,
-    ) -> None:
-        return sync(
-            self.merge_into_access_token_payload(
-                access_token_payload_update, user_context
-            )
-        )
-
-    def sync_update_session_data_in_database(
-        self,
-        new_session_data: Dict[str, Any],
-        user_context: Optional[Dict[str, Any]] = None,
-    ) -> None:
-        return sync(
-            self.update_session_data_in_database(new_session_data, user_context)
-        )
-
-    # Session claims sync functions:
-    def sync_assert_claims(
-        self,
-        claim_validators: List[SessionClaimValidator],
-        user_context: Optional[Dict[str, Any]] = None,
-    ) -> None:
-        return sync(self.assert_claims(claim_validators, user_context))
-
-    def sync_fetch_and_set_claim(
-        self, claim: SessionClaim[Any], user_context: Optional[Dict[str, Any]] = None
-    ) -> None:
-        return sync(self.fetch_and_set_claim(claim, user_context))
-
-    def sync_set_claim_value(
-        self,
-        claim: SessionClaim[_T],
-        value: _T,
-        user_context: Optional[Dict[str, Any]] = None,
-    ) -> None:
-        return sync(self.set_claim_value(claim, value, user_context))
-
-    def sync_get_claim_value(
-        self, claim: SessionClaim[_T], user_context: Optional[Dict[str, Any]] = None
-    ) -> Union[_T, None]:
-        return sync(self.get_claim_value(claim, user_context))
-
-    def sync_remove_claim(
-        self, claim: SessionClaim[Any], user_context: Optional[Dict[str, Any]] = None
-    ) -> None:
-        return sync(self.remove_claim(claim, user_context))
-
-    def sync_attach_to_request_response(
-        self,
-        request: BaseRequest,
-        token_transfer: TokenTransferMethod,
-        user_context: Dict[str, Any],
-    ) -> None:
-        return sync(
-            self.attach_to_request_response(request, token_transfer, user_context)
-        )
-
-    # This is there so that we can do session["..."] to access some of the members of this class
-    def __getitem__(self, item: str):
-        return getattr(self, item)
-
-

Ancestors

-
    -
  • abc.ABC
  • -
-

Subclasses

- -

Methods

-
-
-async def assert_claims(self, claim_validators: List[SessionClaimValidator], user_context: Optional[Dict[str, Any]] = None) ‑> None -
-
-
-
- -Expand source code - -
@abstractmethod
-async def assert_claims(
-    self,
-    claim_validators: List[SessionClaimValidator],
-    user_context: Optional[Dict[str, Any]] = None,
-) -> None:
-    pass
-
-
-
-async def attach_to_request_response(self, request: BaseRequest, transfer_method: TokenTransferMethod, user_context: Dict[str, Any]) -
-
-
-
- -Expand source code - -
@abstractmethod
-async def attach_to_request_response(
-    self,
-    request: BaseRequest,
-    transfer_method: TokenTransferMethod,
-    user_context: Dict[str, Any],
-):
-    pass
-
-
-
-async def fetch_and_set_claim(self, claim: SessionClaim[Any], user_context: Optional[Dict[str, Any]] = None) ‑> None -
-
-
-
- -Expand source code - -
@abstractmethod
-async def fetch_and_set_claim(
-    self, claim: SessionClaim[Any], user_context: Optional[Dict[str, Any]] = None
-) -> None:
-    pass
-
-
-
-def get_access_token(self, user_context: Optional[Dict[str, Any]] = None) ‑> str -
-
-
-
- -Expand source code - -
@abstractmethod
-def get_access_token(self, user_context: Optional[Dict[str, Any]] = None) -> str:
-    pass
-
-
-
-def get_access_token_payload(self, user_context: Optional[Dict[str, Any]] = None) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
@abstractmethod
-def get_access_token_payload(
-    self, user_context: Optional[Dict[str, Any]] = None
-) -> Dict[str, Any]:
-    pass
-
-
-
-def get_all_session_tokens_dangerously(self) ‑> GetSessionTokensDangerouslyDict -
-
-
-
- -Expand source code - -
@abstractmethod
-def get_all_session_tokens_dangerously(self) -> GetSessionTokensDangerouslyDict:
-    pass
-
-
-
-async def get_claim_value(self, claim: SessionClaim[_T], user_context: Optional[Dict[str, Any]] = None) ‑> Optional[~_T] -
-
-
-
- -Expand source code - -
@abstractmethod
-async def get_claim_value(
-    self, claim: SessionClaim[_T], user_context: Optional[Dict[str, Any]] = None
-) -> Union[_T, None]:
-    pass
-
-
-
-async def get_expiry(self, user_context: Optional[Dict[str, Any]] = None) ‑> int -
-
-
-
- -Expand source code - -
@abstractmethod
-async def get_expiry(self, user_context: Optional[Dict[str, Any]] = None) -> int:
-    pass
-
-
-
-def get_handle(self, user_context: Optional[Dict[str, Any]] = None) ‑> str -
-
-
-
- -Expand source code - -
@abstractmethod
-def get_handle(self, user_context: Optional[Dict[str, Any]] = None) -> str:
-    pass
-
-
-
-async def get_session_data_from_database(self, user_context: Optional[Dict[str, Any]] = None) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
@abstractmethod
-async def get_session_data_from_database(
-    self, user_context: Optional[Dict[str, Any]] = None
-) -> Dict[str, Any]:
-    pass
-
-
-
-def get_tenant_id(self, user_context: Optional[Dict[str, Any]] = None) ‑> str -
-
-
-
- -Expand source code - -
@abstractmethod
-def get_tenant_id(self, user_context: Optional[Dict[str, Any]] = None) -> str:
-    pass
-
-
-
-async def get_time_created(self, user_context: Optional[Dict[str, Any]] = None) ‑> int -
-
-
-
- -Expand source code - -
@abstractmethod
-async def get_time_created(
-    self, user_context: Optional[Dict[str, Any]] = None
-) -> int:
-    pass
-
-
-
-def get_user_id(self, user_context: Optional[Dict[str, Any]] = None) ‑> str -
-
-
-
- -Expand source code - -
@abstractmethod
-def get_user_id(self, user_context: Optional[Dict[str, Any]] = None) -> str:
-    pass
-
-
-
-async def merge_into_access_token_payload(self, access_token_payload_update: JSONObject, user_context: Optional[Dict[str, Any]] = None) ‑> None -
-
-
-
- -Expand source code - -
@abstractmethod
-async def merge_into_access_token_payload(
-    self,
-    access_token_payload_update: JSONObject,
-    user_context: Optional[Dict[str, Any]] = None,
-) -> None:
-    pass
-
-
-
-async def remove_claim(self, claim: SessionClaim[Any], user_context: Optional[Dict[str, Any]] = None) ‑> None -
-
-
-
- -Expand source code - -
@abstractmethod
-async def remove_claim(
-    self,
-    claim: SessionClaim[Any],
-    user_context: Optional[Dict[str, Any]] = None,
-) -> None:
-    pass
-
-
-
-async def revoke_session(self, user_context: Optional[Dict[str, Any]] = None) ‑> None -
-
-
-
- -Expand source code - -
@abstractmethod
-async def revoke_session(
-    self, user_context: Optional[Dict[str, Any]] = None
-) -> None:
-    pass
-
-
-
-async def set_claim_value(self, claim: SessionClaim[_T], value: _T, user_context: Optional[Dict[str, Any]] = None) ‑> None -
-
-
-
- -Expand source code - -
@abstractmethod
-async def set_claim_value(
-    self,
-    claim: SessionClaim[_T],
-    value: _T,
-    user_context: Optional[Dict[str, Any]] = None,
-) -> None:
-    pass
-
-
-
-def sync_assert_claims(self, claim_validators: List[SessionClaimValidator], user_context: Optional[Dict[str, Any]] = None) ‑> None -
-
-
-
- -Expand source code - -
def sync_assert_claims(
-    self,
-    claim_validators: List[SessionClaimValidator],
-    user_context: Optional[Dict[str, Any]] = None,
-) -> None:
-    return sync(self.assert_claims(claim_validators, user_context))
-
-
-
-def sync_attach_to_request_response(self, request: BaseRequest, token_transfer: TokenTransferMethod, user_context: Dict[str, Any]) ‑> None -
-
-
-
- -Expand source code - -
def sync_attach_to_request_response(
-    self,
-    request: BaseRequest,
-    token_transfer: TokenTransferMethod,
-    user_context: Dict[str, Any],
-) -> None:
-    return sync(
-        self.attach_to_request_response(request, token_transfer, user_context)
-    )
-
-
-
-def sync_fetch_and_set_claim(self, claim: SessionClaim[Any], user_context: Optional[Dict[str, Any]] = None) ‑> None -
-
-
-
- -Expand source code - -
def sync_fetch_and_set_claim(
-    self, claim: SessionClaim[Any], user_context: Optional[Dict[str, Any]] = None
-) -> None:
-    return sync(self.fetch_and_set_claim(claim, user_context))
-
-
-
-def sync_get_claim_value(self, claim: SessionClaim[_T], user_context: Optional[Dict[str, Any]] = None) ‑> Optional[~_T] -
-
-
-
- -Expand source code - -
def sync_get_claim_value(
-    self, claim: SessionClaim[_T], user_context: Optional[Dict[str, Any]] = None
-) -> Union[_T, None]:
-    return sync(self.get_claim_value(claim, user_context))
-
-
-
-def sync_get_expiry(self, user_context: Optional[Dict[str, Any]] = None) ‑> int -
-
-
-
- -Expand source code - -
def sync_get_expiry(self, user_context: Optional[Dict[str, Any]] = None) -> int:
-    return sync(self.get_expiry(user_context))
-
-
-
-def sync_get_session_data_from_database(self, user_context: Union[Dict[str, Any], None] = None) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
def sync_get_session_data_from_database(
-    self, user_context: Union[Dict[str, Any], None] = None
-) -> Dict[str, Any]:
-    return sync(self.get_session_data_from_database(user_context))
-
-
-
-def sync_get_time_created(self, user_context: Optional[Dict[str, Any]] = None) ‑> int -
-
-
-
- -Expand source code - -
def sync_get_time_created(
-    self, user_context: Optional[Dict[str, Any]] = None
-) -> int:
-    return sync(self.get_time_created(user_context))
-
-
-
-def sync_merge_into_access_token_payload(self, access_token_payload_update: Dict[str, Any], user_context: Optional[Dict[str, Any]] = None) ‑> None -
-
-
-
- -Expand source code - -
def sync_merge_into_access_token_payload(
-    self,
-    access_token_payload_update: Dict[str, Any],
-    user_context: Optional[Dict[str, Any]] = None,
-) -> None:
-    return sync(
-        self.merge_into_access_token_payload(
-            access_token_payload_update, user_context
-        )
-    )
-
-
-
-def sync_remove_claim(self, claim: SessionClaim[Any], user_context: Optional[Dict[str, Any]] = None) ‑> None -
-
-
-
- -Expand source code - -
def sync_remove_claim(
-    self, claim: SessionClaim[Any], user_context: Optional[Dict[str, Any]] = None
-) -> None:
-    return sync(self.remove_claim(claim, user_context))
-
-
-
-def sync_revoke_session(self, user_context: Optional[Dict[str, Any]] = None) ‑> None -
-
-
-
- -Expand source code - -
def sync_revoke_session(
-    self, user_context: Optional[Dict[str, Any]] = None
-) -> None:
-    return sync(self.revoke_session(user_context=user_context))
-
-
-
-def sync_set_claim_value(self, claim: SessionClaim[_T], value: _T, user_context: Optional[Dict[str, Any]] = None) ‑> None -
-
-
-
- -Expand source code - -
def sync_set_claim_value(
-    self,
-    claim: SessionClaim[_T],
-    value: _T,
-    user_context: Optional[Dict[str, Any]] = None,
-) -> None:
-    return sync(self.set_claim_value(claim, value, user_context))
-
-
-
-def sync_update_session_data_in_database(self, new_session_data: Dict[str, Any], user_context: Optional[Dict[str, Any]] = None) ‑> None -
-
-
-
- -Expand source code - -
def sync_update_session_data_in_database(
-    self,
-    new_session_data: Dict[str, Any],
-    user_context: Optional[Dict[str, Any]] = None,
-) -> None:
-    return sync(
-        self.update_session_data_in_database(new_session_data, user_context)
-    )
-
-
-
-async def update_session_data_in_database(self, new_session_data: Dict[str, Any], user_context: Optional[Dict[str, Any]] = None) ‑> None -
-
-
-
- -Expand source code - -
@abstractmethod
-async def update_session_data_in_database(
-    self,
-    new_session_data: Dict[str, Any],
-    user_context: Optional[Dict[str, Any]] = None,
-) -> None:
-    pass
-
-
-
-
-
-class SessionDoesNotExistError -
-
-
-
- -Expand source code - -
class SessionDoesNotExistError:
-    pass
-
-
-
-class SessionInformationResult -(session_handle: str, user_id: str, session_data_in_database: Dict[str, Any], expiry: int, custom_claims_in_access_token_payload: Dict[str, Any], time_created: int, tenant_id: str) -
-
-
-
- -Expand source code - -
class SessionInformationResult:
-    def __init__(
-        self,
-        session_handle: str,
-        user_id: str,
-        session_data_in_database: Dict[str, Any],
-        expiry: int,
-        custom_claims_in_access_token_payload: Dict[str, Any],
-        time_created: int,
-        tenant_id: str,
-    ):
-        self.session_handle = session_handle
-        self.user_id = user_id
-        self.session_data_in_database = session_data_in_database
-        self.expiry = expiry
-        self.custom_claims_in_access_token_payload = (
-            custom_claims_in_access_token_payload
-        )
-        self.time_created = time_created
-        self.tenant_id = tenant_id
-
-
-
-class SessionObj -(handle: str, user_id: str, user_data_in_jwt: Dict[str, Any], tenant_id: str) -
-
-
-
- -Expand source code - -
class SessionObj:
-    def __init__(
-        self,
-        handle: str,
-        user_id: str,
-        user_data_in_jwt: Dict[str, Any],
-        tenant_id: str,
-    ):
-        self.handle = handle
-        self.user_id = user_id
-        self.user_data_in_jwt = user_data_in_jwt
-        self.tenant_id = tenant_id
-
-
-
-class SignOutOkayResponse -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class SignOutOkayResponse(APIResponse):
-    def __init__(self):
-        self.status = "OK"
-
-    def to_json(self) -> Dict[str, Any]:
-        return {"status": self.status}
-
-

Ancestors

- -

Methods

-
-
-def to_json(self) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
def to_json(self) -> Dict[str, Any]:
-    return {"status": self.status}
-
-
-
-
-
-class TokenInfo -(token: str, expiry: int, created_time: int) -
-
-
-
- -Expand source code - -
class TokenInfo:
-    def __init__(self, token: str, expiry: int, created_time: int):
-        self.token = token
-        self.expiry = expiry
-        self.created_time = created_time
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/session/jwks.html b/html/supertokens_python/recipe/session/jwks.html deleted file mode 100644 index 32c9d3f5d..000000000 --- a/html/supertokens_python/recipe/session/jwks.html +++ /dev/null @@ -1,442 +0,0 @@ - - - - - - -supertokens_python.recipe.session.jwks API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.session.jwks

-
-
-
- -Expand source code - -
# Copyright (c) 2023, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-import requests
-from os import environ
-from typing import List, Optional
-from typing_extensions import TypedDict
-
-from jwt import PyJWK, PyJWKSet
-
-from supertokens_python.recipe.session.utils import SessionConfig
-from supertokens_python.utils import RWMutex, RWLockContext, get_timestamp_ms
-from supertokens_python.querier import Querier
-from supertokens_python.logger import log_debug_message
-
-
-class JWKSConfigType(TypedDict):
-    request_timeout: int
-
-
-JWKSConfig: JWKSConfigType = {
-    "request_timeout": 10000,  # 10s
-}
-
-
-class CachedKeys:
-    def __init__(self, keys: List[PyJWK], refresh_interval_sec: int):
-        self.keys = keys
-        self.last_refresh_time = get_timestamp_ms()
-        self.refresh_interval_sec = refresh_interval_sec
-
-    def is_fresh(self):
-        return (
-            get_timestamp_ms() - self.last_refresh_time
-            < self.refresh_interval_sec * 1000
-        )
-
-
-cached_keys: Optional[CachedKeys] = None
-mutex = RWMutex()
-
-# only for testing purposes
-def reset_jwks_cache():
-    with RWLockContext(mutex, read=False):
-        global cached_keys
-        cached_keys = None
-
-
-def get_cached_keys() -> Optional[List[PyJWK]]:
-    if cached_keys is not None:
-        # This means that we have valid JWKs for the given core path
-        # We check if we need to refresh before returning
-
-        # This means that the value in cache is not expired, in this case we return the cached value
-        # Note that this also means that the SDK will not try to query any other core (if there are multiple)
-        # if it has a valid cache entry from one of the core URLs. It will only attempt to fetch
-        # from the cores again after the entry in the cache is expired
-        if cached_keys.is_fresh():
-            return cached_keys.keys
-
-    return None
-
-
-def find_matching_keys(
-    keys: Optional[List[PyJWK]], kid: Optional[str]
-) -> Optional[List[PyJWK]]:
-    if kid is None or keys is None:
-        # return all keys since the token does not have a kid
-        return keys
-
-    # kid has been provided so filter the keys
-    matching_keys = [key for key in keys if key.key_id == kid]  # type: ignore
-    if len(matching_keys) > 0:
-        return matching_keys
-
-    return None
-
-
-def get_latest_keys(config: SessionConfig, kid: Optional[str] = None) -> List[PyJWK]:
-    global cached_keys
-
-    if environ.get("SUPERTOKENS_ENV") == "testing":
-        log_debug_message("Called find_jwk_client")
-
-    with RWLockContext(mutex, read=True):
-        matching_keys = find_matching_keys(get_cached_keys(), kid)
-        if matching_keys is not None:
-            if environ.get("SUPERTOKENS_ENV") == "testing":
-                log_debug_message("Returning JWKS from cache")
-            return matching_keys
-        # otherwise unknown kid, will continue to reload the keys
-
-    core_paths = Querier.get_instance().get_all_core_urls_for_path(
-        "./.well-known/jwks.json"
-    )
-
-    if len(core_paths) == 0:
-        raise Exception(
-            "No SuperTokens core available to query. Please pass supertokens > connection_uri to the init function, or override all the functions of the recipe you are using."
-        )
-
-    last_error: Exception = Exception("No valid JWKS found")
-
-    with RWLockContext(mutex, read=False):
-        # check again if the keys are in cache
-        # because another thread might have fetched the keys while this one was waiting for the lock
-        matching_keys = find_matching_keys(get_cached_keys(), kid)
-        if matching_keys is not None:
-            return matching_keys
-
-        for path in core_paths:
-            if environ.get("SUPERTOKENS_ENV") == "testing":
-                log_debug_message("Attempting to fetch JWKS from path: %s", path)
-
-            cached_jwks: Optional[List[PyJWK]] = None
-            try:
-                log_debug_message("Fetching jwk set from the configured uri")
-                with requests.get(
-                    path, timeout=JWKSConfig["request_timeout"] / 1000
-                ) as response:  # 5 second timeout
-                    response.raise_for_status()
-                    cached_jwks = PyJWKSet.from_dict(response.json()).keys  # type: ignore
-            except Exception as e:
-                last_error = e
-
-            if cached_jwks is not None:  # we found a valid JWKS
-                cached_keys = CachedKeys(cached_jwks, config.jwks_refresh_interval_sec)
-                log_debug_message("Returning JWKS from fetch")
-                matching_keys = find_matching_keys(get_cached_keys(), kid)
-                if matching_keys is not None:
-                    return matching_keys
-
-                raise Exception("No matching JWKS found")
-
-    raise last_error
-
-
-
-
-
-
-
-

Functions

-
-
-def find_matching_keys(keys: Optional[List[jwt.api_jwk.PyJWK]], kid: Optional[str]) ‑> Optional[List[jwt.api_jwk.PyJWK]] -
-
-
-
- -Expand source code - -
def find_matching_keys(
-    keys: Optional[List[PyJWK]], kid: Optional[str]
-) -> Optional[List[PyJWK]]:
-    if kid is None or keys is None:
-        # return all keys since the token does not have a kid
-        return keys
-
-    # kid has been provided so filter the keys
-    matching_keys = [key for key in keys if key.key_id == kid]  # type: ignore
-    if len(matching_keys) > 0:
-        return matching_keys
-
-    return None
-
-
-
-def get_cached_keys() ‑> Optional[List[jwt.api_jwk.PyJWK]] -
-
-
-
- -Expand source code - -
def get_cached_keys() -> Optional[List[PyJWK]]:
-    if cached_keys is not None:
-        # This means that we have valid JWKs for the given core path
-        # We check if we need to refresh before returning
-
-        # This means that the value in cache is not expired, in this case we return the cached value
-        # Note that this also means that the SDK will not try to query any other core (if there are multiple)
-        # if it has a valid cache entry from one of the core URLs. It will only attempt to fetch
-        # from the cores again after the entry in the cache is expired
-        if cached_keys.is_fresh():
-            return cached_keys.keys
-
-    return None
-
-
-
-def get_latest_keys(config: SessionConfig, kid: Optional[str] = None) ‑> List[jwt.api_jwk.PyJWK] -
-
-
-
- -Expand source code - -
def get_latest_keys(config: SessionConfig, kid: Optional[str] = None) -> List[PyJWK]:
-    global cached_keys
-
-    if environ.get("SUPERTOKENS_ENV") == "testing":
-        log_debug_message("Called find_jwk_client")
-
-    with RWLockContext(mutex, read=True):
-        matching_keys = find_matching_keys(get_cached_keys(), kid)
-        if matching_keys is not None:
-            if environ.get("SUPERTOKENS_ENV") == "testing":
-                log_debug_message("Returning JWKS from cache")
-            return matching_keys
-        # otherwise unknown kid, will continue to reload the keys
-
-    core_paths = Querier.get_instance().get_all_core_urls_for_path(
-        "./.well-known/jwks.json"
-    )
-
-    if len(core_paths) == 0:
-        raise Exception(
-            "No SuperTokens core available to query. Please pass supertokens > connection_uri to the init function, or override all the functions of the recipe you are using."
-        )
-
-    last_error: Exception = Exception("No valid JWKS found")
-
-    with RWLockContext(mutex, read=False):
-        # check again if the keys are in cache
-        # because another thread might have fetched the keys while this one was waiting for the lock
-        matching_keys = find_matching_keys(get_cached_keys(), kid)
-        if matching_keys is not None:
-            return matching_keys
-
-        for path in core_paths:
-            if environ.get("SUPERTOKENS_ENV") == "testing":
-                log_debug_message("Attempting to fetch JWKS from path: %s", path)
-
-            cached_jwks: Optional[List[PyJWK]] = None
-            try:
-                log_debug_message("Fetching jwk set from the configured uri")
-                with requests.get(
-                    path, timeout=JWKSConfig["request_timeout"] / 1000
-                ) as response:  # 5 second timeout
-                    response.raise_for_status()
-                    cached_jwks = PyJWKSet.from_dict(response.json()).keys  # type: ignore
-            except Exception as e:
-                last_error = e
-
-            if cached_jwks is not None:  # we found a valid JWKS
-                cached_keys = CachedKeys(cached_jwks, config.jwks_refresh_interval_sec)
-                log_debug_message("Returning JWKS from fetch")
-                matching_keys = find_matching_keys(get_cached_keys(), kid)
-                if matching_keys is not None:
-                    return matching_keys
-
-                raise Exception("No matching JWKS found")
-
-    raise last_error
-
-
-
-def reset_jwks_cache() -
-
-
-
- -Expand source code - -
def reset_jwks_cache():
-    with RWLockContext(mutex, read=False):
-        global cached_keys
-        cached_keys = None
-
-
-
-
-
-

Classes

-
-
-class CachedKeys -(keys: List[jwt.api_jwk.PyJWK], refresh_interval_sec: int) -
-
-
-
- -Expand source code - -
class CachedKeys:
-    def __init__(self, keys: List[PyJWK], refresh_interval_sec: int):
-        self.keys = keys
-        self.last_refresh_time = get_timestamp_ms()
-        self.refresh_interval_sec = refresh_interval_sec
-
-    def is_fresh(self):
-        return (
-            get_timestamp_ms() - self.last_refresh_time
-            < self.refresh_interval_sec * 1000
-        )
-
-

Methods

-
-
-def is_fresh(self) -
-
-
-
- -Expand source code - -
def is_fresh(self):
-    return (
-        get_timestamp_ms() - self.last_refresh_time
-        < self.refresh_interval_sec * 1000
-    )
-
-
-
-
-
-class JWKSConfigType -(*args, **kwargs) -
-
-

dict() -> new empty dictionary -dict(mapping) -> new dictionary initialized from a mapping object's -(key, value) pairs -dict(iterable) -> new dictionary initialized as if via: -d = {} -for k, v in iterable: -d[k] = v -dict(**kwargs) -> new dictionary initialized with the name=value pairs -in the keyword argument list. -For example: -dict(one=1, two=2)

-
- -Expand source code - -
class JWKSConfigType(TypedDict):
-    request_timeout: int
-
-

Ancestors

-
    -
  • builtins.dict
  • -
-

Class variables

-
-
var request_timeout : int
-
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/session/jwt.html b/html/supertokens_python/recipe/session/jwt.html deleted file mode 100644 index 20620dfda..000000000 --- a/html/supertokens_python/recipe/session/jwt.html +++ /dev/null @@ -1,265 +0,0 @@ - - - - - - -supertokens_python.recipe.session.jwt API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.session.jwt

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from json import dumps, loads
-from typing import Any, Dict, Optional
-
-from supertokens_python.utils import utf_base64decode, utf_base64encode
-
-# why separators is used in dumps:
-# - without it's use, output of dumps is: '{"alg": "RS256", "typ": "JWT", "version": "1"}'
-# - with it's use, output of dumps is: '{"alg":"RS256","typ":"JWT","version":"1"}'
-# we require the non-spaced version, else the base64 encoding string will end up different than required
-_allowed_headers = [
-    utf_base64encode(
-        dumps(
-            {"alg": "RS256", "typ": "JWT", "version": "2"},
-            separators=(",", ":"),
-            sort_keys=True,
-        ),
-        urlsafe=False,
-    )
-]
-
-
-class ParsedJWTInfo:
-    def __init__(
-        self,
-        version: int,
-        raw_token_string: str,
-        raw_payload: str,
-        header: str,
-        payload: Dict[str, Any],
-        signature: str,
-        kid: Optional[str],
-        parsed_header: Optional[Dict[str, Any]] = None,
-    ) -> None:
-        self.version = version
-        self.raw_token_string = raw_token_string
-        self.raw_payload = raw_payload
-        self.header = header
-        self.payload = payload
-        self.signature = signature
-        self.kid = kid
-        self.parsed_header = parsed_header
-
-
-def parse_jwt_without_signature_verification(jwt: str) -> ParsedJWTInfo:
-    splitted_input = jwt.split(".")
-    LATEST_TOKEN_VERSION = 3
-    if len(splitted_input) != 3:
-        raise Exception("invalid jwt")
-
-    # V1 and V2 are functionally identical, plus all legacy tokens should be V2 now.
-    # So we can assume these defaults:
-    version = 2
-    kid = None
-    parsed_header = None
-    # V2 or older tokens didn't save the key id
-    header, payload, signature = splitted_input
-    # checking the header
-    if header not in _allowed_headers:
-        parsed_header = loads(utf_base64decode(header, True))
-        header_version = parsed_header.get("version", str(LATEST_TOKEN_VERSION))
-
-        try:
-            version = int(header_version)
-        except ValueError:
-            version = None
-
-        kid = parsed_header.get("kid")
-        # isinstance(version, int) returns False for None (if it fails to parse the version)
-        if (
-            parsed_header["typ"] != "JWT"
-            or not isinstance(version, int)
-            or version < 3
-            or kid is None
-        ):
-            raise Exception("JWT header mismatch")
-
-    return ParsedJWTInfo(
-        version=version,
-        raw_token_string=jwt,
-        raw_payload=payload,
-        header=header,
-        # Ideally we would only parse this after the signature verification is done
-        # We do this at the start, since we want to check if a token can be a supertokens access token or not.
-        payload=loads(utf_base64decode(payload, True)),
-        signature=signature,
-        kid=kid,
-        parsed_header=parsed_header,
-    )
-
-
-
-
-
-
-
-

Functions

-
-
-def parse_jwt_without_signature_verification(jwt: str) ‑> ParsedJWTInfo -
-
-
-
- -Expand source code - -
def parse_jwt_without_signature_verification(jwt: str) -> ParsedJWTInfo:
-    splitted_input = jwt.split(".")
-    LATEST_TOKEN_VERSION = 3
-    if len(splitted_input) != 3:
-        raise Exception("invalid jwt")
-
-    # V1 and V2 are functionally identical, plus all legacy tokens should be V2 now.
-    # So we can assume these defaults:
-    version = 2
-    kid = None
-    parsed_header = None
-    # V2 or older tokens didn't save the key id
-    header, payload, signature = splitted_input
-    # checking the header
-    if header not in _allowed_headers:
-        parsed_header = loads(utf_base64decode(header, True))
-        header_version = parsed_header.get("version", str(LATEST_TOKEN_VERSION))
-
-        try:
-            version = int(header_version)
-        except ValueError:
-            version = None
-
-        kid = parsed_header.get("kid")
-        # isinstance(version, int) returns False for None (if it fails to parse the version)
-        if (
-            parsed_header["typ"] != "JWT"
-            or not isinstance(version, int)
-            or version < 3
-            or kid is None
-        ):
-            raise Exception("JWT header mismatch")
-
-    return ParsedJWTInfo(
-        version=version,
-        raw_token_string=jwt,
-        raw_payload=payload,
-        header=header,
-        # Ideally we would only parse this after the signature verification is done
-        # We do this at the start, since we want to check if a token can be a supertokens access token or not.
-        payload=loads(utf_base64decode(payload, True)),
-        signature=signature,
-        kid=kid,
-        parsed_header=parsed_header,
-    )
-
-
-
-
-
-

Classes

-
-
-class ParsedJWTInfo -(version: int, raw_token_string: str, raw_payload: str, header: str, payload: Dict[str, Any], signature: str, kid: Optional[str], parsed_header: Optional[Dict[str, Any]] = None) -
-
-
-
- -Expand source code - -
class ParsedJWTInfo:
-    def __init__(
-        self,
-        version: int,
-        raw_token_string: str,
-        raw_payload: str,
-        header: str,
-        payload: Dict[str, Any],
-        signature: str,
-        kid: Optional[str],
-        parsed_header: Optional[Dict[str, Any]] = None,
-    ) -> None:
-        self.version = version
-        self.raw_token_string = raw_token_string
-        self.raw_payload = raw_payload
-        self.header = header
-        self.payload = payload
-        self.signature = signature
-        self.kid = kid
-        self.parsed_header = parsed_header
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/session/recipe.html b/html/supertokens_python/recipe/session/recipe.html deleted file mode 100644 index c478a829f..000000000 --- a/html/supertokens_python/recipe/session/recipe.html +++ /dev/null @@ -1,1237 +0,0 @@ - - - - - - -supertokens_python.recipe.session.recipe API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.session.recipe

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from os import environ
-from typing import TYPE_CHECKING, Any, Dict, List, Union, Callable, Optional
-
-from supertokens_python.framework.response import BaseResponse
-from typing_extensions import Literal
-
-from .cookie_and_header import (
-    get_cors_allowed_headers,
-)
-from .exceptions import (
-    ClearDuplicateSessionCookiesError,
-    SuperTokensSessionError,
-    TokenTheftError,
-    UnauthorisedError,
-    InvalidClaimsError,
-)
-from ...types import MaybeAwaitable
-
-if TYPE_CHECKING:
-    from supertokens_python.framework import BaseRequest
-    from supertokens_python.supertokens import AppInfo
-
-from supertokens_python.exceptions import SuperTokensError, raise_general_exception
-from supertokens_python.logger import log_debug_message
-from supertokens_python.normalised_url_path import NormalisedURLPath
-from supertokens_python.querier import Querier
-from supertokens_python.recipe.openid.recipe import OpenIdRecipe
-from supertokens_python.recipe_module import APIHandled, RecipeModule
-
-from .constants import SESSION_REFRESH, SIGNOUT
-from .interfaces import (
-    APIInterface,
-    APIOptions,
-    RecipeInterface,
-    SessionClaim,
-    SessionClaimValidator,
-    SessionContainer,
-)
-from .recipe_implementation import (
-    RecipeImplementation,
-)
-from .api import handle_refresh_api, handle_signout_api
-from .utils import (
-    InputErrorHandlers,
-    InputOverrideConfig,
-    TokenTransferMethod,
-    validate_and_normalise_user_input,
-)
-from .cookie_and_header import clear_session_from_all_token_transfer_methods
-
-
-class SessionRecipe(RecipeModule):
-    recipe_id = "session"
-    __instance = None
-
-    def __init__(
-        self,
-        recipe_id: str,
-        app_info: AppInfo,
-        cookie_domain: Union[str, None] = None,
-        older_cookie_domain: Union[str, None] = None,
-        cookie_secure: Union[bool, None] = None,
-        cookie_same_site: Union[Literal["lax", "none", "strict"], None] = None,
-        session_expired_status_code: Union[int, None] = None,
-        anti_csrf: Union[
-            Literal["VIA_TOKEN", "VIA_CUSTOM_HEADER", "NONE"], None
-        ] = None,
-        get_token_transfer_method: Union[
-            Callable[
-                [BaseRequest, bool, Dict[str, Any]],
-                Union[TokenTransferMethod, Literal["any"]],
-            ],
-            None,
-        ] = None,
-        error_handlers: Union[InputErrorHandlers, None] = None,
-        override: Union[InputOverrideConfig, None] = None,
-        invalid_claim_status_code: Union[int, None] = None,
-        use_dynamic_access_token_signing_key: Union[bool, None] = None,
-        expose_access_token_to_frontend_in_cookie_based_auth: Union[bool, None] = None,
-        jwks_refresh_interval_sec: Union[int, None] = None,
-    ):
-        super().__init__(recipe_id, app_info)
-        self.config = validate_and_normalise_user_input(
-            app_info,
-            cookie_domain,
-            older_cookie_domain,
-            cookie_secure,
-            cookie_same_site,
-            session_expired_status_code,
-            anti_csrf,
-            get_token_transfer_method,
-            error_handlers,
-            override,
-            invalid_claim_status_code,
-            use_dynamic_access_token_signing_key,
-            expose_access_token_to_frontend_in_cookie_based_auth,
-            jwks_refresh_interval_sec,
-        )
-        self.openid_recipe = OpenIdRecipe(
-            recipe_id,
-            app_info,
-            None,
-            None,
-            override.openid_feature if override is not None else None,
-        )
-        log_debug_message(
-            "session init: anti_csrf: %s", self.config.anti_csrf_function_or_string
-        )
-        if self.config.cookie_domain is not None:
-            log_debug_message(
-                "session init: cookie_domain: %s", self.config.cookie_domain
-            )
-        else:
-            log_debug_message("session init: cookie_domain: None")
-
-        # we check the input cookie_same_site because the normalised version is
-        # always a function.
-        if cookie_same_site is not None:
-            log_debug_message("session init: cookie_same_site: %s", cookie_same_site)
-        else:
-            log_debug_message("session init: cookie_same_site: function")
-
-        log_debug_message(
-            "session init: cookie_secure: %s", str(self.config.cookie_secure)
-        )
-        log_debug_message(
-            "session init: refresh_token_path: %s ",
-            self.config.refresh_token_path.get_as_string_dangerous(),
-        )
-        log_debug_message(
-            "session init: session_expired_status_code: %s",
-            str(self.config.session_expired_status_code),
-        )
-        recipe_implementation = RecipeImplementation(
-            Querier.get_instance(recipe_id), self.config, self.app_info
-        )
-        self.recipe_implementation: RecipeInterface = (
-            recipe_implementation
-            if self.config.override.functions is None
-            else self.config.override.functions(recipe_implementation)
-        )
-
-        from .api.implementation import APIImplementation
-
-        api_implementation = APIImplementation()
-        self.api_implementation: APIInterface = (
-            api_implementation
-            if self.config.override.apis is None
-            else self.config.override.apis(api_implementation)
-        )
-
-        self.claims_added_by_other_recipes: List[SessionClaim[Any]] = []
-        self.claim_validators_added_by_other_recipes: List[SessionClaimValidator] = []
-
-    def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
-        return isinstance(err, SuperTokensError) and (
-            isinstance(err, SuperTokensSessionError)
-            or self.openid_recipe.is_error_from_this_recipe_based_on_instance(err)
-        )
-
-    def get_apis_handled(self) -> List[APIHandled]:
-        apis_handled = [
-            APIHandled(
-                NormalisedURLPath(SESSION_REFRESH),
-                "post",
-                SESSION_REFRESH,
-                self.api_implementation.disable_refresh_post,
-            ),
-            APIHandled(
-                NormalisedURLPath(SIGNOUT),
-                "post",
-                SIGNOUT,
-                self.api_implementation.disable_signout_post,
-            ),
-        ]
-        apis_handled.extend(self.openid_recipe.get_apis_handled())
-
-        return apis_handled
-
-    async def handle_api_request(
-        self,
-        request_id: str,
-        tenant_id: str,
-        request: BaseRequest,
-        path: NormalisedURLPath,
-        method: str,
-        response: BaseResponse,
-        user_context: Dict[str, Any],
-    ) -> Union[BaseResponse, None]:
-        if request_id == SESSION_REFRESH:
-            return await handle_refresh_api(
-                self.api_implementation,
-                APIOptions(
-                    request,
-                    response,
-                    self.recipe_id,
-                    self.config,
-                    self.recipe_implementation,
-                ),
-                user_context,
-            )
-        if request_id == SIGNOUT:
-            return await handle_signout_api(
-                self.api_implementation,
-                APIOptions(
-                    request,
-                    response,
-                    self.recipe_id,
-                    self.config,
-                    self.recipe_implementation,
-                ),
-                user_context,
-            )
-        return await self.openid_recipe.handle_api_request(
-            request_id, tenant_id, request, path, method, response, user_context
-        )
-
-    async def handle_error(
-        self,
-        request: BaseRequest,
-        err: SuperTokensError,
-        response: BaseResponse,
-        user_context: Dict[str, Any],
-    ) -> BaseResponse:
-        if (
-            isinstance(err, SuperTokensSessionError)
-            and err.response_mutators is not None
-        ):
-            for mutator in err.response_mutators:
-                mutator(response, user_context)
-
-        if isinstance(err, UnauthorisedError):
-            log_debug_message("errorHandler: returning UNAUTHORISED")
-            if err.clear_tokens:
-                log_debug_message("Clearing tokens because of UNAUTHORISED response")
-                clear_session_from_all_token_transfer_methods(
-                    response, self, request, user_context
-                )
-            return await self.config.error_handlers.on_unauthorised(
-                request, str(err), response
-            )
-        if isinstance(err, TokenTheftError):
-            log_debug_message("errorHandler: returning TOKEN_THEFT_DETECTED")
-            log_debug_message(
-                "Clearing tokens because of TOKEN_THEFT_DETECTED response"
-            )
-            clear_session_from_all_token_transfer_methods(
-                response, self, request, user_context
-            )
-            return await self.config.error_handlers.on_token_theft_detected(
-                request, err.session_handle, err.user_id, response
-            )
-        if isinstance(err, InvalidClaimsError):
-            log_debug_message("errorHandler: returning INVALID_CLAIMS")
-            return await self.config.error_handlers.on_invalid_claim(
-                self, request, err.payload, response
-            )
-        if isinstance(err, ClearDuplicateSessionCookiesError):
-            log_debug_message("errorHandler: returning CLEAR_DUPLICATE_SESSION_COOKIES")
-            return await self.config.error_handlers.on_clear_duplicate_session_cookies(
-                request, str(err), response
-            )
-
-        log_debug_message("errorHandler: returning TRY_REFRESH_TOKEN")
-        return await self.config.error_handlers.on_try_refresh_token(
-            request, str(err), response
-        )
-
-    def get_all_cors_headers(self) -> List[str]:
-        cors_headers = get_cors_allowed_headers()
-        cors_headers.extend(self.openid_recipe.get_all_cors_headers())
-
-        return cors_headers
-
-    @staticmethod
-    def init(
-        cookie_domain: Union[str, None] = None,
-        older_cookie_domain: Union[str, None] = None,
-        cookie_secure: Union[bool, None] = None,
-        cookie_same_site: Union[Literal["lax", "none", "strict"], None] = None,
-        session_expired_status_code: Union[int, None] = None,
-        anti_csrf: Union[
-            Literal["VIA_TOKEN", "VIA_CUSTOM_HEADER", "NONE"], None
-        ] = None,
-        get_token_transfer_method: Union[
-            Callable[
-                [BaseRequest, bool, Dict[str, Any]],
-                Union[TokenTransferMethod, Literal["any"]],
-            ],
-            None,
-        ] = None,
-        error_handlers: Union[InputErrorHandlers, None] = None,
-        override: Union[InputOverrideConfig, None] = None,
-        invalid_claim_status_code: Union[int, None] = None,
-        use_dynamic_access_token_signing_key: Union[bool, None] = None,
-        expose_access_token_to_frontend_in_cookie_based_auth: Union[bool, None] = None,
-        jwks_refresh_interval_sec: Union[int, None] = None,
-    ):
-        def func(app_info: AppInfo):
-            if SessionRecipe.__instance is None:
-                SessionRecipe.__instance = SessionRecipe(
-                    SessionRecipe.recipe_id,
-                    app_info,
-                    cookie_domain,
-                    older_cookie_domain,
-                    cookie_secure,
-                    cookie_same_site,
-                    session_expired_status_code,
-                    anti_csrf,
-                    get_token_transfer_method,
-                    error_handlers,
-                    override,
-                    invalid_claim_status_code,
-                    use_dynamic_access_token_signing_key,
-                    expose_access_token_to_frontend_in_cookie_based_auth,
-                    jwks_refresh_interval_sec,
-                )
-                return SessionRecipe.__instance
-            raise_general_exception(
-                "Session recipe has already been initialised. Please check your code for bugs."
-            )
-
-        return func
-
-    @staticmethod
-    def get_instance() -> SessionRecipe:
-        if SessionRecipe.__instance is not None:
-            return SessionRecipe.__instance
-        raise_general_exception(
-            "Initialisation not done. Did you forget to call the SuperTokens.init function?"
-        )
-
-    @staticmethod
-    def reset():
-        if ("SUPERTOKENS_ENV" not in environ) or (
-            environ["SUPERTOKENS_ENV"] != "testing"
-        ):
-            raise_general_exception("calling testing function in non testing env")
-        SessionRecipe.__instance = None
-
-    def add_claim_from_other_recipe(self, claim: SessionClaim[Any]):
-        # We are throwing here (and not in addClaimValidatorFromOtherRecipe) because if multiple
-        # claims are added with the same key they will overwrite each other. Validators will all run
-        # and work as expected even if they are added multiple times.
-        if claim.key in [c.key for c in self.claims_added_by_other_recipes]:
-            raise Exception("Claim added by multiple recipes")
-
-        self.claims_added_by_other_recipes.append(claim)
-
-    def get_claims_added_by_other_recipes(self) -> List[SessionClaim[Any]]:
-        return self.claims_added_by_other_recipes
-
-    def add_claim_validator_from_other_recipe(
-        self, claim_validator: SessionClaimValidator
-    ):
-        self.claim_validators_added_by_other_recipes.append(claim_validator)
-
-    def get_claim_validators_added_by_other_recipes(
-        self,
-    ) -> List[SessionClaimValidator]:
-        return self.claim_validators_added_by_other_recipes
-
-    async def verify_session(
-        self,
-        request: BaseRequest,
-        anti_csrf_check: Union[bool, None],
-        session_required: bool,
-        check_database: bool,
-        override_global_claim_validators: Optional[
-            Callable[
-                [List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
-                MaybeAwaitable[List[SessionClaimValidator]],
-            ]
-        ],
-        user_context: Dict[str, Any],
-    ):
-        _ = user_context
-
-        return await self.api_implementation.verify_session(
-            APIOptions(
-                request,
-                None,
-                self.recipe_id,
-                self.config,
-                self.recipe_implementation,
-            ),
-            anti_csrf_check,
-            session_required,
-            check_database,
-            override_global_claim_validators,
-            user_context,
-        )
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class SessionRecipe -(recipe_id: str, app_info: AppInfo, cookie_domain: Union[str, None] = None, older_cookie_domain: Union[str, None] = None, cookie_secure: Union[bool, None] = None, cookie_same_site: "Union[Literal['lax', 'none', 'strict'], None]" = None, session_expired_status_code: Union[int, None] = None, anti_csrf: "Union[Literal['VIA_TOKEN', 'VIA_CUSTOM_HEADER', 'NONE'], None]" = None, get_token_transfer_method: "Union[Callable[[BaseRequest, bool, Dict[str, Any]], Union[TokenTransferMethod, Literal['any']]], None]" = None, error_handlers: Union[InputErrorHandlers, None] = None, override: Union[InputOverrideConfig, None] = None, invalid_claim_status_code: Union[int, None] = None, use_dynamic_access_token_signing_key: Union[bool, None] = None, expose_access_token_to_frontend_in_cookie_based_auth: Union[bool, None] = None, jwks_refresh_interval_sec: Union[int, None] = None) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class SessionRecipe(RecipeModule):
-    recipe_id = "session"
-    __instance = None
-
-    def __init__(
-        self,
-        recipe_id: str,
-        app_info: AppInfo,
-        cookie_domain: Union[str, None] = None,
-        older_cookie_domain: Union[str, None] = None,
-        cookie_secure: Union[bool, None] = None,
-        cookie_same_site: Union[Literal["lax", "none", "strict"], None] = None,
-        session_expired_status_code: Union[int, None] = None,
-        anti_csrf: Union[
-            Literal["VIA_TOKEN", "VIA_CUSTOM_HEADER", "NONE"], None
-        ] = None,
-        get_token_transfer_method: Union[
-            Callable[
-                [BaseRequest, bool, Dict[str, Any]],
-                Union[TokenTransferMethod, Literal["any"]],
-            ],
-            None,
-        ] = None,
-        error_handlers: Union[InputErrorHandlers, None] = None,
-        override: Union[InputOverrideConfig, None] = None,
-        invalid_claim_status_code: Union[int, None] = None,
-        use_dynamic_access_token_signing_key: Union[bool, None] = None,
-        expose_access_token_to_frontend_in_cookie_based_auth: Union[bool, None] = None,
-        jwks_refresh_interval_sec: Union[int, None] = None,
-    ):
-        super().__init__(recipe_id, app_info)
-        self.config = validate_and_normalise_user_input(
-            app_info,
-            cookie_domain,
-            older_cookie_domain,
-            cookie_secure,
-            cookie_same_site,
-            session_expired_status_code,
-            anti_csrf,
-            get_token_transfer_method,
-            error_handlers,
-            override,
-            invalid_claim_status_code,
-            use_dynamic_access_token_signing_key,
-            expose_access_token_to_frontend_in_cookie_based_auth,
-            jwks_refresh_interval_sec,
-        )
-        self.openid_recipe = OpenIdRecipe(
-            recipe_id,
-            app_info,
-            None,
-            None,
-            override.openid_feature if override is not None else None,
-        )
-        log_debug_message(
-            "session init: anti_csrf: %s", self.config.anti_csrf_function_or_string
-        )
-        if self.config.cookie_domain is not None:
-            log_debug_message(
-                "session init: cookie_domain: %s", self.config.cookie_domain
-            )
-        else:
-            log_debug_message("session init: cookie_domain: None")
-
-        # we check the input cookie_same_site because the normalised version is
-        # always a function.
-        if cookie_same_site is not None:
-            log_debug_message("session init: cookie_same_site: %s", cookie_same_site)
-        else:
-            log_debug_message("session init: cookie_same_site: function")
-
-        log_debug_message(
-            "session init: cookie_secure: %s", str(self.config.cookie_secure)
-        )
-        log_debug_message(
-            "session init: refresh_token_path: %s ",
-            self.config.refresh_token_path.get_as_string_dangerous(),
-        )
-        log_debug_message(
-            "session init: session_expired_status_code: %s",
-            str(self.config.session_expired_status_code),
-        )
-        recipe_implementation = RecipeImplementation(
-            Querier.get_instance(recipe_id), self.config, self.app_info
-        )
-        self.recipe_implementation: RecipeInterface = (
-            recipe_implementation
-            if self.config.override.functions is None
-            else self.config.override.functions(recipe_implementation)
-        )
-
-        from .api.implementation import APIImplementation
-
-        api_implementation = APIImplementation()
-        self.api_implementation: APIInterface = (
-            api_implementation
-            if self.config.override.apis is None
-            else self.config.override.apis(api_implementation)
-        )
-
-        self.claims_added_by_other_recipes: List[SessionClaim[Any]] = []
-        self.claim_validators_added_by_other_recipes: List[SessionClaimValidator] = []
-
-    def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
-        return isinstance(err, SuperTokensError) and (
-            isinstance(err, SuperTokensSessionError)
-            or self.openid_recipe.is_error_from_this_recipe_based_on_instance(err)
-        )
-
-    def get_apis_handled(self) -> List[APIHandled]:
-        apis_handled = [
-            APIHandled(
-                NormalisedURLPath(SESSION_REFRESH),
-                "post",
-                SESSION_REFRESH,
-                self.api_implementation.disable_refresh_post,
-            ),
-            APIHandled(
-                NormalisedURLPath(SIGNOUT),
-                "post",
-                SIGNOUT,
-                self.api_implementation.disable_signout_post,
-            ),
-        ]
-        apis_handled.extend(self.openid_recipe.get_apis_handled())
-
-        return apis_handled
-
-    async def handle_api_request(
-        self,
-        request_id: str,
-        tenant_id: str,
-        request: BaseRequest,
-        path: NormalisedURLPath,
-        method: str,
-        response: BaseResponse,
-        user_context: Dict[str, Any],
-    ) -> Union[BaseResponse, None]:
-        if request_id == SESSION_REFRESH:
-            return await handle_refresh_api(
-                self.api_implementation,
-                APIOptions(
-                    request,
-                    response,
-                    self.recipe_id,
-                    self.config,
-                    self.recipe_implementation,
-                ),
-                user_context,
-            )
-        if request_id == SIGNOUT:
-            return await handle_signout_api(
-                self.api_implementation,
-                APIOptions(
-                    request,
-                    response,
-                    self.recipe_id,
-                    self.config,
-                    self.recipe_implementation,
-                ),
-                user_context,
-            )
-        return await self.openid_recipe.handle_api_request(
-            request_id, tenant_id, request, path, method, response, user_context
-        )
-
-    async def handle_error(
-        self,
-        request: BaseRequest,
-        err: SuperTokensError,
-        response: BaseResponse,
-        user_context: Dict[str, Any],
-    ) -> BaseResponse:
-        if (
-            isinstance(err, SuperTokensSessionError)
-            and err.response_mutators is not None
-        ):
-            for mutator in err.response_mutators:
-                mutator(response, user_context)
-
-        if isinstance(err, UnauthorisedError):
-            log_debug_message("errorHandler: returning UNAUTHORISED")
-            if err.clear_tokens:
-                log_debug_message("Clearing tokens because of UNAUTHORISED response")
-                clear_session_from_all_token_transfer_methods(
-                    response, self, request, user_context
-                )
-            return await self.config.error_handlers.on_unauthorised(
-                request, str(err), response
-            )
-        if isinstance(err, TokenTheftError):
-            log_debug_message("errorHandler: returning TOKEN_THEFT_DETECTED")
-            log_debug_message(
-                "Clearing tokens because of TOKEN_THEFT_DETECTED response"
-            )
-            clear_session_from_all_token_transfer_methods(
-                response, self, request, user_context
-            )
-            return await self.config.error_handlers.on_token_theft_detected(
-                request, err.session_handle, err.user_id, response
-            )
-        if isinstance(err, InvalidClaimsError):
-            log_debug_message("errorHandler: returning INVALID_CLAIMS")
-            return await self.config.error_handlers.on_invalid_claim(
-                self, request, err.payload, response
-            )
-        if isinstance(err, ClearDuplicateSessionCookiesError):
-            log_debug_message("errorHandler: returning CLEAR_DUPLICATE_SESSION_COOKIES")
-            return await self.config.error_handlers.on_clear_duplicate_session_cookies(
-                request, str(err), response
-            )
-
-        log_debug_message("errorHandler: returning TRY_REFRESH_TOKEN")
-        return await self.config.error_handlers.on_try_refresh_token(
-            request, str(err), response
-        )
-
-    def get_all_cors_headers(self) -> List[str]:
-        cors_headers = get_cors_allowed_headers()
-        cors_headers.extend(self.openid_recipe.get_all_cors_headers())
-
-        return cors_headers
-
-    @staticmethod
-    def init(
-        cookie_domain: Union[str, None] = None,
-        older_cookie_domain: Union[str, None] = None,
-        cookie_secure: Union[bool, None] = None,
-        cookie_same_site: Union[Literal["lax", "none", "strict"], None] = None,
-        session_expired_status_code: Union[int, None] = None,
-        anti_csrf: Union[
-            Literal["VIA_TOKEN", "VIA_CUSTOM_HEADER", "NONE"], None
-        ] = None,
-        get_token_transfer_method: Union[
-            Callable[
-                [BaseRequest, bool, Dict[str, Any]],
-                Union[TokenTransferMethod, Literal["any"]],
-            ],
-            None,
-        ] = None,
-        error_handlers: Union[InputErrorHandlers, None] = None,
-        override: Union[InputOverrideConfig, None] = None,
-        invalid_claim_status_code: Union[int, None] = None,
-        use_dynamic_access_token_signing_key: Union[bool, None] = None,
-        expose_access_token_to_frontend_in_cookie_based_auth: Union[bool, None] = None,
-        jwks_refresh_interval_sec: Union[int, None] = None,
-    ):
-        def func(app_info: AppInfo):
-            if SessionRecipe.__instance is None:
-                SessionRecipe.__instance = SessionRecipe(
-                    SessionRecipe.recipe_id,
-                    app_info,
-                    cookie_domain,
-                    older_cookie_domain,
-                    cookie_secure,
-                    cookie_same_site,
-                    session_expired_status_code,
-                    anti_csrf,
-                    get_token_transfer_method,
-                    error_handlers,
-                    override,
-                    invalid_claim_status_code,
-                    use_dynamic_access_token_signing_key,
-                    expose_access_token_to_frontend_in_cookie_based_auth,
-                    jwks_refresh_interval_sec,
-                )
-                return SessionRecipe.__instance
-            raise_general_exception(
-                "Session recipe has already been initialised. Please check your code for bugs."
-            )
-
-        return func
-
-    @staticmethod
-    def get_instance() -> SessionRecipe:
-        if SessionRecipe.__instance is not None:
-            return SessionRecipe.__instance
-        raise_general_exception(
-            "Initialisation not done. Did you forget to call the SuperTokens.init function?"
-        )
-
-    @staticmethod
-    def reset():
-        if ("SUPERTOKENS_ENV" not in environ) or (
-            environ["SUPERTOKENS_ENV"] != "testing"
-        ):
-            raise_general_exception("calling testing function in non testing env")
-        SessionRecipe.__instance = None
-
-    def add_claim_from_other_recipe(self, claim: SessionClaim[Any]):
-        # We are throwing here (and not in addClaimValidatorFromOtherRecipe) because if multiple
-        # claims are added with the same key they will overwrite each other. Validators will all run
-        # and work as expected even if they are added multiple times.
-        if claim.key in [c.key for c in self.claims_added_by_other_recipes]:
-            raise Exception("Claim added by multiple recipes")
-
-        self.claims_added_by_other_recipes.append(claim)
-
-    def get_claims_added_by_other_recipes(self) -> List[SessionClaim[Any]]:
-        return self.claims_added_by_other_recipes
-
-    def add_claim_validator_from_other_recipe(
-        self, claim_validator: SessionClaimValidator
-    ):
-        self.claim_validators_added_by_other_recipes.append(claim_validator)
-
-    def get_claim_validators_added_by_other_recipes(
-        self,
-    ) -> List[SessionClaimValidator]:
-        return self.claim_validators_added_by_other_recipes
-
-    async def verify_session(
-        self,
-        request: BaseRequest,
-        anti_csrf_check: Union[bool, None],
-        session_required: bool,
-        check_database: bool,
-        override_global_claim_validators: Optional[
-            Callable[
-                [List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
-                MaybeAwaitable[List[SessionClaimValidator]],
-            ]
-        ],
-        user_context: Dict[str, Any],
-    ):
-        _ = user_context
-
-        return await self.api_implementation.verify_session(
-            APIOptions(
-                request,
-                None,
-                self.recipe_id,
-                self.config,
-                self.recipe_implementation,
-            ),
-            anti_csrf_check,
-            session_required,
-            check_database,
-            override_global_claim_validators,
-            user_context,
-        )
-
-

Ancestors

- -

Class variables

-
-
var get_tenant_id : Optional[Callable[[str, Dict[str, Any]], Awaitable[str]]]
-
-
-
-
var recipe_id
-
-
-
-
-

Static methods

-
-
-def get_instance() ‑> SessionRecipe -
-
-
-
- -Expand source code - -
@staticmethod
-def get_instance() -> SessionRecipe:
-    if SessionRecipe.__instance is not None:
-        return SessionRecipe.__instance
-    raise_general_exception(
-        "Initialisation not done. Did you forget to call the SuperTokens.init function?"
-    )
-
-
-
-def init(cookie_domain: Union[str, None] = None, older_cookie_domain: Union[str, None] = None, cookie_secure: Union[bool, None] = None, cookie_same_site: "Union[Literal['lax', 'none', 'strict'], None]" = None, session_expired_status_code: Union[int, None] = None, anti_csrf: "Union[Literal['VIA_TOKEN', 'VIA_CUSTOM_HEADER', 'NONE'], None]" = None, get_token_transfer_method: "Union[Callable[[BaseRequest, bool, Dict[str, Any]], Union[TokenTransferMethod, Literal['any']]], None]" = None, error_handlers: Union[InputErrorHandlers, None] = None, override: Union[InputOverrideConfig, None] = None, invalid_claim_status_code: Union[int, None] = None, use_dynamic_access_token_signing_key: Union[bool, None] = None, expose_access_token_to_frontend_in_cookie_based_auth: Union[bool, None] = None, jwks_refresh_interval_sec: Union[int, None] = None) -
-
-
-
- -Expand source code - -
@staticmethod
-def init(
-    cookie_domain: Union[str, None] = None,
-    older_cookie_domain: Union[str, None] = None,
-    cookie_secure: Union[bool, None] = None,
-    cookie_same_site: Union[Literal["lax", "none", "strict"], None] = None,
-    session_expired_status_code: Union[int, None] = None,
-    anti_csrf: Union[
-        Literal["VIA_TOKEN", "VIA_CUSTOM_HEADER", "NONE"], None
-    ] = None,
-    get_token_transfer_method: Union[
-        Callable[
-            [BaseRequest, bool, Dict[str, Any]],
-            Union[TokenTransferMethod, Literal["any"]],
-        ],
-        None,
-    ] = None,
-    error_handlers: Union[InputErrorHandlers, None] = None,
-    override: Union[InputOverrideConfig, None] = None,
-    invalid_claim_status_code: Union[int, None] = None,
-    use_dynamic_access_token_signing_key: Union[bool, None] = None,
-    expose_access_token_to_frontend_in_cookie_based_auth: Union[bool, None] = None,
-    jwks_refresh_interval_sec: Union[int, None] = None,
-):
-    def func(app_info: AppInfo):
-        if SessionRecipe.__instance is None:
-            SessionRecipe.__instance = SessionRecipe(
-                SessionRecipe.recipe_id,
-                app_info,
-                cookie_domain,
-                older_cookie_domain,
-                cookie_secure,
-                cookie_same_site,
-                session_expired_status_code,
-                anti_csrf,
-                get_token_transfer_method,
-                error_handlers,
-                override,
-                invalid_claim_status_code,
-                use_dynamic_access_token_signing_key,
-                expose_access_token_to_frontend_in_cookie_based_auth,
-                jwks_refresh_interval_sec,
-            )
-            return SessionRecipe.__instance
-        raise_general_exception(
-            "Session recipe has already been initialised. Please check your code for bugs."
-        )
-
-    return func
-
-
-
-def reset() -
-
-
-
- -Expand source code - -
@staticmethod
-def reset():
-    if ("SUPERTOKENS_ENV" not in environ) or (
-        environ["SUPERTOKENS_ENV"] != "testing"
-    ):
-        raise_general_exception("calling testing function in non testing env")
-    SessionRecipe.__instance = None
-
-
-
-

Methods

-
-
-def add_claim_from_other_recipe(self, claim: SessionClaim[Any]) -
-
-
-
- -Expand source code - -
def add_claim_from_other_recipe(self, claim: SessionClaim[Any]):
-    # We are throwing here (and not in addClaimValidatorFromOtherRecipe) because if multiple
-    # claims are added with the same key they will overwrite each other. Validators will all run
-    # and work as expected even if they are added multiple times.
-    if claim.key in [c.key for c in self.claims_added_by_other_recipes]:
-        raise Exception("Claim added by multiple recipes")
-
-    self.claims_added_by_other_recipes.append(claim)
-
-
-
-def add_claim_validator_from_other_recipe(self, claim_validator: SessionClaimValidator) -
-
-
-
- -Expand source code - -
def add_claim_validator_from_other_recipe(
-    self, claim_validator: SessionClaimValidator
-):
-    self.claim_validators_added_by_other_recipes.append(claim_validator)
-
-
-
-def get_all_cors_headers(self) ‑> List[str] -
-
-
-
- -Expand source code - -
def get_all_cors_headers(self) -> List[str]:
-    cors_headers = get_cors_allowed_headers()
-    cors_headers.extend(self.openid_recipe.get_all_cors_headers())
-
-    return cors_headers
-
-
-
-def get_apis_handled(self) ‑> List[APIHandled] -
-
-
-
- -Expand source code - -
def get_apis_handled(self) -> List[APIHandled]:
-    apis_handled = [
-        APIHandled(
-            NormalisedURLPath(SESSION_REFRESH),
-            "post",
-            SESSION_REFRESH,
-            self.api_implementation.disable_refresh_post,
-        ),
-        APIHandled(
-            NormalisedURLPath(SIGNOUT),
-            "post",
-            SIGNOUT,
-            self.api_implementation.disable_signout_post,
-        ),
-    ]
-    apis_handled.extend(self.openid_recipe.get_apis_handled())
-
-    return apis_handled
-
-
-
-def get_claim_validators_added_by_other_recipes(self) ‑> List[SessionClaimValidator] -
-
-
-
- -Expand source code - -
def get_claim_validators_added_by_other_recipes(
-    self,
-) -> List[SessionClaimValidator]:
-    return self.claim_validators_added_by_other_recipes
-
-
-
-def get_claims_added_by_other_recipes(self) ‑> List[SessionClaim[Any]] -
-
-
-
- -Expand source code - -
def get_claims_added_by_other_recipes(self) -> List[SessionClaim[Any]]:
-    return self.claims_added_by_other_recipes
-
-
-
-async def handle_api_request(self, request_id: str, tenant_id: str, request: BaseRequest, path: NormalisedURLPath, method: str, response: BaseResponse, user_context: Dict[str, Any]) ‑> Union[BaseResponse, None] -
-
-
-
- -Expand source code - -
async def handle_api_request(
-    self,
-    request_id: str,
-    tenant_id: str,
-    request: BaseRequest,
-    path: NormalisedURLPath,
-    method: str,
-    response: BaseResponse,
-    user_context: Dict[str, Any],
-) -> Union[BaseResponse, None]:
-    if request_id == SESSION_REFRESH:
-        return await handle_refresh_api(
-            self.api_implementation,
-            APIOptions(
-                request,
-                response,
-                self.recipe_id,
-                self.config,
-                self.recipe_implementation,
-            ),
-            user_context,
-        )
-    if request_id == SIGNOUT:
-        return await handle_signout_api(
-            self.api_implementation,
-            APIOptions(
-                request,
-                response,
-                self.recipe_id,
-                self.config,
-                self.recipe_implementation,
-            ),
-            user_context,
-        )
-    return await self.openid_recipe.handle_api_request(
-        request_id, tenant_id, request, path, method, response, user_context
-    )
-
-
-
-async def handle_error(self, request: BaseRequest, err: SuperTokensError, response: BaseResponse, user_context: Dict[str, Any]) ‑> BaseResponse -
-
-
-
- -Expand source code - -
async def handle_error(
-    self,
-    request: BaseRequest,
-    err: SuperTokensError,
-    response: BaseResponse,
-    user_context: Dict[str, Any],
-) -> BaseResponse:
-    if (
-        isinstance(err, SuperTokensSessionError)
-        and err.response_mutators is not None
-    ):
-        for mutator in err.response_mutators:
-            mutator(response, user_context)
-
-    if isinstance(err, UnauthorisedError):
-        log_debug_message("errorHandler: returning UNAUTHORISED")
-        if err.clear_tokens:
-            log_debug_message("Clearing tokens because of UNAUTHORISED response")
-            clear_session_from_all_token_transfer_methods(
-                response, self, request, user_context
-            )
-        return await self.config.error_handlers.on_unauthorised(
-            request, str(err), response
-        )
-    if isinstance(err, TokenTheftError):
-        log_debug_message("errorHandler: returning TOKEN_THEFT_DETECTED")
-        log_debug_message(
-            "Clearing tokens because of TOKEN_THEFT_DETECTED response"
-        )
-        clear_session_from_all_token_transfer_methods(
-            response, self, request, user_context
-        )
-        return await self.config.error_handlers.on_token_theft_detected(
-            request, err.session_handle, err.user_id, response
-        )
-    if isinstance(err, InvalidClaimsError):
-        log_debug_message("errorHandler: returning INVALID_CLAIMS")
-        return await self.config.error_handlers.on_invalid_claim(
-            self, request, err.payload, response
-        )
-    if isinstance(err, ClearDuplicateSessionCookiesError):
-        log_debug_message("errorHandler: returning CLEAR_DUPLICATE_SESSION_COOKIES")
-        return await self.config.error_handlers.on_clear_duplicate_session_cookies(
-            request, str(err), response
-        )
-
-    log_debug_message("errorHandler: returning TRY_REFRESH_TOKEN")
-    return await self.config.error_handlers.on_try_refresh_token(
-        request, str(err), response
-    )
-
-
-
-def is_error_from_this_recipe_based_on_instance(self, err: Exception) ‑> bool -
-
-
-
- -Expand source code - -
def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
-    return isinstance(err, SuperTokensError) and (
-        isinstance(err, SuperTokensSessionError)
-        or self.openid_recipe.is_error_from_this_recipe_based_on_instance(err)
-    )
-
-
-
-async def verify_session(self, request: BaseRequest, anti_csrf_check: Union[bool, None], session_required: bool, check_database: bool, override_global_claim_validators: Optional[Callable[[List[SessionClaimValidator], SessionContainer, Dict[str, Any]], MaybeAwaitable[List[SessionClaimValidator]]]], user_context: Dict[str, Any]) -
-
-
-
- -Expand source code - -
async def verify_session(
-    self,
-    request: BaseRequest,
-    anti_csrf_check: Union[bool, None],
-    session_required: bool,
-    check_database: bool,
-    override_global_claim_validators: Optional[
-        Callable[
-            [List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
-            MaybeAwaitable[List[SessionClaimValidator]],
-        ]
-    ],
-    user_context: Dict[str, Any],
-):
-    _ = user_context
-
-    return await self.api_implementation.verify_session(
-        APIOptions(
-            request,
-            None,
-            self.recipe_id,
-            self.config,
-            self.recipe_implementation,
-        ),
-        anti_csrf_check,
-        session_required,
-        check_database,
-        override_global_claim_validators,
-        user_context,
-    )
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/session/recipe_implementation.html b/html/supertokens_python/recipe/session/recipe_implementation.html deleted file mode 100644 index fa4be8189..000000000 --- a/html/supertokens_python/recipe/session/recipe_implementation.html +++ /dev/null @@ -1,1687 +0,0 @@ - - - - - - -supertokens_python.recipe.session.recipe_implementation API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.session.recipe_implementation

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-import json
-from typing import TYPE_CHECKING, Any, Callable, Dict, Optional
-
-from supertokens_python.logger import log_debug_message
-from supertokens_python.normalised_url_path import NormalisedURLPath
-from supertokens_python.utils import resolve
-
-from ...types import MaybeAwaitable
-from . import session_functions
-from .access_token import validate_access_token_structure
-from .cookie_and_header import build_front_token
-from .exceptions import UnauthorisedError
-from .interfaces import (
-    AccessTokenObj,
-    ClaimsValidationResult,
-    GetClaimValueOkResult,
-    JSONObject,
-    RecipeInterface,
-    RegenerateAccessTokenOkResult,
-    SessionClaim,
-    SessionClaimValidator,
-    SessionDoesNotExistError,
-    SessionInformationResult,
-    SessionObj,
-)
-from .jwt import ParsedJWTInfo, parse_jwt_without_signature_verification
-from .session_class import Session
-from .utils import SessionConfig, validate_claims_in_payload
-
-if TYPE_CHECKING:
-    from typing import List, Union
-    from supertokens_python import AppInfo
-
-from .interfaces import SessionContainer
-from .constants import protected_props
-from supertokens_python.querier import Querier
-from supertokens_python.recipe.multitenancy.constants import DEFAULT_TENANT_ID
-
-
-class RecipeImplementation(RecipeInterface):  # pylint: disable=too-many-public-methods
-    def __init__(self, querier: Querier, config: SessionConfig, app_info: AppInfo):
-        super().__init__()
-        self.querier = querier
-        self.config = config
-        self.app_info = app_info
-
-    async def create_new_session(
-        self,
-        user_id: str,
-        access_token_payload: Optional[Dict[str, Any]],
-        session_data_in_database: Optional[Dict[str, Any]],
-        disable_anti_csrf: Optional[bool],
-        tenant_id: str,
-        user_context: Dict[str, Any],
-    ) -> SessionContainer:
-        log_debug_message("createNewSession: Started")
-
-        result = await session_functions.create_new_session(
-            self,
-            tenant_id,
-            user_id,
-            disable_anti_csrf is True,
-            access_token_payload,
-            session_data_in_database,
-            user_context=user_context,
-        )
-        log_debug_message("createNewSession: Finished")
-
-        payload = parse_jwt_without_signature_verification(
-            result.accessToken.token
-        ).payload
-
-        new_session = Session(
-            self,
-            self.config,
-            result.accessToken.token,
-            build_front_token(
-                result.session.userId, result.accessToken.expiry, payload
-            ),
-            result.refreshToken,
-            result.antiCsrfToken,
-            result.session.handle,
-            result.session.userId,
-            payload,
-            None,
-            True,
-            tenant_id,
-        )
-
-        return new_session
-
-    async def validate_claims(
-        self,
-        user_id: str,
-        access_token_payload: Dict[str, Any],
-        claim_validators: List[SessionClaimValidator],
-        user_context: Dict[str, Any],
-    ) -> ClaimsValidationResult:
-        access_token_payload_update = None
-        original_access_token_payload = json.dumps(access_token_payload)
-
-        for validator in claim_validators:
-            log_debug_message(
-                "update_claims_in_payload_if_needed checking should_refetch for %s",
-                validator.id,
-            )
-            if validator.claim is not None and validator.should_refetch(
-                access_token_payload, user_context
-            ):
-                log_debug_message(
-                    "update_claims_in_payload_if_needed refetching for %s", validator.id
-                )
-                value = await resolve(
-                    validator.claim.fetch_value(
-                        user_id,
-                        access_token_payload.get("tId", DEFAULT_TENANT_ID),
-                        user_context,
-                    )
-                )
-                log_debug_message(
-                    "update_claims_in_payload_if_needed %s refetch result %s",
-                    validator.id,
-                    json.dumps(value),
-                )
-                if value is not None:
-                    access_token_payload = validator.claim.add_to_payload_(
-                        access_token_payload, value, user_context
-                    )
-
-        if json.dumps(access_token_payload) != original_access_token_payload:
-            access_token_payload_update = access_token_payload
-
-        invalid_claims = await validate_claims_in_payload(
-            claim_validators, access_token_payload, user_context
-        )
-
-        return ClaimsValidationResult(invalid_claims, access_token_payload_update)
-
-    async def validate_claims_in_jwt_payload(
-        self,
-        user_id: str,
-        jwt_payload: JSONObject,
-        claim_validators: List[SessionClaimValidator],
-        user_context: Dict[str, Any],
-    ) -> ClaimsValidationResult:
-        invalid_claims = await validate_claims_in_payload(
-            claim_validators,
-            jwt_payload,
-            user_context,
-        )
-
-        return ClaimsValidationResult(invalid_claims)
-
-    async def get_session(
-        self,
-        access_token: Optional[str],
-        anti_csrf_token: Optional[str] = None,
-        anti_csrf_check: Optional[bool] = None,
-        session_required: Optional[bool] = None,
-        check_database: Optional[bool] = None,
-        override_global_claim_validators: Optional[
-            Callable[
-                [List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
-                MaybeAwaitable[List[SessionClaimValidator]],
-            ]
-        ] = None,
-        user_context: Optional[Dict[str, Any]] = None,
-    ) -> Optional[SessionContainer]:
-        if (
-            anti_csrf_check is not False
-            and isinstance(self.config.anti_csrf_function_or_string, str)
-            and self.config.anti_csrf_function_or_string == "VIA_CUSTOM_HEADER"
-        ):
-            raise Exception(
-                "Since the anti-csrf mode is VIA_CUSTOM_HEADER getSession can't check the CSRF token. Please either use VIA_TOKEN or set anti_csrf_check to false"
-            )
-
-        log_debug_message("getSession: Started")
-
-        if access_token is None:
-            if session_required is False:
-                log_debug_message(
-                    "getSession: returning None because access_token is undefined and session_required is False"
-                )
-                # there is no session that exists here, and the user wants session verification to be optional. So we return None
-                return None
-
-            log_debug_message(
-                "getSession: UNAUTHORISED because accessToken in request is undefined"
-            )
-            # we do not clear the session here because of a race condition mentioned in https://github.com/supertokens/supertokens-node/issues/17
-            raise UnauthorisedError(
-                "Session does not exist. Are you sending the session tokens in the request with the appropriate token transfer method?",
-                clear_tokens=False,
-            )
-
-        access_token_obj: Optional[ParsedJWTInfo] = None
-        try:
-            access_token_obj = parse_jwt_without_signature_verification(access_token)
-            validate_access_token_structure(
-                access_token_obj.payload, access_token_obj.version
-            )
-        except Exception as _:
-            if session_required is False:
-                log_debug_message(
-                    "getSession: Returning undefined because parsing failed and session_required is False"
-                )
-                return None
-
-            log_debug_message(
-                "getSession: UNAUTHORISED because the accessToken couldn't be parsed or had an invalid structure"
-            )
-            raise UnauthorisedError("Token parsing failed", clear_tokens=False)
-
-        response = await session_functions.get_session(
-            self,
-            access_token_obj,
-            anti_csrf_token,
-            (anti_csrf_check is not False),
-            (check_database is True),
-            user_context,
-        )
-
-        log_debug_message("getSession: Success!")
-
-        if access_token_obj.version >= 3:
-            if response.accessToken is not None:
-                payload = parse_jwt_without_signature_verification(
-                    response.accessToken.token
-                ).payload
-            else:
-                payload = access_token_obj.payload
-        else:
-            payload = response.session.userDataInJWT
-
-        if response.accessToken is not None:
-            access_token_str = response.accessToken.token
-            expiry_time = response.accessToken.expiry
-            access_token_updated = True
-        else:
-            access_token_str = access_token
-            expiry_time = response.session.expiryTime
-            access_token_updated = False
-
-        session = Session(
-            self,
-            self.config,
-            access_token_str,
-            build_front_token(response.session.userId, expiry_time, payload),
-            None,  # refresh_token
-            anti_csrf_token,
-            response.session.handle,
-            response.session.userId,
-            payload,
-            None,
-            access_token_updated,
-            response.session.tenant_id,
-        )
-
-        return session
-
-    async def refresh_session(
-        self,
-        refresh_token: str,
-        anti_csrf_token: Optional[str],
-        disable_anti_csrf: bool,
-        user_context: Dict[str, Any],
-    ) -> SessionContainer:
-        if (
-            disable_anti_csrf is not True
-            and isinstance(self.config.anti_csrf_function_or_string, str)
-            and self.config.anti_csrf_function_or_string == "VIA_CUSTOM_HEADER"
-        ):
-            raise Exception(
-                "Since the anti-csrf mode is VIA_CUSTOM_HEADER getSession can't check the CSRF token. Please either use VIA_TOKEN or set antiCsrfCheck to false"
-            )
-
-        log_debug_message("refreshSession: Started")
-
-        response = await session_functions.refresh_session(
-            self,
-            refresh_token,
-            anti_csrf_token,
-            disable_anti_csrf,
-            self.config.use_dynamic_access_token_signing_key,
-            user_context=user_context,
-        )
-
-        log_debug_message("refreshSession: Success!")
-
-        payload = parse_jwt_without_signature_verification(
-            response.accessToken.token,
-        ).payload
-
-        session = Session(
-            self,
-            self.config,
-            response.accessToken.token,
-            build_front_token(
-                response.session.userId,
-                response.accessToken.expiry,
-                payload,
-            ),
-            response.refreshToken,
-            response.antiCsrfToken,
-            response.session.handle,
-            response.session.userId,
-            user_data_in_access_token=payload,
-            req_res_info=None,
-            access_token_updated=True,
-            tenant_id=payload["tId"],
-        )
-
-        return session
-
-    async def revoke_session(
-        self, session_handle: str, user_context: Dict[str, Any]
-    ) -> bool:
-        return await session_functions.revoke_session(
-            self, session_handle, user_context
-        )
-
-    async def revoke_all_sessions_for_user(
-        self,
-        user_id: str,
-        tenant_id: Optional[str],
-        revoke_across_all_tenants: bool,
-        user_context: Dict[str, Any],
-    ) -> List[str]:
-        return await session_functions.revoke_all_sessions_for_user(
-            self, user_id, tenant_id, revoke_across_all_tenants, user_context
-        )
-
-    async def get_all_session_handles_for_user(
-        self,
-        user_id: str,
-        tenant_id: Optional[str],
-        fetch_across_all_tenants: bool,
-        user_context: Dict[str, Any],
-    ) -> List[str]:
-        return await session_functions.get_all_session_handles_for_user(
-            self, user_id, tenant_id, fetch_across_all_tenants, user_context
-        )
-
-    async def revoke_multiple_sessions(
-        self, session_handles: List[str], user_context: Dict[str, Any]
-    ) -> List[str]:
-        return await session_functions.revoke_multiple_sessions(
-            self, session_handles, user_context
-        )
-
-    async def get_session_information(
-        self, session_handle: str, user_context: Dict[str, Any]
-    ) -> Union[SessionInformationResult, None]:
-        return await session_functions.get_session_information(
-            self, session_handle, user_context
-        )
-
-    async def update_session_data_in_database(
-        self,
-        session_handle: str,
-        new_session_data: Dict[str, Any],
-        user_context: Dict[str, Any],
-    ) -> bool:
-        return await session_functions.update_session_data_in_database(
-            self, session_handle, new_session_data, user_context
-        )
-
-    async def merge_into_access_token_payload(
-        self,
-        session_handle: str,
-        access_token_payload_update: Dict[str, Any],
-        user_context: Dict[str, Any],
-    ) -> bool:
-        session_info = await self.get_session_information(session_handle, user_context)
-        if session_info is None:
-            return False
-
-        new_access_token_payload = session_info.custom_claims_in_access_token_payload
-        for k in protected_props:
-            if k in new_access_token_payload:
-                del new_access_token_payload[k]
-
-        new_access_token_payload = {
-            **new_access_token_payload,
-            **access_token_payload_update,
-        }
-        for k in access_token_payload_update.keys():
-            if new_access_token_payload[k] is None:
-                del new_access_token_payload[k]
-
-        return await session_functions.update_access_token_payload(
-            self, session_handle, new_access_token_payload, user_context
-        )
-
-    async def fetch_and_set_claim(
-        self,
-        session_handle: str,
-        claim: SessionClaim[Any],
-        user_context: Dict[str, Any],
-    ) -> bool:
-        session_info = await self.get_session_information(session_handle, user_context)
-        if session_info is None:
-            return False
-
-        access_token_payload_update = await claim.build(
-            session_info.user_id, session_info.tenant_id, user_context
-        )
-        return await self.merge_into_access_token_payload(
-            session_handle, access_token_payload_update, user_context
-        )
-
-    async def set_claim_value(
-        self,
-        session_handle: str,
-        claim: SessionClaim[Any],
-        value: Any,
-        user_context: Dict[str, Any],
-    ):
-        access_token_payload_update = claim.add_to_payload_({}, value, user_context)
-        return await self.merge_into_access_token_payload(
-            session_handle, access_token_payload_update, user_context
-        )
-
-    async def get_claim_value(
-        self,
-        session_handle: str,
-        claim: SessionClaim[Any],
-        user_context: Dict[str, Any],
-    ) -> Union[SessionDoesNotExistError, GetClaimValueOkResult[Any]]:
-        session_info = await self.get_session_information(session_handle, user_context)
-        if session_info is None:
-            return SessionDoesNotExistError()
-
-        return GetClaimValueOkResult(
-            value=claim.get_value_from_payload(
-                session_info.custom_claims_in_access_token_payload, user_context
-            )
-        )
-
-    def get_global_claim_validators(
-        self,
-        tenant_id: str,
-        user_id: str,
-        claim_validators_added_by_other_recipes: List[SessionClaimValidator],
-        user_context: Dict[str, Any],
-    ) -> MaybeAwaitable[List[SessionClaimValidator]]:
-        return claim_validators_added_by_other_recipes
-
-    async def remove_claim(
-        self,
-        session_handle: str,
-        claim: SessionClaim[Any],
-        user_context: Dict[str, Any],
-    ) -> bool:
-        access_token_payload = claim.remove_from_payload_by_merge_({}, user_context)
-        return await self.merge_into_access_token_payload(
-            session_handle, access_token_payload, user_context
-        )
-
-    async def regenerate_access_token(
-        self,
-        access_token: str,
-        new_access_token_payload: Union[Dict[str, Any], None],
-        user_context: Dict[str, Any],
-    ) -> Union[RegenerateAccessTokenOkResult, None]:
-        if new_access_token_payload is None:
-            new_access_token_payload = {}
-        response = await self.querier.send_post_request(
-            NormalisedURLPath("/recipe/session/regenerate"),
-            {"accessToken": access_token, "userDataInJWT": new_access_token_payload},
-            user_context=user_context,
-        )
-        if response["status"] == "UNAUTHORISED":
-            return None
-        access_token_obj: Union[None, AccessTokenObj] = None
-        if "accessToken" in response:
-            access_token_obj = AccessTokenObj(
-                response["accessToken"]["token"],
-                response["accessToken"]["expiry"],
-                response["accessToken"]["createdTime"],
-            )
-        session = SessionObj(
-            response["session"]["handle"],
-            response["session"]["userId"],
-            response["session"]["userDataInJWT"],
-            response["session"]["tenantId"],
-        )
-        return RegenerateAccessTokenOkResult(session, access_token_obj)
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class RecipeImplementation -(querier: Querier, config: SessionConfig, app_info: AppInfo) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class RecipeImplementation(RecipeInterface):  # pylint: disable=too-many-public-methods
-    def __init__(self, querier: Querier, config: SessionConfig, app_info: AppInfo):
-        super().__init__()
-        self.querier = querier
-        self.config = config
-        self.app_info = app_info
-
-    async def create_new_session(
-        self,
-        user_id: str,
-        access_token_payload: Optional[Dict[str, Any]],
-        session_data_in_database: Optional[Dict[str, Any]],
-        disable_anti_csrf: Optional[bool],
-        tenant_id: str,
-        user_context: Dict[str, Any],
-    ) -> SessionContainer:
-        log_debug_message("createNewSession: Started")
-
-        result = await session_functions.create_new_session(
-            self,
-            tenant_id,
-            user_id,
-            disable_anti_csrf is True,
-            access_token_payload,
-            session_data_in_database,
-            user_context=user_context,
-        )
-        log_debug_message("createNewSession: Finished")
-
-        payload = parse_jwt_without_signature_verification(
-            result.accessToken.token
-        ).payload
-
-        new_session = Session(
-            self,
-            self.config,
-            result.accessToken.token,
-            build_front_token(
-                result.session.userId, result.accessToken.expiry, payload
-            ),
-            result.refreshToken,
-            result.antiCsrfToken,
-            result.session.handle,
-            result.session.userId,
-            payload,
-            None,
-            True,
-            tenant_id,
-        )
-
-        return new_session
-
-    async def validate_claims(
-        self,
-        user_id: str,
-        access_token_payload: Dict[str, Any],
-        claim_validators: List[SessionClaimValidator],
-        user_context: Dict[str, Any],
-    ) -> ClaimsValidationResult:
-        access_token_payload_update = None
-        original_access_token_payload = json.dumps(access_token_payload)
-
-        for validator in claim_validators:
-            log_debug_message(
-                "update_claims_in_payload_if_needed checking should_refetch for %s",
-                validator.id,
-            )
-            if validator.claim is not None and validator.should_refetch(
-                access_token_payload, user_context
-            ):
-                log_debug_message(
-                    "update_claims_in_payload_if_needed refetching for %s", validator.id
-                )
-                value = await resolve(
-                    validator.claim.fetch_value(
-                        user_id,
-                        access_token_payload.get("tId", DEFAULT_TENANT_ID),
-                        user_context,
-                    )
-                )
-                log_debug_message(
-                    "update_claims_in_payload_if_needed %s refetch result %s",
-                    validator.id,
-                    json.dumps(value),
-                )
-                if value is not None:
-                    access_token_payload = validator.claim.add_to_payload_(
-                        access_token_payload, value, user_context
-                    )
-
-        if json.dumps(access_token_payload) != original_access_token_payload:
-            access_token_payload_update = access_token_payload
-
-        invalid_claims = await validate_claims_in_payload(
-            claim_validators, access_token_payload, user_context
-        )
-
-        return ClaimsValidationResult(invalid_claims, access_token_payload_update)
-
-    async def validate_claims_in_jwt_payload(
-        self,
-        user_id: str,
-        jwt_payload: JSONObject,
-        claim_validators: List[SessionClaimValidator],
-        user_context: Dict[str, Any],
-    ) -> ClaimsValidationResult:
-        invalid_claims = await validate_claims_in_payload(
-            claim_validators,
-            jwt_payload,
-            user_context,
-        )
-
-        return ClaimsValidationResult(invalid_claims)
-
-    async def get_session(
-        self,
-        access_token: Optional[str],
-        anti_csrf_token: Optional[str] = None,
-        anti_csrf_check: Optional[bool] = None,
-        session_required: Optional[bool] = None,
-        check_database: Optional[bool] = None,
-        override_global_claim_validators: Optional[
-            Callable[
-                [List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
-                MaybeAwaitable[List[SessionClaimValidator]],
-            ]
-        ] = None,
-        user_context: Optional[Dict[str, Any]] = None,
-    ) -> Optional[SessionContainer]:
-        if (
-            anti_csrf_check is not False
-            and isinstance(self.config.anti_csrf_function_or_string, str)
-            and self.config.anti_csrf_function_or_string == "VIA_CUSTOM_HEADER"
-        ):
-            raise Exception(
-                "Since the anti-csrf mode is VIA_CUSTOM_HEADER getSession can't check the CSRF token. Please either use VIA_TOKEN or set anti_csrf_check to false"
-            )
-
-        log_debug_message("getSession: Started")
-
-        if access_token is None:
-            if session_required is False:
-                log_debug_message(
-                    "getSession: returning None because access_token is undefined and session_required is False"
-                )
-                # there is no session that exists here, and the user wants session verification to be optional. So we return None
-                return None
-
-            log_debug_message(
-                "getSession: UNAUTHORISED because accessToken in request is undefined"
-            )
-            # we do not clear the session here because of a race condition mentioned in https://github.com/supertokens/supertokens-node/issues/17
-            raise UnauthorisedError(
-                "Session does not exist. Are you sending the session tokens in the request with the appropriate token transfer method?",
-                clear_tokens=False,
-            )
-
-        access_token_obj: Optional[ParsedJWTInfo] = None
-        try:
-            access_token_obj = parse_jwt_without_signature_verification(access_token)
-            validate_access_token_structure(
-                access_token_obj.payload, access_token_obj.version
-            )
-        except Exception as _:
-            if session_required is False:
-                log_debug_message(
-                    "getSession: Returning undefined because parsing failed and session_required is False"
-                )
-                return None
-
-            log_debug_message(
-                "getSession: UNAUTHORISED because the accessToken couldn't be parsed or had an invalid structure"
-            )
-            raise UnauthorisedError("Token parsing failed", clear_tokens=False)
-
-        response = await session_functions.get_session(
-            self,
-            access_token_obj,
-            anti_csrf_token,
-            (anti_csrf_check is not False),
-            (check_database is True),
-            user_context,
-        )
-
-        log_debug_message("getSession: Success!")
-
-        if access_token_obj.version >= 3:
-            if response.accessToken is not None:
-                payload = parse_jwt_without_signature_verification(
-                    response.accessToken.token
-                ).payload
-            else:
-                payload = access_token_obj.payload
-        else:
-            payload = response.session.userDataInJWT
-
-        if response.accessToken is not None:
-            access_token_str = response.accessToken.token
-            expiry_time = response.accessToken.expiry
-            access_token_updated = True
-        else:
-            access_token_str = access_token
-            expiry_time = response.session.expiryTime
-            access_token_updated = False
-
-        session = Session(
-            self,
-            self.config,
-            access_token_str,
-            build_front_token(response.session.userId, expiry_time, payload),
-            None,  # refresh_token
-            anti_csrf_token,
-            response.session.handle,
-            response.session.userId,
-            payload,
-            None,
-            access_token_updated,
-            response.session.tenant_id,
-        )
-
-        return session
-
-    async def refresh_session(
-        self,
-        refresh_token: str,
-        anti_csrf_token: Optional[str],
-        disable_anti_csrf: bool,
-        user_context: Dict[str, Any],
-    ) -> SessionContainer:
-        if (
-            disable_anti_csrf is not True
-            and isinstance(self.config.anti_csrf_function_or_string, str)
-            and self.config.anti_csrf_function_or_string == "VIA_CUSTOM_HEADER"
-        ):
-            raise Exception(
-                "Since the anti-csrf mode is VIA_CUSTOM_HEADER getSession can't check the CSRF token. Please either use VIA_TOKEN or set antiCsrfCheck to false"
-            )
-
-        log_debug_message("refreshSession: Started")
-
-        response = await session_functions.refresh_session(
-            self,
-            refresh_token,
-            anti_csrf_token,
-            disable_anti_csrf,
-            self.config.use_dynamic_access_token_signing_key,
-            user_context=user_context,
-        )
-
-        log_debug_message("refreshSession: Success!")
-
-        payload = parse_jwt_without_signature_verification(
-            response.accessToken.token,
-        ).payload
-
-        session = Session(
-            self,
-            self.config,
-            response.accessToken.token,
-            build_front_token(
-                response.session.userId,
-                response.accessToken.expiry,
-                payload,
-            ),
-            response.refreshToken,
-            response.antiCsrfToken,
-            response.session.handle,
-            response.session.userId,
-            user_data_in_access_token=payload,
-            req_res_info=None,
-            access_token_updated=True,
-            tenant_id=payload["tId"],
-        )
-
-        return session
-
-    async def revoke_session(
-        self, session_handle: str, user_context: Dict[str, Any]
-    ) -> bool:
-        return await session_functions.revoke_session(
-            self, session_handle, user_context
-        )
-
-    async def revoke_all_sessions_for_user(
-        self,
-        user_id: str,
-        tenant_id: Optional[str],
-        revoke_across_all_tenants: bool,
-        user_context: Dict[str, Any],
-    ) -> List[str]:
-        return await session_functions.revoke_all_sessions_for_user(
-            self, user_id, tenant_id, revoke_across_all_tenants, user_context
-        )
-
-    async def get_all_session_handles_for_user(
-        self,
-        user_id: str,
-        tenant_id: Optional[str],
-        fetch_across_all_tenants: bool,
-        user_context: Dict[str, Any],
-    ) -> List[str]:
-        return await session_functions.get_all_session_handles_for_user(
-            self, user_id, tenant_id, fetch_across_all_tenants, user_context
-        )
-
-    async def revoke_multiple_sessions(
-        self, session_handles: List[str], user_context: Dict[str, Any]
-    ) -> List[str]:
-        return await session_functions.revoke_multiple_sessions(
-            self, session_handles, user_context
-        )
-
-    async def get_session_information(
-        self, session_handle: str, user_context: Dict[str, Any]
-    ) -> Union[SessionInformationResult, None]:
-        return await session_functions.get_session_information(
-            self, session_handle, user_context
-        )
-
-    async def update_session_data_in_database(
-        self,
-        session_handle: str,
-        new_session_data: Dict[str, Any],
-        user_context: Dict[str, Any],
-    ) -> bool:
-        return await session_functions.update_session_data_in_database(
-            self, session_handle, new_session_data, user_context
-        )
-
-    async def merge_into_access_token_payload(
-        self,
-        session_handle: str,
-        access_token_payload_update: Dict[str, Any],
-        user_context: Dict[str, Any],
-    ) -> bool:
-        session_info = await self.get_session_information(session_handle, user_context)
-        if session_info is None:
-            return False
-
-        new_access_token_payload = session_info.custom_claims_in_access_token_payload
-        for k in protected_props:
-            if k in new_access_token_payload:
-                del new_access_token_payload[k]
-
-        new_access_token_payload = {
-            **new_access_token_payload,
-            **access_token_payload_update,
-        }
-        for k in access_token_payload_update.keys():
-            if new_access_token_payload[k] is None:
-                del new_access_token_payload[k]
-
-        return await session_functions.update_access_token_payload(
-            self, session_handle, new_access_token_payload, user_context
-        )
-
-    async def fetch_and_set_claim(
-        self,
-        session_handle: str,
-        claim: SessionClaim[Any],
-        user_context: Dict[str, Any],
-    ) -> bool:
-        session_info = await self.get_session_information(session_handle, user_context)
-        if session_info is None:
-            return False
-
-        access_token_payload_update = await claim.build(
-            session_info.user_id, session_info.tenant_id, user_context
-        )
-        return await self.merge_into_access_token_payload(
-            session_handle, access_token_payload_update, user_context
-        )
-
-    async def set_claim_value(
-        self,
-        session_handle: str,
-        claim: SessionClaim[Any],
-        value: Any,
-        user_context: Dict[str, Any],
-    ):
-        access_token_payload_update = claim.add_to_payload_({}, value, user_context)
-        return await self.merge_into_access_token_payload(
-            session_handle, access_token_payload_update, user_context
-        )
-
-    async def get_claim_value(
-        self,
-        session_handle: str,
-        claim: SessionClaim[Any],
-        user_context: Dict[str, Any],
-    ) -> Union[SessionDoesNotExistError, GetClaimValueOkResult[Any]]:
-        session_info = await self.get_session_information(session_handle, user_context)
-        if session_info is None:
-            return SessionDoesNotExistError()
-
-        return GetClaimValueOkResult(
-            value=claim.get_value_from_payload(
-                session_info.custom_claims_in_access_token_payload, user_context
-            )
-        )
-
-    def get_global_claim_validators(
-        self,
-        tenant_id: str,
-        user_id: str,
-        claim_validators_added_by_other_recipes: List[SessionClaimValidator],
-        user_context: Dict[str, Any],
-    ) -> MaybeAwaitable[List[SessionClaimValidator]]:
-        return claim_validators_added_by_other_recipes
-
-    async def remove_claim(
-        self,
-        session_handle: str,
-        claim: SessionClaim[Any],
-        user_context: Dict[str, Any],
-    ) -> bool:
-        access_token_payload = claim.remove_from_payload_by_merge_({}, user_context)
-        return await self.merge_into_access_token_payload(
-            session_handle, access_token_payload, user_context
-        )
-
-    async def regenerate_access_token(
-        self,
-        access_token: str,
-        new_access_token_payload: Union[Dict[str, Any], None],
-        user_context: Dict[str, Any],
-    ) -> Union[RegenerateAccessTokenOkResult, None]:
-        if new_access_token_payload is None:
-            new_access_token_payload = {}
-        response = await self.querier.send_post_request(
-            NormalisedURLPath("/recipe/session/regenerate"),
-            {"accessToken": access_token, "userDataInJWT": new_access_token_payload},
-            user_context=user_context,
-        )
-        if response["status"] == "UNAUTHORISED":
-            return None
-        access_token_obj: Union[None, AccessTokenObj] = None
-        if "accessToken" in response:
-            access_token_obj = AccessTokenObj(
-                response["accessToken"]["token"],
-                response["accessToken"]["expiry"],
-                response["accessToken"]["createdTime"],
-            )
-        session = SessionObj(
-            response["session"]["handle"],
-            response["session"]["userId"],
-            response["session"]["userDataInJWT"],
-            response["session"]["tenantId"],
-        )
-        return RegenerateAccessTokenOkResult(session, access_token_obj)
-
-

Ancestors

- -

Methods

-
-
-async def create_new_session(self, user_id: str, access_token_payload: Optional[Dict[str, Any]], session_data_in_database: Optional[Dict[str, Any]], disable_anti_csrf: Optional[bool], tenant_id: str, user_context: Dict[str, Any]) ‑> SessionContainer -
-
-
-
- -Expand source code - -
async def create_new_session(
-    self,
-    user_id: str,
-    access_token_payload: Optional[Dict[str, Any]],
-    session_data_in_database: Optional[Dict[str, Any]],
-    disable_anti_csrf: Optional[bool],
-    tenant_id: str,
-    user_context: Dict[str, Any],
-) -> SessionContainer:
-    log_debug_message("createNewSession: Started")
-
-    result = await session_functions.create_new_session(
-        self,
-        tenant_id,
-        user_id,
-        disable_anti_csrf is True,
-        access_token_payload,
-        session_data_in_database,
-        user_context=user_context,
-    )
-    log_debug_message("createNewSession: Finished")
-
-    payload = parse_jwt_without_signature_verification(
-        result.accessToken.token
-    ).payload
-
-    new_session = Session(
-        self,
-        self.config,
-        result.accessToken.token,
-        build_front_token(
-            result.session.userId, result.accessToken.expiry, payload
-        ),
-        result.refreshToken,
-        result.antiCsrfToken,
-        result.session.handle,
-        result.session.userId,
-        payload,
-        None,
-        True,
-        tenant_id,
-    )
-
-    return new_session
-
-
-
-async def fetch_and_set_claim(self, session_handle: str, claim: SessionClaim[Any], user_context: Dict[str, Any]) ‑> bool -
-
-
-
- -Expand source code - -
async def fetch_and_set_claim(
-    self,
-    session_handle: str,
-    claim: SessionClaim[Any],
-    user_context: Dict[str, Any],
-) -> bool:
-    session_info = await self.get_session_information(session_handle, user_context)
-    if session_info is None:
-        return False
-
-    access_token_payload_update = await claim.build(
-        session_info.user_id, session_info.tenant_id, user_context
-    )
-    return await self.merge_into_access_token_payload(
-        session_handle, access_token_payload_update, user_context
-    )
-
-
-
-async def get_all_session_handles_for_user(self, user_id: str, tenant_id: Optional[str], fetch_across_all_tenants: bool, user_context: Dict[str, Any]) ‑> List[str] -
-
-
-
- -Expand source code - -
async def get_all_session_handles_for_user(
-    self,
-    user_id: str,
-    tenant_id: Optional[str],
-    fetch_across_all_tenants: bool,
-    user_context: Dict[str, Any],
-) -> List[str]:
-    return await session_functions.get_all_session_handles_for_user(
-        self, user_id, tenant_id, fetch_across_all_tenants, user_context
-    )
-
-
-
-async def get_claim_value(self, session_handle: str, claim: SessionClaim[Any], user_context: Dict[str, Any]) ‑> Union[SessionDoesNotExistError, GetClaimValueOkResult[Any]] -
-
-
-
- -Expand source code - -
async def get_claim_value(
-    self,
-    session_handle: str,
-    claim: SessionClaim[Any],
-    user_context: Dict[str, Any],
-) -> Union[SessionDoesNotExistError, GetClaimValueOkResult[Any]]:
-    session_info = await self.get_session_information(session_handle, user_context)
-    if session_info is None:
-        return SessionDoesNotExistError()
-
-    return GetClaimValueOkResult(
-        value=claim.get_value_from_payload(
-            session_info.custom_claims_in_access_token_payload, user_context
-        )
-    )
-
-
-
-def get_global_claim_validators(self, tenant_id: str, user_id: str, claim_validators_added_by_other_recipes: List[SessionClaimValidator], user_context: Dict[str, Any]) ‑> MaybeAwaitable[List[SessionClaimValidator]] -
-
-
-
- -Expand source code - -
def get_global_claim_validators(
-    self,
-    tenant_id: str,
-    user_id: str,
-    claim_validators_added_by_other_recipes: List[SessionClaimValidator],
-    user_context: Dict[str, Any],
-) -> MaybeAwaitable[List[SessionClaimValidator]]:
-    return claim_validators_added_by_other_recipes
-
-
-
-async def get_session(self, access_token: Optional[str], anti_csrf_token: Optional[str] = None, anti_csrf_check: Optional[bool] = None, session_required: Optional[bool] = None, check_database: Optional[bool] = None, override_global_claim_validators: Optional[Callable[[List[SessionClaimValidator], SessionContainer, Dict[str, Any]], MaybeAwaitable[List[SessionClaimValidator]]]] = None, user_context: Optional[Dict[str, Any]] = None) ‑> Optional[SessionContainer] -
-
-
-
- -Expand source code - -
async def get_session(
-    self,
-    access_token: Optional[str],
-    anti_csrf_token: Optional[str] = None,
-    anti_csrf_check: Optional[bool] = None,
-    session_required: Optional[bool] = None,
-    check_database: Optional[bool] = None,
-    override_global_claim_validators: Optional[
-        Callable[
-            [List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
-            MaybeAwaitable[List[SessionClaimValidator]],
-        ]
-    ] = None,
-    user_context: Optional[Dict[str, Any]] = None,
-) -> Optional[SessionContainer]:
-    if (
-        anti_csrf_check is not False
-        and isinstance(self.config.anti_csrf_function_or_string, str)
-        and self.config.anti_csrf_function_or_string == "VIA_CUSTOM_HEADER"
-    ):
-        raise Exception(
-            "Since the anti-csrf mode is VIA_CUSTOM_HEADER getSession can't check the CSRF token. Please either use VIA_TOKEN or set anti_csrf_check to false"
-        )
-
-    log_debug_message("getSession: Started")
-
-    if access_token is None:
-        if session_required is False:
-            log_debug_message(
-                "getSession: returning None because access_token is undefined and session_required is False"
-            )
-            # there is no session that exists here, and the user wants session verification to be optional. So we return None
-            return None
-
-        log_debug_message(
-            "getSession: UNAUTHORISED because accessToken in request is undefined"
-        )
-        # we do not clear the session here because of a race condition mentioned in https://github.com/supertokens/supertokens-node/issues/17
-        raise UnauthorisedError(
-            "Session does not exist. Are you sending the session tokens in the request with the appropriate token transfer method?",
-            clear_tokens=False,
-        )
-
-    access_token_obj: Optional[ParsedJWTInfo] = None
-    try:
-        access_token_obj = parse_jwt_without_signature_verification(access_token)
-        validate_access_token_structure(
-            access_token_obj.payload, access_token_obj.version
-        )
-    except Exception as _:
-        if session_required is False:
-            log_debug_message(
-                "getSession: Returning undefined because parsing failed and session_required is False"
-            )
-            return None
-
-        log_debug_message(
-            "getSession: UNAUTHORISED because the accessToken couldn't be parsed or had an invalid structure"
-        )
-        raise UnauthorisedError("Token parsing failed", clear_tokens=False)
-
-    response = await session_functions.get_session(
-        self,
-        access_token_obj,
-        anti_csrf_token,
-        (anti_csrf_check is not False),
-        (check_database is True),
-        user_context,
-    )
-
-    log_debug_message("getSession: Success!")
-
-    if access_token_obj.version >= 3:
-        if response.accessToken is not None:
-            payload = parse_jwt_without_signature_verification(
-                response.accessToken.token
-            ).payload
-        else:
-            payload = access_token_obj.payload
-    else:
-        payload = response.session.userDataInJWT
-
-    if response.accessToken is not None:
-        access_token_str = response.accessToken.token
-        expiry_time = response.accessToken.expiry
-        access_token_updated = True
-    else:
-        access_token_str = access_token
-        expiry_time = response.session.expiryTime
-        access_token_updated = False
-
-    session = Session(
-        self,
-        self.config,
-        access_token_str,
-        build_front_token(response.session.userId, expiry_time, payload),
-        None,  # refresh_token
-        anti_csrf_token,
-        response.session.handle,
-        response.session.userId,
-        payload,
-        None,
-        access_token_updated,
-        response.session.tenant_id,
-    )
-
-    return session
-
-
-
-async def get_session_information(self, session_handle: str, user_context: Dict[str, Any]) ‑> Union[SessionInformationResult, None] -
-
-
-
- -Expand source code - -
async def get_session_information(
-    self, session_handle: str, user_context: Dict[str, Any]
-) -> Union[SessionInformationResult, None]:
-    return await session_functions.get_session_information(
-        self, session_handle, user_context
-    )
-
-
-
-async def merge_into_access_token_payload(self, session_handle: str, access_token_payload_update: Dict[str, Any], user_context: Dict[str, Any]) ‑> bool -
-
-
-
- -Expand source code - -
async def merge_into_access_token_payload(
-    self,
-    session_handle: str,
-    access_token_payload_update: Dict[str, Any],
-    user_context: Dict[str, Any],
-) -> bool:
-    session_info = await self.get_session_information(session_handle, user_context)
-    if session_info is None:
-        return False
-
-    new_access_token_payload = session_info.custom_claims_in_access_token_payload
-    for k in protected_props:
-        if k in new_access_token_payload:
-            del new_access_token_payload[k]
-
-    new_access_token_payload = {
-        **new_access_token_payload,
-        **access_token_payload_update,
-    }
-    for k in access_token_payload_update.keys():
-        if new_access_token_payload[k] is None:
-            del new_access_token_payload[k]
-
-    return await session_functions.update_access_token_payload(
-        self, session_handle, new_access_token_payload, user_context
-    )
-
-
-
-async def refresh_session(self, refresh_token: str, anti_csrf_token: Optional[str], disable_anti_csrf: bool, user_context: Dict[str, Any]) ‑> SessionContainer -
-
-
-
- -Expand source code - -
async def refresh_session(
-    self,
-    refresh_token: str,
-    anti_csrf_token: Optional[str],
-    disable_anti_csrf: bool,
-    user_context: Dict[str, Any],
-) -> SessionContainer:
-    if (
-        disable_anti_csrf is not True
-        and isinstance(self.config.anti_csrf_function_or_string, str)
-        and self.config.anti_csrf_function_or_string == "VIA_CUSTOM_HEADER"
-    ):
-        raise Exception(
-            "Since the anti-csrf mode is VIA_CUSTOM_HEADER getSession can't check the CSRF token. Please either use VIA_TOKEN or set antiCsrfCheck to false"
-        )
-
-    log_debug_message("refreshSession: Started")
-
-    response = await session_functions.refresh_session(
-        self,
-        refresh_token,
-        anti_csrf_token,
-        disable_anti_csrf,
-        self.config.use_dynamic_access_token_signing_key,
-        user_context=user_context,
-    )
-
-    log_debug_message("refreshSession: Success!")
-
-    payload = parse_jwt_without_signature_verification(
-        response.accessToken.token,
-    ).payload
-
-    session = Session(
-        self,
-        self.config,
-        response.accessToken.token,
-        build_front_token(
-            response.session.userId,
-            response.accessToken.expiry,
-            payload,
-        ),
-        response.refreshToken,
-        response.antiCsrfToken,
-        response.session.handle,
-        response.session.userId,
-        user_data_in_access_token=payload,
-        req_res_info=None,
-        access_token_updated=True,
-        tenant_id=payload["tId"],
-    )
-
-    return session
-
-
-
-async def regenerate_access_token(self, access_token: str, new_access_token_payload: Union[Dict[str, Any], None], user_context: Dict[str, Any]) ‑> Union[RegenerateAccessTokenOkResult, None] -
-
-
-
- -Expand source code - -
async def regenerate_access_token(
-    self,
-    access_token: str,
-    new_access_token_payload: Union[Dict[str, Any], None],
-    user_context: Dict[str, Any],
-) -> Union[RegenerateAccessTokenOkResult, None]:
-    if new_access_token_payload is None:
-        new_access_token_payload = {}
-    response = await self.querier.send_post_request(
-        NormalisedURLPath("/recipe/session/regenerate"),
-        {"accessToken": access_token, "userDataInJWT": new_access_token_payload},
-        user_context=user_context,
-    )
-    if response["status"] == "UNAUTHORISED":
-        return None
-    access_token_obj: Union[None, AccessTokenObj] = None
-    if "accessToken" in response:
-        access_token_obj = AccessTokenObj(
-            response["accessToken"]["token"],
-            response["accessToken"]["expiry"],
-            response["accessToken"]["createdTime"],
-        )
-    session = SessionObj(
-        response["session"]["handle"],
-        response["session"]["userId"],
-        response["session"]["userDataInJWT"],
-        response["session"]["tenantId"],
-    )
-    return RegenerateAccessTokenOkResult(session, access_token_obj)
-
-
-
-async def remove_claim(self, session_handle: str, claim: SessionClaim[Any], user_context: Dict[str, Any]) ‑> bool -
-
-
-
- -Expand source code - -
async def remove_claim(
-    self,
-    session_handle: str,
-    claim: SessionClaim[Any],
-    user_context: Dict[str, Any],
-) -> bool:
-    access_token_payload = claim.remove_from_payload_by_merge_({}, user_context)
-    return await self.merge_into_access_token_payload(
-        session_handle, access_token_payload, user_context
-    )
-
-
-
-async def revoke_all_sessions_for_user(self, user_id: str, tenant_id: Optional[str], revoke_across_all_tenants: bool, user_context: Dict[str, Any]) ‑> List[str] -
-
-
-
- -Expand source code - -
async def revoke_all_sessions_for_user(
-    self,
-    user_id: str,
-    tenant_id: Optional[str],
-    revoke_across_all_tenants: bool,
-    user_context: Dict[str, Any],
-) -> List[str]:
-    return await session_functions.revoke_all_sessions_for_user(
-        self, user_id, tenant_id, revoke_across_all_tenants, user_context
-    )
-
-
-
-async def revoke_multiple_sessions(self, session_handles: List[str], user_context: Dict[str, Any]) ‑> List[str] -
-
-
-
- -Expand source code - -
async def revoke_multiple_sessions(
-    self, session_handles: List[str], user_context: Dict[str, Any]
-) -> List[str]:
-    return await session_functions.revoke_multiple_sessions(
-        self, session_handles, user_context
-    )
-
-
-
-async def revoke_session(self, session_handle: str, user_context: Dict[str, Any]) ‑> bool -
-
-
-
- -Expand source code - -
async def revoke_session(
-    self, session_handle: str, user_context: Dict[str, Any]
-) -> bool:
-    return await session_functions.revoke_session(
-        self, session_handle, user_context
-    )
-
-
-
-async def set_claim_value(self, session_handle: str, claim: SessionClaim[Any], value: Any, user_context: Dict[str, Any]) -
-
-
-
- -Expand source code - -
async def set_claim_value(
-    self,
-    session_handle: str,
-    claim: SessionClaim[Any],
-    value: Any,
-    user_context: Dict[str, Any],
-):
-    access_token_payload_update = claim.add_to_payload_({}, value, user_context)
-    return await self.merge_into_access_token_payload(
-        session_handle, access_token_payload_update, user_context
-    )
-
-
-
-async def update_session_data_in_database(self, session_handle: str, new_session_data: Dict[str, Any], user_context: Dict[str, Any]) ‑> bool -
-
-
-
- -Expand source code - -
async def update_session_data_in_database(
-    self,
-    session_handle: str,
-    new_session_data: Dict[str, Any],
-    user_context: Dict[str, Any],
-) -> bool:
-    return await session_functions.update_session_data_in_database(
-        self, session_handle, new_session_data, user_context
-    )
-
-
-
-async def validate_claims(self, user_id: str, access_token_payload: Dict[str, Any], claim_validators: List[SessionClaimValidator], user_context: Dict[str, Any]) ‑> ClaimsValidationResult -
-
-
-
- -Expand source code - -
async def validate_claims(
-    self,
-    user_id: str,
-    access_token_payload: Dict[str, Any],
-    claim_validators: List[SessionClaimValidator],
-    user_context: Dict[str, Any],
-) -> ClaimsValidationResult:
-    access_token_payload_update = None
-    original_access_token_payload = json.dumps(access_token_payload)
-
-    for validator in claim_validators:
-        log_debug_message(
-            "update_claims_in_payload_if_needed checking should_refetch for %s",
-            validator.id,
-        )
-        if validator.claim is not None and validator.should_refetch(
-            access_token_payload, user_context
-        ):
-            log_debug_message(
-                "update_claims_in_payload_if_needed refetching for %s", validator.id
-            )
-            value = await resolve(
-                validator.claim.fetch_value(
-                    user_id,
-                    access_token_payload.get("tId", DEFAULT_TENANT_ID),
-                    user_context,
-                )
-            )
-            log_debug_message(
-                "update_claims_in_payload_if_needed %s refetch result %s",
-                validator.id,
-                json.dumps(value),
-            )
-            if value is not None:
-                access_token_payload = validator.claim.add_to_payload_(
-                    access_token_payload, value, user_context
-                )
-
-    if json.dumps(access_token_payload) != original_access_token_payload:
-        access_token_payload_update = access_token_payload
-
-    invalid_claims = await validate_claims_in_payload(
-        claim_validators, access_token_payload, user_context
-    )
-
-    return ClaimsValidationResult(invalid_claims, access_token_payload_update)
-
-
-
-async def validate_claims_in_jwt_payload(self, user_id: str, jwt_payload: JSONObject, claim_validators: List[SessionClaimValidator], user_context: Dict[str, Any]) ‑> ClaimsValidationResult -
-
-
-
- -Expand source code - -
async def validate_claims_in_jwt_payload(
-    self,
-    user_id: str,
-    jwt_payload: JSONObject,
-    claim_validators: List[SessionClaimValidator],
-    user_context: Dict[str, Any],
-) -> ClaimsValidationResult:
-    invalid_claims = await validate_claims_in_payload(
-        claim_validators,
-        jwt_payload,
-        user_context,
-    )
-
-    return ClaimsValidationResult(invalid_claims)
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/session/session_class.html b/html/supertokens_python/recipe/session/session_class.html deleted file mode 100644 index 95eb665c4..000000000 --- a/html/supertokens_python/recipe/session/session_class.html +++ /dev/null @@ -1,1208 +0,0 @@ - - - - - - -supertokens_python.recipe.session.session_class API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.session.session_class

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from typing import Any, Dict, List, Optional, TypeVar, Union
-
-from supertokens_python.recipe.session.exceptions import (
-    raise_invalid_claims_exception,
-    raise_unauthorised_exception,
-)
-from .jwt import parse_jwt_without_signature_verification
-from .utils import TokenTransferMethod
-
-from .cookie_and_header import (
-    clear_session_response_mutator,
-    token_response_mutator,
-    build_front_token,
-    anti_csrf_response_mutator,
-    access_token_mutator,
-)
-from .interfaces import (
-    ReqResInfo,
-    SessionClaim,
-    SessionClaimValidator,
-    SessionContainer,
-    GetSessionTokensDangerouslyDict,
-)
-from .constants import protected_props
-from ...framework import BaseRequest
-from supertokens_python.utils import log_debug_message
-
-_T = TypeVar("_T")
-
-
-class Session(SessionContainer):
-    async def attach_to_request_response(
-        self,
-        request: BaseRequest,
-        transfer_method: TokenTransferMethod,
-        user_context: Optional[Dict[str, Any]],
-    ) -> None:
-        self.req_res_info = ReqResInfo(request, transfer_method)
-
-        if self.access_token_updated:
-            self.response_mutators.append(
-                access_token_mutator(
-                    self.access_token,
-                    self.front_token,
-                    self.config,
-                    transfer_method,
-                    request,
-                )
-            )
-            if self.refresh_token is not None:
-                self.response_mutators.append(
-                    token_response_mutator(
-                        self.config,
-                        "refresh",
-                        self.refresh_token.token,
-                        self.refresh_token.expiry,
-                        transfer_method,
-                        request,
-                    )
-                )
-            if self.anti_csrf_token is not None:
-                self.response_mutators.append(
-                    anti_csrf_response_mutator(self.anti_csrf_token)
-                )
-
-        request.set_session(
-            self
-        )  # Although this is called in recipe/session/framework/**/__init__.py. It's required in case of python because functions like create_new_session(req, "user-id") can be called in the framework view handler as well
-
-    async def revoke_session(self, user_context: Union[Any, None] = None) -> None:
-        if user_context is None:
-            user_context = {}
-
-        await self.recipe_implementation.revoke_session(
-            self.session_handle, user_context
-        )
-
-        if self.req_res_info is not None:
-            # we do not check the output of calling revokeSession
-            # before clearing the cookies because we are revoking the
-            # current API request's session.
-            # If we instead clear the cookies only when revokeSession
-            # returns true, it can cause this kind of bug:
-            # https://github.com/supertokens/supertokens-node/issues/343
-            transfer_method: TokenTransferMethod = self.req_res_info.transfer_method  # type: ignore
-            self.response_mutators.append(
-                clear_session_response_mutator(
-                    self.config,
-                    transfer_method,
-                    self.req_res_info.request,
-                )
-            )
-
-    async def get_session_data_from_database(
-        self, user_context: Union[Dict[str, Any], None] = None
-    ) -> Dict[str, Any]:
-        if user_context is None:
-            user_context = {}
-        session_info = await self.recipe_implementation.get_session_information(
-            self.session_handle, user_context
-        )
-        if session_info is None:
-            log_debug_message(
-                "getSessionDataFromDatabase: Throwing UNAUTHORISED because session does not exist anymore"
-            )
-            raise_unauthorised_exception("Session does not exist anymore.")
-
-        return session_info.session_data_in_database
-
-    async def update_session_data_in_database(
-        self,
-        new_session_data: Dict[str, Any],
-        user_context: Union[Dict[str, Any], None] = None,
-    ) -> None:
-        if user_context is None:
-            user_context = {}
-        updated = await self.recipe_implementation.update_session_data_in_database(
-            self.session_handle, new_session_data, user_context
-        )
-        if not updated:
-            log_debug_message(
-                "updateSessionDataInDatabase: Throwing UNAUTHORISED because session does not exist anymore"
-            )
-            raise_unauthorised_exception("Session does not exist anymore.")
-
-    def get_user_id(self, user_context: Union[Dict[str, Any], None] = None) -> str:
-        return self.user_id
-
-    def get_tenant_id(self, user_context: Union[Dict[str, Any], None] = None) -> str:
-        return self.tenant_id
-
-    def get_access_token_payload(
-        self, user_context: Union[Dict[str, Any], None] = None
-    ) -> Dict[str, Any]:
-        return self.user_data_in_access_token
-
-    def get_handle(self, user_context: Union[Dict[str, Any], None] = None) -> str:
-        return self.session_handle
-
-    def get_access_token(self, user_context: Union[Dict[str, Any], None] = None) -> str:
-        return self.access_token
-
-    def get_all_session_tokens_dangerously(self) -> GetSessionTokensDangerouslyDict:
-        return {
-            "accessToken": self.access_token,
-            "accessAndFrontTokenUpdated": self.access_token_updated,
-            "refreshToken": None
-            if self.refresh_token is None
-            else self.refresh_token.token,
-            "frontToken": self.front_token,
-            "antiCsrfToken": self.anti_csrf_token,
-        }
-
-    async def get_time_created(
-        self, user_context: Union[Dict[str, Any], None] = None
-    ) -> int:
-        if user_context is None:
-            user_context = {}
-        session_info = await self.recipe_implementation.get_session_information(
-            self.session_handle, user_context
-        )
-        if session_info is None:
-            log_debug_message(
-                "getTimeCreated: Throwing UNAUTHORISED because session does not exist anymore"
-            )
-            raise_unauthorised_exception("Session does not exist anymore.")
-
-        return session_info.time_created
-
-    async def get_expiry(self, user_context: Union[Dict[str, Any], None] = None) -> int:
-        if user_context is None:
-            user_context = {}
-        session_info = await self.recipe_implementation.get_session_information(
-            self.session_handle, user_context
-        )
-        if session_info is None:
-            log_debug_message(
-                "getExpiry: Throwing UNAUTHORISED because session does not exist anymore"
-            )
-            raise_unauthorised_exception("Session does not exist anymore.")
-
-        return session_info.expiry
-
-    async def assert_claims(
-        self,
-        claim_validators: List[SessionClaimValidator],
-        user_context: Union[Dict[str, Any], None] = None,
-    ) -> None:
-        if user_context is None:
-            user_context = {}
-
-        validate_claim_res = await self.recipe_implementation.validate_claims(
-            self.get_user_id(user_context),
-            self.get_access_token_payload(user_context),
-            claim_validators,
-            user_context,
-        )
-
-        if validate_claim_res.access_token_payload_update is not None:
-            for k in protected_props:
-                try:
-                    del validate_claim_res.access_token_payload_update[k]
-                except KeyError:
-                    pass
-            await self.merge_into_access_token_payload(
-                validate_claim_res.access_token_payload_update, user_context
-            )
-
-        validation_errors = validate_claim_res.invalid_claims
-        if len(validation_errors) > 0:
-            raise_invalid_claims_exception("INVALID_CLAIMS", validation_errors)
-
-    async def fetch_and_set_claim(
-        self, claim: SessionClaim[Any], user_context: Union[Dict[str, Any], None] = None
-    ) -> None:
-        if user_context is None:
-            user_context = {}
-
-        update = await claim.build(
-            self.get_user_id(), self.get_tenant_id(), user_context
-        )
-        return await self.merge_into_access_token_payload(update, user_context)
-
-    async def set_claim_value(
-        self,
-        claim: SessionClaim[_T],
-        value: _T,
-        user_context: Union[Dict[str, Any], None] = None,
-    ) -> None:
-        if user_context is None:
-            user_context = {}
-
-        update = claim.add_to_payload_({}, value, user_context)
-        return await self.merge_into_access_token_payload(update, user_context)
-
-    async def get_claim_value(
-        self, claim: SessionClaim[_T], user_context: Union[Dict[str, Any], None] = None
-    ) -> Union[_T, None]:
-        if user_context is None:
-            user_context = {}
-
-        return claim.get_value_from_payload(
-            self.get_access_token_payload(user_context), user_context
-        )
-
-    async def remove_claim(
-        self, claim: SessionClaim[Any], user_context: Union[Dict[str, Any], None] = None
-    ) -> None:
-        if user_context is None:
-            user_context = {}
-
-        update = claim.remove_from_payload_by_merge_({}, user_context)
-        return await self.merge_into_access_token_payload(update, user_context)
-
-    async def merge_into_access_token_payload(
-        self,
-        access_token_payload_update: Dict[str, Any],
-        user_context: Union[Dict[str, Any], None] = None,
-    ) -> None:
-        if user_context is None:
-            user_context = {}
-
-        new_access_token_payload = {**self.get_access_token_payload(user_context)}
-        for k in protected_props:
-            try:
-                del new_access_token_payload[k]
-            except KeyError:
-                pass
-
-        new_access_token_payload = {
-            **new_access_token_payload,
-            **access_token_payload_update,
-        }
-
-        for k in access_token_payload_update.keys():
-            if access_token_payload_update[k] is None:
-                del new_access_token_payload[k]
-
-        response = await self.recipe_implementation.regenerate_access_token(
-            self.get_access_token(), new_access_token_payload, user_context
-        )
-
-        if response is None:
-            log_debug_message(
-                "mergeIntoAccessTokenPayload: Throwing UNAUTHORISED because session does not exist anymore"
-            )
-            raise_unauthorised_exception("Session does not exist anymore.")
-
-        if response.access_token is not None:
-            resp_token = parse_jwt_without_signature_verification(
-                response.access_token.token
-            )
-            payload = (
-                resp_token.payload
-                if resp_token.version >= 3
-                else response.session.user_data_in_jwt
-            )
-            self.user_data_in_access_token = payload
-            self.access_token = response.access_token.token
-            self.front_token = build_front_token(
-                self.get_user_id(), response.access_token.expiry, payload
-            )
-            self.access_token_updated = True
-            if self.req_res_info is not None:
-                transfer_method: TokenTransferMethod = self.req_res_info.transfer_method  # type: ignore
-                self.response_mutators.append(
-                    access_token_mutator(
-                        self.access_token,
-                        self.front_token,
-                        self.config,
-                        transfer_method,
-                        self.req_res_info.request,
-                    )
-                )
-        else:
-            # This case means that the access token has expired between the validation and this update
-            # We can't update the access token on the FE, as it will need to call refresh anyway but we handle this as a successful update during this request
-            # the changes will be reflected on the FE after refresh is called
-            self.user_data_in_access_token = {
-                **self.get_access_token_payload(),
-                **response.session.user_data_in_jwt,
-            }
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class Session -(recipe_implementation: RecipeInterface, config: SessionConfig, access_token: str, front_token: str, refresh_token: Optional[TokenInfo], anti_csrf_token: Optional[str], session_handle: str, user_id: str, user_data_in_access_token: Optional[Dict[str, Any]], req_res_info: Optional[ReqResInfo], access_token_updated: bool, tenant_id: str) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class Session(SessionContainer):
-    async def attach_to_request_response(
-        self,
-        request: BaseRequest,
-        transfer_method: TokenTransferMethod,
-        user_context: Optional[Dict[str, Any]],
-    ) -> None:
-        self.req_res_info = ReqResInfo(request, transfer_method)
-
-        if self.access_token_updated:
-            self.response_mutators.append(
-                access_token_mutator(
-                    self.access_token,
-                    self.front_token,
-                    self.config,
-                    transfer_method,
-                    request,
-                )
-            )
-            if self.refresh_token is not None:
-                self.response_mutators.append(
-                    token_response_mutator(
-                        self.config,
-                        "refresh",
-                        self.refresh_token.token,
-                        self.refresh_token.expiry,
-                        transfer_method,
-                        request,
-                    )
-                )
-            if self.anti_csrf_token is not None:
-                self.response_mutators.append(
-                    anti_csrf_response_mutator(self.anti_csrf_token)
-                )
-
-        request.set_session(
-            self
-        )  # Although this is called in recipe/session/framework/**/__init__.py. It's required in case of python because functions like create_new_session(req, "user-id") can be called in the framework view handler as well
-
-    async def revoke_session(self, user_context: Union[Any, None] = None) -> None:
-        if user_context is None:
-            user_context = {}
-
-        await self.recipe_implementation.revoke_session(
-            self.session_handle, user_context
-        )
-
-        if self.req_res_info is not None:
-            # we do not check the output of calling revokeSession
-            # before clearing the cookies because we are revoking the
-            # current API request's session.
-            # If we instead clear the cookies only when revokeSession
-            # returns true, it can cause this kind of bug:
-            # https://github.com/supertokens/supertokens-node/issues/343
-            transfer_method: TokenTransferMethod = self.req_res_info.transfer_method  # type: ignore
-            self.response_mutators.append(
-                clear_session_response_mutator(
-                    self.config,
-                    transfer_method,
-                    self.req_res_info.request,
-                )
-            )
-
-    async def get_session_data_from_database(
-        self, user_context: Union[Dict[str, Any], None] = None
-    ) -> Dict[str, Any]:
-        if user_context is None:
-            user_context = {}
-        session_info = await self.recipe_implementation.get_session_information(
-            self.session_handle, user_context
-        )
-        if session_info is None:
-            log_debug_message(
-                "getSessionDataFromDatabase: Throwing UNAUTHORISED because session does not exist anymore"
-            )
-            raise_unauthorised_exception("Session does not exist anymore.")
-
-        return session_info.session_data_in_database
-
-    async def update_session_data_in_database(
-        self,
-        new_session_data: Dict[str, Any],
-        user_context: Union[Dict[str, Any], None] = None,
-    ) -> None:
-        if user_context is None:
-            user_context = {}
-        updated = await self.recipe_implementation.update_session_data_in_database(
-            self.session_handle, new_session_data, user_context
-        )
-        if not updated:
-            log_debug_message(
-                "updateSessionDataInDatabase: Throwing UNAUTHORISED because session does not exist anymore"
-            )
-            raise_unauthorised_exception("Session does not exist anymore.")
-
-    def get_user_id(self, user_context: Union[Dict[str, Any], None] = None) -> str:
-        return self.user_id
-
-    def get_tenant_id(self, user_context: Union[Dict[str, Any], None] = None) -> str:
-        return self.tenant_id
-
-    def get_access_token_payload(
-        self, user_context: Union[Dict[str, Any], None] = None
-    ) -> Dict[str, Any]:
-        return self.user_data_in_access_token
-
-    def get_handle(self, user_context: Union[Dict[str, Any], None] = None) -> str:
-        return self.session_handle
-
-    def get_access_token(self, user_context: Union[Dict[str, Any], None] = None) -> str:
-        return self.access_token
-
-    def get_all_session_tokens_dangerously(self) -> GetSessionTokensDangerouslyDict:
-        return {
-            "accessToken": self.access_token,
-            "accessAndFrontTokenUpdated": self.access_token_updated,
-            "refreshToken": None
-            if self.refresh_token is None
-            else self.refresh_token.token,
-            "frontToken": self.front_token,
-            "antiCsrfToken": self.anti_csrf_token,
-        }
-
-    async def get_time_created(
-        self, user_context: Union[Dict[str, Any], None] = None
-    ) -> int:
-        if user_context is None:
-            user_context = {}
-        session_info = await self.recipe_implementation.get_session_information(
-            self.session_handle, user_context
-        )
-        if session_info is None:
-            log_debug_message(
-                "getTimeCreated: Throwing UNAUTHORISED because session does not exist anymore"
-            )
-            raise_unauthorised_exception("Session does not exist anymore.")
-
-        return session_info.time_created
-
-    async def get_expiry(self, user_context: Union[Dict[str, Any], None] = None) -> int:
-        if user_context is None:
-            user_context = {}
-        session_info = await self.recipe_implementation.get_session_information(
-            self.session_handle, user_context
-        )
-        if session_info is None:
-            log_debug_message(
-                "getExpiry: Throwing UNAUTHORISED because session does not exist anymore"
-            )
-            raise_unauthorised_exception("Session does not exist anymore.")
-
-        return session_info.expiry
-
-    async def assert_claims(
-        self,
-        claim_validators: List[SessionClaimValidator],
-        user_context: Union[Dict[str, Any], None] = None,
-    ) -> None:
-        if user_context is None:
-            user_context = {}
-
-        validate_claim_res = await self.recipe_implementation.validate_claims(
-            self.get_user_id(user_context),
-            self.get_access_token_payload(user_context),
-            claim_validators,
-            user_context,
-        )
-
-        if validate_claim_res.access_token_payload_update is not None:
-            for k in protected_props:
-                try:
-                    del validate_claim_res.access_token_payload_update[k]
-                except KeyError:
-                    pass
-            await self.merge_into_access_token_payload(
-                validate_claim_res.access_token_payload_update, user_context
-            )
-
-        validation_errors = validate_claim_res.invalid_claims
-        if len(validation_errors) > 0:
-            raise_invalid_claims_exception("INVALID_CLAIMS", validation_errors)
-
-    async def fetch_and_set_claim(
-        self, claim: SessionClaim[Any], user_context: Union[Dict[str, Any], None] = None
-    ) -> None:
-        if user_context is None:
-            user_context = {}
-
-        update = await claim.build(
-            self.get_user_id(), self.get_tenant_id(), user_context
-        )
-        return await self.merge_into_access_token_payload(update, user_context)
-
-    async def set_claim_value(
-        self,
-        claim: SessionClaim[_T],
-        value: _T,
-        user_context: Union[Dict[str, Any], None] = None,
-    ) -> None:
-        if user_context is None:
-            user_context = {}
-
-        update = claim.add_to_payload_({}, value, user_context)
-        return await self.merge_into_access_token_payload(update, user_context)
-
-    async def get_claim_value(
-        self, claim: SessionClaim[_T], user_context: Union[Dict[str, Any], None] = None
-    ) -> Union[_T, None]:
-        if user_context is None:
-            user_context = {}
-
-        return claim.get_value_from_payload(
-            self.get_access_token_payload(user_context), user_context
-        )
-
-    async def remove_claim(
-        self, claim: SessionClaim[Any], user_context: Union[Dict[str, Any], None] = None
-    ) -> None:
-        if user_context is None:
-            user_context = {}
-
-        update = claim.remove_from_payload_by_merge_({}, user_context)
-        return await self.merge_into_access_token_payload(update, user_context)
-
-    async def merge_into_access_token_payload(
-        self,
-        access_token_payload_update: Dict[str, Any],
-        user_context: Union[Dict[str, Any], None] = None,
-    ) -> None:
-        if user_context is None:
-            user_context = {}
-
-        new_access_token_payload = {**self.get_access_token_payload(user_context)}
-        for k in protected_props:
-            try:
-                del new_access_token_payload[k]
-            except KeyError:
-                pass
-
-        new_access_token_payload = {
-            **new_access_token_payload,
-            **access_token_payload_update,
-        }
-
-        for k in access_token_payload_update.keys():
-            if access_token_payload_update[k] is None:
-                del new_access_token_payload[k]
-
-        response = await self.recipe_implementation.regenerate_access_token(
-            self.get_access_token(), new_access_token_payload, user_context
-        )
-
-        if response is None:
-            log_debug_message(
-                "mergeIntoAccessTokenPayload: Throwing UNAUTHORISED because session does not exist anymore"
-            )
-            raise_unauthorised_exception("Session does not exist anymore.")
-
-        if response.access_token is not None:
-            resp_token = parse_jwt_without_signature_verification(
-                response.access_token.token
-            )
-            payload = (
-                resp_token.payload
-                if resp_token.version >= 3
-                else response.session.user_data_in_jwt
-            )
-            self.user_data_in_access_token = payload
-            self.access_token = response.access_token.token
-            self.front_token = build_front_token(
-                self.get_user_id(), response.access_token.expiry, payload
-            )
-            self.access_token_updated = True
-            if self.req_res_info is not None:
-                transfer_method: TokenTransferMethod = self.req_res_info.transfer_method  # type: ignore
-                self.response_mutators.append(
-                    access_token_mutator(
-                        self.access_token,
-                        self.front_token,
-                        self.config,
-                        transfer_method,
-                        self.req_res_info.request,
-                    )
-                )
-        else:
-            # This case means that the access token has expired between the validation and this update
-            # We can't update the access token on the FE, as it will need to call refresh anyway but we handle this as a successful update during this request
-            # the changes will be reflected on the FE after refresh is called
-            self.user_data_in_access_token = {
-                **self.get_access_token_payload(),
-                **response.session.user_data_in_jwt,
-            }
-
-

Ancestors

- -

Methods

-
-
-async def assert_claims(self, claim_validators: List[SessionClaimValidator], user_context: Optional[Dict[str, Any]] = None) ‑> None -
-
-
-
- -Expand source code - -
async def assert_claims(
-    self,
-    claim_validators: List[SessionClaimValidator],
-    user_context: Union[Dict[str, Any], None] = None,
-) -> None:
-    if user_context is None:
-        user_context = {}
-
-    validate_claim_res = await self.recipe_implementation.validate_claims(
-        self.get_user_id(user_context),
-        self.get_access_token_payload(user_context),
-        claim_validators,
-        user_context,
-    )
-
-    if validate_claim_res.access_token_payload_update is not None:
-        for k in protected_props:
-            try:
-                del validate_claim_res.access_token_payload_update[k]
-            except KeyError:
-                pass
-        await self.merge_into_access_token_payload(
-            validate_claim_res.access_token_payload_update, user_context
-        )
-
-    validation_errors = validate_claim_res.invalid_claims
-    if len(validation_errors) > 0:
-        raise_invalid_claims_exception("INVALID_CLAIMS", validation_errors)
-
-
-
-async def attach_to_request_response(self, request: BaseRequest, transfer_method: Literal['cookie', 'header'], user_context: Optional[Dict[str, Any]]) ‑> None -
-
-
-
- -Expand source code - -
async def attach_to_request_response(
-    self,
-    request: BaseRequest,
-    transfer_method: TokenTransferMethod,
-    user_context: Optional[Dict[str, Any]],
-) -> None:
-    self.req_res_info = ReqResInfo(request, transfer_method)
-
-    if self.access_token_updated:
-        self.response_mutators.append(
-            access_token_mutator(
-                self.access_token,
-                self.front_token,
-                self.config,
-                transfer_method,
-                request,
-            )
-        )
-        if self.refresh_token is not None:
-            self.response_mutators.append(
-                token_response_mutator(
-                    self.config,
-                    "refresh",
-                    self.refresh_token.token,
-                    self.refresh_token.expiry,
-                    transfer_method,
-                    request,
-                )
-            )
-        if self.anti_csrf_token is not None:
-            self.response_mutators.append(
-                anti_csrf_response_mutator(self.anti_csrf_token)
-            )
-
-    request.set_session(
-        self
-    )  # Although this is called in recipe/session/framework/**/__init__.py. It's required in case of python because functions like create_new_session(req, "user-id") can be called in the framework view handler as well
-
-
-
-async def fetch_and_set_claim(self, claim: SessionClaim[typing.Any], user_context: Optional[Dict[str, Any]] = None) ‑> None -
-
-
-
- -Expand source code - -
async def fetch_and_set_claim(
-    self, claim: SessionClaim[Any], user_context: Union[Dict[str, Any], None] = None
-) -> None:
-    if user_context is None:
-        user_context = {}
-
-    update = await claim.build(
-        self.get_user_id(), self.get_tenant_id(), user_context
-    )
-    return await self.merge_into_access_token_payload(update, user_context)
-
-
-
-def get_access_token(self, user_context: Optional[Dict[str, Any]] = None) ‑> str -
-
-
-
- -Expand source code - -
def get_access_token(self, user_context: Union[Dict[str, Any], None] = None) -> str:
-    return self.access_token
-
-
-
-def get_access_token_payload(self, user_context: Optional[Dict[str, Any]] = None) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
def get_access_token_payload(
-    self, user_context: Union[Dict[str, Any], None] = None
-) -> Dict[str, Any]:
-    return self.user_data_in_access_token
-
-
-
-def get_all_session_tokens_dangerously(self) ‑> GetSessionTokensDangerouslyDict -
-
-
-
- -Expand source code - -
def get_all_session_tokens_dangerously(self) -> GetSessionTokensDangerouslyDict:
-    return {
-        "accessToken": self.access_token,
-        "accessAndFrontTokenUpdated": self.access_token_updated,
-        "refreshToken": None
-        if self.refresh_token is None
-        else self.refresh_token.token,
-        "frontToken": self.front_token,
-        "antiCsrfToken": self.anti_csrf_token,
-    }
-
-
-
-async def get_claim_value(self, claim: SessionClaim[~_T], user_context: Optional[Dict[str, Any]] = None) ‑> Optional[~_T] -
-
-
-
- -Expand source code - -
async def get_claim_value(
-    self, claim: SessionClaim[_T], user_context: Union[Dict[str, Any], None] = None
-) -> Union[_T, None]:
-    if user_context is None:
-        user_context = {}
-
-    return claim.get_value_from_payload(
-        self.get_access_token_payload(user_context), user_context
-    )
-
-
-
-async def get_expiry(self, user_context: Optional[Dict[str, Any]] = None) ‑> int -
-
-
-
- -Expand source code - -
async def get_expiry(self, user_context: Union[Dict[str, Any], None] = None) -> int:
-    if user_context is None:
-        user_context = {}
-    session_info = await self.recipe_implementation.get_session_information(
-        self.session_handle, user_context
-    )
-    if session_info is None:
-        log_debug_message(
-            "getExpiry: Throwing UNAUTHORISED because session does not exist anymore"
-        )
-        raise_unauthorised_exception("Session does not exist anymore.")
-
-    return session_info.expiry
-
-
-
-def get_handle(self, user_context: Optional[Dict[str, Any]] = None) ‑> str -
-
-
-
- -Expand source code - -
def get_handle(self, user_context: Union[Dict[str, Any], None] = None) -> str:
-    return self.session_handle
-
-
-
-async def get_session_data_from_database(self, user_context: Optional[Dict[str, Any]] = None) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
async def get_session_data_from_database(
-    self, user_context: Union[Dict[str, Any], None] = None
-) -> Dict[str, Any]:
-    if user_context is None:
-        user_context = {}
-    session_info = await self.recipe_implementation.get_session_information(
-        self.session_handle, user_context
-    )
-    if session_info is None:
-        log_debug_message(
-            "getSessionDataFromDatabase: Throwing UNAUTHORISED because session does not exist anymore"
-        )
-        raise_unauthorised_exception("Session does not exist anymore.")
-
-    return session_info.session_data_in_database
-
-
-
-def get_tenant_id(self, user_context: Optional[Dict[str, Any]] = None) ‑> str -
-
-
-
- -Expand source code - -
def get_tenant_id(self, user_context: Union[Dict[str, Any], None] = None) -> str:
-    return self.tenant_id
-
-
-
-async def get_time_created(self, user_context: Optional[Dict[str, Any]] = None) ‑> int -
-
-
-
- -Expand source code - -
async def get_time_created(
-    self, user_context: Union[Dict[str, Any], None] = None
-) -> int:
-    if user_context is None:
-        user_context = {}
-    session_info = await self.recipe_implementation.get_session_information(
-        self.session_handle, user_context
-    )
-    if session_info is None:
-        log_debug_message(
-            "getTimeCreated: Throwing UNAUTHORISED because session does not exist anymore"
-        )
-        raise_unauthorised_exception("Session does not exist anymore.")
-
-    return session_info.time_created
-
-
-
-def get_user_id(self, user_context: Optional[Dict[str, Any]] = None) ‑> str -
-
-
-
- -Expand source code - -
def get_user_id(self, user_context: Union[Dict[str, Any], None] = None) -> str:
-    return self.user_id
-
-
-
-async def merge_into_access_token_payload(self, access_token_payload_update: Dict[str, Any], user_context: Optional[Dict[str, Any]] = None) ‑> None -
-
-
-
- -Expand source code - -
async def merge_into_access_token_payload(
-    self,
-    access_token_payload_update: Dict[str, Any],
-    user_context: Union[Dict[str, Any], None] = None,
-) -> None:
-    if user_context is None:
-        user_context = {}
-
-    new_access_token_payload = {**self.get_access_token_payload(user_context)}
-    for k in protected_props:
-        try:
-            del new_access_token_payload[k]
-        except KeyError:
-            pass
-
-    new_access_token_payload = {
-        **new_access_token_payload,
-        **access_token_payload_update,
-    }
-
-    for k in access_token_payload_update.keys():
-        if access_token_payload_update[k] is None:
-            del new_access_token_payload[k]
-
-    response = await self.recipe_implementation.regenerate_access_token(
-        self.get_access_token(), new_access_token_payload, user_context
-    )
-
-    if response is None:
-        log_debug_message(
-            "mergeIntoAccessTokenPayload: Throwing UNAUTHORISED because session does not exist anymore"
-        )
-        raise_unauthorised_exception("Session does not exist anymore.")
-
-    if response.access_token is not None:
-        resp_token = parse_jwt_without_signature_verification(
-            response.access_token.token
-        )
-        payload = (
-            resp_token.payload
-            if resp_token.version >= 3
-            else response.session.user_data_in_jwt
-        )
-        self.user_data_in_access_token = payload
-        self.access_token = response.access_token.token
-        self.front_token = build_front_token(
-            self.get_user_id(), response.access_token.expiry, payload
-        )
-        self.access_token_updated = True
-        if self.req_res_info is not None:
-            transfer_method: TokenTransferMethod = self.req_res_info.transfer_method  # type: ignore
-            self.response_mutators.append(
-                access_token_mutator(
-                    self.access_token,
-                    self.front_token,
-                    self.config,
-                    transfer_method,
-                    self.req_res_info.request,
-                )
-            )
-    else:
-        # This case means that the access token has expired between the validation and this update
-        # We can't update the access token on the FE, as it will need to call refresh anyway but we handle this as a successful update during this request
-        # the changes will be reflected on the FE after refresh is called
-        self.user_data_in_access_token = {
-            **self.get_access_token_payload(),
-            **response.session.user_data_in_jwt,
-        }
-
-
-
-async def remove_claim(self, claim: SessionClaim[typing.Any], user_context: Optional[Dict[str, Any]] = None) ‑> None -
-
-
-
- -Expand source code - -
async def remove_claim(
-    self, claim: SessionClaim[Any], user_context: Union[Dict[str, Any], None] = None
-) -> None:
-    if user_context is None:
-        user_context = {}
-
-    update = claim.remove_from_payload_by_merge_({}, user_context)
-    return await self.merge_into_access_token_payload(update, user_context)
-
-
-
-async def revoke_session(self, user_context: Optional[Any] = None) ‑> None -
-
-
-
- -Expand source code - -
async def revoke_session(self, user_context: Union[Any, None] = None) -> None:
-    if user_context is None:
-        user_context = {}
-
-    await self.recipe_implementation.revoke_session(
-        self.session_handle, user_context
-    )
-
-    if self.req_res_info is not None:
-        # we do not check the output of calling revokeSession
-        # before clearing the cookies because we are revoking the
-        # current API request's session.
-        # If we instead clear the cookies only when revokeSession
-        # returns true, it can cause this kind of bug:
-        # https://github.com/supertokens/supertokens-node/issues/343
-        transfer_method: TokenTransferMethod = self.req_res_info.transfer_method  # type: ignore
-        self.response_mutators.append(
-            clear_session_response_mutator(
-                self.config,
-                transfer_method,
-                self.req_res_info.request,
-            )
-        )
-
-
-
-async def set_claim_value(self, claim: SessionClaim[~_T], value: ~_T, user_context: Optional[Dict[str, Any]] = None) ‑> None -
-
-
-
- -Expand source code - -
async def set_claim_value(
-    self,
-    claim: SessionClaim[_T],
-    value: _T,
-    user_context: Union[Dict[str, Any], None] = None,
-) -> None:
-    if user_context is None:
-        user_context = {}
-
-    update = claim.add_to_payload_({}, value, user_context)
-    return await self.merge_into_access_token_payload(update, user_context)
-
-
-
-async def update_session_data_in_database(self, new_session_data: Dict[str, Any], user_context: Optional[Dict[str, Any]] = None) ‑> None -
-
-
-
- -Expand source code - -
async def update_session_data_in_database(
-    self,
-    new_session_data: Dict[str, Any],
-    user_context: Union[Dict[str, Any], None] = None,
-) -> None:
-    if user_context is None:
-        user_context = {}
-    updated = await self.recipe_implementation.update_session_data_in_database(
-        self.session_handle, new_session_data, user_context
-    )
-    if not updated:
-        log_debug_message(
-            "updateSessionDataInDatabase: Throwing UNAUTHORISED because session does not exist anymore"
-        )
-        raise_unauthorised_exception("Session does not exist anymore.")
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/session/session_functions.html b/html/supertokens_python/recipe/session/session_functions.html deleted file mode 100644 index 5621a67d5..000000000 --- a/html/supertokens_python/recipe/session/session_functions.html +++ /dev/null @@ -1,1262 +0,0 @@ - - - - - - -supertokens_python.recipe.session.session_functions API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.session.session_functions

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-import time
-from typing import TYPE_CHECKING, Any, Dict, List, Union, Optional
-
-from supertokens_python.recipe.session.interfaces import SessionInformationResult
-
-from .access_token import get_info_from_access_token
-from .jwt import ParsedJWTInfo
-
-if TYPE_CHECKING:
-    from .recipe_implementation import RecipeImplementation
-
-from supertokens_python.logger import log_debug_message
-from supertokens_python.normalised_url_path import NormalisedURLPath
-from supertokens_python.process_state import AllowedProcessStates, ProcessState
-from supertokens_python.recipe.session.interfaces import TokenInfo
-
-from .exceptions import (
-    TryRefreshTokenError,
-    raise_token_theft_exception,
-    raise_try_refresh_token_exception,
-    raise_unauthorised_exception,
-)
-
-from supertokens_python.recipe.multitenancy.constants import DEFAULT_TENANT_ID
-
-
-class CreateOrRefreshAPIResponseSession:
-    def __init__(self, handle: str, userId: str, userDataInJWT: Any, tenant_id: str):
-        self.handle = handle
-        self.userId = userId
-        self.userDataInJWT = userDataInJWT
-        self.tenant_id = tenant_id
-
-
-class CreateOrRefreshAPIResponse:
-    def __init__(
-        self,
-        session: CreateOrRefreshAPIResponseSession,
-        accessToken: TokenInfo,
-        refreshToken: TokenInfo,
-        antiCsrfToken: Optional[str],
-    ):
-        self.session = session
-        self.accessToken = accessToken
-        self.refreshToken = refreshToken
-        self.antiCsrfToken = antiCsrfToken
-
-
-class GetSessionAPIResponseSession:
-    def __init__(
-        self,
-        handle: str,
-        userId: str,
-        userDataInJWT: Dict[str, Any],
-        expiryTime: int,
-        tenant_id: str,
-    ) -> None:
-        self.handle = handle
-        self.userId = userId
-        self.userDataInJWT = userDataInJWT
-        self.expiryTime = expiryTime
-        self.tenant_id = tenant_id
-
-
-class GetSessionAPIResponseAccessToken:
-    def __init__(self, token: str, expiry: int, createdTime: int) -> None:
-        self.token = token
-        self.expiry = expiry
-        self.createdTime = createdTime
-
-
-class GetSessionAPIResponse:
-    def __init__(
-        self,
-        session: GetSessionAPIResponseSession,
-        accessToken: Optional[GetSessionAPIResponseAccessToken] = None,
-    ) -> None:
-        self.session = session
-        self.accessToken = accessToken
-
-
-async def create_new_session(
-    recipe_implementation: RecipeImplementation,
-    tenant_id: str,
-    user_id: str,
-    disable_anti_csrf: bool,
-    access_token_payload: Union[None, Dict[str, Any]],
-    session_data_in_database: Union[None, Dict[str, Any]],
-    user_context: Optional[Dict[str, Any]],
-) -> CreateOrRefreshAPIResponse:
-    if session_data_in_database is None:
-        session_data_in_database = {}
-    if access_token_payload is None:
-        access_token_payload = {}
-    enable_anti_csrf = (
-        disable_anti_csrf is False
-        # We dont need to check if anti csrf is a function here because checking for "VIA_TOKEN" is enough
-        and recipe_implementation.config.anti_csrf_function_or_string == "VIA_TOKEN"
-    )
-    response = await recipe_implementation.querier.send_post_request(
-        NormalisedURLPath(f"{tenant_id}/recipe/session"),
-        {
-            "userId": user_id,
-            "userDataInJWT": access_token_payload,
-            "userDataInDatabase": session_data_in_database,
-            "useDynamicSigningKey": recipe_implementation.config.use_dynamic_access_token_signing_key,
-            "enableAntiCsrf": enable_anti_csrf,
-        },
-        user_context=user_context,
-    )
-
-    return CreateOrRefreshAPIResponse(
-        CreateOrRefreshAPIResponseSession(
-            response["session"]["handle"],
-            response["session"]["userId"],
-            response["session"]["userDataInJWT"],
-            response["session"]["tenantId"],
-        ),
-        TokenInfo(
-            response["accessToken"]["token"],
-            response["accessToken"]["expiry"],
-            response["accessToken"]["createdTime"],
-        ),
-        TokenInfo(
-            response["refreshToken"]["token"],
-            response["refreshToken"]["expiry"],
-            response["refreshToken"]["createdTime"],
-        ),
-        response["antiCsrfToken"] if "antiCsrfToken" in response else None,
-    )
-
-
-async def get_session(
-    recipe_implementation: RecipeImplementation,
-    parsed_access_token: ParsedJWTInfo,
-    anti_csrf_token: Union[str, None],
-    do_anti_csrf_check: bool,
-    always_check_core: bool,
-    user_context: Optional[Dict[str, Any]],
-) -> GetSessionAPIResponse:
-    config = recipe_implementation.config
-    access_token_info: Optional[Dict[str, Any]] = None
-
-    try:
-        access_token_info = get_info_from_access_token(
-            config,
-            parsed_access_token,
-            config.anti_csrf_function_or_string == "VIA_TOKEN" and do_anti_csrf_check,
-        )
-
-    except Exception as e:
-        if not isinstance(e, TryRefreshTokenError):
-            raise e
-
-        # if it comes here, it means token verification has failed.
-        # It may be due to:
-        # - signing key was updated and this token was signed with new key
-        # - access token is actually expired
-        # - access token was signed with the older signing key
-
-        # if access token is actually expired, we don't need to call core and
-        # just return TRY_REFRESH_TOKEN to the client
-
-        # if access token creation time is after this signing key was created
-        # we need to call core as there are chances that the token
-        # was signed with the updated signing key
-
-        # if access token creation time is before oldest signing key was created,
-        # so if foundASigningKeyThatIsOlderThanTheAccessToken is still false after
-        # the loop we just return TRY_REFRESH_TOKEN
-
-        payload = parsed_access_token.payload
-
-        time_created = payload.get("timeCreated")
-        expiry_time = payload.get("expiryTime")
-
-        if not isinstance(time_created, int) or not isinstance(expiry_time, int):
-            raise e
-
-        if parsed_access_token.version < 3:
-            if expiry_time < time.time():
-                raise e
-
-            # We check if the token was created since the last time we refreshed the keys from the core
-            # Since we do not know the exact timing of the last refresh, we check against the max age
-
-            if time_created <= time.time() - config.jwks_refresh_interval_sec:
-                raise e
-        else:
-            # Since v3 (and above) tokens contain a kid we can trust the cache refresh mechanism built on top of the pyjwt lib
-            # This means we do not need to call the core since the signature wouldn't pass verification anyway.
-            raise e
-
-    if parsed_access_token.version >= 3:
-        token_use_dynamic_key = (
-            parsed_access_token.kid.startswith("d-")
-            if parsed_access_token.kid is not None
-            else False
-        )
-
-        if token_use_dynamic_key != config.use_dynamic_access_token_signing_key:
-            log_debug_message(
-                "getSession: Returning TRY_REFRESH_TOKEN because the access token doesn't match the useDynamicAccessTokenSigningKey in the config"
-            )
-
-            raise_try_refresh_token_exception(
-                "The access token doesn't match the use_dynamic_access_token_signing_key setting"
-            )
-
-    # If we get here we either have a V2 token that doesn't pass verification or a valid V3> token
-    # anti-csrf check if accesstokenInfo is not undefined which means token verification was successful
-
-    if do_anti_csrf_check:
-        if config.anti_csrf_function_or_string == "VIA_TOKEN":
-            if access_token_info is not None:
-                if (
-                    anti_csrf_token is None
-                    or anti_csrf_token != access_token_info["antiCsrfToken"]
-                ):
-                    if anti_csrf_token is None:
-                        log_debug_message(
-                            "getSession: Returning TRY_REFRESH_TOKEN because antiCsrfToken is missing from request"
-                        )
-                        raise_try_refresh_token_exception(
-                            "Provided antiCsrfToken is undefined. If you do not want anti-csrf check for this API, please set doAntiCsrfCheck to false for this API"
-                        )
-                    else:
-                        log_debug_message(
-                            "getSession: Returning TRY_REFRESH_TOKEN because the passed antiCsrfToken is not the same as in the access token"
-                        )
-                        raise_try_refresh_token_exception("anti-csrf check failed")
-
-        elif (
-            isinstance(config.anti_csrf_function_or_string, str)
-            and config.anti_csrf_function_or_string == "VIA_CUSTOM_HEADER"
-        ):
-            # The function should never be called by this (we check this outside the function as well)
-            # There we can add a bit more information to the error, so that's the primary check, this is just making sure.
-            raise Exception(
-                "Please either use VIA_TOKEN, NONE or call with doAntiCsrfCheck false"
-            )
-
-    if (
-        access_token_info is not None
-        and not always_check_core
-        and access_token_info["parentRefreshTokenHash1"] is None
-    ):
-        return GetSessionAPIResponse(
-            GetSessionAPIResponseSession(
-                access_token_info["sessionHandle"],
-                access_token_info["userId"],
-                access_token_info["userData"],
-                access_token_info["expiryTime"],
-                access_token_info["tenantId"],
-            )
-        )
-
-    ProcessState.get_instance().add_state(
-        AllowedProcessStates.CALLING_SERVICE_IN_VERIFY
-    )
-
-    data = {
-        "accessToken": parsed_access_token.raw_token_string,
-        "doAntiCsrfCheck": do_anti_csrf_check,
-        "enableAntiCsrf": config.anti_csrf_function_or_string == "VIA_TOKEN",
-        "checkDatabase": always_check_core,
-    }
-    if anti_csrf_token is not None:
-        data["antiCsrfToken"] = anti_csrf_token
-
-    response = await recipe_implementation.querier.send_post_request(
-        NormalisedURLPath("/recipe/session/verify"),
-        data,
-        user_context=user_context,
-    )
-    if response["status"] == "OK":
-        return GetSessionAPIResponse(
-            GetSessionAPIResponseSession(
-                response["session"]["handle"],
-                response["session"]["userId"],
-                response["session"]["userDataInJWT"],
-                (
-                    response.get("accessToken", {}).get(
-                        "expiry"
-                    )  # if we got a new accesstoken we take the expiry time from there
-                    or (
-                        access_token_info is not None
-                        and access_token_info.get("expiryTime")
-                    )  # if we didn't get a new access token but could validate the token take that info (alwaysCheckCore === true, or parentRefreshTokenHash1 !== null)
-                    or parsed_access_token.payload[
-                        "expiryTime"
-                    ]  # if the token didn't pass validation, but we got here, it means it was a v2 token that we didn't have the key cached for.
-                ),  # This will throw error if others are none and 'expiryTime' key doesn't exist in the payload
-                response["session"].get("tenantId")
-                or (access_token_info or {}).get("tenantId"),
-            ),
-            (
-                GetSessionAPIResponseAccessToken(
-                    response["accessToken"]["token"],
-                    response["accessToken"]["expiry"],
-                    response["accessToken"]["createdTime"],
-                )
-                if "accessToken" in response
-                else None
-            ),
-        )
-    if response["status"] == "UNAUTHORISED":
-        log_debug_message("getSession: Returning UNAUTHORISED because of core response")
-        raise_unauthorised_exception(response["message"])
-
-    log_debug_message(
-        "getSession: Returning TRY_REFRESH_TOKEN because of core response"
-    )
-    raise_try_refresh_token_exception(response["message"])
-
-
-async def refresh_session(
-    recipe_implementation: RecipeImplementation,
-    refresh_token: str,
-    anti_csrf_token: Union[str, None],
-    disable_anti_csrf: bool,
-    use_dynamic_access_token_signing_key: bool,
-    user_context: Optional[Dict[str, Any]],
-) -> CreateOrRefreshAPIResponse:
-    data = {
-        "refreshToken": refresh_token,
-        "enableAntiCsrf": (
-            not disable_anti_csrf
-            and recipe_implementation.config.anti_csrf_function_or_string == "VIA_TOKEN"
-        ),
-        "useDynamicSigningKey": use_dynamic_access_token_signing_key,
-    }
-
-    if anti_csrf_token is not None:
-        data["antiCsrfToken"] = anti_csrf_token
-
-    if (
-        isinstance(recipe_implementation.config.anti_csrf_function_or_string, str)
-        and recipe_implementation.config.anti_csrf_function_or_string
-        == "VIA_CUSTOM_HEADER"
-        and not disable_anti_csrf
-    ):
-        # The function should never be called by this (we check this outside the function as well)
-        # There we can add a bit more information to the error, so that's the primary check, this is just making sure.
-        raise Exception(
-            "Please either use VIA_TOKEN, NONE or call with doAntiCsrfCheck false"
-        )
-
-    response = await recipe_implementation.querier.send_post_request(
-        NormalisedURLPath("/recipe/session/refresh"),
-        data,
-        user_context=user_context,
-    )
-    if response["status"] == "OK":
-        return CreateOrRefreshAPIResponse(
-            CreateOrRefreshAPIResponseSession(
-                response["session"]["handle"],
-                response["session"]["userId"],
-                response["session"]["userDataInJWT"],
-                response["session"]["tenantId"],
-            ),
-            TokenInfo(
-                response["accessToken"]["token"],
-                response["accessToken"]["expiry"],
-                response["accessToken"]["createdTime"],
-            ),
-            TokenInfo(
-                response["refreshToken"]["token"],
-                response["refreshToken"]["expiry"],
-                response["refreshToken"]["createdTime"],
-            ),
-            response["antiCsrfToken"] if "antiCsrfToken" in response else None,
-        )
-    if response["status"] == "UNAUTHORISED":
-        log_debug_message(
-            "refreshSession: Returning UNAUTHORISED because of core response"
-        )
-        raise_unauthorised_exception(response["message"])
-    log_debug_message(
-        "refreshSession: Returning TOKEN_THEFT_DETECTED because of core response"
-    )
-    raise_token_theft_exception(
-        response["session"]["userId"], response["session"]["handle"]
-    )
-
-
-async def revoke_all_sessions_for_user(
-    recipe_implementation: RecipeImplementation,
-    user_id: str,
-    tenant_id: Optional[str],
-    revoke_across_all_tenants: bool,
-    user_context: Optional[Dict[str, Any]],
-) -> List[str]:
-    if tenant_id is None:
-        tenant_id = DEFAULT_TENANT_ID
-
-    if revoke_across_all_tenants:
-        response = await recipe_implementation.querier.send_post_request(
-            NormalisedURLPath("/recipe/session/remove"),
-            {"userId": user_id, "revokeAcrossAllTenants": revoke_across_all_tenants},
-            user_context=user_context,
-        )
-    else:
-        response = await recipe_implementation.querier.send_post_request(
-            NormalisedURLPath(f"{tenant_id}/recipe/session/remove"),
-            {"userId": user_id, "revokeAcrossAllTenants": revoke_across_all_tenants},
-            user_context=user_context,
-        )
-    return response["sessionHandlesRevoked"]
-
-
-async def get_all_session_handles_for_user(
-    recipe_implementation: RecipeImplementation,
-    user_id: str,
-    tenant_id: Optional[str],
-    fetch_across_all_tenants: bool,
-    user_context: Optional[Dict[str, Any]],
-) -> List[str]:
-    if tenant_id is None:
-        tenant_id = DEFAULT_TENANT_ID
-
-    if fetch_across_all_tenants:
-        response = await recipe_implementation.querier.send_get_request(
-            NormalisedURLPath("/recipe/session/user"),
-            {"userId": user_id, "fetchAcrossAllTenants": fetch_across_all_tenants},
-            user_context=user_context,
-        )
-    else:
-        response = await recipe_implementation.querier.send_get_request(
-            NormalisedURLPath(f"{tenant_id}/recipe/session/user"),
-            {"userId": user_id, "fetchAcrossAllTenants": fetch_across_all_tenants},
-            user_context=user_context,
-        )
-    return response["sessionHandles"]
-
-
-async def revoke_session(
-    recipe_implementation: RecipeImplementation,
-    session_handle: str,
-    user_context: Optional[Dict[str, Any]],
-) -> bool:
-    response = await recipe_implementation.querier.send_post_request(
-        NormalisedURLPath("/recipe/session/remove"),
-        {"sessionHandles": [session_handle]},
-        user_context=user_context,
-    )
-    return len(response["sessionHandlesRevoked"]) == 1
-
-
-async def revoke_multiple_sessions(
-    recipe_implementation: RecipeImplementation,
-    session_handles: List[str],
-    user_context: Optional[Dict[str, Any]],
-) -> List[str]:
-    response = await recipe_implementation.querier.send_post_request(
-        NormalisedURLPath("/recipe/session/remove"),
-        {"sessionHandles": session_handles},
-        user_context=user_context,
-    )
-    return response["sessionHandlesRevoked"]
-
-
-async def update_session_data_in_database(
-    recipe_implementation: RecipeImplementation,
-    session_handle: str,
-    new_session_data: Dict[str, Any],
-    user_context: Optional[Dict[str, Any]],
-) -> bool:
-    response = await recipe_implementation.querier.send_put_request(
-        NormalisedURLPath("/recipe/session/data"),
-        {"sessionHandle": session_handle, "userDataInDatabase": new_session_data},
-        user_context=user_context,
-    )
-    if response["status"] == "UNAUTHORISED":
-        return False
-
-    return True
-
-
-async def update_access_token_payload(
-    recipe_implementation: RecipeImplementation,
-    session_handle: str,
-    new_access_token_payload: Dict[str, Any],
-    user_context: Optional[Dict[str, Any]],
-) -> bool:
-    response = await recipe_implementation.querier.send_put_request(
-        NormalisedURLPath("/recipe/jwt/data"),
-        {"sessionHandle": session_handle, "userDataInJWT": new_access_token_payload},
-        user_context=user_context,
-    )
-    if response["status"] == "UNAUTHORISED":
-        return False
-
-    return True
-
-
-async def get_session_information(
-    recipe_implementation: RecipeImplementation,
-    session_handle: str,
-    user_context: Optional[Dict[str, Any]],
-) -> Union[SessionInformationResult, None]:
-    response = await recipe_implementation.querier.send_get_request(
-        NormalisedURLPath("/recipe/session"),
-        {"sessionHandle": session_handle},
-        user_context=user_context,
-    )
-    if response["status"] == "OK":
-        return SessionInformationResult(
-            response["sessionHandle"],
-            response["userId"],
-            response["userDataInDatabase"],
-            response["expiry"],
-            response["userDataInJWT"],
-            response["timeCreated"],
-            response["tenantId"],
-        )
-    return None
-
-
-
-
-
-
-
-

Functions

-
-
-async def create_new_session(recipe_implementation: RecipeImplementation, tenant_id: str, user_id: str, disable_anti_csrf: bool, access_token_payload: Union[None, Dict[str, Any]], session_data_in_database: Union[None, Dict[str, Any]], user_context: Optional[Dict[str, Any]]) ‑> CreateOrRefreshAPIResponse -
-
-
-
- -Expand source code - -
async def create_new_session(
-    recipe_implementation: RecipeImplementation,
-    tenant_id: str,
-    user_id: str,
-    disable_anti_csrf: bool,
-    access_token_payload: Union[None, Dict[str, Any]],
-    session_data_in_database: Union[None, Dict[str, Any]],
-    user_context: Optional[Dict[str, Any]],
-) -> CreateOrRefreshAPIResponse:
-    if session_data_in_database is None:
-        session_data_in_database = {}
-    if access_token_payload is None:
-        access_token_payload = {}
-    enable_anti_csrf = (
-        disable_anti_csrf is False
-        # We dont need to check if anti csrf is a function here because checking for "VIA_TOKEN" is enough
-        and recipe_implementation.config.anti_csrf_function_or_string == "VIA_TOKEN"
-    )
-    response = await recipe_implementation.querier.send_post_request(
-        NormalisedURLPath(f"{tenant_id}/recipe/session"),
-        {
-            "userId": user_id,
-            "userDataInJWT": access_token_payload,
-            "userDataInDatabase": session_data_in_database,
-            "useDynamicSigningKey": recipe_implementation.config.use_dynamic_access_token_signing_key,
-            "enableAntiCsrf": enable_anti_csrf,
-        },
-        user_context=user_context,
-    )
-
-    return CreateOrRefreshAPIResponse(
-        CreateOrRefreshAPIResponseSession(
-            response["session"]["handle"],
-            response["session"]["userId"],
-            response["session"]["userDataInJWT"],
-            response["session"]["tenantId"],
-        ),
-        TokenInfo(
-            response["accessToken"]["token"],
-            response["accessToken"]["expiry"],
-            response["accessToken"]["createdTime"],
-        ),
-        TokenInfo(
-            response["refreshToken"]["token"],
-            response["refreshToken"]["expiry"],
-            response["refreshToken"]["createdTime"],
-        ),
-        response["antiCsrfToken"] if "antiCsrfToken" in response else None,
-    )
-
-
-
-async def get_all_session_handles_for_user(recipe_implementation: RecipeImplementation, user_id: str, tenant_id: Optional[str], fetch_across_all_tenants: bool, user_context: Optional[Dict[str, Any]]) ‑> List[str] -
-
-
-
- -Expand source code - -
async def get_all_session_handles_for_user(
-    recipe_implementation: RecipeImplementation,
-    user_id: str,
-    tenant_id: Optional[str],
-    fetch_across_all_tenants: bool,
-    user_context: Optional[Dict[str, Any]],
-) -> List[str]:
-    if tenant_id is None:
-        tenant_id = DEFAULT_TENANT_ID
-
-    if fetch_across_all_tenants:
-        response = await recipe_implementation.querier.send_get_request(
-            NormalisedURLPath("/recipe/session/user"),
-            {"userId": user_id, "fetchAcrossAllTenants": fetch_across_all_tenants},
-            user_context=user_context,
-        )
-    else:
-        response = await recipe_implementation.querier.send_get_request(
-            NormalisedURLPath(f"{tenant_id}/recipe/session/user"),
-            {"userId": user_id, "fetchAcrossAllTenants": fetch_across_all_tenants},
-            user_context=user_context,
-        )
-    return response["sessionHandles"]
-
-
-
-async def get_session(recipe_implementation: RecipeImplementation, parsed_access_token: ParsedJWTInfo, anti_csrf_token: Union[str, None], do_anti_csrf_check: bool, always_check_core: bool, user_context: Optional[Dict[str, Any]]) ‑> GetSessionAPIResponse -
-
-
-
- -Expand source code - -
async def get_session(
-    recipe_implementation: RecipeImplementation,
-    parsed_access_token: ParsedJWTInfo,
-    anti_csrf_token: Union[str, None],
-    do_anti_csrf_check: bool,
-    always_check_core: bool,
-    user_context: Optional[Dict[str, Any]],
-) -> GetSessionAPIResponse:
-    config = recipe_implementation.config
-    access_token_info: Optional[Dict[str, Any]] = None
-
-    try:
-        access_token_info = get_info_from_access_token(
-            config,
-            parsed_access_token,
-            config.anti_csrf_function_or_string == "VIA_TOKEN" and do_anti_csrf_check,
-        )
-
-    except Exception as e:
-        if not isinstance(e, TryRefreshTokenError):
-            raise e
-
-        # if it comes here, it means token verification has failed.
-        # It may be due to:
-        # - signing key was updated and this token was signed with new key
-        # - access token is actually expired
-        # - access token was signed with the older signing key
-
-        # if access token is actually expired, we don't need to call core and
-        # just return TRY_REFRESH_TOKEN to the client
-
-        # if access token creation time is after this signing key was created
-        # we need to call core as there are chances that the token
-        # was signed with the updated signing key
-
-        # if access token creation time is before oldest signing key was created,
-        # so if foundASigningKeyThatIsOlderThanTheAccessToken is still false after
-        # the loop we just return TRY_REFRESH_TOKEN
-
-        payload = parsed_access_token.payload
-
-        time_created = payload.get("timeCreated")
-        expiry_time = payload.get("expiryTime")
-
-        if not isinstance(time_created, int) or not isinstance(expiry_time, int):
-            raise e
-
-        if parsed_access_token.version < 3:
-            if expiry_time < time.time():
-                raise e
-
-            # We check if the token was created since the last time we refreshed the keys from the core
-            # Since we do not know the exact timing of the last refresh, we check against the max age
-
-            if time_created <= time.time() - config.jwks_refresh_interval_sec:
-                raise e
-        else:
-            # Since v3 (and above) tokens contain a kid we can trust the cache refresh mechanism built on top of the pyjwt lib
-            # This means we do not need to call the core since the signature wouldn't pass verification anyway.
-            raise e
-
-    if parsed_access_token.version >= 3:
-        token_use_dynamic_key = (
-            parsed_access_token.kid.startswith("d-")
-            if parsed_access_token.kid is not None
-            else False
-        )
-
-        if token_use_dynamic_key != config.use_dynamic_access_token_signing_key:
-            log_debug_message(
-                "getSession: Returning TRY_REFRESH_TOKEN because the access token doesn't match the useDynamicAccessTokenSigningKey in the config"
-            )
-
-            raise_try_refresh_token_exception(
-                "The access token doesn't match the use_dynamic_access_token_signing_key setting"
-            )
-
-    # If we get here we either have a V2 token that doesn't pass verification or a valid V3> token
-    # anti-csrf check if accesstokenInfo is not undefined which means token verification was successful
-
-    if do_anti_csrf_check:
-        if config.anti_csrf_function_or_string == "VIA_TOKEN":
-            if access_token_info is not None:
-                if (
-                    anti_csrf_token is None
-                    or anti_csrf_token != access_token_info["antiCsrfToken"]
-                ):
-                    if anti_csrf_token is None:
-                        log_debug_message(
-                            "getSession: Returning TRY_REFRESH_TOKEN because antiCsrfToken is missing from request"
-                        )
-                        raise_try_refresh_token_exception(
-                            "Provided antiCsrfToken is undefined. If you do not want anti-csrf check for this API, please set doAntiCsrfCheck to false for this API"
-                        )
-                    else:
-                        log_debug_message(
-                            "getSession: Returning TRY_REFRESH_TOKEN because the passed antiCsrfToken is not the same as in the access token"
-                        )
-                        raise_try_refresh_token_exception("anti-csrf check failed")
-
-        elif (
-            isinstance(config.anti_csrf_function_or_string, str)
-            and config.anti_csrf_function_or_string == "VIA_CUSTOM_HEADER"
-        ):
-            # The function should never be called by this (we check this outside the function as well)
-            # There we can add a bit more information to the error, so that's the primary check, this is just making sure.
-            raise Exception(
-                "Please either use VIA_TOKEN, NONE or call with doAntiCsrfCheck false"
-            )
-
-    if (
-        access_token_info is not None
-        and not always_check_core
-        and access_token_info["parentRefreshTokenHash1"] is None
-    ):
-        return GetSessionAPIResponse(
-            GetSessionAPIResponseSession(
-                access_token_info["sessionHandle"],
-                access_token_info["userId"],
-                access_token_info["userData"],
-                access_token_info["expiryTime"],
-                access_token_info["tenantId"],
-            )
-        )
-
-    ProcessState.get_instance().add_state(
-        AllowedProcessStates.CALLING_SERVICE_IN_VERIFY
-    )
-
-    data = {
-        "accessToken": parsed_access_token.raw_token_string,
-        "doAntiCsrfCheck": do_anti_csrf_check,
-        "enableAntiCsrf": config.anti_csrf_function_or_string == "VIA_TOKEN",
-        "checkDatabase": always_check_core,
-    }
-    if anti_csrf_token is not None:
-        data["antiCsrfToken"] = anti_csrf_token
-
-    response = await recipe_implementation.querier.send_post_request(
-        NormalisedURLPath("/recipe/session/verify"),
-        data,
-        user_context=user_context,
-    )
-    if response["status"] == "OK":
-        return GetSessionAPIResponse(
-            GetSessionAPIResponseSession(
-                response["session"]["handle"],
-                response["session"]["userId"],
-                response["session"]["userDataInJWT"],
-                (
-                    response.get("accessToken", {}).get(
-                        "expiry"
-                    )  # if we got a new accesstoken we take the expiry time from there
-                    or (
-                        access_token_info is not None
-                        and access_token_info.get("expiryTime")
-                    )  # if we didn't get a new access token but could validate the token take that info (alwaysCheckCore === true, or parentRefreshTokenHash1 !== null)
-                    or parsed_access_token.payload[
-                        "expiryTime"
-                    ]  # if the token didn't pass validation, but we got here, it means it was a v2 token that we didn't have the key cached for.
-                ),  # This will throw error if others are none and 'expiryTime' key doesn't exist in the payload
-                response["session"].get("tenantId")
-                or (access_token_info or {}).get("tenantId"),
-            ),
-            (
-                GetSessionAPIResponseAccessToken(
-                    response["accessToken"]["token"],
-                    response["accessToken"]["expiry"],
-                    response["accessToken"]["createdTime"],
-                )
-                if "accessToken" in response
-                else None
-            ),
-        )
-    if response["status"] == "UNAUTHORISED":
-        log_debug_message("getSession: Returning UNAUTHORISED because of core response")
-        raise_unauthorised_exception(response["message"])
-
-    log_debug_message(
-        "getSession: Returning TRY_REFRESH_TOKEN because of core response"
-    )
-    raise_try_refresh_token_exception(response["message"])
-
-
-
-async def get_session_information(recipe_implementation: RecipeImplementation, session_handle: str, user_context: Optional[Dict[str, Any]]) ‑> Union[SessionInformationResult, None] -
-
-
-
- -Expand source code - -
async def get_session_information(
-    recipe_implementation: RecipeImplementation,
-    session_handle: str,
-    user_context: Optional[Dict[str, Any]],
-) -> Union[SessionInformationResult, None]:
-    response = await recipe_implementation.querier.send_get_request(
-        NormalisedURLPath("/recipe/session"),
-        {"sessionHandle": session_handle},
-        user_context=user_context,
-    )
-    if response["status"] == "OK":
-        return SessionInformationResult(
-            response["sessionHandle"],
-            response["userId"],
-            response["userDataInDatabase"],
-            response["expiry"],
-            response["userDataInJWT"],
-            response["timeCreated"],
-            response["tenantId"],
-        )
-    return None
-
-
-
-async def refresh_session(recipe_implementation: RecipeImplementation, refresh_token: str, anti_csrf_token: Union[str, None], disable_anti_csrf: bool, use_dynamic_access_token_signing_key: bool, user_context: Optional[Dict[str, Any]]) ‑> CreateOrRefreshAPIResponse -
-
-
-
- -Expand source code - -
async def refresh_session(
-    recipe_implementation: RecipeImplementation,
-    refresh_token: str,
-    anti_csrf_token: Union[str, None],
-    disable_anti_csrf: bool,
-    use_dynamic_access_token_signing_key: bool,
-    user_context: Optional[Dict[str, Any]],
-) -> CreateOrRefreshAPIResponse:
-    data = {
-        "refreshToken": refresh_token,
-        "enableAntiCsrf": (
-            not disable_anti_csrf
-            and recipe_implementation.config.anti_csrf_function_or_string == "VIA_TOKEN"
-        ),
-        "useDynamicSigningKey": use_dynamic_access_token_signing_key,
-    }
-
-    if anti_csrf_token is not None:
-        data["antiCsrfToken"] = anti_csrf_token
-
-    if (
-        isinstance(recipe_implementation.config.anti_csrf_function_or_string, str)
-        and recipe_implementation.config.anti_csrf_function_or_string
-        == "VIA_CUSTOM_HEADER"
-        and not disable_anti_csrf
-    ):
-        # The function should never be called by this (we check this outside the function as well)
-        # There we can add a bit more information to the error, so that's the primary check, this is just making sure.
-        raise Exception(
-            "Please either use VIA_TOKEN, NONE or call with doAntiCsrfCheck false"
-        )
-
-    response = await recipe_implementation.querier.send_post_request(
-        NormalisedURLPath("/recipe/session/refresh"),
-        data,
-        user_context=user_context,
-    )
-    if response["status"] == "OK":
-        return CreateOrRefreshAPIResponse(
-            CreateOrRefreshAPIResponseSession(
-                response["session"]["handle"],
-                response["session"]["userId"],
-                response["session"]["userDataInJWT"],
-                response["session"]["tenantId"],
-            ),
-            TokenInfo(
-                response["accessToken"]["token"],
-                response["accessToken"]["expiry"],
-                response["accessToken"]["createdTime"],
-            ),
-            TokenInfo(
-                response["refreshToken"]["token"],
-                response["refreshToken"]["expiry"],
-                response["refreshToken"]["createdTime"],
-            ),
-            response["antiCsrfToken"] if "antiCsrfToken" in response else None,
-        )
-    if response["status"] == "UNAUTHORISED":
-        log_debug_message(
-            "refreshSession: Returning UNAUTHORISED because of core response"
-        )
-        raise_unauthorised_exception(response["message"])
-    log_debug_message(
-        "refreshSession: Returning TOKEN_THEFT_DETECTED because of core response"
-    )
-    raise_token_theft_exception(
-        response["session"]["userId"], response["session"]["handle"]
-    )
-
-
-
-async def revoke_all_sessions_for_user(recipe_implementation: RecipeImplementation, user_id: str, tenant_id: Optional[str], revoke_across_all_tenants: bool, user_context: Optional[Dict[str, Any]]) ‑> List[str] -
-
-
-
- -Expand source code - -
async def revoke_all_sessions_for_user(
-    recipe_implementation: RecipeImplementation,
-    user_id: str,
-    tenant_id: Optional[str],
-    revoke_across_all_tenants: bool,
-    user_context: Optional[Dict[str, Any]],
-) -> List[str]:
-    if tenant_id is None:
-        tenant_id = DEFAULT_TENANT_ID
-
-    if revoke_across_all_tenants:
-        response = await recipe_implementation.querier.send_post_request(
-            NormalisedURLPath("/recipe/session/remove"),
-            {"userId": user_id, "revokeAcrossAllTenants": revoke_across_all_tenants},
-            user_context=user_context,
-        )
-    else:
-        response = await recipe_implementation.querier.send_post_request(
-            NormalisedURLPath(f"{tenant_id}/recipe/session/remove"),
-            {"userId": user_id, "revokeAcrossAllTenants": revoke_across_all_tenants},
-            user_context=user_context,
-        )
-    return response["sessionHandlesRevoked"]
-
-
-
-async def revoke_multiple_sessions(recipe_implementation: RecipeImplementation, session_handles: List[str], user_context: Optional[Dict[str, Any]]) ‑> List[str] -
-
-
-
- -Expand source code - -
async def revoke_multiple_sessions(
-    recipe_implementation: RecipeImplementation,
-    session_handles: List[str],
-    user_context: Optional[Dict[str, Any]],
-) -> List[str]:
-    response = await recipe_implementation.querier.send_post_request(
-        NormalisedURLPath("/recipe/session/remove"),
-        {"sessionHandles": session_handles},
-        user_context=user_context,
-    )
-    return response["sessionHandlesRevoked"]
-
-
-
-async def revoke_session(recipe_implementation: RecipeImplementation, session_handle: str, user_context: Optional[Dict[str, Any]]) ‑> bool -
-
-
-
- -Expand source code - -
async def revoke_session(
-    recipe_implementation: RecipeImplementation,
-    session_handle: str,
-    user_context: Optional[Dict[str, Any]],
-) -> bool:
-    response = await recipe_implementation.querier.send_post_request(
-        NormalisedURLPath("/recipe/session/remove"),
-        {"sessionHandles": [session_handle]},
-        user_context=user_context,
-    )
-    return len(response["sessionHandlesRevoked"]) == 1
-
-
-
-async def update_access_token_payload(recipe_implementation: RecipeImplementation, session_handle: str, new_access_token_payload: Dict[str, Any], user_context: Optional[Dict[str, Any]]) ‑> bool -
-
-
-
- -Expand source code - -
async def update_access_token_payload(
-    recipe_implementation: RecipeImplementation,
-    session_handle: str,
-    new_access_token_payload: Dict[str, Any],
-    user_context: Optional[Dict[str, Any]],
-) -> bool:
-    response = await recipe_implementation.querier.send_put_request(
-        NormalisedURLPath("/recipe/jwt/data"),
-        {"sessionHandle": session_handle, "userDataInJWT": new_access_token_payload},
-        user_context=user_context,
-    )
-    if response["status"] == "UNAUTHORISED":
-        return False
-
-    return True
-
-
-
-async def update_session_data_in_database(recipe_implementation: RecipeImplementation, session_handle: str, new_session_data: Dict[str, Any], user_context: Optional[Dict[str, Any]]) ‑> bool -
-
-
-
- -Expand source code - -
async def update_session_data_in_database(
-    recipe_implementation: RecipeImplementation,
-    session_handle: str,
-    new_session_data: Dict[str, Any],
-    user_context: Optional[Dict[str, Any]],
-) -> bool:
-    response = await recipe_implementation.querier.send_put_request(
-        NormalisedURLPath("/recipe/session/data"),
-        {"sessionHandle": session_handle, "userDataInDatabase": new_session_data},
-        user_context=user_context,
-    )
-    if response["status"] == "UNAUTHORISED":
-        return False
-
-    return True
-
-
-
-
-
-

Classes

-
-
-class CreateOrRefreshAPIResponse -(session: CreateOrRefreshAPIResponseSession, accessToken: TokenInfo, refreshToken: TokenInfo, antiCsrfToken: Optional[str]) -
-
-
-
- -Expand source code - -
class CreateOrRefreshAPIResponse:
-    def __init__(
-        self,
-        session: CreateOrRefreshAPIResponseSession,
-        accessToken: TokenInfo,
-        refreshToken: TokenInfo,
-        antiCsrfToken: Optional[str],
-    ):
-        self.session = session
-        self.accessToken = accessToken
-        self.refreshToken = refreshToken
-        self.antiCsrfToken = antiCsrfToken
-
-
-
-class CreateOrRefreshAPIResponseSession -(handle: str, userId: str, userDataInJWT: Any, tenant_id: str) -
-
-
-
- -Expand source code - -
class CreateOrRefreshAPIResponseSession:
-    def __init__(self, handle: str, userId: str, userDataInJWT: Any, tenant_id: str):
-        self.handle = handle
-        self.userId = userId
-        self.userDataInJWT = userDataInJWT
-        self.tenant_id = tenant_id
-
-
-
-class GetSessionAPIResponse -(session: GetSessionAPIResponseSession, accessToken: Optional[GetSessionAPIResponseAccessToken] = None) -
-
-
-
- -Expand source code - -
class GetSessionAPIResponse:
-    def __init__(
-        self,
-        session: GetSessionAPIResponseSession,
-        accessToken: Optional[GetSessionAPIResponseAccessToken] = None,
-    ) -> None:
-        self.session = session
-        self.accessToken = accessToken
-
-
-
-class GetSessionAPIResponseAccessToken -(token: str, expiry: int, createdTime: int) -
-
-
-
- -Expand source code - -
class GetSessionAPIResponseAccessToken:
-    def __init__(self, token: str, expiry: int, createdTime: int) -> None:
-        self.token = token
-        self.expiry = expiry
-        self.createdTime = createdTime
-
-
-
-class GetSessionAPIResponseSession -(handle: str, userId: str, userDataInJWT: Dict[str, Any], expiryTime: int, tenant_id: str) -
-
-
-
- -Expand source code - -
class GetSessionAPIResponseSession:
-    def __init__(
-        self,
-        handle: str,
-        userId: str,
-        userDataInJWT: Dict[str, Any],
-        expiryTime: int,
-        tenant_id: str,
-    ) -> None:
-        self.handle = handle
-        self.userId = userId
-        self.userDataInJWT = userDataInJWT
-        self.expiryTime = expiryTime
-        self.tenant_id = tenant_id
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/session/session_request_functions.html b/html/supertokens_python/recipe/session/session_request_functions.html deleted file mode 100644 index 65f0ced09..000000000 --- a/html/supertokens_python/recipe/session/session_request_functions.html +++ /dev/null @@ -1,1084 +0,0 @@ - - - - - - -supertokens_python.recipe.session.session_request_functions API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.session.session_request_functions

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from typing import Any, Callable, Dict, List, Optional, Union, TYPE_CHECKING
-
-from supertokens_python.logger import log_debug_message
-from supertokens_python.recipe.session.access_token import (
-    validate_access_token_structure,
-)
-from supertokens_python.recipe.session.constants import available_token_transfer_methods
-from supertokens_python.recipe.session.cookie_and_header import (
-    clear_session_cookies_from_older_cookie_domain,
-    clear_session_mutator,
-    get_anti_csrf_header,
-    get_token,
-    has_multiple_cookies_for_token_type,
-    set_cookie_response_mutator,
-)
-from supertokens_python.recipe.session.exceptions import (
-    raise_try_refresh_token_exception,
-    raise_unauthorised_exception,
-)
-from supertokens_python.recipe.session.interfaces import (
-    RecipeInterface as SessionRecipeInterface,
-)
-from supertokens_python.recipe.session.interfaces import (
-    SessionClaimValidator,
-    SessionContainer,
-)
-from supertokens_python.recipe.session.exceptions import (
-    SuperTokensSessionError,
-    TokenTheftError,
-    UnauthorisedError,
-)
-from supertokens_python.recipe.session.jwt import (
-    ParsedJWTInfo,
-    parse_jwt_without_signature_verification,
-)
-from supertokens_python.recipe.session.utils import (
-    SessionConfig,
-    TokenTransferMethod,
-    get_required_claim_validators,
-    get_auth_mode_from_header,
-)
-from supertokens_python.types import MaybeAwaitable
-from supertokens_python.utils import (
-    FRAMEWORKS,
-    get_rid_from_header,
-    is_an_ip_address,
-    normalise_http_method,
-    set_request_in_user_context_if_not_defined,
-)
-from supertokens_python.supertokens import Supertokens
-from .constants import protected_props
-
-if TYPE_CHECKING:
-    from supertokens_python.recipe.session.recipe import SessionRecipe
-    from supertokens_python.supertokens import AppInfo
-    from .interfaces import ResponseMutator
-
-LEGACY_ID_REFRESH_TOKEN_COOKIE_NAME = "sIdRefreshToken"
-
-
-async def get_session_from_request(
-    request: Any,
-    config: SessionConfig,
-    recipe_interface_impl: SessionRecipeInterface,
-    session_required: Optional[bool] = None,
-    anti_csrf_check: Optional[bool] = None,
-    check_database: Optional[bool] = None,
-    override_global_claim_validators: Optional[
-        Callable[
-            [List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
-            MaybeAwaitable[List[SessionClaimValidator]],
-        ]
-    ] = None,
-    user_context: Optional[Dict[str, Any]] = None,
-) -> Optional[SessionContainer]:
-    log_debug_message("getSession: Started")
-
-    if not hasattr(request, "wrapper_used") or not request.wrapper_used:
-        request = FRAMEWORKS[
-            Supertokens.get_instance().app_info.framework
-        ].wrap_request(request)
-
-    log_debug_message("getSession: Wrapping done")
-
-    user_context = set_request_in_user_context_if_not_defined(user_context, request)
-
-    # This token isn't handled by getToken to limit the scope of this legacy/migration code
-    if request.get_cookie(LEGACY_ID_REFRESH_TOKEN_COOKIE_NAME) is not None:
-        log_debug_message(
-            "getSession: Throwing TRY_REFRESH_TOKEN because the request is using a legacy session"
-        )
-        # This could create a spike on refresh calls during the update of the backend SDK
-        return raise_try_refresh_token_exception(
-            "using legacy session, please call the refresh API"
-        )
-
-    session_optional = not session_required
-    log_debug_message("getSession: optional validation: %s", session_optional)
-
-    access_tokens: Dict[TokenTransferMethod, ParsedJWTInfo] = {}
-
-    # We check all token transfer methods for available access tokens
-    for transfer_method in available_token_transfer_methods:
-        token_string = get_token(request, "access", transfer_method)
-
-        if token_string is not None:
-            try:
-                info = parse_jwt_without_signature_verification(token_string)
-                validate_access_token_structure(info.payload, info.version)
-                log_debug_message(
-                    "getSession: got access token from %s", transfer_method
-                )
-                access_tokens[transfer_method] = info
-            except Exception:
-                log_debug_message(
-                    "getSession: ignoring token in %s, because it doesn't match our access token structure",
-                    transfer_method,
-                )
-
-    allowed_transfer_method = config.get_token_transfer_method(
-        request, False, user_context
-    )
-    request_transfer_method: Optional[TokenTransferMethod] = None
-    request_access_token: Union[ParsedJWTInfo, None] = None
-
-    if (allowed_transfer_method in ("any", "header")) and access_tokens.get(
-        "header"
-    ) is not None:
-        log_debug_message("getSession: using header transfer method")
-        request_transfer_method = "header"
-        request_access_token = access_tokens["header"]
-    elif (allowed_transfer_method in ("any", "cookie")) and access_tokens.get(
-        "cookie"
-    ) is not None:
-        log_debug_message("getSession: using cookie transfer method")
-
-        # If multiple access tokens exist in the request cookie, throw TRY_REFRESH_TOKEN.
-        # This prompts the client to call the refresh endpoint, clearing older_cookie_domain cookies (if set).
-        # ensuring outdated token payload isn't used.
-        if has_multiple_cookies_for_token_type(request, "access"):
-            log_debug_message(
-                "getSession: Throwing TRY_REFRESH_TOKEN because multiple access tokens are present in request cookies"
-            )
-            raise_try_refresh_token_exception(
-                "Multiple access tokens present in the request cookies."
-            )
-
-        request_transfer_method = "cookie"
-        request_access_token = access_tokens["cookie"]
-
-    anti_csrf_token = get_anti_csrf_header(request)
-    do_anti_csrf_check = anti_csrf_check
-
-    if do_anti_csrf_check is None:
-        do_anti_csrf_check = normalise_http_method(request.method()) != "get"
-    if request_transfer_method == "header":
-        do_anti_csrf_check = False
-    if request_access_token is None:
-        do_anti_csrf_check = False
-
-    if callable(config.anti_csrf_function_or_string):
-        anti_csrf = config.anti_csrf_function_or_string(request, user_context)
-    else:
-        anti_csrf = config.anti_csrf_function_or_string
-
-    if do_anti_csrf_check and anti_csrf == "VIA_CUSTOM_HEADER":
-        if anti_csrf == "VIA_CUSTOM_HEADER":
-            if get_rid_from_header(request) is None:
-                log_debug_message(
-                    "getSession: Returning TRY_REFRESH_TOKEN because custom header (rid) was not passed"
-                )
-                raise_try_refresh_token_exception(
-                    "anti-csrf check failed. Please pass 'rid: \"session\"' header in the request, or set doAntiCsrfCheck to false for this API"
-                )
-
-            log_debug_message("getSession: VIA_CUSTOM_HEADER anti-csrf check passed")
-            do_anti_csrf_check = False
-
-    log_debug_message("getSession: Value of antiCsrfToken is: %s", do_anti_csrf_check)
-
-    session = await recipe_interface_impl.get_session(
-        access_token=(
-            request_access_token.raw_token_string
-            if request_access_token is not None
-            else None
-        ),
-        anti_csrf_token=anti_csrf_token,
-        anti_csrf_check=do_anti_csrf_check,
-        session_required=session_required,
-        check_database=check_database,
-        override_global_claim_validators=override_global_claim_validators,
-        user_context=user_context,
-    )
-
-    if session is not None:
-        claim_validators = await get_required_claim_validators(
-            session, override_global_claim_validators, user_context
-        )
-        await session.assert_claims(claim_validators, user_context)
-
-        # request_transfer_method can only be None here if the user overriddes get_session
-        # to load the session by a custom method in that (very niche) case they also need to
-        # override how the session is attached to the response.
-        # In that scenario the transferMethod passed to attachToRequestResponse likely doesn't
-        # matter, still, we follow the general fallback logic
-
-        if request_transfer_method is not None:
-            final_transfer_method = request_transfer_method
-        elif allowed_transfer_method != "any":
-            final_transfer_method = allowed_transfer_method
-        else:
-            final_transfer_method = "header"
-
-        await session.attach_to_request_response(
-            request, final_transfer_method, user_context
-        )
-
-    return session
-
-
-async def create_new_session_in_request(
-    request: Any,
-    user_context: Dict[str, Any],
-    recipe_instance: SessionRecipe,
-    access_token_payload: Dict[str, Any],
-    user_id: str,
-    config: SessionConfig,
-    app_info: AppInfo,
-    session_data_in_database: Dict[str, Any],
-    tenant_id: str,
-) -> SessionContainer:
-    log_debug_message("createNewSession: Started")
-
-    # Handling framework specific request/response wrapping
-    if not hasattr(request, "wrapper_used") or not request.wrapper_used:
-        request = FRAMEWORKS[
-            Supertokens.get_instance().app_info.framework
-        ].wrap_request(request)
-
-    log_debug_message("createNewSession: Wrapping done")
-    user_context = set_request_in_user_context_if_not_defined(user_context, request)
-
-    claims_added_by_other_recipes = recipe_instance.get_claims_added_by_other_recipes()
-    issuer = (
-        app_info.api_domain.get_as_string_dangerous()
-        + app_info.api_base_path.get_as_string_dangerous()
-    )
-
-    final_access_token_payload = {**access_token_payload, "iss": issuer}
-
-    for prop in protected_props:
-        if prop in final_access_token_payload:
-            del final_access_token_payload[prop]
-
-    for claim in claims_added_by_other_recipes:
-        update = await claim.build(user_id, tenant_id, user_context)
-        final_access_token_payload.update(update)
-
-    log_debug_message("createNewSession: Access token payload built")
-
-    output_transfer_method = config.get_token_transfer_method(
-        request, True, user_context
-    )
-    if output_transfer_method == "any":
-        auth_mode_header = get_auth_mode_from_header(request)
-        if auth_mode_header == "cookie":
-            output_transfer_method = auth_mode_header
-        else:
-            output_transfer_method = "header"
-
-    log_debug_message(
-        "createNewSession: using transfer method %s", output_transfer_method
-    )
-
-    if (
-        output_transfer_method == "cookie"
-        and config.get_cookie_same_site(request, user_context) == "none"
-        and not config.cookie_secure
-        and not (
-            (
-                app_info.top_level_api_domain == "localhost"
-                or is_an_ip_address(app_info.top_level_api_domain)
-            )
-            and (
-                app_info.get_top_level_website_domain(request, user_context)
-                == "localhost"
-                or is_an_ip_address(
-                    app_info.get_top_level_website_domain(request, user_context)
-                )
-            )
-        )
-    ):
-        # We can allow insecure cookie when both website & API domain are localhost or an IP
-        # When either of them is a different domain, API domain needs to have https and a secure cookie to work
-        raise Exception(
-            "Since your API and website domain are different, for sessions to work, please use https on your apiDomain and don't set cookieSecure to false."
-        )
-
-    disable_anti_csrf = output_transfer_method == "header"
-    session = await recipe_instance.recipe_implementation.create_new_session(
-        user_id,
-        final_access_token_payload,
-        session_data_in_database,
-        disable_anti_csrf,
-        tenant_id,
-        user_context=user_context,
-    )
-
-    log_debug_message("createNewSession: Session created in core built")
-
-    for transfer_method in available_token_transfer_methods:
-        if (
-            transfer_method != output_transfer_method
-            and get_token(request, "access", transfer_method) is not None
-        ):
-            session.response_mutators.append(
-                clear_session_mutator(config, transfer_method, request)
-            )
-
-    log_debug_message("createNewSession: Cleared old tokens")
-
-    await session.attach_to_request_response(
-        request, output_transfer_method, user_context
-    )
-    log_debug_message("createNewSession: Attached new tokens to res")
-
-    return session
-
-
-# In all cases: if sIdRefreshToken token exists (so it's a legacy session) we clear it.
-# Check http://localhost:3002/docs/contribute/decisions/session/0008 for further details and a table of expected behaviours
-
-
-async def refresh_session_in_request(
-    request: Any,
-    user_context: Dict[str, Any],
-    config: SessionConfig,
-    recipe_interface_impl: SessionRecipeInterface,
-) -> SessionContainer:
-    log_debug_message("refreshSession: Started")
-
-    response_mutators: List[ResponseMutator] = []
-
-    if not hasattr(request, "wrapper_used") or not request.wrapper_used:
-        request = FRAMEWORKS[
-            Supertokens.get_instance().app_info.framework
-        ].wrap_request(request)
-
-    log_debug_message("refreshSession: Wrapping done")
-    user_context = set_request_in_user_context_if_not_defined(user_context, request)
-
-    clear_session_cookies_from_older_cookie_domain(request, config, user_context)
-
-    refresh_tokens: Dict[TokenTransferMethod, Optional[str]] = {}
-
-    for transfer_method in available_token_transfer_methods:
-        refresh_tokens[transfer_method] = get_token(request, "refresh", transfer_method)
-        if refresh_tokens[transfer_method] is not None:
-            log_debug_message(
-                "refreshSession: got refresh token from %s", transfer_method
-            )
-
-    allowed_transfer_method = config.get_token_transfer_method(
-        request, False, user_context
-    )
-    log_debug_message(
-        "refreshSession: getTokenTransferMethod returned: %s",
-        allowed_transfer_method,
-    )
-
-    request_transfer_method: TokenTransferMethod
-    refresh_token: Optional[str]
-
-    if (allowed_transfer_method in ("any", "header")) and (
-        refresh_tokens.get("header")
-    ):
-        log_debug_message("refreshSession: using header transfer method")
-        request_transfer_method = "header"
-        refresh_token = refresh_tokens["header"]
-    elif (allowed_transfer_method in ("any", "cookie")) and (
-        refresh_tokens.get("cookie")
-    ):
-        log_debug_message("refreshSession: using cookie transfer method")
-        request_transfer_method = "cookie"
-        refresh_token = refresh_tokens["cookie"]
-    else:
-        # This token isn't handled by getToken/setToken to limit the scope of this legacy/migration code
-        if request.get_cookie(LEGACY_ID_REFRESH_TOKEN_COOKIE_NAME) is not None:
-            log_debug_message(
-                "refreshSession: cleared legacy id refresh token because refresh token was not found"
-            )
-            response_mutators.append(
-                set_cookie_response_mutator(
-                    config,
-                    LEGACY_ID_REFRESH_TOKEN_COOKIE_NAME,
-                    "",
-                    0,
-                    "access_token_path",
-                    request,
-                )
-            )
-
-        # We need to clear the access token cookie if
-        # - the refresh token is not found, and
-        # - the allowed_transfer_method is 'cookie' or 'any', and
-        # - an access token cookie exists (otherwise it'd be a no-op)
-        # See: https://github.com/supertokens/supertokens-node/issues/790
-        if (
-            allowed_transfer_method in ("cookie", "any")
-            and get_token(request, "access", "cookie") is not None
-        ):
-            log_debug_message(
-                "refreshSession: cleared all session tokens and returning UNAUTHORISED because refresh_token in request is None"
-            )
-
-            # We're clearing all session tokens instead of just the access token and then throwing an UNAUTHORISED
-            # error with `clear_tokens: False`. This approach avoids confusion and we don't want to retain session
-            # tokens on the client in any case if the refresh API is called without a refresh token but with an access token.
-            return raise_unauthorised_exception(
-                "Refresh token not found but access token is present. Clearing all tokens.",
-                clear_tokens=True,
-                response_mutators=response_mutators,
-            )
-
-        return raise_unauthorised_exception(
-            "Refresh token not found. Are you sending the refresh token in the request?",
-            clear_tokens=True,
-            response_mutators=response_mutators,
-        )
-
-    assert refresh_token is not None
-
-    disable_anti_csrf = request_transfer_method == "header"
-    anti_csrf_token = get_anti_csrf_header(request)
-
-    anti_csrf = config.anti_csrf_function_or_string
-    if callable(anti_csrf):
-        anti_csrf = anti_csrf(request, user_context)
-
-    if anti_csrf == "VIA_CUSTOM_HEADER" and not disable_anti_csrf:
-        if get_rid_from_header(request) is None:
-            log_debug_message(
-                "refreshSession: Returning UNAUTHORISED because anti-csrf token is undefined"
-            )
-            # see https://github.com/supertokens/supertokens-node/issues/141
-            raise_unauthorised_exception(
-                "anti-csrf check failed. Please pass 'rid: \"session\"' header in the request.",
-                clear_tokens=True,
-            )
-        disable_anti_csrf = True
-
-    session: Optional[SessionContainer] = None
-    try:
-        session = await recipe_interface_impl.refresh_session(
-            refresh_token, anti_csrf_token, disable_anti_csrf, user_context
-        )
-    except SuperTokensSessionError as e:
-        if isinstance(e, TokenTheftError) or (
-            isinstance(e, UnauthorisedError) and getattr(e, "clear_tokens") is True
-        ):
-            # We clear the LEGACY_ID_REFRESH_TOKEN_COOKIE_NAME here because we want to limit the scope of
-            # this legacy/migration code so the token clearing functions in the error handlers do not.
-            if request.get_cookie(LEGACY_ID_REFRESH_TOKEN_COOKIE_NAME) is not None:
-                log_debug_message(
-                    "refreshSession: cleared legacy id refresh token because refresh token was not found"
-                )
-                response_mutators.append(
-                    set_cookie_response_mutator(
-                        config,
-                        LEGACY_ID_REFRESH_TOKEN_COOKIE_NAME,
-                        "",
-                        0,
-                        "access_token_path",
-                        request,
-                    )
-                )
-
-        e.response_mutators.extend(response_mutators)
-        raise e
-
-    log_debug_message(
-        "refreshSession: Attaching refreshed session info as %s",
-        request_transfer_method,
-    )
-
-    # We clear the tokens in all token transfer methods we are not going to overwrite:
-    for transfer_method in available_token_transfer_methods:
-        if (
-            transfer_method != request_transfer_method
-            and refresh_tokens[transfer_method] is not None
-        ):
-            response_mutators.append(
-                clear_session_mutator(config, transfer_method, request)
-            )
-
-    await session.attach_to_request_response(
-        request, request_transfer_method, user_context
-    )
-    log_debug_message("refreshSession: Success!")
-
-    # This token isn't handled by getToken/setToken to limit the scope of this legacy/migration code
-    if request.get_cookie(LEGACY_ID_REFRESH_TOKEN_COOKIE_NAME) is not None:
-        log_debug_message(
-            "refreshSession: cleared legacy id refresh token after successful refresh"
-        )
-        response_mutators.append(
-            set_cookie_response_mutator(
-                config,
-                LEGACY_ID_REFRESH_TOKEN_COOKIE_NAME,
-                "",
-                0,
-                "access_token_path",
-                request,
-            )
-        )
-
-    session.response_mutators.extend(response_mutators)
-    return session
-
-
-
-
-
-
-
-

Functions

-
-
-async def create_new_session_in_request(request: Any, user_context: Dict[str, Any], recipe_instance: SessionRecipe, access_token_payload: Dict[str, Any], user_id: str, config: SessionConfig, app_info: AppInfo, session_data_in_database: Dict[str, Any], tenant_id: str) ‑> SessionContainer -
-
-
-
- -Expand source code - -
async def create_new_session_in_request(
-    request: Any,
-    user_context: Dict[str, Any],
-    recipe_instance: SessionRecipe,
-    access_token_payload: Dict[str, Any],
-    user_id: str,
-    config: SessionConfig,
-    app_info: AppInfo,
-    session_data_in_database: Dict[str, Any],
-    tenant_id: str,
-) -> SessionContainer:
-    log_debug_message("createNewSession: Started")
-
-    # Handling framework specific request/response wrapping
-    if not hasattr(request, "wrapper_used") or not request.wrapper_used:
-        request = FRAMEWORKS[
-            Supertokens.get_instance().app_info.framework
-        ].wrap_request(request)
-
-    log_debug_message("createNewSession: Wrapping done")
-    user_context = set_request_in_user_context_if_not_defined(user_context, request)
-
-    claims_added_by_other_recipes = recipe_instance.get_claims_added_by_other_recipes()
-    issuer = (
-        app_info.api_domain.get_as_string_dangerous()
-        + app_info.api_base_path.get_as_string_dangerous()
-    )
-
-    final_access_token_payload = {**access_token_payload, "iss": issuer}
-
-    for prop in protected_props:
-        if prop in final_access_token_payload:
-            del final_access_token_payload[prop]
-
-    for claim in claims_added_by_other_recipes:
-        update = await claim.build(user_id, tenant_id, user_context)
-        final_access_token_payload.update(update)
-
-    log_debug_message("createNewSession: Access token payload built")
-
-    output_transfer_method = config.get_token_transfer_method(
-        request, True, user_context
-    )
-    if output_transfer_method == "any":
-        auth_mode_header = get_auth_mode_from_header(request)
-        if auth_mode_header == "cookie":
-            output_transfer_method = auth_mode_header
-        else:
-            output_transfer_method = "header"
-
-    log_debug_message(
-        "createNewSession: using transfer method %s", output_transfer_method
-    )
-
-    if (
-        output_transfer_method == "cookie"
-        and config.get_cookie_same_site(request, user_context) == "none"
-        and not config.cookie_secure
-        and not (
-            (
-                app_info.top_level_api_domain == "localhost"
-                or is_an_ip_address(app_info.top_level_api_domain)
-            )
-            and (
-                app_info.get_top_level_website_domain(request, user_context)
-                == "localhost"
-                or is_an_ip_address(
-                    app_info.get_top_level_website_domain(request, user_context)
-                )
-            )
-        )
-    ):
-        # We can allow insecure cookie when both website & API domain are localhost or an IP
-        # When either of them is a different domain, API domain needs to have https and a secure cookie to work
-        raise Exception(
-            "Since your API and website domain are different, for sessions to work, please use https on your apiDomain and don't set cookieSecure to false."
-        )
-
-    disable_anti_csrf = output_transfer_method == "header"
-    session = await recipe_instance.recipe_implementation.create_new_session(
-        user_id,
-        final_access_token_payload,
-        session_data_in_database,
-        disable_anti_csrf,
-        tenant_id,
-        user_context=user_context,
-    )
-
-    log_debug_message("createNewSession: Session created in core built")
-
-    for transfer_method in available_token_transfer_methods:
-        if (
-            transfer_method != output_transfer_method
-            and get_token(request, "access", transfer_method) is not None
-        ):
-            session.response_mutators.append(
-                clear_session_mutator(config, transfer_method, request)
-            )
-
-    log_debug_message("createNewSession: Cleared old tokens")
-
-    await session.attach_to_request_response(
-        request, output_transfer_method, user_context
-    )
-    log_debug_message("createNewSession: Attached new tokens to res")
-
-    return session
-
-
-
-async def get_session_from_request(request: Any, config: SessionConfig, recipe_interface_impl: SessionRecipeInterface, session_required: Optional[bool] = None, anti_csrf_check: Optional[bool] = None, check_database: Optional[bool] = None, override_global_claim_validators: Optional[Callable[[List[SessionClaimValidator], SessionContainer, Dict[str, Any]], MaybeAwaitable[List[SessionClaimValidator]]]] = None, user_context: Optional[Dict[str, Any]] = None) ‑> Optional[SessionContainer] -
-
-
-
- -Expand source code - -
async def get_session_from_request(
-    request: Any,
-    config: SessionConfig,
-    recipe_interface_impl: SessionRecipeInterface,
-    session_required: Optional[bool] = None,
-    anti_csrf_check: Optional[bool] = None,
-    check_database: Optional[bool] = None,
-    override_global_claim_validators: Optional[
-        Callable[
-            [List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
-            MaybeAwaitable[List[SessionClaimValidator]],
-        ]
-    ] = None,
-    user_context: Optional[Dict[str, Any]] = None,
-) -> Optional[SessionContainer]:
-    log_debug_message("getSession: Started")
-
-    if not hasattr(request, "wrapper_used") or not request.wrapper_used:
-        request = FRAMEWORKS[
-            Supertokens.get_instance().app_info.framework
-        ].wrap_request(request)
-
-    log_debug_message("getSession: Wrapping done")
-
-    user_context = set_request_in_user_context_if_not_defined(user_context, request)
-
-    # This token isn't handled by getToken to limit the scope of this legacy/migration code
-    if request.get_cookie(LEGACY_ID_REFRESH_TOKEN_COOKIE_NAME) is not None:
-        log_debug_message(
-            "getSession: Throwing TRY_REFRESH_TOKEN because the request is using a legacy session"
-        )
-        # This could create a spike on refresh calls during the update of the backend SDK
-        return raise_try_refresh_token_exception(
-            "using legacy session, please call the refresh API"
-        )
-
-    session_optional = not session_required
-    log_debug_message("getSession: optional validation: %s", session_optional)
-
-    access_tokens: Dict[TokenTransferMethod, ParsedJWTInfo] = {}
-
-    # We check all token transfer methods for available access tokens
-    for transfer_method in available_token_transfer_methods:
-        token_string = get_token(request, "access", transfer_method)
-
-        if token_string is not None:
-            try:
-                info = parse_jwt_without_signature_verification(token_string)
-                validate_access_token_structure(info.payload, info.version)
-                log_debug_message(
-                    "getSession: got access token from %s", transfer_method
-                )
-                access_tokens[transfer_method] = info
-            except Exception:
-                log_debug_message(
-                    "getSession: ignoring token in %s, because it doesn't match our access token structure",
-                    transfer_method,
-                )
-
-    allowed_transfer_method = config.get_token_transfer_method(
-        request, False, user_context
-    )
-    request_transfer_method: Optional[TokenTransferMethod] = None
-    request_access_token: Union[ParsedJWTInfo, None] = None
-
-    if (allowed_transfer_method in ("any", "header")) and access_tokens.get(
-        "header"
-    ) is not None:
-        log_debug_message("getSession: using header transfer method")
-        request_transfer_method = "header"
-        request_access_token = access_tokens["header"]
-    elif (allowed_transfer_method in ("any", "cookie")) and access_tokens.get(
-        "cookie"
-    ) is not None:
-        log_debug_message("getSession: using cookie transfer method")
-
-        # If multiple access tokens exist in the request cookie, throw TRY_REFRESH_TOKEN.
-        # This prompts the client to call the refresh endpoint, clearing older_cookie_domain cookies (if set).
-        # ensuring outdated token payload isn't used.
-        if has_multiple_cookies_for_token_type(request, "access"):
-            log_debug_message(
-                "getSession: Throwing TRY_REFRESH_TOKEN because multiple access tokens are present in request cookies"
-            )
-            raise_try_refresh_token_exception(
-                "Multiple access tokens present in the request cookies."
-            )
-
-        request_transfer_method = "cookie"
-        request_access_token = access_tokens["cookie"]
-
-    anti_csrf_token = get_anti_csrf_header(request)
-    do_anti_csrf_check = anti_csrf_check
-
-    if do_anti_csrf_check is None:
-        do_anti_csrf_check = normalise_http_method(request.method()) != "get"
-    if request_transfer_method == "header":
-        do_anti_csrf_check = False
-    if request_access_token is None:
-        do_anti_csrf_check = False
-
-    if callable(config.anti_csrf_function_or_string):
-        anti_csrf = config.anti_csrf_function_or_string(request, user_context)
-    else:
-        anti_csrf = config.anti_csrf_function_or_string
-
-    if do_anti_csrf_check and anti_csrf == "VIA_CUSTOM_HEADER":
-        if anti_csrf == "VIA_CUSTOM_HEADER":
-            if get_rid_from_header(request) is None:
-                log_debug_message(
-                    "getSession: Returning TRY_REFRESH_TOKEN because custom header (rid) was not passed"
-                )
-                raise_try_refresh_token_exception(
-                    "anti-csrf check failed. Please pass 'rid: \"session\"' header in the request, or set doAntiCsrfCheck to false for this API"
-                )
-
-            log_debug_message("getSession: VIA_CUSTOM_HEADER anti-csrf check passed")
-            do_anti_csrf_check = False
-
-    log_debug_message("getSession: Value of antiCsrfToken is: %s", do_anti_csrf_check)
-
-    session = await recipe_interface_impl.get_session(
-        access_token=(
-            request_access_token.raw_token_string
-            if request_access_token is not None
-            else None
-        ),
-        anti_csrf_token=anti_csrf_token,
-        anti_csrf_check=do_anti_csrf_check,
-        session_required=session_required,
-        check_database=check_database,
-        override_global_claim_validators=override_global_claim_validators,
-        user_context=user_context,
-    )
-
-    if session is not None:
-        claim_validators = await get_required_claim_validators(
-            session, override_global_claim_validators, user_context
-        )
-        await session.assert_claims(claim_validators, user_context)
-
-        # request_transfer_method can only be None here if the user overriddes get_session
-        # to load the session by a custom method in that (very niche) case they also need to
-        # override how the session is attached to the response.
-        # In that scenario the transferMethod passed to attachToRequestResponse likely doesn't
-        # matter, still, we follow the general fallback logic
-
-        if request_transfer_method is not None:
-            final_transfer_method = request_transfer_method
-        elif allowed_transfer_method != "any":
-            final_transfer_method = allowed_transfer_method
-        else:
-            final_transfer_method = "header"
-
-        await session.attach_to_request_response(
-            request, final_transfer_method, user_context
-        )
-
-    return session
-
-
-
-async def refresh_session_in_request(request: Any, user_context: Dict[str, Any], config: SessionConfig, recipe_interface_impl: SessionRecipeInterface) ‑> SessionContainer -
-
-
-
- -Expand source code - -
async def refresh_session_in_request(
-    request: Any,
-    user_context: Dict[str, Any],
-    config: SessionConfig,
-    recipe_interface_impl: SessionRecipeInterface,
-) -> SessionContainer:
-    log_debug_message("refreshSession: Started")
-
-    response_mutators: List[ResponseMutator] = []
-
-    if not hasattr(request, "wrapper_used") or not request.wrapper_used:
-        request = FRAMEWORKS[
-            Supertokens.get_instance().app_info.framework
-        ].wrap_request(request)
-
-    log_debug_message("refreshSession: Wrapping done")
-    user_context = set_request_in_user_context_if_not_defined(user_context, request)
-
-    clear_session_cookies_from_older_cookie_domain(request, config, user_context)
-
-    refresh_tokens: Dict[TokenTransferMethod, Optional[str]] = {}
-
-    for transfer_method in available_token_transfer_methods:
-        refresh_tokens[transfer_method] = get_token(request, "refresh", transfer_method)
-        if refresh_tokens[transfer_method] is not None:
-            log_debug_message(
-                "refreshSession: got refresh token from %s", transfer_method
-            )
-
-    allowed_transfer_method = config.get_token_transfer_method(
-        request, False, user_context
-    )
-    log_debug_message(
-        "refreshSession: getTokenTransferMethod returned: %s",
-        allowed_transfer_method,
-    )
-
-    request_transfer_method: TokenTransferMethod
-    refresh_token: Optional[str]
-
-    if (allowed_transfer_method in ("any", "header")) and (
-        refresh_tokens.get("header")
-    ):
-        log_debug_message("refreshSession: using header transfer method")
-        request_transfer_method = "header"
-        refresh_token = refresh_tokens["header"]
-    elif (allowed_transfer_method in ("any", "cookie")) and (
-        refresh_tokens.get("cookie")
-    ):
-        log_debug_message("refreshSession: using cookie transfer method")
-        request_transfer_method = "cookie"
-        refresh_token = refresh_tokens["cookie"]
-    else:
-        # This token isn't handled by getToken/setToken to limit the scope of this legacy/migration code
-        if request.get_cookie(LEGACY_ID_REFRESH_TOKEN_COOKIE_NAME) is not None:
-            log_debug_message(
-                "refreshSession: cleared legacy id refresh token because refresh token was not found"
-            )
-            response_mutators.append(
-                set_cookie_response_mutator(
-                    config,
-                    LEGACY_ID_REFRESH_TOKEN_COOKIE_NAME,
-                    "",
-                    0,
-                    "access_token_path",
-                    request,
-                )
-            )
-
-        # We need to clear the access token cookie if
-        # - the refresh token is not found, and
-        # - the allowed_transfer_method is 'cookie' or 'any', and
-        # - an access token cookie exists (otherwise it'd be a no-op)
-        # See: https://github.com/supertokens/supertokens-node/issues/790
-        if (
-            allowed_transfer_method in ("cookie", "any")
-            and get_token(request, "access", "cookie") is not None
-        ):
-            log_debug_message(
-                "refreshSession: cleared all session tokens and returning UNAUTHORISED because refresh_token in request is None"
-            )
-
-            # We're clearing all session tokens instead of just the access token and then throwing an UNAUTHORISED
-            # error with `clear_tokens: False`. This approach avoids confusion and we don't want to retain session
-            # tokens on the client in any case if the refresh API is called without a refresh token but with an access token.
-            return raise_unauthorised_exception(
-                "Refresh token not found but access token is present. Clearing all tokens.",
-                clear_tokens=True,
-                response_mutators=response_mutators,
-            )
-
-        return raise_unauthorised_exception(
-            "Refresh token not found. Are you sending the refresh token in the request?",
-            clear_tokens=True,
-            response_mutators=response_mutators,
-        )
-
-    assert refresh_token is not None
-
-    disable_anti_csrf = request_transfer_method == "header"
-    anti_csrf_token = get_anti_csrf_header(request)
-
-    anti_csrf = config.anti_csrf_function_or_string
-    if callable(anti_csrf):
-        anti_csrf = anti_csrf(request, user_context)
-
-    if anti_csrf == "VIA_CUSTOM_HEADER" and not disable_anti_csrf:
-        if get_rid_from_header(request) is None:
-            log_debug_message(
-                "refreshSession: Returning UNAUTHORISED because anti-csrf token is undefined"
-            )
-            # see https://github.com/supertokens/supertokens-node/issues/141
-            raise_unauthorised_exception(
-                "anti-csrf check failed. Please pass 'rid: \"session\"' header in the request.",
-                clear_tokens=True,
-            )
-        disable_anti_csrf = True
-
-    session: Optional[SessionContainer] = None
-    try:
-        session = await recipe_interface_impl.refresh_session(
-            refresh_token, anti_csrf_token, disable_anti_csrf, user_context
-        )
-    except SuperTokensSessionError as e:
-        if isinstance(e, TokenTheftError) or (
-            isinstance(e, UnauthorisedError) and getattr(e, "clear_tokens") is True
-        ):
-            # We clear the LEGACY_ID_REFRESH_TOKEN_COOKIE_NAME here because we want to limit the scope of
-            # this legacy/migration code so the token clearing functions in the error handlers do not.
-            if request.get_cookie(LEGACY_ID_REFRESH_TOKEN_COOKIE_NAME) is not None:
-                log_debug_message(
-                    "refreshSession: cleared legacy id refresh token because refresh token was not found"
-                )
-                response_mutators.append(
-                    set_cookie_response_mutator(
-                        config,
-                        LEGACY_ID_REFRESH_TOKEN_COOKIE_NAME,
-                        "",
-                        0,
-                        "access_token_path",
-                        request,
-                    )
-                )
-
-        e.response_mutators.extend(response_mutators)
-        raise e
-
-    log_debug_message(
-        "refreshSession: Attaching refreshed session info as %s",
-        request_transfer_method,
-    )
-
-    # We clear the tokens in all token transfer methods we are not going to overwrite:
-    for transfer_method in available_token_transfer_methods:
-        if (
-            transfer_method != request_transfer_method
-            and refresh_tokens[transfer_method] is not None
-        ):
-            response_mutators.append(
-                clear_session_mutator(config, transfer_method, request)
-            )
-
-    await session.attach_to_request_response(
-        request, request_transfer_method, user_context
-    )
-    log_debug_message("refreshSession: Success!")
-
-    # This token isn't handled by getToken/setToken to limit the scope of this legacy/migration code
-    if request.get_cookie(LEGACY_ID_REFRESH_TOKEN_COOKIE_NAME) is not None:
-        log_debug_message(
-            "refreshSession: cleared legacy id refresh token after successful refresh"
-        )
-        response_mutators.append(
-            set_cookie_response_mutator(
-                config,
-                LEGACY_ID_REFRESH_TOKEN_COOKIE_NAME,
-                "",
-                0,
-                "access_token_path",
-                request,
-            )
-        )
-
-    session.response_mutators.extend(response_mutators)
-    return session
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/session/syncio/index.html b/html/supertokens_python/recipe/session/syncio/index.html deleted file mode 100644 index 43d714068..000000000 --- a/html/supertokens_python/recipe/session/syncio/index.html +++ /dev/null @@ -1,1033 +0,0 @@ - - - - - - -supertokens_python.recipe.session.syncio API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.session.syncio

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-from typing import Any, Dict, List, Union, Callable, Optional, TypeVar
-
-from supertokens_python.async_to_sync_wrapper import sync
-from supertokens_python.recipe.openid.interfaces import (
-    GetOpenIdDiscoveryConfigurationResult,
-)
-from supertokens_python.types import MaybeAwaitable
-
-from ...jwt.interfaces import (
-    CreateJwtOkResult,
-    CreateJwtResultUnsupportedAlgorithm,
-    GetJWKSResult,
-)
-from ..interfaces import (
-    SessionContainer,
-    SessionInformationResult,
-    SessionClaimValidator,
-    SessionClaim,
-    JSONObject,
-    ClaimsValidationResult,
-    SessionDoesNotExistError,
-    GetClaimValueOkResult,
-)
-
-
-def create_new_session(
-    request: Any,
-    tenant_id: str,
-    user_id: str,
-    access_token_payload: Union[Dict[str, Any], None] = None,
-    session_data_in_database: Union[Dict[str, Any], None] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> SessionContainer:
-    from supertokens_python.recipe.session.asyncio import (
-        create_new_session as async_create_new_session,
-    )
-
-    return sync(
-        async_create_new_session(
-            tenant_id=tenant_id,
-            request=request,
-            user_id=user_id,
-            access_token_payload=access_token_payload,
-            session_data_in_database=session_data_in_database,
-            user_context=user_context,
-        )
-    )
-
-
-def create_new_session_without_request_response(
-    tenant_id: str,
-    user_id: str,
-    access_token_payload: Union[Dict[str, Any], None] = None,
-    session_data_in_database: Union[Dict[str, Any], None] = None,
-    disable_anti_csrf: bool = False,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> SessionContainer:
-    from supertokens_python.recipe.session.asyncio import (
-        create_new_session_without_request_response as async_create_new_session_without_request_response,
-    )
-
-    return sync(
-        async_create_new_session_without_request_response(
-            tenant_id,
-            user_id,
-            access_token_payload,
-            session_data_in_database,
-            disable_anti_csrf,
-            user_context,
-        )
-    )
-
-
-def get_session(
-    request: Any,
-    session_required: bool = True,
-    anti_csrf_check: Union[bool, None] = None,
-    check_database: Optional[bool] = None,
-    override_global_claim_validators: Optional[
-        Callable[
-            [List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
-            MaybeAwaitable[List[SessionClaimValidator]],
-        ]
-    ] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> Union[SessionContainer, None]:
-    from supertokens_python.recipe.session.asyncio import (
-        get_session as async_get_session,
-    )
-
-    return sync(
-        async_get_session(
-            request,
-            session_required,
-            anti_csrf_check,
-            check_database,
-            override_global_claim_validators,
-            user_context,
-        )
-    )
-
-
-def get_session_without_request_response(
-    access_token: str,
-    anti_csrf_token: Optional[str] = None,
-    anti_csrf_check: Optional[bool] = None,
-    session_required: Optional[bool] = None,
-    check_database: Optional[bool] = None,
-    override_global_claim_validators: Optional[
-        Callable[
-            [List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
-            MaybeAwaitable[List[SessionClaimValidator]],
-        ]
-    ] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> Optional[SessionContainer]:
-    from supertokens_python.recipe.session.asyncio import (
-        get_session_without_request_response as async_get_session_without_request_response,
-    )
-
-    return sync(
-        async_get_session_without_request_response(
-            access_token,
-            anti_csrf_token,
-            anti_csrf_check,
-            session_required,
-            check_database,
-            override_global_claim_validators,
-            user_context,
-        )
-    )
-
-
-def refresh_session(
-    request: Any, user_context: Union[None, Dict[str, Any]] = None
-) -> SessionContainer:
-    from supertokens_python.recipe.session.asyncio import (
-        refresh_session as async_refresh_session,
-    )
-
-    return sync(async_refresh_session(request, user_context))
-
-
-def refresh_session_without_request_response(
-    refresh_token: str,
-    disable_anti_csrf: bool = False,
-    anti_csrf_token: Optional[str] = None,
-    user_context: Optional[Dict[str, Any]] = None,
-) -> SessionContainer:
-    from supertokens_python.recipe.session.asyncio import (
-        refresh_session_without_request_response as async_refresh_session_without_request_response,
-    )
-
-    return sync(
-        async_refresh_session_without_request_response(
-            refresh_token,
-            disable_anti_csrf,
-            anti_csrf_token,
-            user_context,
-        )
-    )
-
-
-def revoke_session(
-    session_handle: str, user_context: Union[None, Dict[str, Any]] = None
-) -> bool:
-    from supertokens_python.recipe.session.asyncio import (
-        revoke_session as async_revoke_session,
-    )
-
-    return sync(async_revoke_session(session_handle, user_context))
-
-
-def revoke_all_sessions_for_user(
-    user_id: str,
-    tenant_id: Optional[str] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> List[str]:
-    from supertokens_python.recipe.session.asyncio import (
-        revoke_all_sessions_for_user as async_revoke_all_sessions_for_user,
-    )
-
-    return sync(async_revoke_all_sessions_for_user(user_id, tenant_id, user_context))
-
-
-def get_all_session_handles_for_user(
-    user_id: str,
-    tenant_id: Optional[str] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> List[str]:
-    from supertokens_python.recipe.session.asyncio import (
-        get_all_session_handles_for_user as async_get_all_session_handles_for_user,
-    )
-
-    return sync(
-        async_get_all_session_handles_for_user(user_id, tenant_id, user_context)
-    )
-
-
-def revoke_multiple_sessions(
-    session_handles: List[str], user_context: Union[None, Dict[str, Any]] = None
-) -> List[str]:
-    from supertokens_python.recipe.session.asyncio import (
-        revoke_multiple_sessions as async_revoke_multiple_sessions,
-    )
-
-    return sync(async_revoke_multiple_sessions(session_handles, user_context))
-
-
-def get_session_information(
-    session_handle: str, user_context: Union[None, Dict[str, Any]] = None
-) -> Union[SessionInformationResult, None]:
-    from supertokens_python.recipe.session.asyncio import (
-        get_session_information as async_get_session_information,
-    )
-
-    return sync(async_get_session_information(session_handle, user_context))
-
-
-def update_session_data_in_database(
-    session_handle: str,
-    new_session_data: Dict[str, Any],
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> bool:
-    from supertokens_python.recipe.session.asyncio import (
-        update_session_data_in_database as async_update_session_data_in_database,
-    )
-
-    return sync(
-        async_update_session_data_in_database(
-            session_handle, new_session_data, user_context
-        )
-    )
-
-
-def merge_into_access_token_payload(
-    session_handle: str,
-    new_access_token_payload: Dict[str, Any],
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> bool:
-    from supertokens_python.recipe.session.asyncio import (
-        merge_into_access_token_payload as async_merge_into_access_token_payload,
-    )
-
-    return sync(
-        async_merge_into_access_token_payload(
-            session_handle, new_access_token_payload, user_context
-        )
-    )
-
-
-def create_jwt(
-    payload: Dict[str, Any],
-    validity_seconds: Optional[int] = None,
-    use_static_signing_key: Optional[bool] = None,
-    user_context: Optional[Dict[str, Any]] = None,
-) -> Union[CreateJwtOkResult, CreateJwtResultUnsupportedAlgorithm]:
-    from supertokens_python.recipe.session.asyncio import create_jwt as async_create_jwt
-
-    return sync(
-        async_create_jwt(
-            payload, validity_seconds, use_static_signing_key, user_context
-        )
-    )
-
-
-def get_jwks(user_context: Optional[Dict[str, Any]] = None) -> GetJWKSResult:
-    from supertokens_python.recipe.session.asyncio import get_jwks as async_get_jwks
-
-    return sync(async_get_jwks(user_context))
-
-
-def get_open_id_discovery_configuration(
-    user_context: Optional[Dict[str, Any]] = None
-) -> GetOpenIdDiscoveryConfigurationResult:
-    from supertokens_python.recipe.session.asyncio import (
-        get_open_id_discovery_configuration as async_get_open_id_discovery_configuration,
-    )
-
-    return sync(async_get_open_id_discovery_configuration(user_context))
-
-
-_T = TypeVar("_T")
-
-
-def fetch_and_set_claim(
-    session_handle: str,
-    claim: SessionClaim[Any],
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> bool:
-    from supertokens_python.recipe.session.asyncio import (
-        fetch_and_set_claim as async_fetch_and_set_claim,
-    )
-
-    return sync(async_fetch_and_set_claim(session_handle, claim, user_context))
-
-
-def set_claim_value(
-    session_handle: str,
-    claim: SessionClaim[_T],
-    value: _T,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> bool:
-    from supertokens_python.recipe.session.asyncio import (
-        set_claim_value as async_set_claim_value,
-    )
-
-    return sync(async_set_claim_value(session_handle, claim, value, user_context))
-
-
-def get_claim_value(
-    session_handle: str,
-    claim: SessionClaim[_T],
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> Union[SessionDoesNotExistError, GetClaimValueOkResult[_T]]:
-    from supertokens_python.recipe.session.asyncio import (
-        get_claim_value as async_get_claim_value,
-    )
-
-    return sync(async_get_claim_value(session_handle, claim, user_context))
-
-
-def remove_claim(
-    session_handle: str,
-    claim: SessionClaim[Any],
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> bool:
-    from supertokens_python.recipe.session.asyncio import (
-        remove_claim as async_remove_claim,
-    )
-
-    return sync(async_remove_claim(session_handle, claim, user_context))
-
-
-def validate_claims_for_session_handle(
-    session_handle: str,
-    override_global_claim_validators: Optional[
-        Callable[
-            [List[SessionClaimValidator], SessionInformationResult, Dict[str, Any]],
-            MaybeAwaitable[List[SessionClaimValidator]],
-        ]
-    ] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> Union[SessionDoesNotExistError, ClaimsValidationResult]:
-    from supertokens_python.recipe.session.asyncio import (
-        validate_claims_for_session_handle as async_validate_claims_for_session_handle,
-    )
-
-    return sync(
-        async_validate_claims_for_session_handle(
-            session_handle, override_global_claim_validators, user_context
-        )
-    )
-
-
-def validate_claims_in_jwt_payload(
-    tenant_id: str,
-    user_id: str,
-    jwt_payload: JSONObject,
-    override_global_claim_validators: Optional[
-        Callable[
-            [List[SessionClaimValidator], str, Dict[str, Any]],
-            MaybeAwaitable[List[SessionClaimValidator]],
-        ]
-    ] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-):
-    from supertokens_python.recipe.session.asyncio import (
-        validate_claims_in_jwt_payload as async_validate_claims_in_jwt_payload,
-    )
-
-    return sync(
-        async_validate_claims_in_jwt_payload(
-            tenant_id,
-            user_id,
-            jwt_payload,
-            override_global_claim_validators,
-            user_context,
-        )
-    )
-
-
-
-
-
-
-
-

Functions

-
-
-def create_jwt(payload: Dict[str, Any], validity_seconds: Optional[int] = None, use_static_signing_key: Optional[bool] = None, user_context: Optional[Dict[str, Any]] = None) ‑> Union[CreateJwtOkResultCreateJwtResultUnsupportedAlgorithm] -
-
-
-
- -Expand source code - -
def create_jwt(
-    payload: Dict[str, Any],
-    validity_seconds: Optional[int] = None,
-    use_static_signing_key: Optional[bool] = None,
-    user_context: Optional[Dict[str, Any]] = None,
-) -> Union[CreateJwtOkResult, CreateJwtResultUnsupportedAlgorithm]:
-    from supertokens_python.recipe.session.asyncio import create_jwt as async_create_jwt
-
-    return sync(
-        async_create_jwt(
-            payload, validity_seconds, use_static_signing_key, user_context
-        )
-    )
-
-
-
-def create_new_session(request: Any, tenant_id: str, user_id: str, access_token_payload: Union[Dict[str, Any], None] = None, session_data_in_database: Union[Dict[str, Any], None] = None, user_context: Union[None, Dict[str, Any]] = None) ‑> SessionContainer -
-
-
-
- -Expand source code - -
def create_new_session(
-    request: Any,
-    tenant_id: str,
-    user_id: str,
-    access_token_payload: Union[Dict[str, Any], None] = None,
-    session_data_in_database: Union[Dict[str, Any], None] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> SessionContainer:
-    from supertokens_python.recipe.session.asyncio import (
-        create_new_session as async_create_new_session,
-    )
-
-    return sync(
-        async_create_new_session(
-            tenant_id=tenant_id,
-            request=request,
-            user_id=user_id,
-            access_token_payload=access_token_payload,
-            session_data_in_database=session_data_in_database,
-            user_context=user_context,
-        )
-    )
-
-
-
-def create_new_session_without_request_response(tenant_id: str, user_id: str, access_token_payload: Union[Dict[str, Any], None] = None, session_data_in_database: Union[Dict[str, Any], None] = None, disable_anti_csrf: bool = False, user_context: Union[None, Dict[str, Any]] = None) ‑> SessionContainer -
-
-
-
- -Expand source code - -
def create_new_session_without_request_response(
-    tenant_id: str,
-    user_id: str,
-    access_token_payload: Union[Dict[str, Any], None] = None,
-    session_data_in_database: Union[Dict[str, Any], None] = None,
-    disable_anti_csrf: bool = False,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> SessionContainer:
-    from supertokens_python.recipe.session.asyncio import (
-        create_new_session_without_request_response as async_create_new_session_without_request_response,
-    )
-
-    return sync(
-        async_create_new_session_without_request_response(
-            tenant_id,
-            user_id,
-            access_token_payload,
-            session_data_in_database,
-            disable_anti_csrf,
-            user_context,
-        )
-    )
-
-
-
-def fetch_and_set_claim(session_handle: str, claim: SessionClaim[Any], user_context: Union[None, Dict[str, Any]] = None) ‑> bool -
-
-
-
- -Expand source code - -
def fetch_and_set_claim(
-    session_handle: str,
-    claim: SessionClaim[Any],
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> bool:
-    from supertokens_python.recipe.session.asyncio import (
-        fetch_and_set_claim as async_fetch_and_set_claim,
-    )
-
-    return sync(async_fetch_and_set_claim(session_handle, claim, user_context))
-
-
-
-def get_all_session_handles_for_user(user_id: str, tenant_id: Optional[str] = None, user_context: Union[None, Dict[str, Any]] = None) ‑> List[str] -
-
-
-
- -Expand source code - -
def get_all_session_handles_for_user(
-    user_id: str,
-    tenant_id: Optional[str] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> List[str]:
-    from supertokens_python.recipe.session.asyncio import (
-        get_all_session_handles_for_user as async_get_all_session_handles_for_user,
-    )
-
-    return sync(
-        async_get_all_session_handles_for_user(user_id, tenant_id, user_context)
-    )
-
-
-
-def get_claim_value(session_handle: str, claim: SessionClaim[_T], user_context: Union[None, Dict[str, Any]] = None) ‑> Union[SessionDoesNotExistErrorGetClaimValueOkResult[~_T]] -
-
-
-
- -Expand source code - -
def get_claim_value(
-    session_handle: str,
-    claim: SessionClaim[_T],
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> Union[SessionDoesNotExistError, GetClaimValueOkResult[_T]]:
-    from supertokens_python.recipe.session.asyncio import (
-        get_claim_value as async_get_claim_value,
-    )
-
-    return sync(async_get_claim_value(session_handle, claim, user_context))
-
-
-
-def get_jwks(user_context: Optional[Dict[str, Any]] = None) ‑> GetJWKSResult -
-
-
-
- -Expand source code - -
def get_jwks(user_context: Optional[Dict[str, Any]] = None) -> GetJWKSResult:
-    from supertokens_python.recipe.session.asyncio import get_jwks as async_get_jwks
-
-    return sync(async_get_jwks(user_context))
-
-
-
-def get_open_id_discovery_configuration(user_context: Optional[Dict[str, Any]] = None) ‑> GetOpenIdDiscoveryConfigurationResult -
-
-
-
- -Expand source code - -
def get_open_id_discovery_configuration(
-    user_context: Optional[Dict[str, Any]] = None
-) -> GetOpenIdDiscoveryConfigurationResult:
-    from supertokens_python.recipe.session.asyncio import (
-        get_open_id_discovery_configuration as async_get_open_id_discovery_configuration,
-    )
-
-    return sync(async_get_open_id_discovery_configuration(user_context))
-
-
-
-def get_session(request: Any, session_required: bool = True, anti_csrf_check: Union[bool, None] = None, check_database: Optional[bool] = None, override_global_claim_validators: Optional[Callable[[List[SessionClaimValidator], SessionContainer, Dict[str, Any]], MaybeAwaitable[List[SessionClaimValidator]]]] = None, user_context: Union[None, Dict[str, Any]] = None) ‑> Optional[SessionContainer] -
-
-
-
- -Expand source code - -
def get_session(
-    request: Any,
-    session_required: bool = True,
-    anti_csrf_check: Union[bool, None] = None,
-    check_database: Optional[bool] = None,
-    override_global_claim_validators: Optional[
-        Callable[
-            [List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
-            MaybeAwaitable[List[SessionClaimValidator]],
-        ]
-    ] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> Union[SessionContainer, None]:
-    from supertokens_python.recipe.session.asyncio import (
-        get_session as async_get_session,
-    )
-
-    return sync(
-        async_get_session(
-            request,
-            session_required,
-            anti_csrf_check,
-            check_database,
-            override_global_claim_validators,
-            user_context,
-        )
-    )
-
-
-
-def get_session_information(session_handle: str, user_context: Union[None, Dict[str, Any]] = None) ‑> Optional[SessionInformationResult] -
-
-
-
- -Expand source code - -
def get_session_information(
-    session_handle: str, user_context: Union[None, Dict[str, Any]] = None
-) -> Union[SessionInformationResult, None]:
-    from supertokens_python.recipe.session.asyncio import (
-        get_session_information as async_get_session_information,
-    )
-
-    return sync(async_get_session_information(session_handle, user_context))
-
-
-
-def get_session_without_request_response(access_token: str, anti_csrf_token: Optional[str] = None, anti_csrf_check: Optional[bool] = None, session_required: Optional[bool] = None, check_database: Optional[bool] = None, override_global_claim_validators: Optional[Callable[[List[SessionClaimValidator], SessionContainer, Dict[str, Any]], MaybeAwaitable[List[SessionClaimValidator]]]] = None, user_context: Union[None, Dict[str, Any]] = None) ‑> Optional[SessionContainer] -
-
-
-
- -Expand source code - -
def get_session_without_request_response(
-    access_token: str,
-    anti_csrf_token: Optional[str] = None,
-    anti_csrf_check: Optional[bool] = None,
-    session_required: Optional[bool] = None,
-    check_database: Optional[bool] = None,
-    override_global_claim_validators: Optional[
-        Callable[
-            [List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
-            MaybeAwaitable[List[SessionClaimValidator]],
-        ]
-    ] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> Optional[SessionContainer]:
-    from supertokens_python.recipe.session.asyncio import (
-        get_session_without_request_response as async_get_session_without_request_response,
-    )
-
-    return sync(
-        async_get_session_without_request_response(
-            access_token,
-            anti_csrf_token,
-            anti_csrf_check,
-            session_required,
-            check_database,
-            override_global_claim_validators,
-            user_context,
-        )
-    )
-
-
-
-def merge_into_access_token_payload(session_handle: str, new_access_token_payload: Dict[str, Any], user_context: Union[None, Dict[str, Any]] = None) ‑> bool -
-
-
-
- -Expand source code - -
def merge_into_access_token_payload(
-    session_handle: str,
-    new_access_token_payload: Dict[str, Any],
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> bool:
-    from supertokens_python.recipe.session.asyncio import (
-        merge_into_access_token_payload as async_merge_into_access_token_payload,
-    )
-
-    return sync(
-        async_merge_into_access_token_payload(
-            session_handle, new_access_token_payload, user_context
-        )
-    )
-
-
-
-def refresh_session(request: Any, user_context: Union[None, Dict[str, Any]] = None) ‑> SessionContainer -
-
-
-
- -Expand source code - -
def refresh_session(
-    request: Any, user_context: Union[None, Dict[str, Any]] = None
-) -> SessionContainer:
-    from supertokens_python.recipe.session.asyncio import (
-        refresh_session as async_refresh_session,
-    )
-
-    return sync(async_refresh_session(request, user_context))
-
-
-
-def refresh_session_without_request_response(refresh_token: str, disable_anti_csrf: bool = False, anti_csrf_token: Optional[str] = None, user_context: Optional[Dict[str, Any]] = None) ‑> SessionContainer -
-
-
-
- -Expand source code - -
def refresh_session_without_request_response(
-    refresh_token: str,
-    disable_anti_csrf: bool = False,
-    anti_csrf_token: Optional[str] = None,
-    user_context: Optional[Dict[str, Any]] = None,
-) -> SessionContainer:
-    from supertokens_python.recipe.session.asyncio import (
-        refresh_session_without_request_response as async_refresh_session_without_request_response,
-    )
-
-    return sync(
-        async_refresh_session_without_request_response(
-            refresh_token,
-            disable_anti_csrf,
-            anti_csrf_token,
-            user_context,
-        )
-    )
-
-
-
-def remove_claim(session_handle: str, claim: SessionClaim[Any], user_context: Union[None, Dict[str, Any]] = None) ‑> bool -
-
-
-
- -Expand source code - -
def remove_claim(
-    session_handle: str,
-    claim: SessionClaim[Any],
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> bool:
-    from supertokens_python.recipe.session.asyncio import (
-        remove_claim as async_remove_claim,
-    )
-
-    return sync(async_remove_claim(session_handle, claim, user_context))
-
-
-
-def revoke_all_sessions_for_user(user_id: str, tenant_id: Optional[str] = None, user_context: Union[None, Dict[str, Any]] = None) ‑> List[str] -
-
-
-
- -Expand source code - -
def revoke_all_sessions_for_user(
-    user_id: str,
-    tenant_id: Optional[str] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> List[str]:
-    from supertokens_python.recipe.session.asyncio import (
-        revoke_all_sessions_for_user as async_revoke_all_sessions_for_user,
-    )
-
-    return sync(async_revoke_all_sessions_for_user(user_id, tenant_id, user_context))
-
-
-
-def revoke_multiple_sessions(session_handles: List[str], user_context: Union[None, Dict[str, Any]] = None) ‑> List[str] -
-
-
-
- -Expand source code - -
def revoke_multiple_sessions(
-    session_handles: List[str], user_context: Union[None, Dict[str, Any]] = None
-) -> List[str]:
-    from supertokens_python.recipe.session.asyncio import (
-        revoke_multiple_sessions as async_revoke_multiple_sessions,
-    )
-
-    return sync(async_revoke_multiple_sessions(session_handles, user_context))
-
-
-
-def revoke_session(session_handle: str, user_context: Union[None, Dict[str, Any]] = None) ‑> bool -
-
-
-
- -Expand source code - -
def revoke_session(
-    session_handle: str, user_context: Union[None, Dict[str, Any]] = None
-) -> bool:
-    from supertokens_python.recipe.session.asyncio import (
-        revoke_session as async_revoke_session,
-    )
-
-    return sync(async_revoke_session(session_handle, user_context))
-
-
-
-def set_claim_value(session_handle: str, claim: SessionClaim[_T], value: _T, user_context: Union[None, Dict[str, Any]] = None) ‑> bool -
-
-
-
- -Expand source code - -
def set_claim_value(
-    session_handle: str,
-    claim: SessionClaim[_T],
-    value: _T,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> bool:
-    from supertokens_python.recipe.session.asyncio import (
-        set_claim_value as async_set_claim_value,
-    )
-
-    return sync(async_set_claim_value(session_handle, claim, value, user_context))
-
-
-
-def update_session_data_in_database(session_handle: str, new_session_data: Dict[str, Any], user_context: Union[None, Dict[str, Any]] = None) ‑> bool -
-
-
-
- -Expand source code - -
def update_session_data_in_database(
-    session_handle: str,
-    new_session_data: Dict[str, Any],
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> bool:
-    from supertokens_python.recipe.session.asyncio import (
-        update_session_data_in_database as async_update_session_data_in_database,
-    )
-
-    return sync(
-        async_update_session_data_in_database(
-            session_handle, new_session_data, user_context
-        )
-    )
-
-
-
-def validate_claims_for_session_handle(session_handle: str, override_global_claim_validators: Optional[Callable[[List[SessionClaimValidator], SessionInformationResult, Dict[str, Any]], MaybeAwaitable[List[SessionClaimValidator]]]] = None, user_context: Union[None, Dict[str, Any]] = None) ‑> Union[SessionDoesNotExistErrorClaimsValidationResult] -
-
-
-
- -Expand source code - -
def validate_claims_for_session_handle(
-    session_handle: str,
-    override_global_claim_validators: Optional[
-        Callable[
-            [List[SessionClaimValidator], SessionInformationResult, Dict[str, Any]],
-            MaybeAwaitable[List[SessionClaimValidator]],
-        ]
-    ] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> Union[SessionDoesNotExistError, ClaimsValidationResult]:
-    from supertokens_python.recipe.session.asyncio import (
-        validate_claims_for_session_handle as async_validate_claims_for_session_handle,
-    )
-
-    return sync(
-        async_validate_claims_for_session_handle(
-            session_handle, override_global_claim_validators, user_context
-        )
-    )
-
-
-
-def validate_claims_in_jwt_payload(tenant_id: str, user_id: str, jwt_payload: JSONObject, override_global_claim_validators: Optional[Callable[[List[SessionClaimValidator], str, Dict[str, Any]], MaybeAwaitable[List[SessionClaimValidator]]]] = None, user_context: Union[None, Dict[str, Any]] = None) -
-
-
-
- -Expand source code - -
def validate_claims_in_jwt_payload(
-    tenant_id: str,
-    user_id: str,
-    jwt_payload: JSONObject,
-    override_global_claim_validators: Optional[
-        Callable[
-            [List[SessionClaimValidator], str, Dict[str, Any]],
-            MaybeAwaitable[List[SessionClaimValidator]],
-        ]
-    ] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-):
-    from supertokens_python.recipe.session.asyncio import (
-        validate_claims_in_jwt_payload as async_validate_claims_in_jwt_payload,
-    )
-
-    return sync(
-        async_validate_claims_in_jwt_payload(
-            tenant_id,
-            user_id,
-            jwt_payload,
-            override_global_claim_validators,
-            user_context,
-        )
-    )
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/session/utils.html b/html/supertokens_python/recipe/session/utils.html deleted file mode 100644 index 8b0773bfc..000000000 --- a/html/supertokens_python/recipe/session/utils.html +++ /dev/null @@ -1,1556 +0,0 @@ - - - - - - -supertokens_python.recipe.session.utils API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.session.utils

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-import json
-from typing import TYPE_CHECKING, Any, Awaitable, Callable, Dict, List, Optional, Union
-from urllib.parse import urlparse
-
-from typing_extensions import Literal
-
-from supertokens_python.exceptions import raise_general_exception
-from supertokens_python.framework import BaseResponse
-from supertokens_python.normalised_url_path import NormalisedURLPath
-from supertokens_python.utils import (
-    is_an_ip_address,
-    resolve,
-    send_200_response,
-    send_non_200_response,
-    send_non_200_response_with_message,
-)
-
-from ...types import MaybeAwaitable
-from .constants import AUTH_MODE_HEADER_KEY, SESSION_REFRESH
-from .exceptions import ClaimValidationError
-
-if TYPE_CHECKING:
-    from supertokens_python.framework import BaseRequest
-    from supertokens_python.supertokens import AppInfo
-    from supertokens_python.recipe.openid import (
-        InputOverrideConfig as OpenIdInputOverrideConfig,
-    )
-
-    from .interfaces import (
-        APIInterface,
-        RecipeInterface,
-        SessionContainer,
-        SessionClaimValidator,
-    )
-    from .recipe import SessionRecipe
-
-from supertokens_python.logger import log_debug_message
-
-
-def normalise_session_scope(session_scope: str) -> str:
-    def helper(scope: str) -> str:
-        scope = scope.strip()
-
-        if scope.startswith("."):
-            scope = scope[1:]
-
-        if (not scope.startswith("https://")) and (not scope.startswith("http://")):
-            scope = "http://" + scope
-
-        try:
-            url_obj = urlparse(scope)
-            if url_obj.hostname is None:
-                raise Exception("Should not come here")
-            scope = url_obj.hostname
-
-            return scope
-        except Exception:
-            raise_general_exception("Please provide a valid session_scope")
-
-    no_dot_normalised = helper(session_scope)
-    if no_dot_normalised == "localhost" or is_an_ip_address(no_dot_normalised):
-        return no_dot_normalised
-
-    if session_scope.startswith("."):
-        return "." + no_dot_normalised
-
-    return no_dot_normalised
-
-
-def normalise_same_site(same_site: str) -> Literal["strict", "lax", "none"]:
-    same_site = same_site.strip()
-    same_site = same_site.lower()
-    allowed_values = {"strict", "lax", "none"}
-    if same_site not in allowed_values:
-        raise Exception('cookie same site must be one of "strict", "lax", or "none"')
-    return same_site  # type: ignore
-
-
-def get_url_scheme(url: str) -> str:
-    url_obj = urlparse(url)
-    return url_obj.scheme
-
-
-class ErrorHandlers:
-    def __init__(
-        self,
-        on_token_theft_detected: Callable[
-            [BaseRequest, str, str, BaseResponse],
-            Union[BaseResponse, Awaitable[BaseResponse]],
-        ],
-        on_try_refresh_token: Callable[
-            [BaseRequest, str, BaseResponse],
-            Union[BaseResponse, Awaitable[BaseResponse]],
-        ],
-        on_unauthorised: Callable[
-            [BaseRequest, str, BaseResponse],
-            Union[BaseResponse, Awaitable[BaseResponse]],
-        ],
-        on_invalid_claim: Callable[
-            [BaseRequest, List[ClaimValidationError], BaseResponse],
-            Union[BaseResponse, Awaitable[BaseResponse]],
-        ],
-        on_clear_duplicate_session_cookies: Callable[
-            [BaseRequest, str, BaseResponse],
-            Union[BaseResponse, Awaitable[BaseResponse]],
-        ],
-    ):
-        self.__on_token_theft_detected = on_token_theft_detected
-        self.__on_try_refresh_token = on_try_refresh_token
-        self.__on_unauthorised = on_unauthorised
-        self.__on_invalid_claim = on_invalid_claim
-        self.__on_clear_duplicate_session_cookies = on_clear_duplicate_session_cookies
-
-    async def on_token_theft_detected(
-        self,
-        request: BaseRequest,
-        session_handle: str,
-        user_id: str,
-        response: BaseResponse,
-    ) -> BaseResponse:
-        return await resolve(
-            self.__on_token_theft_detected(request, session_handle, user_id, response)
-        )
-
-    async def on_try_refresh_token(
-        self, request: BaseRequest, message: str, response: BaseResponse
-    ):
-        result = await resolve(self.__on_try_refresh_token(request, message, response))
-        return result
-
-    async def on_unauthorised(
-        self,
-        request: BaseRequest,
-        message: str,
-        response: BaseResponse,
-    ):
-        return await resolve(self.__on_unauthorised(request, message, response))
-
-    async def on_invalid_claim(
-        self,
-        recipe: SessionRecipe,
-        request: BaseRequest,
-        claim_validation_errors: List[ClaimValidationError],
-        response: BaseResponse,
-    ):
-        _ = recipe
-        result = await resolve(
-            self.__on_invalid_claim(request, claim_validation_errors, response)
-        )
-        return result
-
-    async def on_clear_duplicate_session_cookies(
-        self,
-        request: BaseRequest,
-        message: str,
-        response: BaseResponse,
-    ):
-        result = await resolve(
-            self.__on_clear_duplicate_session_cookies(request, message, response)
-        )
-        return result
-
-
-class InputErrorHandlers(ErrorHandlers):
-    def __init__(
-        self,
-        on_token_theft_detected: Union[
-            None,
-            Callable[
-                [BaseRequest, str, str, BaseResponse],
-                Union[BaseResponse, Awaitable[BaseResponse]],
-            ],
-        ] = None,
-        on_try_refresh_token: Union[
-            None,
-            Callable[
-                [BaseRequest, str, BaseResponse],
-                Union[BaseResponse, Awaitable[BaseResponse]],
-            ],
-        ] = None,
-        on_unauthorised: Union[
-            Callable[
-                [BaseRequest, str, BaseResponse],
-                Union[BaseResponse, Awaitable[BaseResponse]],
-            ],
-            None,
-        ] = None,
-        on_invalid_claim: Union[
-            Callable[
-                [BaseRequest, List[ClaimValidationError], BaseResponse],
-                Union[BaseResponse, Awaitable[BaseResponse]],
-            ],
-            None,
-        ] = None,
-        on_clear_duplicate_session_cookies: Union[
-            None,
-            Callable[
-                [BaseRequest, str, BaseResponse],
-                Union[BaseResponse, Awaitable[BaseResponse]],
-            ],
-        ] = None,
-    ):
-        if on_token_theft_detected is None:
-            on_token_theft_detected = default_token_theft_detected_callback
-        if on_try_refresh_token is None:
-            on_try_refresh_token = default_try_refresh_token_callback
-        if on_unauthorised is None:
-            on_unauthorised = default_unauthorised_callback
-        if on_invalid_claim is None:
-            on_invalid_claim = default_invalid_claim_callback
-        if on_clear_duplicate_session_cookies is None:
-            on_clear_duplicate_session_cookies = (
-                default_clear_duplicate_session_cookies_callback
-            )
-        super().__init__(
-            on_token_theft_detected,
-            on_try_refresh_token,
-            on_unauthorised,
-            on_invalid_claim,
-            on_clear_duplicate_session_cookies,
-        )
-
-
-async def default_unauthorised_callback(
-    _: BaseRequest, __: str, response: BaseResponse
-) -> BaseResponse:
-    from .recipe import SessionRecipe
-
-    return send_non_200_response_with_message(
-        "unauthorised",
-        SessionRecipe.get_instance().config.session_expired_status_code,
-        response,
-    )
-
-
-async def default_try_refresh_token_callback(
-    _: BaseRequest, __: str, response: BaseResponse
-) -> BaseResponse:
-    from .recipe import SessionRecipe
-
-    return send_non_200_response_with_message(
-        "try refresh token",
-        SessionRecipe.get_instance().config.session_expired_status_code,
-        response,
-    )
-
-
-async def default_token_theft_detected_callback(
-    _: BaseRequest, session_handle: str, __: str, response: BaseResponse
-) -> BaseResponse:
-    from .recipe import SessionRecipe
-
-    await SessionRecipe.get_instance().recipe_implementation.revoke_session(
-        session_handle, {}
-    )
-    return send_non_200_response_with_message(
-        "token theft detected",
-        SessionRecipe.get_instance().config.session_expired_status_code,
-        response,
-    )
-
-
-async def default_invalid_claim_callback(
-    _: BaseRequest,
-    claim_validation_errors: List[ClaimValidationError],
-    response: BaseResponse,
-) -> BaseResponse:
-    from .recipe import SessionRecipe
-
-    return send_non_200_response(
-        {
-            "message": "invalid claim",
-            "claimValidationErrors": [err.to_json() for err in claim_validation_errors],
-        },
-        SessionRecipe.get_instance().config.invalid_claim_status_code,
-        response,
-    )
-
-
-async def default_clear_duplicate_session_cookies_callback(
-    _: BaseRequest, message: str, response: BaseResponse
-) -> BaseResponse:
-    # This error occurs in the `refresh_post` API when multiple session
-    # cookies are found in the request and the user has set `older_cookie_domain`.
-    # We remove session cookies from the older_cookie_domain. The response must return `200 OK`
-    # to avoid logging out the user, allowing the session to continue with the valid cookie.
-    return send_200_response({"message": message}, response)
-
-
-def get_auth_mode_from_header(request: BaseRequest) -> Optional[str]:
-    auth_mode = request.get_header(AUTH_MODE_HEADER_KEY)
-    if auth_mode is None:
-        return None
-    return auth_mode.lower()
-
-
-def get_token_transfer_method_default(
-    req: BaseRequest,
-    for_create_new_session: bool,
-    user_context: Dict[str, Any],
-):
-    _ = user_context
-
-    # We allow fallback (checking headers then cookies) by default when validating
-    if not for_create_new_session:
-        return "any"
-
-    auth_mode = get_auth_mode_from_header(req)
-    if auth_mode in ("header", "cookie"):
-        return auth_mode
-
-    return "any"
-
-
-class InputOverrideConfig:
-    def __init__(
-        self,
-        functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None,
-        apis: Union[Callable[[APIInterface], APIInterface], None] = None,
-        openid_feature: Union[OpenIdInputOverrideConfig, None] = None,
-    ):
-        self.functions = functions
-        self.apis = apis
-        self.openid_feature = openid_feature
-
-
-class OverrideConfig:
-    def __init__(
-        self,
-        functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None,
-        apis: Union[Callable[[APIInterface], APIInterface], None] = None,
-    ):
-        self.functions = functions
-        self.apis = apis
-
-
-TokenType = Literal["access", "refresh"]
-TokenTransferMethod = Literal["cookie", "header"]
-
-
-class SessionConfig:
-    def __init__(
-        self,
-        refresh_token_path: NormalisedURLPath,
-        cookie_domain: Union[None, str],
-        older_cookie_domain: Union[None, str],
-        get_cookie_same_site: Callable[
-            [Optional[BaseRequest], Dict[str, Any]],
-            Literal["lax", "strict", "none"],
-        ],
-        cookie_secure: bool,
-        session_expired_status_code: int,
-        error_handlers: ErrorHandlers,
-        anti_csrf_function_or_string: Union[
-            Callable[
-                [Optional[BaseRequest], Dict[str, Any]],
-                Literal["VIA_CUSTOM_HEADER", "NONE"],
-            ],
-            Literal["VIA_CUSTOM_HEADER", "NONE", "VIA_TOKEN"],
-        ],
-        get_token_transfer_method: Callable[
-            [BaseRequest, bool, Dict[str, Any]],
-            Union[TokenTransferMethod, Literal["any"]],
-        ],
-        override: OverrideConfig,
-        framework: str,
-        mode: str,
-        invalid_claim_status_code: int,
-        use_dynamic_access_token_signing_key: bool,
-        expose_access_token_to_frontend_in_cookie_based_auth: bool,
-        jwks_refresh_interval_sec: int,
-    ):
-        self.session_expired_status_code = session_expired_status_code
-        self.invalid_claim_status_code = invalid_claim_status_code
-        self.use_dynamic_access_token_signing_key = use_dynamic_access_token_signing_key
-        self.expose_access_token_to_frontend_in_cookie_based_auth = (
-            expose_access_token_to_frontend_in_cookie_based_auth
-        )
-
-        self.refresh_token_path = refresh_token_path
-        self.cookie_domain = cookie_domain
-        self.older_cookie_domain = older_cookie_domain
-        self.get_cookie_same_site = get_cookie_same_site
-        self.cookie_secure = cookie_secure
-        self.error_handlers = error_handlers
-        self.anti_csrf_function_or_string = anti_csrf_function_or_string
-        self.get_token_transfer_method = get_token_transfer_method
-        self.override = override
-        self.framework = framework
-        self.mode = mode
-        self.jwks_refresh_interval_sec = jwks_refresh_interval_sec
-
-
-def validate_and_normalise_user_input(
-    app_info: AppInfo,
-    cookie_domain: Union[str, None] = None,
-    older_cookie_domain: Union[str, None] = None,
-    cookie_secure: Union[bool, None] = None,
-    cookie_same_site: Union[Literal["lax", "strict", "none"], None] = None,
-    session_expired_status_code: Union[int, None] = None,
-    anti_csrf: Union[Literal["VIA_TOKEN", "VIA_CUSTOM_HEADER", "NONE"], None] = None,
-    get_token_transfer_method: Union[
-        Callable[
-            [BaseRequest, bool, Dict[str, Any]],
-            Union[TokenTransferMethod, Literal["any"]],
-        ],
-        None,
-    ] = None,
-    error_handlers: Union[ErrorHandlers, None] = None,
-    override: Union[InputOverrideConfig, None] = None,
-    invalid_claim_status_code: Union[int, None] = None,
-    use_dynamic_access_token_signing_key: Union[bool, None] = None,
-    expose_access_token_to_frontend_in_cookie_based_auth: Union[bool, None] = None,
-    jwks_refresh_interval_sec: Union[int, None] = None,
-):
-    _ = cookie_same_site  # we have this otherwise pylint complains that cookie_same_site is unused, but it is being used in the get_cookie_same_site function.
-    if anti_csrf not in {"VIA_TOKEN", "VIA_CUSTOM_HEADER", "NONE", None}:
-        raise ValueError(
-            "anti_csrf must be one of VIA_TOKEN, VIA_CUSTOM_HEADER, NONE or None"
-        )
-
-    if error_handlers is not None and not isinstance(error_handlers, ErrorHandlers):  # type: ignore
-        raise ValueError("error_handlers must be an instance of ErrorHandlers or None")
-
-    if override is not None and not isinstance(override, InputOverrideConfig):  # type: ignore
-        raise ValueError("override must be an instance of InputOverrideConfig or None")
-
-    cookie_domain = (
-        normalise_session_scope(cookie_domain) if cookie_domain is not None else None
-    )
-
-    older_cookie_domain = (
-        older_cookie_domain
-        if older_cookie_domain is None or older_cookie_domain == ""
-        else normalise_session_scope(older_cookie_domain)
-    )
-
-    cookie_secure = (
-        cookie_secure
-        if cookie_secure is not None
-        else app_info.api_domain.get_as_string_dangerous().startswith("https")
-    )
-
-    session_expired_status_code = (
-        session_expired_status_code if session_expired_status_code is not None else 401
-    )
-
-    invalid_claim_status_code = (
-        invalid_claim_status_code if invalid_claim_status_code is not None else 403
-    )
-
-    if session_expired_status_code == invalid_claim_status_code:
-        raise Exception(
-            "session_expired_status_code and invalid_claim_status_code cannot be the same "
-            f"({invalid_claim_status_code})"
-        )
-
-    if get_token_transfer_method is None:
-        get_token_transfer_method = get_token_transfer_method_default
-
-    if error_handlers is None:
-        error_handlers = InputErrorHandlers()
-
-    if override is None:
-        override = InputOverrideConfig()
-
-    if use_dynamic_access_token_signing_key is None:
-        use_dynamic_access_token_signing_key = True
-
-    if expose_access_token_to_frontend_in_cookie_based_auth is None:
-        expose_access_token_to_frontend_in_cookie_based_auth = False
-
-    if cookie_same_site is not None:
-        # this is just so that we check that the user has provided the right
-        # values, since normalise_same_site throws an error if the user
-        # as provided an empty string.
-        _ = normalise_same_site(cookie_same_site)
-
-    def get_cookie_same_site(
-        request: Optional[BaseRequest], user_context: Dict[str, Any]
-    ) -> Literal["lax", "strict", "none"]:
-        nonlocal cookie_same_site
-        if cookie_same_site is not None:
-            return normalise_same_site(cookie_same_site)
-        top_level_api_domain = app_info.top_level_api_domain
-        top_level_website_domain = app_info.get_top_level_website_domain(
-            request, user_context
-        )
-
-        api_domain_scheme = get_url_scheme(
-            app_info.api_domain.get_as_string_dangerous()
-        )
-        website_domain_scheme = get_url_scheme(
-            app_info.get_origin(request, user_context).get_as_string_dangerous()
-        )
-        if (top_level_api_domain != top_level_website_domain) or (
-            api_domain_scheme != website_domain_scheme
-        ):
-            cookie_same_site = "none"
-        else:
-            cookie_same_site = "lax"
-        return cookie_same_site
-
-    def anti_csrf_function(
-        request: Optional[BaseRequest], user_context: Dict[str, Any]
-    ) -> Literal["NONE", "VIA_CUSTOM_HEADER"]:
-        same_site = get_cookie_same_site(request, user_context)
-        if same_site == "none":
-            return "VIA_CUSTOM_HEADER"
-        return "NONE"
-
-    anti_csrf_function_or_string: Union[
-        Callable[
-            [Optional[BaseRequest], Dict[str, Any]],
-            Literal["VIA_CUSTOM_HEADER", "NONE"],
-        ],
-        Literal["VIA_CUSTOM_HEADER", "NONE", "VIA_TOKEN"],
-    ] = anti_csrf_function
-    if anti_csrf is not None:
-        anti_csrf_function_or_string = anti_csrf
-
-    if jwks_refresh_interval_sec is None:
-        jwks_refresh_interval_sec = 4 * 3600  # 4 hours
-
-    return SessionConfig(
-        app_info.api_base_path.append(NormalisedURLPath(SESSION_REFRESH)),
-        cookie_domain,
-        older_cookie_domain,
-        get_cookie_same_site,
-        cookie_secure,
-        session_expired_status_code,
-        error_handlers,
-        anti_csrf_function_or_string,
-        get_token_transfer_method,
-        OverrideConfig(override.functions, override.apis),
-        app_info.framework,
-        app_info.mode,
-        invalid_claim_status_code,
-        use_dynamic_access_token_signing_key,
-        expose_access_token_to_frontend_in_cookie_based_auth,
-        jwks_refresh_interval_sec,
-    )
-
-
-async def get_required_claim_validators(
-    session: SessionContainer,
-    override_global_claim_validators: Optional[
-        Callable[
-            [List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
-            MaybeAwaitable[List[SessionClaimValidator]],
-        ]
-    ],
-    user_context: Dict[str, Any],
-) -> List[SessionClaimValidator]:
-    from .recipe import SessionRecipe
-
-    claim_validators_added_by_other_recipes = (
-        SessionRecipe.get_instance().get_claim_validators_added_by_other_recipes()
-    )
-    global_claim_validators = await resolve(
-        SessionRecipe.get_instance().recipe_implementation.get_global_claim_validators(
-            session.get_tenant_id(),
-            session.get_user_id(),
-            claim_validators_added_by_other_recipes,
-            user_context,
-        )
-    )
-
-    if override_global_claim_validators is not None:
-        return await resolve(
-            override_global_claim_validators(
-                global_claim_validators, session, user_context
-            )
-        )
-
-    return global_claim_validators
-
-
-async def validate_claims_in_payload(
-    claim_validators: List[SessionClaimValidator],
-    new_access_token_payload: Dict[str, Any],
-    user_context: Dict[str, Any],
-):
-    validation_errors: List[ClaimValidationError] = []
-    for validator in claim_validators:
-        claim_validation_res = await validator.validate(
-            new_access_token_payload, user_context
-        )
-        log_debug_message(
-            "validate_claims_in_payload %s validate res %s",
-            validator.id,
-            json.dumps(claim_validation_res.__dict__),
-        )
-        if not claim_validation_res.is_valid:
-            validation_errors.append(
-                ClaimValidationError(validator.id, claim_validation_res.reason)
-            )
-
-    return validation_errors
-
-
-
-
-
-
-
-

Functions

-
-
-async def default_clear_duplicate_session_cookies_callback(_: BaseRequest, message: str, response: BaseResponse) ‑> BaseResponse -
-
-
-
- -Expand source code - -
async def default_clear_duplicate_session_cookies_callback(
-    _: BaseRequest, message: str, response: BaseResponse
-) -> BaseResponse:
-    # This error occurs in the `refresh_post` API when multiple session
-    # cookies are found in the request and the user has set `older_cookie_domain`.
-    # We remove session cookies from the older_cookie_domain. The response must return `200 OK`
-    # to avoid logging out the user, allowing the session to continue with the valid cookie.
-    return send_200_response({"message": message}, response)
-
-
-
-async def default_invalid_claim_callback(_: BaseRequest, claim_validation_errors: List[ClaimValidationError], response: BaseResponse) ‑> BaseResponse -
-
-
-
- -Expand source code - -
async def default_invalid_claim_callback(
-    _: BaseRequest,
-    claim_validation_errors: List[ClaimValidationError],
-    response: BaseResponse,
-) -> BaseResponse:
-    from .recipe import SessionRecipe
-
-    return send_non_200_response(
-        {
-            "message": "invalid claim",
-            "claimValidationErrors": [err.to_json() for err in claim_validation_errors],
-        },
-        SessionRecipe.get_instance().config.invalid_claim_status_code,
-        response,
-    )
-
-
-
-async def default_token_theft_detected_callback(_: BaseRequest, session_handle: str, __: str, response: BaseResponse) ‑> BaseResponse -
-
-
-
- -Expand source code - -
async def default_token_theft_detected_callback(
-    _: BaseRequest, session_handle: str, __: str, response: BaseResponse
-) -> BaseResponse:
-    from .recipe import SessionRecipe
-
-    await SessionRecipe.get_instance().recipe_implementation.revoke_session(
-        session_handle, {}
-    )
-    return send_non_200_response_with_message(
-        "token theft detected",
-        SessionRecipe.get_instance().config.session_expired_status_code,
-        response,
-    )
-
-
-
-async def default_try_refresh_token_callback(_: BaseRequest, __: str, response: BaseResponse) ‑> BaseResponse -
-
-
-
- -Expand source code - -
async def default_try_refresh_token_callback(
-    _: BaseRequest, __: str, response: BaseResponse
-) -> BaseResponse:
-    from .recipe import SessionRecipe
-
-    return send_non_200_response_with_message(
-        "try refresh token",
-        SessionRecipe.get_instance().config.session_expired_status_code,
-        response,
-    )
-
-
-
-async def default_unauthorised_callback(_: BaseRequest, __: str, response: BaseResponse) ‑> BaseResponse -
-
-
-
- -Expand source code - -
async def default_unauthorised_callback(
-    _: BaseRequest, __: str, response: BaseResponse
-) -> BaseResponse:
-    from .recipe import SessionRecipe
-
-    return send_non_200_response_with_message(
-        "unauthorised",
-        SessionRecipe.get_instance().config.session_expired_status_code,
-        response,
-    )
-
-
-
-def get_auth_mode_from_header(request: BaseRequest) ‑> Optional[str] -
-
-
-
- -Expand source code - -
def get_auth_mode_from_header(request: BaseRequest) -> Optional[str]:
-    auth_mode = request.get_header(AUTH_MODE_HEADER_KEY)
-    if auth_mode is None:
-        return None
-    return auth_mode.lower()
-
-
-
-async def get_required_claim_validators(session: SessionContainer, override_global_claim_validators: Optional[Callable[[List[SessionClaimValidator], SessionContainer, Dict[str, Any]], MaybeAwaitable[List[SessionClaimValidator]]]], user_context: Dict[str, Any]) ‑> List[SessionClaimValidator] -
-
-
-
- -Expand source code - -
async def get_required_claim_validators(
-    session: SessionContainer,
-    override_global_claim_validators: Optional[
-        Callable[
-            [List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
-            MaybeAwaitable[List[SessionClaimValidator]],
-        ]
-    ],
-    user_context: Dict[str, Any],
-) -> List[SessionClaimValidator]:
-    from .recipe import SessionRecipe
-
-    claim_validators_added_by_other_recipes = (
-        SessionRecipe.get_instance().get_claim_validators_added_by_other_recipes()
-    )
-    global_claim_validators = await resolve(
-        SessionRecipe.get_instance().recipe_implementation.get_global_claim_validators(
-            session.get_tenant_id(),
-            session.get_user_id(),
-            claim_validators_added_by_other_recipes,
-            user_context,
-        )
-    )
-
-    if override_global_claim_validators is not None:
-        return await resolve(
-            override_global_claim_validators(
-                global_claim_validators, session, user_context
-            )
-        )
-
-    return global_claim_validators
-
-
-
-def get_token_transfer_method_default(req: BaseRequest, for_create_new_session: bool, user_context: Dict[str, Any]) -
-
-
-
- -Expand source code - -
def get_token_transfer_method_default(
-    req: BaseRequest,
-    for_create_new_session: bool,
-    user_context: Dict[str, Any],
-):
-    _ = user_context
-
-    # We allow fallback (checking headers then cookies) by default when validating
-    if not for_create_new_session:
-        return "any"
-
-    auth_mode = get_auth_mode_from_header(req)
-    if auth_mode in ("header", "cookie"):
-        return auth_mode
-
-    return "any"
-
-
-
-def get_url_scheme(url: str) ‑> str -
-
-
-
- -Expand source code - -
def get_url_scheme(url: str) -> str:
-    url_obj = urlparse(url)
-    return url_obj.scheme
-
-
-
-def normalise_same_site(same_site: str) ‑> Literal['strict', 'lax', 'none'] -
-
-
-
- -Expand source code - -
def normalise_same_site(same_site: str) -> Literal["strict", "lax", "none"]:
-    same_site = same_site.strip()
-    same_site = same_site.lower()
-    allowed_values = {"strict", "lax", "none"}
-    if same_site not in allowed_values:
-        raise Exception('cookie same site must be one of "strict", "lax", or "none"')
-    return same_site  # type: ignore
-
-
-
-def normalise_session_scope(session_scope: str) ‑> str -
-
-
-
- -Expand source code - -
def normalise_session_scope(session_scope: str) -> str:
-    def helper(scope: str) -> str:
-        scope = scope.strip()
-
-        if scope.startswith("."):
-            scope = scope[1:]
-
-        if (not scope.startswith("https://")) and (not scope.startswith("http://")):
-            scope = "http://" + scope
-
-        try:
-            url_obj = urlparse(scope)
-            if url_obj.hostname is None:
-                raise Exception("Should not come here")
-            scope = url_obj.hostname
-
-            return scope
-        except Exception:
-            raise_general_exception("Please provide a valid session_scope")
-
-    no_dot_normalised = helper(session_scope)
-    if no_dot_normalised == "localhost" or is_an_ip_address(no_dot_normalised):
-        return no_dot_normalised
-
-    if session_scope.startswith("."):
-        return "." + no_dot_normalised
-
-    return no_dot_normalised
-
-
-
-def validate_and_normalise_user_input(app_info: AppInfo, cookie_domain: Union[str, None] = None, older_cookie_domain: Union[str, None] = None, cookie_secure: Union[bool, None] = None, cookie_same_site: "Union[Literal['lax', 'strict', 'none'], None]" = None, session_expired_status_code: Union[int, None] = None, anti_csrf: "Union[Literal['VIA_TOKEN', 'VIA_CUSTOM_HEADER', 'NONE'], None]" = None, get_token_transfer_method: "Union[Callable[[BaseRequest, bool, Dict[str, Any]], Union[TokenTransferMethod, Literal['any']]], None]" = None, error_handlers: Union[ErrorHandlers, None] = None, override: Union[InputOverrideConfig, None] = None, invalid_claim_status_code: Union[int, None] = None, use_dynamic_access_token_signing_key: Union[bool, None] = None, expose_access_token_to_frontend_in_cookie_based_auth: Union[bool, None] = None, jwks_refresh_interval_sec: Union[int, None] = None) -
-
-
-
- -Expand source code - -
def validate_and_normalise_user_input(
-    app_info: AppInfo,
-    cookie_domain: Union[str, None] = None,
-    older_cookie_domain: Union[str, None] = None,
-    cookie_secure: Union[bool, None] = None,
-    cookie_same_site: Union[Literal["lax", "strict", "none"], None] = None,
-    session_expired_status_code: Union[int, None] = None,
-    anti_csrf: Union[Literal["VIA_TOKEN", "VIA_CUSTOM_HEADER", "NONE"], None] = None,
-    get_token_transfer_method: Union[
-        Callable[
-            [BaseRequest, bool, Dict[str, Any]],
-            Union[TokenTransferMethod, Literal["any"]],
-        ],
-        None,
-    ] = None,
-    error_handlers: Union[ErrorHandlers, None] = None,
-    override: Union[InputOverrideConfig, None] = None,
-    invalid_claim_status_code: Union[int, None] = None,
-    use_dynamic_access_token_signing_key: Union[bool, None] = None,
-    expose_access_token_to_frontend_in_cookie_based_auth: Union[bool, None] = None,
-    jwks_refresh_interval_sec: Union[int, None] = None,
-):
-    _ = cookie_same_site  # we have this otherwise pylint complains that cookie_same_site is unused, but it is being used in the get_cookie_same_site function.
-    if anti_csrf not in {"VIA_TOKEN", "VIA_CUSTOM_HEADER", "NONE", None}:
-        raise ValueError(
-            "anti_csrf must be one of VIA_TOKEN, VIA_CUSTOM_HEADER, NONE or None"
-        )
-
-    if error_handlers is not None and not isinstance(error_handlers, ErrorHandlers):  # type: ignore
-        raise ValueError("error_handlers must be an instance of ErrorHandlers or None")
-
-    if override is not None and not isinstance(override, InputOverrideConfig):  # type: ignore
-        raise ValueError("override must be an instance of InputOverrideConfig or None")
-
-    cookie_domain = (
-        normalise_session_scope(cookie_domain) if cookie_domain is not None else None
-    )
-
-    older_cookie_domain = (
-        older_cookie_domain
-        if older_cookie_domain is None or older_cookie_domain == ""
-        else normalise_session_scope(older_cookie_domain)
-    )
-
-    cookie_secure = (
-        cookie_secure
-        if cookie_secure is not None
-        else app_info.api_domain.get_as_string_dangerous().startswith("https")
-    )
-
-    session_expired_status_code = (
-        session_expired_status_code if session_expired_status_code is not None else 401
-    )
-
-    invalid_claim_status_code = (
-        invalid_claim_status_code if invalid_claim_status_code is not None else 403
-    )
-
-    if session_expired_status_code == invalid_claim_status_code:
-        raise Exception(
-            "session_expired_status_code and invalid_claim_status_code cannot be the same "
-            f"({invalid_claim_status_code})"
-        )
-
-    if get_token_transfer_method is None:
-        get_token_transfer_method = get_token_transfer_method_default
-
-    if error_handlers is None:
-        error_handlers = InputErrorHandlers()
-
-    if override is None:
-        override = InputOverrideConfig()
-
-    if use_dynamic_access_token_signing_key is None:
-        use_dynamic_access_token_signing_key = True
-
-    if expose_access_token_to_frontend_in_cookie_based_auth is None:
-        expose_access_token_to_frontend_in_cookie_based_auth = False
-
-    if cookie_same_site is not None:
-        # this is just so that we check that the user has provided the right
-        # values, since normalise_same_site throws an error if the user
-        # as provided an empty string.
-        _ = normalise_same_site(cookie_same_site)
-
-    def get_cookie_same_site(
-        request: Optional[BaseRequest], user_context: Dict[str, Any]
-    ) -> Literal["lax", "strict", "none"]:
-        nonlocal cookie_same_site
-        if cookie_same_site is not None:
-            return normalise_same_site(cookie_same_site)
-        top_level_api_domain = app_info.top_level_api_domain
-        top_level_website_domain = app_info.get_top_level_website_domain(
-            request, user_context
-        )
-
-        api_domain_scheme = get_url_scheme(
-            app_info.api_domain.get_as_string_dangerous()
-        )
-        website_domain_scheme = get_url_scheme(
-            app_info.get_origin(request, user_context).get_as_string_dangerous()
-        )
-        if (top_level_api_domain != top_level_website_domain) or (
-            api_domain_scheme != website_domain_scheme
-        ):
-            cookie_same_site = "none"
-        else:
-            cookie_same_site = "lax"
-        return cookie_same_site
-
-    def anti_csrf_function(
-        request: Optional[BaseRequest], user_context: Dict[str, Any]
-    ) -> Literal["NONE", "VIA_CUSTOM_HEADER"]:
-        same_site = get_cookie_same_site(request, user_context)
-        if same_site == "none":
-            return "VIA_CUSTOM_HEADER"
-        return "NONE"
-
-    anti_csrf_function_or_string: Union[
-        Callable[
-            [Optional[BaseRequest], Dict[str, Any]],
-            Literal["VIA_CUSTOM_HEADER", "NONE"],
-        ],
-        Literal["VIA_CUSTOM_HEADER", "NONE", "VIA_TOKEN"],
-    ] = anti_csrf_function
-    if anti_csrf is not None:
-        anti_csrf_function_or_string = anti_csrf
-
-    if jwks_refresh_interval_sec is None:
-        jwks_refresh_interval_sec = 4 * 3600  # 4 hours
-
-    return SessionConfig(
-        app_info.api_base_path.append(NormalisedURLPath(SESSION_REFRESH)),
-        cookie_domain,
-        older_cookie_domain,
-        get_cookie_same_site,
-        cookie_secure,
-        session_expired_status_code,
-        error_handlers,
-        anti_csrf_function_or_string,
-        get_token_transfer_method,
-        OverrideConfig(override.functions, override.apis),
-        app_info.framework,
-        app_info.mode,
-        invalid_claim_status_code,
-        use_dynamic_access_token_signing_key,
-        expose_access_token_to_frontend_in_cookie_based_auth,
-        jwks_refresh_interval_sec,
-    )
-
-
-
-async def validate_claims_in_payload(claim_validators: List[SessionClaimValidator], new_access_token_payload: Dict[str, Any], user_context: Dict[str, Any]) -
-
-
-
- -Expand source code - -
async def validate_claims_in_payload(
-    claim_validators: List[SessionClaimValidator],
-    new_access_token_payload: Dict[str, Any],
-    user_context: Dict[str, Any],
-):
-    validation_errors: List[ClaimValidationError] = []
-    for validator in claim_validators:
-        claim_validation_res = await validator.validate(
-            new_access_token_payload, user_context
-        )
-        log_debug_message(
-            "validate_claims_in_payload %s validate res %s",
-            validator.id,
-            json.dumps(claim_validation_res.__dict__),
-        )
-        if not claim_validation_res.is_valid:
-            validation_errors.append(
-                ClaimValidationError(validator.id, claim_validation_res.reason)
-            )
-
-    return validation_errors
-
-
-
-
-
-

Classes

-
-
-class ErrorHandlers -(on_token_theft_detected: Callable[[BaseRequest, str, str, BaseResponse], Union[BaseResponse, Awaitable[BaseResponse]]], on_try_refresh_token: Callable[[BaseRequest, str, BaseResponse], Union[BaseResponse, Awaitable[BaseResponse]]], on_unauthorised: Callable[[BaseRequest, str, BaseResponse], Union[BaseResponse, Awaitable[BaseResponse]]], on_invalid_claim: Callable[[BaseRequest, List[ClaimValidationError], BaseResponse], Union[BaseResponse, Awaitable[BaseResponse]]], on_clear_duplicate_session_cookies: Callable[[BaseRequest, str, BaseResponse], Union[BaseResponse, Awaitable[BaseResponse]]]) -
-
-
-
- -Expand source code - -
class ErrorHandlers:
-    def __init__(
-        self,
-        on_token_theft_detected: Callable[
-            [BaseRequest, str, str, BaseResponse],
-            Union[BaseResponse, Awaitable[BaseResponse]],
-        ],
-        on_try_refresh_token: Callable[
-            [BaseRequest, str, BaseResponse],
-            Union[BaseResponse, Awaitable[BaseResponse]],
-        ],
-        on_unauthorised: Callable[
-            [BaseRequest, str, BaseResponse],
-            Union[BaseResponse, Awaitable[BaseResponse]],
-        ],
-        on_invalid_claim: Callable[
-            [BaseRequest, List[ClaimValidationError], BaseResponse],
-            Union[BaseResponse, Awaitable[BaseResponse]],
-        ],
-        on_clear_duplicate_session_cookies: Callable[
-            [BaseRequest, str, BaseResponse],
-            Union[BaseResponse, Awaitable[BaseResponse]],
-        ],
-    ):
-        self.__on_token_theft_detected = on_token_theft_detected
-        self.__on_try_refresh_token = on_try_refresh_token
-        self.__on_unauthorised = on_unauthorised
-        self.__on_invalid_claim = on_invalid_claim
-        self.__on_clear_duplicate_session_cookies = on_clear_duplicate_session_cookies
-
-    async def on_token_theft_detected(
-        self,
-        request: BaseRequest,
-        session_handle: str,
-        user_id: str,
-        response: BaseResponse,
-    ) -> BaseResponse:
-        return await resolve(
-            self.__on_token_theft_detected(request, session_handle, user_id, response)
-        )
-
-    async def on_try_refresh_token(
-        self, request: BaseRequest, message: str, response: BaseResponse
-    ):
-        result = await resolve(self.__on_try_refresh_token(request, message, response))
-        return result
-
-    async def on_unauthorised(
-        self,
-        request: BaseRequest,
-        message: str,
-        response: BaseResponse,
-    ):
-        return await resolve(self.__on_unauthorised(request, message, response))
-
-    async def on_invalid_claim(
-        self,
-        recipe: SessionRecipe,
-        request: BaseRequest,
-        claim_validation_errors: List[ClaimValidationError],
-        response: BaseResponse,
-    ):
-        _ = recipe
-        result = await resolve(
-            self.__on_invalid_claim(request, claim_validation_errors, response)
-        )
-        return result
-
-    async def on_clear_duplicate_session_cookies(
-        self,
-        request: BaseRequest,
-        message: str,
-        response: BaseResponse,
-    ):
-        result = await resolve(
-            self.__on_clear_duplicate_session_cookies(request, message, response)
-        )
-        return result
-
-

Subclasses

- -

Methods

-
-
-async def on_clear_duplicate_session_cookies(self, request: BaseRequest, message: str, response: BaseResponse) -
-
-
-
- -Expand source code - -
async def on_clear_duplicate_session_cookies(
-    self,
-    request: BaseRequest,
-    message: str,
-    response: BaseResponse,
-):
-    result = await resolve(
-        self.__on_clear_duplicate_session_cookies(request, message, response)
-    )
-    return result
-
-
-
-async def on_invalid_claim(self, recipe: SessionRecipe, request: BaseRequest, claim_validation_errors: List[ClaimValidationError], response: BaseResponse) -
-
-
-
- -Expand source code - -
async def on_invalid_claim(
-    self,
-    recipe: SessionRecipe,
-    request: BaseRequest,
-    claim_validation_errors: List[ClaimValidationError],
-    response: BaseResponse,
-):
-    _ = recipe
-    result = await resolve(
-        self.__on_invalid_claim(request, claim_validation_errors, response)
-    )
-    return result
-
-
-
-async def on_token_theft_detected(self, request: BaseRequest, session_handle: str, user_id: str, response: BaseResponse) ‑> BaseResponse -
-
-
-
- -Expand source code - -
async def on_token_theft_detected(
-    self,
-    request: BaseRequest,
-    session_handle: str,
-    user_id: str,
-    response: BaseResponse,
-) -> BaseResponse:
-    return await resolve(
-        self.__on_token_theft_detected(request, session_handle, user_id, response)
-    )
-
-
-
-async def on_try_refresh_token(self, request: BaseRequest, message: str, response: BaseResponse) -
-
-
-
- -Expand source code - -
async def on_try_refresh_token(
-    self, request: BaseRequest, message: str, response: BaseResponse
-):
-    result = await resolve(self.__on_try_refresh_token(request, message, response))
-    return result
-
-
-
-async def on_unauthorised(self, request: BaseRequest, message: str, response: BaseResponse) -
-
-
-
- -Expand source code - -
async def on_unauthorised(
-    self,
-    request: BaseRequest,
-    message: str,
-    response: BaseResponse,
-):
-    return await resolve(self.__on_unauthorised(request, message, response))
-
-
-
-
-
-class InputErrorHandlers -(on_token_theft_detected: Union[None, Callable[[BaseRequest, str, str, BaseResponse], Union[BaseResponse, Awaitable[BaseResponse]]]] = None, on_try_refresh_token: Union[None, Callable[[BaseRequest, str, BaseResponse], Union[BaseResponse, Awaitable[BaseResponse]]]] = None, on_unauthorised: Union[Callable[[BaseRequest, str, BaseResponse], Union[BaseResponse, Awaitable[BaseResponse]]], None] = None, on_invalid_claim: Union[Callable[[BaseRequest, List[ClaimValidationError], BaseResponse], Union[BaseResponse, Awaitable[BaseResponse]]], None] = None, on_clear_duplicate_session_cookies: Union[None, Callable[[BaseRequest, str, BaseResponse], Union[BaseResponse, Awaitable[BaseResponse]]]] = None) -
-
-
-
- -Expand source code - -
class InputErrorHandlers(ErrorHandlers):
-    def __init__(
-        self,
-        on_token_theft_detected: Union[
-            None,
-            Callable[
-                [BaseRequest, str, str, BaseResponse],
-                Union[BaseResponse, Awaitable[BaseResponse]],
-            ],
-        ] = None,
-        on_try_refresh_token: Union[
-            None,
-            Callable[
-                [BaseRequest, str, BaseResponse],
-                Union[BaseResponse, Awaitable[BaseResponse]],
-            ],
-        ] = None,
-        on_unauthorised: Union[
-            Callable[
-                [BaseRequest, str, BaseResponse],
-                Union[BaseResponse, Awaitable[BaseResponse]],
-            ],
-            None,
-        ] = None,
-        on_invalid_claim: Union[
-            Callable[
-                [BaseRequest, List[ClaimValidationError], BaseResponse],
-                Union[BaseResponse, Awaitable[BaseResponse]],
-            ],
-            None,
-        ] = None,
-        on_clear_duplicate_session_cookies: Union[
-            None,
-            Callable[
-                [BaseRequest, str, BaseResponse],
-                Union[BaseResponse, Awaitable[BaseResponse]],
-            ],
-        ] = None,
-    ):
-        if on_token_theft_detected is None:
-            on_token_theft_detected = default_token_theft_detected_callback
-        if on_try_refresh_token is None:
-            on_try_refresh_token = default_try_refresh_token_callback
-        if on_unauthorised is None:
-            on_unauthorised = default_unauthorised_callback
-        if on_invalid_claim is None:
-            on_invalid_claim = default_invalid_claim_callback
-        if on_clear_duplicate_session_cookies is None:
-            on_clear_duplicate_session_cookies = (
-                default_clear_duplicate_session_cookies_callback
-            )
-        super().__init__(
-            on_token_theft_detected,
-            on_try_refresh_token,
-            on_unauthorised,
-            on_invalid_claim,
-            on_clear_duplicate_session_cookies,
-        )
-
-

Ancestors

- -
-
-class InputOverrideConfig -(functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None, apis: Union[Callable[[APIInterface], APIInterface], None] = None, openid_feature: Union[OpenIdInputOverrideConfig, None] = None) -
-
-
-
- -Expand source code - -
class InputOverrideConfig:
-    def __init__(
-        self,
-        functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None,
-        apis: Union[Callable[[APIInterface], APIInterface], None] = None,
-        openid_feature: Union[OpenIdInputOverrideConfig, None] = None,
-    ):
-        self.functions = functions
-        self.apis = apis
-        self.openid_feature = openid_feature
-
-
-
-class OverrideConfig -(functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None, apis: Union[Callable[[APIInterface], APIInterface], None] = None) -
-
-
-
- -Expand source code - -
class OverrideConfig:
-    def __init__(
-        self,
-        functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None,
-        apis: Union[Callable[[APIInterface], APIInterface], None] = None,
-    ):
-        self.functions = functions
-        self.apis = apis
-
-
-
-class SessionConfig -(refresh_token_path: NormalisedURLPath, cookie_domain: Union[None, str], older_cookie_domain: Union[None, str], get_cookie_same_site: "Callable[[Optional[BaseRequest], Dict[str, Any]], Literal['lax', 'strict', 'none']]", cookie_secure: bool, session_expired_status_code: int, error_handlers: ErrorHandlers, anti_csrf_function_or_string: "Union[Callable[[Optional[BaseRequest], Dict[str, Any]], Literal['VIA_CUSTOM_HEADER', 'NONE']], Literal['VIA_CUSTOM_HEADER', 'NONE', 'VIA_TOKEN']]", get_token_transfer_method: "Callable[[BaseRequest, bool, Dict[str, Any]], Union[TokenTransferMethod, Literal['any']]]", override: OverrideConfig, framework: str, mode: str, invalid_claim_status_code: int, use_dynamic_access_token_signing_key: bool, expose_access_token_to_frontend_in_cookie_based_auth: bool, jwks_refresh_interval_sec: int) -
-
-
-
- -Expand source code - -
class SessionConfig:
-    def __init__(
-        self,
-        refresh_token_path: NormalisedURLPath,
-        cookie_domain: Union[None, str],
-        older_cookie_domain: Union[None, str],
-        get_cookie_same_site: Callable[
-            [Optional[BaseRequest], Dict[str, Any]],
-            Literal["lax", "strict", "none"],
-        ],
-        cookie_secure: bool,
-        session_expired_status_code: int,
-        error_handlers: ErrorHandlers,
-        anti_csrf_function_or_string: Union[
-            Callable[
-                [Optional[BaseRequest], Dict[str, Any]],
-                Literal["VIA_CUSTOM_HEADER", "NONE"],
-            ],
-            Literal["VIA_CUSTOM_HEADER", "NONE", "VIA_TOKEN"],
-        ],
-        get_token_transfer_method: Callable[
-            [BaseRequest, bool, Dict[str, Any]],
-            Union[TokenTransferMethod, Literal["any"]],
-        ],
-        override: OverrideConfig,
-        framework: str,
-        mode: str,
-        invalid_claim_status_code: int,
-        use_dynamic_access_token_signing_key: bool,
-        expose_access_token_to_frontend_in_cookie_based_auth: bool,
-        jwks_refresh_interval_sec: int,
-    ):
-        self.session_expired_status_code = session_expired_status_code
-        self.invalid_claim_status_code = invalid_claim_status_code
-        self.use_dynamic_access_token_signing_key = use_dynamic_access_token_signing_key
-        self.expose_access_token_to_frontend_in_cookie_based_auth = (
-            expose_access_token_to_frontend_in_cookie_based_auth
-        )
-
-        self.refresh_token_path = refresh_token_path
-        self.cookie_domain = cookie_domain
-        self.older_cookie_domain = older_cookie_domain
-        self.get_cookie_same_site = get_cookie_same_site
-        self.cookie_secure = cookie_secure
-        self.error_handlers = error_handlers
-        self.anti_csrf_function_or_string = anti_csrf_function_or_string
-        self.get_token_transfer_method = get_token_transfer_method
-        self.override = override
-        self.framework = framework
-        self.mode = mode
-        self.jwks_refresh_interval_sec = jwks_refresh_interval_sec
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/thirdparty/api/apple_redirect.html b/html/supertokens_python/recipe/thirdparty/api/apple_redirect.html deleted file mode 100644 index d2c5c4962..000000000 --- a/html/supertokens_python/recipe/thirdparty/api/apple_redirect.html +++ /dev/null @@ -1,130 +0,0 @@ - - - - - - -supertokens_python.recipe.thirdparty.api.apple_redirect API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.thirdparty.api.apple_redirect

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from typing import TYPE_CHECKING, Any, Dict
-
-if TYPE_CHECKING:
-    from supertokens_python.recipe.thirdparty.interfaces import APIInterface, APIOptions
-
-
-async def handle_apple_redirect_api(
-    api_implementation: APIInterface,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-):
-    if api_implementation.disable_apple_redirect_handler_post:
-        return None
-
-    body = await api_options.request.form_data()
-
-    # this will redirect the user...
-    await api_implementation.apple_redirect_handler_post(
-        body, api_options, user_context
-    )
-
-    return api_options.response
-
-
-
-
-
-
-
-

Functions

-
-
-async def handle_apple_redirect_api(api_implementation: APIInterface, api_options: APIOptions, user_context: Dict[str, Any]) -
-
-
-
- -Expand source code - -
async def handle_apple_redirect_api(
-    api_implementation: APIInterface,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-):
-    if api_implementation.disable_apple_redirect_handler_post:
-        return None
-
-    body = await api_options.request.form_data()
-
-    # this will redirect the user...
-    await api_implementation.apple_redirect_handler_post(
-        body, api_options, user_context
-    )
-
-    return api_options.response
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/thirdparty/api/authorisation_url.html b/html/supertokens_python/recipe/thirdparty/api/authorisation_url.html deleted file mode 100644 index 6e9d4680d..000000000 --- a/html/supertokens_python/recipe/thirdparty/api/authorisation_url.html +++ /dev/null @@ -1,188 +0,0 @@ - - - - - - -supertokens_python.recipe.thirdparty.api.authorisation_url API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.thirdparty.api.authorisation_url

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from typing import TYPE_CHECKING, Any, Dict
-
-
-if TYPE_CHECKING:
-    from supertokens_python.recipe.thirdparty.interfaces import APIOptions, APIInterface
-
-from supertokens_python.exceptions import raise_bad_input_exception, BadInputError
-from supertokens_python.utils import send_200_response
-
-
-async def handle_authorisation_url_api(
-    api_implementation: APIInterface,
-    tenant_id: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-):
-    if api_implementation.disable_authorisation_url_get:
-        return None
-
-    third_party_id = api_options.request.get_query_param("thirdPartyId")
-    redirect_uri_on_provider_dashboard = api_options.request.get_query_param(
-        "redirectURIOnProviderDashboard"
-    )
-    client_type = api_options.request.get_query_param("clientType")
-
-    if third_party_id is None:
-        raise_bad_input_exception("Please provide the thirdPartyId as a GET param")
-
-    if redirect_uri_on_provider_dashboard is None:
-        raise_bad_input_exception(
-            "Please provide the redirectURIOnProviderDashboard as a GET param"
-        )
-
-    provider_response = await api_options.recipe_implementation.get_provider(
-        third_party_id=third_party_id,
-        client_type=client_type,
-        tenant_id=tenant_id,
-        user_context=user_context,
-    )
-
-    if provider_response is None:
-        raise BadInputError(
-            f"the provider {third_party_id} could not be found in the configuration"
-        )
-
-    provider = provider_response
-    result = await api_implementation.authorisation_url_get(
-        provider=provider,
-        redirect_uri_on_provider_dashboard=redirect_uri_on_provider_dashboard,
-        api_options=api_options,
-        user_context=user_context,
-    )
-    return send_200_response(result.to_json(), api_options.response)
-
-
-
-
-
-
-
-

Functions

-
-
-async def handle_authorisation_url_api(api_implementation: APIInterface, tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) -
-
-
-
- -Expand source code - -
async def handle_authorisation_url_api(
-    api_implementation: APIInterface,
-    tenant_id: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-):
-    if api_implementation.disable_authorisation_url_get:
-        return None
-
-    third_party_id = api_options.request.get_query_param("thirdPartyId")
-    redirect_uri_on_provider_dashboard = api_options.request.get_query_param(
-        "redirectURIOnProviderDashboard"
-    )
-    client_type = api_options.request.get_query_param("clientType")
-
-    if third_party_id is None:
-        raise_bad_input_exception("Please provide the thirdPartyId as a GET param")
-
-    if redirect_uri_on_provider_dashboard is None:
-        raise_bad_input_exception(
-            "Please provide the redirectURIOnProviderDashboard as a GET param"
-        )
-
-    provider_response = await api_options.recipe_implementation.get_provider(
-        third_party_id=third_party_id,
-        client_type=client_type,
-        tenant_id=tenant_id,
-        user_context=user_context,
-    )
-
-    if provider_response is None:
-        raise BadInputError(
-            f"the provider {third_party_id} could not be found in the configuration"
-        )
-
-    provider = provider_response
-    result = await api_implementation.authorisation_url_get(
-        provider=provider,
-        redirect_uri_on_provider_dashboard=redirect_uri_on_provider_dashboard,
-        api_options=api_options,
-        user_context=user_context,
-    )
-    return send_200_response(result.to_json(), api_options.response)
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/thirdparty/api/implementation.html b/html/supertokens_python/recipe/thirdparty/api/implementation.html deleted file mode 100644 index ea736a1dd..000000000 --- a/html/supertokens_python/recipe/thirdparty/api/implementation.html +++ /dev/null @@ -1,546 +0,0 @@ - - - - - - -supertokens_python.recipe.thirdparty.api.implementation API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.thirdparty.api.implementation

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-from base64 import b64decode
-import json
-
-from typing import TYPE_CHECKING, Any, Dict, Optional, Union
-from urllib.parse import parse_qs, urlencode, urlparse
-
-from supertokens_python.recipe.emailverification import EmailVerificationRecipe
-from supertokens_python.recipe.emailverification.interfaces import (
-    CreateEmailVerificationTokenOkResult,
-)
-from supertokens_python.recipe.session.asyncio import create_new_session
-from supertokens_python.recipe.thirdparty.interfaces import (
-    APIInterface,
-    AuthorisationUrlGetOkResult,
-    SignInUpPostNoEmailGivenByProviderResponse,
-    SignInUpPostOkResult,
-)
-from supertokens_python.recipe.thirdparty.provider import RedirectUriInfo
-from supertokens_python.recipe.thirdparty.types import UserInfoEmail
-
-if TYPE_CHECKING:
-    from supertokens_python.recipe.thirdparty.interfaces import APIOptions
-    from supertokens_python.recipe.thirdparty.provider import Provider
-
-from supertokens_python.types import GeneralErrorResponse
-
-
-class APIImplementation(APIInterface):
-    async def authorisation_url_get(
-        self,
-        provider: Provider,
-        redirect_uri_on_provider_dashboard: str,
-        api_options: APIOptions,
-        user_context: Dict[str, Any],
-    ) -> Union[AuthorisationUrlGetOkResult, GeneralErrorResponse]:
-        authorisation_url_info = await provider.get_authorisation_redirect_url(
-            redirect_uri_on_provider_dashboard=redirect_uri_on_provider_dashboard,
-            user_context=user_context,
-        )
-
-        return AuthorisationUrlGetOkResult(
-            url_with_query_params=authorisation_url_info.url_with_query_params,
-            pkce_code_verifier=authorisation_url_info.pkce_code_verifier,
-        )
-
-    async def sign_in_up_post(
-        self,
-        provider: Provider,
-        redirect_uri_info: Optional[RedirectUriInfo],
-        oauth_tokens: Optional[Dict[str, Any]],
-        tenant_id: str,
-        api_options: APIOptions,
-        user_context: Dict[str, Any],
-    ) -> Union[
-        SignInUpPostOkResult,
-        SignInUpPostNoEmailGivenByProviderResponse,
-        GeneralErrorResponse,
-    ]:
-        oauth_tokens_to_use: Dict[str, Any] = {}
-
-        if redirect_uri_info is not None:
-            oauth_tokens_to_use = await provider.exchange_auth_code_for_oauth_tokens(
-                redirect_uri_info=redirect_uri_info,
-                user_context=user_context,
-            )
-        else:
-            oauth_tokens_to_use = oauth_tokens  # type: ignore
-
-        user_info = await provider.get_user_info(
-            oauth_tokens=oauth_tokens_to_use,
-            user_context=user_context,
-        )
-
-        if user_info.email is None and provider.config.require_email is False:
-            # We don't expect to get an email from this provider.
-            # So we generate a fake one
-            if provider.config.generate_fake_email is not None:
-                user_info.email = UserInfoEmail(
-                    email=await provider.config.generate_fake_email(
-                        tenant_id, user_info.third_party_user_id, user_context
-                    ),
-                    is_verified=True,
-                )
-
-        email = user_info.email.id if user_info.email is not None else None
-        email_verified = (
-            user_info.email.is_verified if user_info.email is not None else None
-        )
-        if email is None:
-            return SignInUpPostNoEmailGivenByProviderResponse()
-
-        signinup_response = await api_options.recipe_implementation.sign_in_up(
-            third_party_id=provider.id,
-            third_party_user_id=user_info.third_party_user_id,
-            email=email,
-            oauth_tokens=oauth_tokens_to_use,
-            raw_user_info_from_provider=user_info.raw_user_info_from_provider,
-            tenant_id=tenant_id,
-            user_context=user_context,
-        )
-
-        if email_verified:
-            ev_instance = EmailVerificationRecipe.get_instance_optional()
-            if ev_instance is not None:
-                token_response = await ev_instance.recipe_implementation.create_email_verification_token(
-                    tenant_id=tenant_id,
-                    user_id=signinup_response.user.user_id,
-                    email=signinup_response.user.email,
-                    user_context=user_context,
-                )
-
-                if isinstance(token_response, CreateEmailVerificationTokenOkResult):
-                    await ev_instance.recipe_implementation.verify_email_using_token(
-                        token=token_response.token,
-                        tenant_id=tenant_id,
-                        user_context=user_context,
-                    )
-
-        user = signinup_response.user
-        session = await create_new_session(
-            request=api_options.request,
-            tenant_id=tenant_id,
-            user_id=user.user_id,
-            user_context=user_context,
-        )
-
-        return SignInUpPostOkResult(
-            created_new_user=signinup_response.created_new_user,
-            user=user,
-            session=session,
-            oauth_tokens=oauth_tokens_to_use,
-            raw_user_info_from_provider=user_info.raw_user_info_from_provider,
-        )
-
-    async def apple_redirect_handler_post(
-        self,
-        form_post_info: Dict[str, Any],
-        api_options: APIOptions,
-        user_context: Dict[str, Any],
-    ):
-        state_in_b64: str = form_post_info["state"]
-        state = b64decode(state_in_b64).decode("utf-8")
-        state_obj = json.loads(state)
-        redirect_uri: str = state_obj["frontendRedirectURI"]
-
-        url_obj = urlparse(redirect_uri)
-        qparams = parse_qs(url_obj.query)
-        for k, v in form_post_info.items():
-            qparams[k] = [v]
-
-        redirect_uri = url_obj._replace(query=urlencode(qparams, doseq=True)).geturl()
-
-        api_options.response.set_header("Location", redirect_uri)
-        api_options.response.set_status_code(303)
-        api_options.response.set_html_content("")
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class APIImplementation -
-
-
-
- -Expand source code - -
class APIImplementation(APIInterface):
-    async def authorisation_url_get(
-        self,
-        provider: Provider,
-        redirect_uri_on_provider_dashboard: str,
-        api_options: APIOptions,
-        user_context: Dict[str, Any],
-    ) -> Union[AuthorisationUrlGetOkResult, GeneralErrorResponse]:
-        authorisation_url_info = await provider.get_authorisation_redirect_url(
-            redirect_uri_on_provider_dashboard=redirect_uri_on_provider_dashboard,
-            user_context=user_context,
-        )
-
-        return AuthorisationUrlGetOkResult(
-            url_with_query_params=authorisation_url_info.url_with_query_params,
-            pkce_code_verifier=authorisation_url_info.pkce_code_verifier,
-        )
-
-    async def sign_in_up_post(
-        self,
-        provider: Provider,
-        redirect_uri_info: Optional[RedirectUriInfo],
-        oauth_tokens: Optional[Dict[str, Any]],
-        tenant_id: str,
-        api_options: APIOptions,
-        user_context: Dict[str, Any],
-    ) -> Union[
-        SignInUpPostOkResult,
-        SignInUpPostNoEmailGivenByProviderResponse,
-        GeneralErrorResponse,
-    ]:
-        oauth_tokens_to_use: Dict[str, Any] = {}
-
-        if redirect_uri_info is not None:
-            oauth_tokens_to_use = await provider.exchange_auth_code_for_oauth_tokens(
-                redirect_uri_info=redirect_uri_info,
-                user_context=user_context,
-            )
-        else:
-            oauth_tokens_to_use = oauth_tokens  # type: ignore
-
-        user_info = await provider.get_user_info(
-            oauth_tokens=oauth_tokens_to_use,
-            user_context=user_context,
-        )
-
-        if user_info.email is None and provider.config.require_email is False:
-            # We don't expect to get an email from this provider.
-            # So we generate a fake one
-            if provider.config.generate_fake_email is not None:
-                user_info.email = UserInfoEmail(
-                    email=await provider.config.generate_fake_email(
-                        tenant_id, user_info.third_party_user_id, user_context
-                    ),
-                    is_verified=True,
-                )
-
-        email = user_info.email.id if user_info.email is not None else None
-        email_verified = (
-            user_info.email.is_verified if user_info.email is not None else None
-        )
-        if email is None:
-            return SignInUpPostNoEmailGivenByProviderResponse()
-
-        signinup_response = await api_options.recipe_implementation.sign_in_up(
-            third_party_id=provider.id,
-            third_party_user_id=user_info.third_party_user_id,
-            email=email,
-            oauth_tokens=oauth_tokens_to_use,
-            raw_user_info_from_provider=user_info.raw_user_info_from_provider,
-            tenant_id=tenant_id,
-            user_context=user_context,
-        )
-
-        if email_verified:
-            ev_instance = EmailVerificationRecipe.get_instance_optional()
-            if ev_instance is not None:
-                token_response = await ev_instance.recipe_implementation.create_email_verification_token(
-                    tenant_id=tenant_id,
-                    user_id=signinup_response.user.user_id,
-                    email=signinup_response.user.email,
-                    user_context=user_context,
-                )
-
-                if isinstance(token_response, CreateEmailVerificationTokenOkResult):
-                    await ev_instance.recipe_implementation.verify_email_using_token(
-                        token=token_response.token,
-                        tenant_id=tenant_id,
-                        user_context=user_context,
-                    )
-
-        user = signinup_response.user
-        session = await create_new_session(
-            request=api_options.request,
-            tenant_id=tenant_id,
-            user_id=user.user_id,
-            user_context=user_context,
-        )
-
-        return SignInUpPostOkResult(
-            created_new_user=signinup_response.created_new_user,
-            user=user,
-            session=session,
-            oauth_tokens=oauth_tokens_to_use,
-            raw_user_info_from_provider=user_info.raw_user_info_from_provider,
-        )
-
-    async def apple_redirect_handler_post(
-        self,
-        form_post_info: Dict[str, Any],
-        api_options: APIOptions,
-        user_context: Dict[str, Any],
-    ):
-        state_in_b64: str = form_post_info["state"]
-        state = b64decode(state_in_b64).decode("utf-8")
-        state_obj = json.loads(state)
-        redirect_uri: str = state_obj["frontendRedirectURI"]
-
-        url_obj = urlparse(redirect_uri)
-        qparams = parse_qs(url_obj.query)
-        for k, v in form_post_info.items():
-            qparams[k] = [v]
-
-        redirect_uri = url_obj._replace(query=urlencode(qparams, doseq=True)).geturl()
-
-        api_options.response.set_header("Location", redirect_uri)
-        api_options.response.set_status_code(303)
-        api_options.response.set_html_content("")
-
-

Ancestors

- -

Methods

-
-
-async def apple_redirect_handler_post(self, form_post_info: Dict[str, Any], api_options: APIOptions, user_context: Dict[str, Any]) -
-
-
-
- -Expand source code - -
async def apple_redirect_handler_post(
-    self,
-    form_post_info: Dict[str, Any],
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-):
-    state_in_b64: str = form_post_info["state"]
-    state = b64decode(state_in_b64).decode("utf-8")
-    state_obj = json.loads(state)
-    redirect_uri: str = state_obj["frontendRedirectURI"]
-
-    url_obj = urlparse(redirect_uri)
-    qparams = parse_qs(url_obj.query)
-    for k, v in form_post_info.items():
-        qparams[k] = [v]
-
-    redirect_uri = url_obj._replace(query=urlencode(qparams, doseq=True)).geturl()
-
-    api_options.response.set_header("Location", redirect_uri)
-    api_options.response.set_status_code(303)
-    api_options.response.set_html_content("")
-
-
-
-async def authorisation_url_get(self, provider: Provider, redirect_uri_on_provider_dashboard: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[AuthorisationUrlGetOkResult, GeneralErrorResponse] -
-
-
-
- -Expand source code - -
async def authorisation_url_get(
-    self,
-    provider: Provider,
-    redirect_uri_on_provider_dashboard: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-) -> Union[AuthorisationUrlGetOkResult, GeneralErrorResponse]:
-    authorisation_url_info = await provider.get_authorisation_redirect_url(
-        redirect_uri_on_provider_dashboard=redirect_uri_on_provider_dashboard,
-        user_context=user_context,
-    )
-
-    return AuthorisationUrlGetOkResult(
-        url_with_query_params=authorisation_url_info.url_with_query_params,
-        pkce_code_verifier=authorisation_url_info.pkce_code_verifier,
-    )
-
-
-
-async def sign_in_up_post(self, provider: Provider, redirect_uri_info: Optional[RedirectUriInfo], oauth_tokens: Optional[Dict[str, Any]], tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[SignInUpPostOkResult, SignInUpPostNoEmailGivenByProviderResponse, GeneralErrorResponse] -
-
-
-
- -Expand source code - -
async def sign_in_up_post(
-    self,
-    provider: Provider,
-    redirect_uri_info: Optional[RedirectUriInfo],
-    oauth_tokens: Optional[Dict[str, Any]],
-    tenant_id: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-) -> Union[
-    SignInUpPostOkResult,
-    SignInUpPostNoEmailGivenByProviderResponse,
-    GeneralErrorResponse,
-]:
-    oauth_tokens_to_use: Dict[str, Any] = {}
-
-    if redirect_uri_info is not None:
-        oauth_tokens_to_use = await provider.exchange_auth_code_for_oauth_tokens(
-            redirect_uri_info=redirect_uri_info,
-            user_context=user_context,
-        )
-    else:
-        oauth_tokens_to_use = oauth_tokens  # type: ignore
-
-    user_info = await provider.get_user_info(
-        oauth_tokens=oauth_tokens_to_use,
-        user_context=user_context,
-    )
-
-    if user_info.email is None and provider.config.require_email is False:
-        # We don't expect to get an email from this provider.
-        # So we generate a fake one
-        if provider.config.generate_fake_email is not None:
-            user_info.email = UserInfoEmail(
-                email=await provider.config.generate_fake_email(
-                    tenant_id, user_info.third_party_user_id, user_context
-                ),
-                is_verified=True,
-            )
-
-    email = user_info.email.id if user_info.email is not None else None
-    email_verified = (
-        user_info.email.is_verified if user_info.email is not None else None
-    )
-    if email is None:
-        return SignInUpPostNoEmailGivenByProviderResponse()
-
-    signinup_response = await api_options.recipe_implementation.sign_in_up(
-        third_party_id=provider.id,
-        third_party_user_id=user_info.third_party_user_id,
-        email=email,
-        oauth_tokens=oauth_tokens_to_use,
-        raw_user_info_from_provider=user_info.raw_user_info_from_provider,
-        tenant_id=tenant_id,
-        user_context=user_context,
-    )
-
-    if email_verified:
-        ev_instance = EmailVerificationRecipe.get_instance_optional()
-        if ev_instance is not None:
-            token_response = await ev_instance.recipe_implementation.create_email_verification_token(
-                tenant_id=tenant_id,
-                user_id=signinup_response.user.user_id,
-                email=signinup_response.user.email,
-                user_context=user_context,
-            )
-
-            if isinstance(token_response, CreateEmailVerificationTokenOkResult):
-                await ev_instance.recipe_implementation.verify_email_using_token(
-                    token=token_response.token,
-                    tenant_id=tenant_id,
-                    user_context=user_context,
-                )
-
-    user = signinup_response.user
-    session = await create_new_session(
-        request=api_options.request,
-        tenant_id=tenant_id,
-        user_id=user.user_id,
-        user_context=user_context,
-    )
-
-    return SignInUpPostOkResult(
-        created_new_user=signinup_response.created_new_user,
-        user=user,
-        session=session,
-        oauth_tokens=oauth_tokens_to_use,
-        raw_user_info_from_provider=user_info.raw_user_info_from_provider,
-    )
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/thirdparty/api/index.html b/html/supertokens_python/recipe/thirdparty/api/index.html deleted file mode 100644 index c0ef90f06..000000000 --- a/html/supertokens_python/recipe/thirdparty/api/index.html +++ /dev/null @@ -1,275 +0,0 @@ - - - - - - -supertokens_python.recipe.thirdparty.api API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.thirdparty.api

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from .apple_redirect import handle_apple_redirect_api
-from .authorisation_url import handle_authorisation_url_api
-from .signinup import handle_sign_in_up_api
-
-__all__ = [
-    "handle_apple_redirect_api",
-    "handle_authorisation_url_api",
-    "handle_sign_in_up_api",
-]
-
-
-
-

Sub-modules

-
-
supertokens_python.recipe.thirdparty.api.apple_redirect
-
-
-
-
supertokens_python.recipe.thirdparty.api.authorisation_url
-
-
-
-
supertokens_python.recipe.thirdparty.api.implementation
-
-
-
-
supertokens_python.recipe.thirdparty.api.signinup
-
-
-
-
-
-
-
-
-

Functions

-
-
-async def handle_apple_redirect_api(api_implementation: APIInterface, api_options: APIOptions, user_context: Dict[str, Any]) -
-
-
-
- -Expand source code - -
async def handle_apple_redirect_api(
-    api_implementation: APIInterface,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-):
-    if api_implementation.disable_apple_redirect_handler_post:
-        return None
-
-    body = await api_options.request.form_data()
-
-    # this will redirect the user...
-    await api_implementation.apple_redirect_handler_post(
-        body, api_options, user_context
-    )
-
-    return api_options.response
-
-
-
-async def handle_authorisation_url_api(api_implementation: APIInterface, tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) -
-
-
-
- -Expand source code - -
async def handle_authorisation_url_api(
-    api_implementation: APIInterface,
-    tenant_id: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-):
-    if api_implementation.disable_authorisation_url_get:
-        return None
-
-    third_party_id = api_options.request.get_query_param("thirdPartyId")
-    redirect_uri_on_provider_dashboard = api_options.request.get_query_param(
-        "redirectURIOnProviderDashboard"
-    )
-    client_type = api_options.request.get_query_param("clientType")
-
-    if third_party_id is None:
-        raise_bad_input_exception("Please provide the thirdPartyId as a GET param")
-
-    if redirect_uri_on_provider_dashboard is None:
-        raise_bad_input_exception(
-            "Please provide the redirectURIOnProviderDashboard as a GET param"
-        )
-
-    provider_response = await api_options.recipe_implementation.get_provider(
-        third_party_id=third_party_id,
-        client_type=client_type,
-        tenant_id=tenant_id,
-        user_context=user_context,
-    )
-
-    if provider_response is None:
-        raise BadInputError(
-            f"the provider {third_party_id} could not be found in the configuration"
-        )
-
-    provider = provider_response
-    result = await api_implementation.authorisation_url_get(
-        provider=provider,
-        redirect_uri_on_provider_dashboard=redirect_uri_on_provider_dashboard,
-        api_options=api_options,
-        user_context=user_context,
-    )
-    return send_200_response(result.to_json(), api_options.response)
-
-
-
-async def handle_sign_in_up_api(api_implementation: APIInterface, tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) -
-
-
-
- -Expand source code - -
async def handle_sign_in_up_api(
-    api_implementation: APIInterface,
-    tenant_id: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-):
-    if api_implementation.disable_sign_in_up_post:
-        return None
-
-    body = await api_options.request.json()
-    if body is None:
-        raise_bad_input_exception("Please provide a JSON input")
-
-    third_party_id = body.get("thirdPartyId")
-    client_type = body.get("clientType")
-
-    if third_party_id is None or not isinstance(third_party_id, str):
-        raise_bad_input_exception("Please provide the thirdPartyId in request body")
-
-    oauth_tokens = None
-    redirect_uri_info = None
-    if body.get("redirectURIInfo") is not None:
-        if body.get("redirectURIInfo").get("redirectURIOnProviderDashboard") is None:
-            raise_bad_input_exception(
-                "Please provide the redirectURIOnProviderDashboard in request body"
-            )
-        redirect_uri_info = body.get("redirectURIInfo")
-    elif body.get("oAuthTokens") is not None:
-        oauth_tokens = body.get("oAuthTokens")
-    else:
-        raise_bad_input_exception(
-            "Please provide one of redirectURIInfo or oAuthTokens in the request body"
-        )
-
-    provider_response = await api_options.recipe_implementation.get_provider(
-        third_party_id=third_party_id,
-        client_type=client_type,
-        tenant_id=tenant_id,
-        user_context=user_context,
-    )
-
-    if provider_response is None:
-        raise BadInputError(
-            f"the provider {third_party_id} could not be found in the configuration"
-        )
-
-    provider = provider_response
-
-    if redirect_uri_info is not None:
-        redirect_uri_info = RedirectUriInfo(
-            redirect_uri_on_provider_dashboard=redirect_uri_info.get(
-                "redirectURIOnProviderDashboard"
-            ),
-            redirect_uri_query_params=redirect_uri_info.get("redirectURIQueryParams"),
-            pkce_code_verifier=redirect_uri_info.get("pkceCodeVerifier"),
-        )
-
-    result = await api_implementation.sign_in_up_post(
-        provider=provider,
-        redirect_uri_info=redirect_uri_info,
-        oauth_tokens=oauth_tokens,
-        tenant_id=tenant_id,
-        api_options=api_options,
-        user_context=user_context,
-    )
-    return send_200_response(result.to_json(), api_options.response)
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/thirdparty/api/signinup.html b/html/supertokens_python/recipe/thirdparty/api/signinup.html deleted file mode 100644 index df45e0cad..000000000 --- a/html/supertokens_python/recipe/thirdparty/api/signinup.html +++ /dev/null @@ -1,234 +0,0 @@ - - - - - - -supertokens_python.recipe.thirdparty.api.signinup API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.thirdparty.api.signinup

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from typing import TYPE_CHECKING, Any, Dict
-from supertokens_python.recipe.thirdparty.provider import RedirectUriInfo
-
-if TYPE_CHECKING:
-    from supertokens_python.recipe.thirdparty.interfaces import APIOptions, APIInterface
-
-from supertokens_python.exceptions import raise_bad_input_exception, BadInputError
-from supertokens_python.utils import send_200_response
-
-
-async def handle_sign_in_up_api(
-    api_implementation: APIInterface,
-    tenant_id: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-):
-    if api_implementation.disable_sign_in_up_post:
-        return None
-
-    body = await api_options.request.json()
-    if body is None:
-        raise_bad_input_exception("Please provide a JSON input")
-
-    third_party_id = body.get("thirdPartyId")
-    client_type = body.get("clientType")
-
-    if third_party_id is None or not isinstance(third_party_id, str):
-        raise_bad_input_exception("Please provide the thirdPartyId in request body")
-
-    oauth_tokens = None
-    redirect_uri_info = None
-    if body.get("redirectURIInfo") is not None:
-        if body.get("redirectURIInfo").get("redirectURIOnProviderDashboard") is None:
-            raise_bad_input_exception(
-                "Please provide the redirectURIOnProviderDashboard in request body"
-            )
-        redirect_uri_info = body.get("redirectURIInfo")
-    elif body.get("oAuthTokens") is not None:
-        oauth_tokens = body.get("oAuthTokens")
-    else:
-        raise_bad_input_exception(
-            "Please provide one of redirectURIInfo or oAuthTokens in the request body"
-        )
-
-    provider_response = await api_options.recipe_implementation.get_provider(
-        third_party_id=third_party_id,
-        client_type=client_type,
-        tenant_id=tenant_id,
-        user_context=user_context,
-    )
-
-    if provider_response is None:
-        raise BadInputError(
-            f"the provider {third_party_id} could not be found in the configuration"
-        )
-
-    provider = provider_response
-
-    if redirect_uri_info is not None:
-        redirect_uri_info = RedirectUriInfo(
-            redirect_uri_on_provider_dashboard=redirect_uri_info.get(
-                "redirectURIOnProviderDashboard"
-            ),
-            redirect_uri_query_params=redirect_uri_info.get("redirectURIQueryParams"),
-            pkce_code_verifier=redirect_uri_info.get("pkceCodeVerifier"),
-        )
-
-    result = await api_implementation.sign_in_up_post(
-        provider=provider,
-        redirect_uri_info=redirect_uri_info,
-        oauth_tokens=oauth_tokens,
-        tenant_id=tenant_id,
-        api_options=api_options,
-        user_context=user_context,
-    )
-    return send_200_response(result.to_json(), api_options.response)
-
-
-
-
-
-
-
-

Functions

-
-
-async def handle_sign_in_up_api(api_implementation: APIInterface, tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) -
-
-
-
- -Expand source code - -
async def handle_sign_in_up_api(
-    api_implementation: APIInterface,
-    tenant_id: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-):
-    if api_implementation.disable_sign_in_up_post:
-        return None
-
-    body = await api_options.request.json()
-    if body is None:
-        raise_bad_input_exception("Please provide a JSON input")
-
-    third_party_id = body.get("thirdPartyId")
-    client_type = body.get("clientType")
-
-    if third_party_id is None or not isinstance(third_party_id, str):
-        raise_bad_input_exception("Please provide the thirdPartyId in request body")
-
-    oauth_tokens = None
-    redirect_uri_info = None
-    if body.get("redirectURIInfo") is not None:
-        if body.get("redirectURIInfo").get("redirectURIOnProviderDashboard") is None:
-            raise_bad_input_exception(
-                "Please provide the redirectURIOnProviderDashboard in request body"
-            )
-        redirect_uri_info = body.get("redirectURIInfo")
-    elif body.get("oAuthTokens") is not None:
-        oauth_tokens = body.get("oAuthTokens")
-    else:
-        raise_bad_input_exception(
-            "Please provide one of redirectURIInfo or oAuthTokens in the request body"
-        )
-
-    provider_response = await api_options.recipe_implementation.get_provider(
-        third_party_id=third_party_id,
-        client_type=client_type,
-        tenant_id=tenant_id,
-        user_context=user_context,
-    )
-
-    if provider_response is None:
-        raise BadInputError(
-            f"the provider {third_party_id} could not be found in the configuration"
-        )
-
-    provider = provider_response
-
-    if redirect_uri_info is not None:
-        redirect_uri_info = RedirectUriInfo(
-            redirect_uri_on_provider_dashboard=redirect_uri_info.get(
-                "redirectURIOnProviderDashboard"
-            ),
-            redirect_uri_query_params=redirect_uri_info.get("redirectURIQueryParams"),
-            pkce_code_verifier=redirect_uri_info.get("pkceCodeVerifier"),
-        )
-
-    result = await api_implementation.sign_in_up_post(
-        provider=provider,
-        redirect_uri_info=redirect_uri_info,
-        oauth_tokens=oauth_tokens,
-        tenant_id=tenant_id,
-        api_options=api_options,
-        user_context=user_context,
-    )
-    return send_200_response(result.to_json(), api_options.response)
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/thirdparty/asyncio/index.html b/html/supertokens_python/recipe/thirdparty/asyncio/index.html deleted file mode 100644 index 621358407..000000000 --- a/html/supertokens_python/recipe/thirdparty/asyncio/index.html +++ /dev/null @@ -1,272 +0,0 @@ - - - - - - -supertokens_python.recipe.thirdparty.asyncio API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.thirdparty.asyncio

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from typing import Any, Dict, List, Optional, Union
-
-from supertokens_python.recipe.thirdparty.recipe import ThirdPartyRecipe
-
-from ..types import User
-
-
-async def get_user_by_id(
-    user_id: str, user_context: Union[None, Dict[str, Any]] = None
-) -> Union[User, None]:
-    if user_context is None:
-        user_context = {}
-    return await ThirdPartyRecipe.get_instance().recipe_implementation.get_user_by_id(
-        user_id, user_context
-    )
-
-
-async def get_users_by_email(
-    tenant_id: str, email: str, user_context: Union[None, Dict[str, Any]] = None
-) -> List[User]:
-    if user_context is None:
-        user_context = {}
-    return (
-        await ThirdPartyRecipe.get_instance().recipe_implementation.get_users_by_email(
-            email, tenant_id, user_context
-        )
-    )
-
-
-async def get_user_by_third_party_info(
-    tenant_id: str,
-    third_party_id: str,
-    third_party_user_id: str,
-    user_context: Union[None, Dict[str, Any]] = None,
-):
-    if user_context is None:
-        user_context = {}
-    return await ThirdPartyRecipe.get_instance().recipe_implementation.get_user_by_thirdparty_info(
-        third_party_id,
-        third_party_user_id,
-        tenant_id,
-        user_context,
-    )
-
-
-async def manually_create_or_update_user(
-    tenant_id: str,
-    third_party_id: str,
-    third_party_user_id: str,
-    email: str,
-    user_context: Union[None, Dict[str, Any]] = None,
-):
-    if user_context is None:
-        user_context = {}
-    return await ThirdPartyRecipe.get_instance().recipe_implementation.manually_create_or_update_user(
-        third_party_id,
-        third_party_user_id,
-        email,
-        tenant_id,
-        user_context,
-    )
-
-
-async def get_provider(
-    tenant_id: str,
-    third_party_id: str,
-    client_type: Optional[str] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-):
-    if user_context is None:
-        user_context = {}
-    return await ThirdPartyRecipe.get_instance().recipe_implementation.get_provider(
-        third_party_id, client_type, tenant_id, user_context
-    )
-
-
-
-
-
-
-
-

Functions

-
-
-async def get_provider(tenant_id: str, third_party_id: str, client_type: Optional[str] = None, user_context: Optional[Dict[str, Any]] = None) -
-
-
-
- -Expand source code - -
async def get_provider(
-    tenant_id: str,
-    third_party_id: str,
-    client_type: Optional[str] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-):
-    if user_context is None:
-        user_context = {}
-    return await ThirdPartyRecipe.get_instance().recipe_implementation.get_provider(
-        third_party_id, client_type, tenant_id, user_context
-    )
-
-
-
-async def get_user_by_id(user_id: str, user_context: Optional[Dict[str, Any]] = None) ‑> Optional[User] -
-
-
-
- -Expand source code - -
async def get_user_by_id(
-    user_id: str, user_context: Union[None, Dict[str, Any]] = None
-) -> Union[User, None]:
-    if user_context is None:
-        user_context = {}
-    return await ThirdPartyRecipe.get_instance().recipe_implementation.get_user_by_id(
-        user_id, user_context
-    )
-
-
-
-async def get_user_by_third_party_info(tenant_id: str, third_party_id: str, third_party_user_id: str, user_context: Optional[Dict[str, Any]] = None) -
-
-
-
- -Expand source code - -
async def get_user_by_third_party_info(
-    tenant_id: str,
-    third_party_id: str,
-    third_party_user_id: str,
-    user_context: Union[None, Dict[str, Any]] = None,
-):
-    if user_context is None:
-        user_context = {}
-    return await ThirdPartyRecipe.get_instance().recipe_implementation.get_user_by_thirdparty_info(
-        third_party_id,
-        third_party_user_id,
-        tenant_id,
-        user_context,
-    )
-
-
-
-async def get_users_by_email(tenant_id: str, email: str, user_context: Optional[Dict[str, Any]] = None) ‑> List[User] -
-
-
-
- -Expand source code - -
async def get_users_by_email(
-    tenant_id: str, email: str, user_context: Union[None, Dict[str, Any]] = None
-) -> List[User]:
-    if user_context is None:
-        user_context = {}
-    return (
-        await ThirdPartyRecipe.get_instance().recipe_implementation.get_users_by_email(
-            email, tenant_id, user_context
-        )
-    )
-
-
-
-async def manually_create_or_update_user(tenant_id: str, third_party_id: str, third_party_user_id: str, email: str, user_context: Optional[Dict[str, Any]] = None) -
-
-
-
- -Expand source code - -
async def manually_create_or_update_user(
-    tenant_id: str,
-    third_party_id: str,
-    third_party_user_id: str,
-    email: str,
-    user_context: Union[None, Dict[str, Any]] = None,
-):
-    if user_context is None:
-        user_context = {}
-    return await ThirdPartyRecipe.get_instance().recipe_implementation.manually_create_or_update_user(
-        third_party_id,
-        third_party_user_id,
-        email,
-        tenant_id,
-        user_context,
-    )
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/thirdparty/constants.html b/html/supertokens_python/recipe/thirdparty/constants.html deleted file mode 100644 index 8c400fa86..000000000 --- a/html/supertokens_python/recipe/thirdparty/constants.html +++ /dev/null @@ -1,77 +0,0 @@ - - - - - - -supertokens_python.recipe.thirdparty.constants API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.thirdparty.constants

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-AUTHORISATIONURL = "/authorisationurl"
-SIGNINUP = "/signinup"
-SIGNOUT = "/signout"
-SIGNUP_EMAIL_EXISTS = "/signup/email/exists"
-RESET_PASSWORD = "/reset-password"
-APPLE_REDIRECT_HANDLER = "/callback/apple"
-
-
-
-
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/thirdparty/exceptions.html b/html/supertokens_python/recipe/thirdparty/exceptions.html deleted file mode 100644 index 0b8ae4059..000000000 --- a/html/supertokens_python/recipe/thirdparty/exceptions.html +++ /dev/null @@ -1,139 +0,0 @@ - - - - - - -supertokens_python.recipe.thirdparty.exceptions API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.thirdparty.exceptions

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from supertokens_python.exceptions import SuperTokensError
-
-
-class SuperTokensThirdPartyError(SuperTokensError):
-    pass
-
-
-class ClientTypeNotFoundError(SuperTokensError):
-    def __init__(self, message: str):
-        super().__init__()
-        self.message = message
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class ClientTypeNotFoundError -(message: str) -
-
-

Common base class for all non-exit exceptions.

-
- -Expand source code - -
class ClientTypeNotFoundError(SuperTokensError):
-    def __init__(self, message: str):
-        super().__init__()
-        self.message = message
-
-

Ancestors

- -
-
-class SuperTokensThirdPartyError -(*args, **kwargs) -
-
-

Common base class for all non-exit exceptions.

-
- -Expand source code - -
class SuperTokensThirdPartyError(SuperTokensError):
-    pass
-
-

Ancestors

- -
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/thirdparty/index.html b/html/supertokens_python/recipe/thirdparty/index.html deleted file mode 100644 index 24fc6586f..000000000 --- a/html/supertokens_python/recipe/thirdparty/index.html +++ /dev/null @@ -1,194 +0,0 @@ - - - - - - -supertokens_python.recipe.thirdparty API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.thirdparty

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from __future__ import annotations
-
-from typing import TYPE_CHECKING, Callable, Optional, Union
-
-
-from . import exceptions as ex
-from . import utils, provider
-from .recipe import ThirdPartyRecipe
-
-InputOverrideConfig = utils.InputOverrideConfig
-SignInAndUpFeature = utils.SignInAndUpFeature
-ProviderInput = provider.ProviderInput
-ProviderConfig = provider.ProviderConfig
-ProviderClientConfig = provider.ProviderClientConfig
-exceptions = ex
-
-if TYPE_CHECKING:
-    from supertokens_python.supertokens import AppInfo
-
-    from ...recipe_module import RecipeModule
-
-
-def init(
-    sign_in_and_up_feature: Optional[SignInAndUpFeature] = None,
-    override: Union[InputOverrideConfig, None] = None,
-) -> Callable[[AppInfo], RecipeModule]:
-    if sign_in_and_up_feature is None:
-        sign_in_and_up_feature = SignInAndUpFeature()
-    return ThirdPartyRecipe.init(sign_in_and_up_feature, override)
-
-
-
-

Sub-modules

-
-
supertokens_python.recipe.thirdparty.api
-
-
-
-
supertokens_python.recipe.thirdparty.asyncio
-
-
-
-
supertokens_python.recipe.thirdparty.constants
-
-
-
-
supertokens_python.recipe.thirdparty.exceptions
-
-
-
-
supertokens_python.recipe.thirdparty.interfaces
-
-
-
-
supertokens_python.recipe.thirdparty.provider
-
-
-
-
supertokens_python.recipe.thirdparty.providers
-
-
-
-
supertokens_python.recipe.thirdparty.recipe
-
-
-
-
supertokens_python.recipe.thirdparty.recipe_implementation
-
-
-
-
supertokens_python.recipe.thirdparty.syncio
-
-
-
-
supertokens_python.recipe.thirdparty.types
-
-
-
-
supertokens_python.recipe.thirdparty.utils
-
-
-
-
-
-
-
-
-

Functions

-
-
-def init(sign_in_and_up_feature: Optional[SignInAndUpFeature] = None, override: Union[InputOverrideConfig, None] = None) ‑> Callable[[AppInfo], RecipeModule] -
-
-
-
- -Expand source code - -
def init(
-    sign_in_and_up_feature: Optional[SignInAndUpFeature] = None,
-    override: Union[InputOverrideConfig, None] = None,
-) -> Callable[[AppInfo], RecipeModule]:
-    if sign_in_and_up_feature is None:
-        sign_in_and_up_feature = SignInAndUpFeature()
-    return ThirdPartyRecipe.init(sign_in_and_up_feature, override)
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/thirdparty/interfaces.html b/html/supertokens_python/recipe/thirdparty/interfaces.html deleted file mode 100644 index 02434433c..000000000 --- a/html/supertokens_python/recipe/thirdparty/interfaces.html +++ /dev/null @@ -1,953 +0,0 @@ - - - - - - -supertokens_python.recipe.thirdparty.interfaces API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.thirdparty.interfaces

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from abc import ABC, abstractmethod
-from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union
-
-from ...types import APIResponse, GeneralErrorResponse
-from .provider import Provider, ProviderInput, RedirectUriInfo
-
-if TYPE_CHECKING:
-    from supertokens_python.framework import BaseRequest, BaseResponse
-    from supertokens_python.recipe.session import SessionContainer
-    from supertokens_python.supertokens import AppInfo
-
-    from .types import User, RawUserInfoFromProvider
-    from .utils import ThirdPartyConfig
-
-
-class SignInUpOkResult:
-    def __init__(
-        self,
-        user: User,
-        created_new_user: bool,
-        oauth_tokens: Dict[str, Any],
-        raw_user_info_from_provider: RawUserInfoFromProvider,
-    ):
-        self.user = user
-        self.created_new_user = created_new_user
-        self.oauth_tokens = oauth_tokens
-        self.raw_user_info_from_provider = raw_user_info_from_provider
-
-
-class ManuallyCreateOrUpdateUserOkResult:
-    def __init__(
-        self,
-        user: User,
-        created_new_user: bool,
-    ):
-        self.user = user
-        self.created_new_user = created_new_user
-
-
-class GetProviderOkResult:
-    def __init__(self, provider: Provider):
-        self.provider = provider
-
-
-class RecipeInterface(ABC):
-    def __init__(self):
-        pass
-
-    @abstractmethod
-    async def get_user_by_id(
-        self, user_id: str, user_context: Dict[str, Any]
-    ) -> Union[User, None]:
-        pass
-
-    @abstractmethod
-    async def get_users_by_email(
-        self, email: str, tenant_id: str, user_context: Dict[str, Any]
-    ) -> List[User]:
-        pass
-
-    @abstractmethod
-    async def get_user_by_thirdparty_info(
-        self,
-        third_party_id: str,
-        third_party_user_id: str,
-        tenant_id: str,
-        user_context: Dict[str, Any],
-    ) -> Union[User, None]:
-        pass
-
-    @abstractmethod
-    async def manually_create_or_update_user(
-        self,
-        third_party_id: str,
-        third_party_user_id: str,
-        email: str,
-        tenant_id: str,
-        user_context: Dict[str, Any],
-    ) -> ManuallyCreateOrUpdateUserOkResult:
-        pass
-
-    @abstractmethod
-    async def sign_in_up(
-        self,
-        third_party_id: str,
-        third_party_user_id: str,
-        email: str,
-        oauth_tokens: Dict[str, Any],
-        raw_user_info_from_provider: RawUserInfoFromProvider,
-        tenant_id: str,
-        user_context: Dict[str, Any],
-    ) -> SignInUpOkResult:
-        pass
-
-    @abstractmethod
-    async def get_provider(
-        self,
-        third_party_id: str,
-        client_type: Optional[str],
-        tenant_id: str,
-        user_context: Dict[str, Any],
-    ) -> Optional[Provider]:
-        pass
-
-
-class APIOptions:
-    def __init__(
-        self,
-        request: BaseRequest,
-        response: BaseResponse,
-        recipe_id: str,
-        config: ThirdPartyConfig,
-        recipe_implementation: RecipeInterface,
-        providers: List[ProviderInput],
-        app_info: AppInfo,
-    ):
-        self.request: BaseRequest = request
-        self.response: BaseResponse = response
-        self.recipe_id: str = recipe_id
-        self.config: ThirdPartyConfig = config
-        self.providers: List[ProviderInput] = providers
-        self.recipe_implementation: RecipeInterface = recipe_implementation
-        self.app_info: AppInfo = app_info
-
-
-class SignInUpPostOkResult(APIResponse):
-    status: str = "OK"
-
-    def __init__(
-        self,
-        user: User,
-        created_new_user: bool,
-        session: SessionContainer,
-        oauth_tokens: Dict[str, Any],
-        raw_user_info_from_provider: RawUserInfoFromProvider,
-    ):
-        self.user = user
-        self.created_new_user = created_new_user
-        self.session = session
-        self.oauth_tokens = oauth_tokens
-        self.raw_user_info_from_provider = raw_user_info_from_provider
-
-    def to_json(self) -> Dict[str, Any]:
-        return {
-            "status": self.status,
-            "user": {
-                "id": self.user.user_id,
-                "email": self.user.email,
-                "timeJoined": self.user.time_joined,
-                "thirdParty": {
-                    "id": self.user.third_party_info.id,
-                    "userId": self.user.third_party_info.user_id,
-                },
-            },
-            "createdNewUser": self.created_new_user,
-        }
-
-
-class SignInUpPostNoEmailGivenByProviderResponse(APIResponse):
-    status: str = "NO_EMAIL_GIVEN_BY_PROVIDER"
-
-    def to_json(self) -> Dict[str, Any]:
-        return {"status": self.status}
-
-
-class AuthorisationUrlGetOkResult(APIResponse):
-    status: str = "OK"
-
-    def __init__(
-        self, url_with_query_params: str, pkce_code_verifier: Optional[str] = None
-    ):
-        self.url_with_query_params = url_with_query_params
-        self.pkce_code_verifier = pkce_code_verifier
-
-    def to_json(self):
-        return {
-            "status": self.status,
-            "urlWithQueryParams": self.url_with_query_params,
-            "pkceCodeVerifier": self.pkce_code_verifier,
-        }
-
-
-class APIInterface:
-    def __init__(self):
-        self.disable_sign_in_up_post = False
-        self.disable_authorisation_url_get = False
-        self.disable_apple_redirect_handler_post = False
-
-    @abstractmethod
-    async def authorisation_url_get(
-        self,
-        provider: Provider,
-        redirect_uri_on_provider_dashboard: str,
-        api_options: APIOptions,
-        user_context: Dict[str, Any],
-    ) -> Union[AuthorisationUrlGetOkResult, GeneralErrorResponse]:
-        pass
-
-    @abstractmethod
-    async def sign_in_up_post(
-        self,
-        provider: Provider,
-        redirect_uri_info: Optional[RedirectUriInfo],
-        oauth_tokens: Optional[Dict[str, Any]],
-        tenant_id: str,
-        api_options: APIOptions,
-        user_context: Dict[str, Any],
-    ) -> Union[
-        SignInUpPostOkResult,
-        SignInUpPostNoEmailGivenByProviderResponse,
-        GeneralErrorResponse,
-    ]:
-        pass
-
-    @abstractmethod
-    async def apple_redirect_handler_post(
-        self,
-        form_post_info: Dict[str, Any],
-        api_options: APIOptions,
-        user_context: Dict[str, Any],
-    ):
-        pass
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class APIInterface -
-
-
-
- -Expand source code - -
class APIInterface:
-    def __init__(self):
-        self.disable_sign_in_up_post = False
-        self.disable_authorisation_url_get = False
-        self.disable_apple_redirect_handler_post = False
-
-    @abstractmethod
-    async def authorisation_url_get(
-        self,
-        provider: Provider,
-        redirect_uri_on_provider_dashboard: str,
-        api_options: APIOptions,
-        user_context: Dict[str, Any],
-    ) -> Union[AuthorisationUrlGetOkResult, GeneralErrorResponse]:
-        pass
-
-    @abstractmethod
-    async def sign_in_up_post(
-        self,
-        provider: Provider,
-        redirect_uri_info: Optional[RedirectUriInfo],
-        oauth_tokens: Optional[Dict[str, Any]],
-        tenant_id: str,
-        api_options: APIOptions,
-        user_context: Dict[str, Any],
-    ) -> Union[
-        SignInUpPostOkResult,
-        SignInUpPostNoEmailGivenByProviderResponse,
-        GeneralErrorResponse,
-    ]:
-        pass
-
-    @abstractmethod
-    async def apple_redirect_handler_post(
-        self,
-        form_post_info: Dict[str, Any],
-        api_options: APIOptions,
-        user_context: Dict[str, Any],
-    ):
-        pass
-
-

Subclasses

- -

Methods

-
-
-async def apple_redirect_handler_post(self, form_post_info: Dict[str, Any], api_options: APIOptions, user_context: Dict[str, Any]) -
-
-
-
- -Expand source code - -
@abstractmethod
-async def apple_redirect_handler_post(
-    self,
-    form_post_info: Dict[str, Any],
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-):
-    pass
-
-
-
-async def authorisation_url_get(self, provider: Provider, redirect_uri_on_provider_dashboard: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[AuthorisationUrlGetOkResultGeneralErrorResponse] -
-
-
-
- -Expand source code - -
@abstractmethod
-async def authorisation_url_get(
-    self,
-    provider: Provider,
-    redirect_uri_on_provider_dashboard: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-) -> Union[AuthorisationUrlGetOkResult, GeneralErrorResponse]:
-    pass
-
-
-
-async def sign_in_up_post(self, provider: Provider, redirect_uri_info: Optional[RedirectUriInfo], oauth_tokens: Optional[Dict[str, Any]], tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[SignInUpPostOkResultSignInUpPostNoEmailGivenByProviderResponseGeneralErrorResponse] -
-
-
-
- -Expand source code - -
@abstractmethod
-async def sign_in_up_post(
-    self,
-    provider: Provider,
-    redirect_uri_info: Optional[RedirectUriInfo],
-    oauth_tokens: Optional[Dict[str, Any]],
-    tenant_id: str,
-    api_options: APIOptions,
-    user_context: Dict[str, Any],
-) -> Union[
-    SignInUpPostOkResult,
-    SignInUpPostNoEmailGivenByProviderResponse,
-    GeneralErrorResponse,
-]:
-    pass
-
-
-
-
-
-class APIOptions -(request: BaseRequest, response: BaseResponse, recipe_id: str, config: ThirdPartyConfig, recipe_implementation: RecipeInterface, providers: List[ProviderInput], app_info: AppInfo) -
-
-
-
- -Expand source code - -
class APIOptions:
-    def __init__(
-        self,
-        request: BaseRequest,
-        response: BaseResponse,
-        recipe_id: str,
-        config: ThirdPartyConfig,
-        recipe_implementation: RecipeInterface,
-        providers: List[ProviderInput],
-        app_info: AppInfo,
-    ):
-        self.request: BaseRequest = request
-        self.response: BaseResponse = response
-        self.recipe_id: str = recipe_id
-        self.config: ThirdPartyConfig = config
-        self.providers: List[ProviderInput] = providers
-        self.recipe_implementation: RecipeInterface = recipe_implementation
-        self.app_info: AppInfo = app_info
-
-
-
-class AuthorisationUrlGetOkResult -(url_with_query_params: str, pkce_code_verifier: Optional[str] = None) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class AuthorisationUrlGetOkResult(APIResponse):
-    status: str = "OK"
-
-    def __init__(
-        self, url_with_query_params: str, pkce_code_verifier: Optional[str] = None
-    ):
-        self.url_with_query_params = url_with_query_params
-        self.pkce_code_verifier = pkce_code_verifier
-
-    def to_json(self):
-        return {
-            "status": self.status,
-            "urlWithQueryParams": self.url_with_query_params,
-            "pkceCodeVerifier": self.pkce_code_verifier,
-        }
-
-

Ancestors

- -

Class variables

-
-
var status : str
-
-
-
-
-

Methods

-
-
-def to_json(self) -
-
-
-
- -Expand source code - -
def to_json(self):
-    return {
-        "status": self.status,
-        "urlWithQueryParams": self.url_with_query_params,
-        "pkceCodeVerifier": self.pkce_code_verifier,
-    }
-
-
-
-
-
-class GetProviderOkResult -(provider: Provider) -
-
-
-
- -Expand source code - -
class GetProviderOkResult:
-    def __init__(self, provider: Provider):
-        self.provider = provider
-
-
-
-class ManuallyCreateOrUpdateUserOkResult -(user: User, created_new_user: bool) -
-
-
-
- -Expand source code - -
class ManuallyCreateOrUpdateUserOkResult:
-    def __init__(
-        self,
-        user: User,
-        created_new_user: bool,
-    ):
-        self.user = user
-        self.created_new_user = created_new_user
-
-
-
-class RecipeInterface -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class RecipeInterface(ABC):
-    def __init__(self):
-        pass
-
-    @abstractmethod
-    async def get_user_by_id(
-        self, user_id: str, user_context: Dict[str, Any]
-    ) -> Union[User, None]:
-        pass
-
-    @abstractmethod
-    async def get_users_by_email(
-        self, email: str, tenant_id: str, user_context: Dict[str, Any]
-    ) -> List[User]:
-        pass
-
-    @abstractmethod
-    async def get_user_by_thirdparty_info(
-        self,
-        third_party_id: str,
-        third_party_user_id: str,
-        tenant_id: str,
-        user_context: Dict[str, Any],
-    ) -> Union[User, None]:
-        pass
-
-    @abstractmethod
-    async def manually_create_or_update_user(
-        self,
-        third_party_id: str,
-        third_party_user_id: str,
-        email: str,
-        tenant_id: str,
-        user_context: Dict[str, Any],
-    ) -> ManuallyCreateOrUpdateUserOkResult:
-        pass
-
-    @abstractmethod
-    async def sign_in_up(
-        self,
-        third_party_id: str,
-        third_party_user_id: str,
-        email: str,
-        oauth_tokens: Dict[str, Any],
-        raw_user_info_from_provider: RawUserInfoFromProvider,
-        tenant_id: str,
-        user_context: Dict[str, Any],
-    ) -> SignInUpOkResult:
-        pass
-
-    @abstractmethod
-    async def get_provider(
-        self,
-        third_party_id: str,
-        client_type: Optional[str],
-        tenant_id: str,
-        user_context: Dict[str, Any],
-    ) -> Optional[Provider]:
-        pass
-
-

Ancestors

-
    -
  • abc.ABC
  • -
-

Subclasses

- -

Methods

-
-
-async def get_provider(self, third_party_id: str, client_type: Optional[str], tenant_id: str, user_context: Dict[str, Any]) ‑> Optional[Provider] -
-
-
-
- -Expand source code - -
@abstractmethod
-async def get_provider(
-    self,
-    third_party_id: str,
-    client_type: Optional[str],
-    tenant_id: str,
-    user_context: Dict[str, Any],
-) -> Optional[Provider]:
-    pass
-
-
-
-async def get_user_by_id(self, user_id: str, user_context: Dict[str, Any]) ‑> Union[User, None] -
-
-
-
- -Expand source code - -
@abstractmethod
-async def get_user_by_id(
-    self, user_id: str, user_context: Dict[str, Any]
-) -> Union[User, None]:
-    pass
-
-
-
-async def get_user_by_thirdparty_info(self, third_party_id: str, third_party_user_id: str, tenant_id: str, user_context: Dict[str, Any]) ‑> Union[User, None] -
-
-
-
- -Expand source code - -
@abstractmethod
-async def get_user_by_thirdparty_info(
-    self,
-    third_party_id: str,
-    third_party_user_id: str,
-    tenant_id: str,
-    user_context: Dict[str, Any],
-) -> Union[User, None]:
-    pass
-
-
-
-async def get_users_by_email(self, email: str, tenant_id: str, user_context: Dict[str, Any]) ‑> List[User] -
-
-
-
- -Expand source code - -
@abstractmethod
-async def get_users_by_email(
-    self, email: str, tenant_id: str, user_context: Dict[str, Any]
-) -> List[User]:
-    pass
-
-
-
-async def manually_create_or_update_user(self, third_party_id: str, third_party_user_id: str, email: str, tenant_id: str, user_context: Dict[str, Any]) ‑> ManuallyCreateOrUpdateUserOkResult -
-
-
-
- -Expand source code - -
@abstractmethod
-async def manually_create_or_update_user(
-    self,
-    third_party_id: str,
-    third_party_user_id: str,
-    email: str,
-    tenant_id: str,
-    user_context: Dict[str, Any],
-) -> ManuallyCreateOrUpdateUserOkResult:
-    pass
-
-
-
-async def sign_in_up(self, third_party_id: str, third_party_user_id: str, email: str, oauth_tokens: Dict[str, Any], raw_user_info_from_provider: RawUserInfoFromProvider, tenant_id: str, user_context: Dict[str, Any]) ‑> SignInUpOkResult -
-
-
-
- -Expand source code - -
@abstractmethod
-async def sign_in_up(
-    self,
-    third_party_id: str,
-    third_party_user_id: str,
-    email: str,
-    oauth_tokens: Dict[str, Any],
-    raw_user_info_from_provider: RawUserInfoFromProvider,
-    tenant_id: str,
-    user_context: Dict[str, Any],
-) -> SignInUpOkResult:
-    pass
-
-
-
-
-
-class SignInUpOkResult -(user: User, created_new_user: bool, oauth_tokens: Dict[str, Any], raw_user_info_from_provider: RawUserInfoFromProvider) -
-
-
-
- -Expand source code - -
class SignInUpOkResult:
-    def __init__(
-        self,
-        user: User,
-        created_new_user: bool,
-        oauth_tokens: Dict[str, Any],
-        raw_user_info_from_provider: RawUserInfoFromProvider,
-    ):
-        self.user = user
-        self.created_new_user = created_new_user
-        self.oauth_tokens = oauth_tokens
-        self.raw_user_info_from_provider = raw_user_info_from_provider
-
-
-
-class SignInUpPostNoEmailGivenByProviderResponse -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class SignInUpPostNoEmailGivenByProviderResponse(APIResponse):
-    status: str = "NO_EMAIL_GIVEN_BY_PROVIDER"
-
-    def to_json(self) -> Dict[str, Any]:
-        return {"status": self.status}
-
-

Ancestors

- -

Class variables

-
-
var status : str
-
-
-
-
-

Methods

-
-
-def to_json(self) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
def to_json(self) -> Dict[str, Any]:
-    return {"status": self.status}
-
-
-
-
-
-class SignInUpPostOkResult -(user: User, created_new_user: bool, session: SessionContainer, oauth_tokens: Dict[str, Any], raw_user_info_from_provider: RawUserInfoFromProvider) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class SignInUpPostOkResult(APIResponse):
-    status: str = "OK"
-
-    def __init__(
-        self,
-        user: User,
-        created_new_user: bool,
-        session: SessionContainer,
-        oauth_tokens: Dict[str, Any],
-        raw_user_info_from_provider: RawUserInfoFromProvider,
-    ):
-        self.user = user
-        self.created_new_user = created_new_user
-        self.session = session
-        self.oauth_tokens = oauth_tokens
-        self.raw_user_info_from_provider = raw_user_info_from_provider
-
-    def to_json(self) -> Dict[str, Any]:
-        return {
-            "status": self.status,
-            "user": {
-                "id": self.user.user_id,
-                "email": self.user.email,
-                "timeJoined": self.user.time_joined,
-                "thirdParty": {
-                    "id": self.user.third_party_info.id,
-                    "userId": self.user.third_party_info.user_id,
-                },
-            },
-            "createdNewUser": self.created_new_user,
-        }
-
-

Ancestors

- -

Class variables

-
-
var status : str
-
-
-
-
-

Methods

-
-
-def to_json(self) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
def to_json(self) -> Dict[str, Any]:
-    return {
-        "status": self.status,
-        "user": {
-            "id": self.user.user_id,
-            "email": self.user.email,
-            "timeJoined": self.user.time_joined,
-            "thirdParty": {
-                "id": self.user.third_party_info.id,
-                "userId": self.user.third_party_info.user_id,
-            },
-        },
-        "createdNewUser": self.created_new_user,
-    }
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/thirdparty/provider.html b/html/supertokens_python/recipe/thirdparty/provider.html deleted file mode 100644 index 47c10c2e9..000000000 --- a/html/supertokens_python/recipe/thirdparty/provider.html +++ /dev/null @@ -1,1193 +0,0 @@ - - - - - - -supertokens_python.recipe.thirdparty.provider API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.thirdparty.provider

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from __future__ import annotations
-
-from typing import TYPE_CHECKING, Any, Awaitable, Dict, Union, Optional, List, Callable
-
-if TYPE_CHECKING:
-    from .types import UserInfo
-
-
-class AuthorisationRedirect:
-    def __init__(
-        self, url_with_query_params: str, pkce_code_verifier: Optional[str] = None
-    ):
-        self.url_with_query_params = url_with_query_params
-        self.pkce_code_verifier = pkce_code_verifier
-
-
-class RedirectUriInfo:
-    def __init__(
-        self,
-        redirect_uri_on_provider_dashboard: str,
-        redirect_uri_query_params: Dict[str, Any],
-        pkce_code_verifier: Optional[str] = None,
-    ):
-        self.redirect_uri_on_provider_dashboard = redirect_uri_on_provider_dashboard
-        self.redirect_uri_query_params = redirect_uri_query_params
-        self.pkce_code_verifier = pkce_code_verifier
-
-
-class Provider:
-    def __init__(
-        self, id: str, config: ProviderConfigForClient
-    ):  # pylint: disable=redefined-builtin
-        self.id = id
-        self.config = config
-
-    async def get_config_for_client_type(  # pylint: disable=no-self-use
-        self, client_type: Optional[str], user_context: Dict[str, Any]
-    ) -> ProviderConfigForClient:
-        _ = client_type
-        __ = user_context
-        raise NotImplementedError()
-
-    async def get_authorisation_redirect_url(  # pylint: disable=no-self-use
-        self,
-        redirect_uri_on_provider_dashboard: str,
-        user_context: Dict[str, Any],
-    ) -> AuthorisationRedirect:
-        _ = redirect_uri_on_provider_dashboard
-        __ = user_context
-        raise NotImplementedError()
-
-    async def exchange_auth_code_for_oauth_tokens(  # pylint: disable=no-self-use
-        self,
-        redirect_uri_info: RedirectUriInfo,
-        user_context: Dict[str, Any],
-    ) -> Dict[str, Any]:
-        _ = redirect_uri_info
-        __ = user_context
-        raise NotImplementedError()
-
-    async def get_user_info(  # pylint: disable=no-self-use
-        self,
-        oauth_tokens: Dict[str, Any],
-        user_context: Dict[str, Any],
-    ) -> UserInfo:
-        _ = oauth_tokens
-        __ = user_context
-        raise NotImplementedError()
-
-
-class ProviderClientConfig:
-    def __init__(
-        self,
-        client_id: str,
-        client_secret: Optional[str] = None,
-        client_type: Optional[str] = None,
-        scope: Optional[List[str]] = None,
-        force_pkce: Optional[bool] = None,
-        additional_config: Optional[Dict[str, Any]] = None,
-    ):
-        self.client_id = client_id
-        self.client_secret = client_secret
-        self.client_type = client_type
-        self.scope = scope
-        self.force_pkce = force_pkce
-        self.additional_config = additional_config
-
-    def to_json(self) -> Dict[str, Any]:
-        res = {
-            "clientId": self.client_id,
-            "clientSecret": self.client_secret,
-            "clientType": self.client_type,
-            "scope": self.scope,
-            "forcePKCE": self.force_pkce,
-            "additionalConfig": self.additional_config,
-        }
-
-        return {k: v for k, v in res.items() if v is not None}
-
-
-class UserFields:
-    def __init__(
-        self,
-        user_id: Optional[str] = None,
-        email: Optional[str] = None,
-        email_verified: Optional[str] = None,
-    ):
-        self.user_id = user_id
-        self.email = email
-        self.email_verified = email_verified
-
-    def to_json(self) -> Dict[str, Any]:
-        res = {
-            "userId": self.user_id,
-            "email": self.email,
-            "emailVerified": self.email_verified,
-        }
-
-        return {k: v for k, v in res.items() if v is not None}
-
-
-class UserInfoMap:
-    def __init__(
-        self,
-        from_id_token_payload: Optional[UserFields] = None,
-        from_user_info_api: Optional[UserFields] = None,
-    ):
-        self.from_id_token_payload = from_id_token_payload
-        self.from_user_info_api = from_user_info_api
-
-    def to_json(self) -> Dict[str, Any]:
-        res: Dict[str, Any] = {}
-        if self.from_id_token_payload:
-            res["fromIdTokenPayload"] = self.from_id_token_payload.to_json()
-        if self.from_user_info_api:
-            res["fromUserInfoAPI"] = self.from_user_info_api.to_json()
-        return res
-
-
-class CommonProviderConfig:
-    def __init__(
-        self,
-        third_party_id: str,
-        name: Optional[str] = None,
-        authorization_endpoint: Optional[str] = None,
-        authorization_endpoint_query_params: Optional[Dict[str, Any]] = None,
-        token_endpoint: Optional[str] = None,
-        token_endpoint_body_params: Optional[Dict[str, Union[str, None]]] = None,
-        user_info_endpoint: Optional[str] = None,
-        user_info_endpoint_query_params: Optional[Dict[str, Union[str, None]]] = None,
-        user_info_endpoint_headers: Optional[Dict[str, Union[str, None]]] = None,
-        jwks_uri: Optional[str] = None,
-        oidc_discovery_endpoint: Optional[str] = None,
-        user_info_map: Optional[UserInfoMap] = None,
-        require_email: Optional[bool] = None,
-        validate_id_token_payload: Optional[
-            Callable[
-                [Dict[str, Any], ProviderConfigForClient, Dict[str, Any]],
-                Awaitable[None],
-            ]
-        ] = None,
-        generate_fake_email: Optional[
-            Callable[[str, str, Dict[str, Any]], Awaitable[str]]
-        ] = None,
-        validate_access_token: Optional[
-            Callable[
-                [str, ProviderConfigForClient, Dict[str, Any]],
-                Awaitable[None],
-            ]
-        ] = None,
-    ):
-        self.third_party_id = third_party_id
-        self.name = name
-        self.authorization_endpoint = authorization_endpoint
-        self.authorization_endpoint_query_params = authorization_endpoint_query_params
-        self.token_endpoint = token_endpoint
-        self.token_endpoint_body_params = token_endpoint_body_params
-        self.user_info_endpoint = user_info_endpoint
-        self.user_info_endpoint_query_params = user_info_endpoint_query_params
-        self.user_info_endpoint_headers = user_info_endpoint_headers
-        self.jwks_uri = jwks_uri
-        self.oidc_discovery_endpoint = oidc_discovery_endpoint
-        self.user_info_map = user_info_map
-        self.require_email = require_email
-        self.validate_id_token_payload = validate_id_token_payload
-        self.generate_fake_email = generate_fake_email
-        self.validate_access_token = validate_access_token
-
-    def to_json(self) -> Dict[str, Any]:
-        res = {
-            "thirdPartyId": self.third_party_id,
-            "name": self.name,
-            "authorizationEndpoint": self.authorization_endpoint,
-            "authorizationEndpointQueryParams": self.authorization_endpoint_query_params,
-            "tokenEndpoint": self.token_endpoint,
-            "tokenEndpointBodyParams": self.token_endpoint_body_params,
-            "userInfoEndpoint": self.user_info_endpoint,
-            "userInfoEndpointQueryParams": self.user_info_endpoint_query_params,
-            "userInfoEndpointHeaders": self.user_info_endpoint_headers,
-            "jwksURI": self.jwks_uri,
-            "oidcDiscoveryEndpoint": self.oidc_discovery_endpoint,
-            "userInfoMap": self.user_info_map.to_json()
-            if self.user_info_map is not None
-            else None,
-            "requireEmail": self.require_email,
-        }
-
-        return {k: v for k, v in res.items() if v is not None}
-
-
-class ProviderConfigForClient(ProviderClientConfig, CommonProviderConfig):
-    def __init__(
-        self,
-        # ProviderClientConfig:
-        client_id: str,
-        client_secret: Optional[str] = None,
-        client_type: Optional[str] = None,
-        scope: Optional[List[str]] = None,
-        force_pkce: Optional[bool] = None,
-        additional_config: Optional[Dict[str, Any]] = None,
-        # CommonProviderConfig:
-        third_party_id: str = "temp",
-        name: Optional[str] = None,
-        authorization_endpoint: Optional[str] = None,
-        authorization_endpoint_query_params: Optional[
-            Dict[str, Union[str, None]]
-        ] = None,
-        token_endpoint: Optional[str] = None,
-        token_endpoint_body_params: Optional[Dict[str, Union[str, None]]] = None,
-        user_info_endpoint: Optional[str] = None,
-        user_info_endpoint_query_params: Optional[Dict[str, Union[str, None]]] = None,
-        user_info_endpoint_headers: Optional[Dict[str, Union[str, None]]] = None,
-        jwks_uri: Optional[str] = None,
-        oidc_discovery_endpoint: Optional[str] = None,
-        user_info_map: Optional[UserInfoMap] = None,
-        require_email: Optional[bool] = None,
-        validate_id_token_payload: Optional[
-            Callable[
-                [Dict[str, Any], ProviderConfigForClient, Dict[str, Any]],
-                Awaitable[None],
-            ]
-        ] = None,
-        generate_fake_email: Optional[
-            Callable[[str, str, Dict[str, Any]], Awaitable[str]]
-        ] = None,
-        validate_access_token: Optional[
-            Callable[
-                [str, ProviderConfigForClient, Dict[str, Any]],
-                Awaitable[None],
-            ]
-        ] = None,
-    ):
-        ProviderClientConfig.__init__(
-            self,
-            client_id,
-            client_secret,
-            client_type,
-            scope,
-            force_pkce,
-            additional_config,
-        )
-        CommonProviderConfig.__init__(
-            self,
-            third_party_id,
-            name,
-            authorization_endpoint,
-            authorization_endpoint_query_params,
-            token_endpoint,
-            token_endpoint_body_params,
-            user_info_endpoint,
-            user_info_endpoint_query_params,
-            user_info_endpoint_headers,
-            jwks_uri,
-            oidc_discovery_endpoint,
-            user_info_map,
-            require_email,
-            validate_id_token_payload,
-            generate_fake_email,
-            validate_access_token,
-        )
-
-    def to_json(self) -> Dict[str, Any]:
-        d1 = ProviderClientConfig.to_json(self)
-        d2 = CommonProviderConfig.to_json(self)
-        return {**d1, **d2}
-
-
-class ProviderConfig(CommonProviderConfig):
-    def __init__(
-        self,
-        third_party_id: str,
-        name: Optional[str] = None,
-        clients: Optional[List[ProviderClientConfig]] = None,
-        authorization_endpoint: Optional[str] = None,
-        authorization_endpoint_query_params: Optional[
-            Dict[str, Union[str, None]]
-        ] = None,
-        token_endpoint: Optional[str] = None,
-        token_endpoint_body_params: Optional[Dict[str, Union[str, None]]] = None,
-        user_info_endpoint: Optional[str] = None,
-        user_info_endpoint_query_params: Optional[Dict[str, Union[str, None]]] = None,
-        user_info_endpoint_headers: Optional[Dict[str, Union[str, None]]] = None,
-        jwks_uri: Optional[str] = None,
-        oidc_discovery_endpoint: Optional[str] = None,
-        user_info_map: Optional[UserInfoMap] = None,
-        require_email: Optional[bool] = None,
-        validate_id_token_payload: Optional[
-            Callable[
-                [Dict[str, Any], ProviderConfigForClient, Dict[str, Any]],
-                Awaitable[None],
-            ]
-        ] = None,
-        generate_fake_email: Optional[
-            Callable[[str, str, Dict[str, Any]], Awaitable[str]]
-        ] = None,
-        validate_access_token: Optional[
-            Callable[
-                [str, ProviderConfigForClient, Dict[str, Any]],
-                Awaitable[None],
-            ]
-        ] = None,
-    ):
-        super().__init__(
-            third_party_id,
-            name,
-            authorization_endpoint,
-            authorization_endpoint_query_params,
-            token_endpoint,
-            token_endpoint_body_params,
-            user_info_endpoint,
-            user_info_endpoint_query_params,
-            user_info_endpoint_headers,
-            jwks_uri,
-            oidc_discovery_endpoint,
-            user_info_map,
-            require_email,
-            validate_id_token_payload,
-            generate_fake_email,
-            validate_access_token,
-        )
-        self.clients = clients
-
-    def to_json(self) -> Dict[str, Any]:
-        d = CommonProviderConfig.to_json(self)
-
-        if self.clients is not None:
-            d["clients"] = [c.to_json() for c in self.clients]
-
-        return d
-
-
-class ProviderInput:
-    def __init__(
-        self,
-        config: ProviderConfig,
-        include_in_non_public_tenants_by_default: bool = False,
-        override: Optional[Callable[[Provider], Provider]] = None,
-    ):
-        self.config = config
-        self.include_in_non_public_tenants_by_default = (
-            include_in_non_public_tenants_by_default
-        )
-        self.override = override
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class AuthorisationRedirect -(url_with_query_params: str, pkce_code_verifier: Optional[str] = None) -
-
-
-
- -Expand source code - -
class AuthorisationRedirect:
-    def __init__(
-        self, url_with_query_params: str, pkce_code_verifier: Optional[str] = None
-    ):
-        self.url_with_query_params = url_with_query_params
-        self.pkce_code_verifier = pkce_code_verifier
-
-
-
-class CommonProviderConfig -(third_party_id: str, name: Optional[str] = None, authorization_endpoint: Optional[str] = None, authorization_endpoint_query_params: Optional[Dict[str, Any]] = None, token_endpoint: Optional[str] = None, token_endpoint_body_params: Optional[Dict[str, Union[str, None]]] = None, user_info_endpoint: Optional[str] = None, user_info_endpoint_query_params: Optional[Dict[str, Union[str, None]]] = None, user_info_endpoint_headers: Optional[Dict[str, Union[str, None]]] = None, jwks_uri: Optional[str] = None, oidc_discovery_endpoint: Optional[str] = None, user_info_map: Optional[UserInfoMap] = None, require_email: Optional[bool] = None, validate_id_token_payload: Optional[Callable[[Dict[str, Any], ProviderConfigForClient, Dict[str, Any]], Awaitable[None]]] = None, generate_fake_email: Optional[Callable[[str, str, Dict[str, Any]], Awaitable[str]]] = None, validate_access_token: Optional[Callable[[str, ProviderConfigForClient, Dict[str, Any]], Awaitable[None]]] = None) -
-
-
-
- -Expand source code - -
class CommonProviderConfig:
-    def __init__(
-        self,
-        third_party_id: str,
-        name: Optional[str] = None,
-        authorization_endpoint: Optional[str] = None,
-        authorization_endpoint_query_params: Optional[Dict[str, Any]] = None,
-        token_endpoint: Optional[str] = None,
-        token_endpoint_body_params: Optional[Dict[str, Union[str, None]]] = None,
-        user_info_endpoint: Optional[str] = None,
-        user_info_endpoint_query_params: Optional[Dict[str, Union[str, None]]] = None,
-        user_info_endpoint_headers: Optional[Dict[str, Union[str, None]]] = None,
-        jwks_uri: Optional[str] = None,
-        oidc_discovery_endpoint: Optional[str] = None,
-        user_info_map: Optional[UserInfoMap] = None,
-        require_email: Optional[bool] = None,
-        validate_id_token_payload: Optional[
-            Callable[
-                [Dict[str, Any], ProviderConfigForClient, Dict[str, Any]],
-                Awaitable[None],
-            ]
-        ] = None,
-        generate_fake_email: Optional[
-            Callable[[str, str, Dict[str, Any]], Awaitable[str]]
-        ] = None,
-        validate_access_token: Optional[
-            Callable[
-                [str, ProviderConfigForClient, Dict[str, Any]],
-                Awaitable[None],
-            ]
-        ] = None,
-    ):
-        self.third_party_id = third_party_id
-        self.name = name
-        self.authorization_endpoint = authorization_endpoint
-        self.authorization_endpoint_query_params = authorization_endpoint_query_params
-        self.token_endpoint = token_endpoint
-        self.token_endpoint_body_params = token_endpoint_body_params
-        self.user_info_endpoint = user_info_endpoint
-        self.user_info_endpoint_query_params = user_info_endpoint_query_params
-        self.user_info_endpoint_headers = user_info_endpoint_headers
-        self.jwks_uri = jwks_uri
-        self.oidc_discovery_endpoint = oidc_discovery_endpoint
-        self.user_info_map = user_info_map
-        self.require_email = require_email
-        self.validate_id_token_payload = validate_id_token_payload
-        self.generate_fake_email = generate_fake_email
-        self.validate_access_token = validate_access_token
-
-    def to_json(self) -> Dict[str, Any]:
-        res = {
-            "thirdPartyId": self.third_party_id,
-            "name": self.name,
-            "authorizationEndpoint": self.authorization_endpoint,
-            "authorizationEndpointQueryParams": self.authorization_endpoint_query_params,
-            "tokenEndpoint": self.token_endpoint,
-            "tokenEndpointBodyParams": self.token_endpoint_body_params,
-            "userInfoEndpoint": self.user_info_endpoint,
-            "userInfoEndpointQueryParams": self.user_info_endpoint_query_params,
-            "userInfoEndpointHeaders": self.user_info_endpoint_headers,
-            "jwksURI": self.jwks_uri,
-            "oidcDiscoveryEndpoint": self.oidc_discovery_endpoint,
-            "userInfoMap": self.user_info_map.to_json()
-            if self.user_info_map is not None
-            else None,
-            "requireEmail": self.require_email,
-        }
-
-        return {k: v for k, v in res.items() if v is not None}
-
-

Subclasses

- -

Methods

-
-
-def to_json(self) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
def to_json(self) -> Dict[str, Any]:
-    res = {
-        "thirdPartyId": self.third_party_id,
-        "name": self.name,
-        "authorizationEndpoint": self.authorization_endpoint,
-        "authorizationEndpointQueryParams": self.authorization_endpoint_query_params,
-        "tokenEndpoint": self.token_endpoint,
-        "tokenEndpointBodyParams": self.token_endpoint_body_params,
-        "userInfoEndpoint": self.user_info_endpoint,
-        "userInfoEndpointQueryParams": self.user_info_endpoint_query_params,
-        "userInfoEndpointHeaders": self.user_info_endpoint_headers,
-        "jwksURI": self.jwks_uri,
-        "oidcDiscoveryEndpoint": self.oidc_discovery_endpoint,
-        "userInfoMap": self.user_info_map.to_json()
-        if self.user_info_map is not None
-        else None,
-        "requireEmail": self.require_email,
-    }
-
-    return {k: v for k, v in res.items() if v is not None}
-
-
-
-
-
-class Provider -(id: str, config: ProviderConfigForClient) -
-
-
-
- -Expand source code - -
class Provider:
-    def __init__(
-        self, id: str, config: ProviderConfigForClient
-    ):  # pylint: disable=redefined-builtin
-        self.id = id
-        self.config = config
-
-    async def get_config_for_client_type(  # pylint: disable=no-self-use
-        self, client_type: Optional[str], user_context: Dict[str, Any]
-    ) -> ProviderConfigForClient:
-        _ = client_type
-        __ = user_context
-        raise NotImplementedError()
-
-    async def get_authorisation_redirect_url(  # pylint: disable=no-self-use
-        self,
-        redirect_uri_on_provider_dashboard: str,
-        user_context: Dict[str, Any],
-    ) -> AuthorisationRedirect:
-        _ = redirect_uri_on_provider_dashboard
-        __ = user_context
-        raise NotImplementedError()
-
-    async def exchange_auth_code_for_oauth_tokens(  # pylint: disable=no-self-use
-        self,
-        redirect_uri_info: RedirectUriInfo,
-        user_context: Dict[str, Any],
-    ) -> Dict[str, Any]:
-        _ = redirect_uri_info
-        __ = user_context
-        raise NotImplementedError()
-
-    async def get_user_info(  # pylint: disable=no-self-use
-        self,
-        oauth_tokens: Dict[str, Any],
-        user_context: Dict[str, Any],
-    ) -> UserInfo:
-        _ = oauth_tokens
-        __ = user_context
-        raise NotImplementedError()
-
-

Subclasses

- -

Methods

-
-
-async def exchange_auth_code_for_oauth_tokens(self, redirect_uri_info: RedirectUriInfo, user_context: Dict[str, Any]) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
async def exchange_auth_code_for_oauth_tokens(  # pylint: disable=no-self-use
-    self,
-    redirect_uri_info: RedirectUriInfo,
-    user_context: Dict[str, Any],
-) -> Dict[str, Any]:
-    _ = redirect_uri_info
-    __ = user_context
-    raise NotImplementedError()
-
-
-
-async def get_authorisation_redirect_url(self, redirect_uri_on_provider_dashboard: str, user_context: Dict[str, Any]) ‑> AuthorisationRedirect -
-
-
-
- -Expand source code - -
async def get_authorisation_redirect_url(  # pylint: disable=no-self-use
-    self,
-    redirect_uri_on_provider_dashboard: str,
-    user_context: Dict[str, Any],
-) -> AuthorisationRedirect:
-    _ = redirect_uri_on_provider_dashboard
-    __ = user_context
-    raise NotImplementedError()
-
-
-
-async def get_config_for_client_type(self, client_type: Optional[str], user_context: Dict[str, Any]) ‑> ProviderConfigForClient -
-
-
-
- -Expand source code - -
async def get_config_for_client_type(  # pylint: disable=no-self-use
-    self, client_type: Optional[str], user_context: Dict[str, Any]
-) -> ProviderConfigForClient:
-    _ = client_type
-    __ = user_context
-    raise NotImplementedError()
-
-
-
-async def get_user_info(self, oauth_tokens: Dict[str, Any], user_context: Dict[str, Any]) ‑> UserInfo -
-
-
-
- -Expand source code - -
async def get_user_info(  # pylint: disable=no-self-use
-    self,
-    oauth_tokens: Dict[str, Any],
-    user_context: Dict[str, Any],
-) -> UserInfo:
-    _ = oauth_tokens
-    __ = user_context
-    raise NotImplementedError()
-
-
-
-
-
-class ProviderClientConfig -(client_id: str, client_secret: Optional[str] = None, client_type: Optional[str] = None, scope: Optional[List[str]] = None, force_pkce: Optional[bool] = None, additional_config: Optional[Dict[str, Any]] = None) -
-
-
-
- -Expand source code - -
class ProviderClientConfig:
-    def __init__(
-        self,
-        client_id: str,
-        client_secret: Optional[str] = None,
-        client_type: Optional[str] = None,
-        scope: Optional[List[str]] = None,
-        force_pkce: Optional[bool] = None,
-        additional_config: Optional[Dict[str, Any]] = None,
-    ):
-        self.client_id = client_id
-        self.client_secret = client_secret
-        self.client_type = client_type
-        self.scope = scope
-        self.force_pkce = force_pkce
-        self.additional_config = additional_config
-
-    def to_json(self) -> Dict[str, Any]:
-        res = {
-            "clientId": self.client_id,
-            "clientSecret": self.client_secret,
-            "clientType": self.client_type,
-            "scope": self.scope,
-            "forcePKCE": self.force_pkce,
-            "additionalConfig": self.additional_config,
-        }
-
-        return {k: v for k, v in res.items() if v is not None}
-
-

Subclasses

- -

Methods

-
-
-def to_json(self) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
def to_json(self) -> Dict[str, Any]:
-    res = {
-        "clientId": self.client_id,
-        "clientSecret": self.client_secret,
-        "clientType": self.client_type,
-        "scope": self.scope,
-        "forcePKCE": self.force_pkce,
-        "additionalConfig": self.additional_config,
-    }
-
-    return {k: v for k, v in res.items() if v is not None}
-
-
-
-
-
-class ProviderConfig -(third_party_id: str, name: Optional[str] = None, clients: Optional[List[ProviderClientConfig]] = None, authorization_endpoint: Optional[str] = None, authorization_endpoint_query_params: Optional[Dict[str, Union[str, None]]] = None, token_endpoint: Optional[str] = None, token_endpoint_body_params: Optional[Dict[str, Union[str, None]]] = None, user_info_endpoint: Optional[str] = None, user_info_endpoint_query_params: Optional[Dict[str, Union[str, None]]] = None, user_info_endpoint_headers: Optional[Dict[str, Union[str, None]]] = None, jwks_uri: Optional[str] = None, oidc_discovery_endpoint: Optional[str] = None, user_info_map: Optional[UserInfoMap] = None, require_email: Optional[bool] = None, validate_id_token_payload: Optional[Callable[[Dict[str, Any], ProviderConfigForClient, Dict[str, Any]], Awaitable[None]]] = None, generate_fake_email: Optional[Callable[[str, str, Dict[str, Any]], Awaitable[str]]] = None, validate_access_token: Optional[Callable[[str, ProviderConfigForClient, Dict[str, Any]], Awaitable[None]]] = None) -
-
-
-
- -Expand source code - -
class ProviderConfig(CommonProviderConfig):
-    def __init__(
-        self,
-        third_party_id: str,
-        name: Optional[str] = None,
-        clients: Optional[List[ProviderClientConfig]] = None,
-        authorization_endpoint: Optional[str] = None,
-        authorization_endpoint_query_params: Optional[
-            Dict[str, Union[str, None]]
-        ] = None,
-        token_endpoint: Optional[str] = None,
-        token_endpoint_body_params: Optional[Dict[str, Union[str, None]]] = None,
-        user_info_endpoint: Optional[str] = None,
-        user_info_endpoint_query_params: Optional[Dict[str, Union[str, None]]] = None,
-        user_info_endpoint_headers: Optional[Dict[str, Union[str, None]]] = None,
-        jwks_uri: Optional[str] = None,
-        oidc_discovery_endpoint: Optional[str] = None,
-        user_info_map: Optional[UserInfoMap] = None,
-        require_email: Optional[bool] = None,
-        validate_id_token_payload: Optional[
-            Callable[
-                [Dict[str, Any], ProviderConfigForClient, Dict[str, Any]],
-                Awaitable[None],
-            ]
-        ] = None,
-        generate_fake_email: Optional[
-            Callable[[str, str, Dict[str, Any]], Awaitable[str]]
-        ] = None,
-        validate_access_token: Optional[
-            Callable[
-                [str, ProviderConfigForClient, Dict[str, Any]],
-                Awaitable[None],
-            ]
-        ] = None,
-    ):
-        super().__init__(
-            third_party_id,
-            name,
-            authorization_endpoint,
-            authorization_endpoint_query_params,
-            token_endpoint,
-            token_endpoint_body_params,
-            user_info_endpoint,
-            user_info_endpoint_query_params,
-            user_info_endpoint_headers,
-            jwks_uri,
-            oidc_discovery_endpoint,
-            user_info_map,
-            require_email,
-            validate_id_token_payload,
-            generate_fake_email,
-            validate_access_token,
-        )
-        self.clients = clients
-
-    def to_json(self) -> Dict[str, Any]:
-        d = CommonProviderConfig.to_json(self)
-
-        if self.clients is not None:
-            d["clients"] = [c.to_json() for c in self.clients]
-
-        return d
-
-

Ancestors

- -

Methods

-
-
-def to_json(self) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
def to_json(self) -> Dict[str, Any]:
-    d = CommonProviderConfig.to_json(self)
-
-    if self.clients is not None:
-        d["clients"] = [c.to_json() for c in self.clients]
-
-    return d
-
-
-
-
-
-class ProviderConfigForClient -(client_id: str, client_secret: Optional[str] = None, client_type: Optional[str] = None, scope: Optional[List[str]] = None, force_pkce: Optional[bool] = None, additional_config: Optional[Dict[str, Any]] = None, third_party_id: str = 'temp', name: Optional[str] = None, authorization_endpoint: Optional[str] = None, authorization_endpoint_query_params: Optional[Dict[str, Union[str, None]]] = None, token_endpoint: Optional[str] = None, token_endpoint_body_params: Optional[Dict[str, Union[str, None]]] = None, user_info_endpoint: Optional[str] = None, user_info_endpoint_query_params: Optional[Dict[str, Union[str, None]]] = None, user_info_endpoint_headers: Optional[Dict[str, Union[str, None]]] = None, jwks_uri: Optional[str] = None, oidc_discovery_endpoint: Optional[str] = None, user_info_map: Optional[UserInfoMap] = None, require_email: Optional[bool] = None, validate_id_token_payload: Optional[Callable[[Dict[str, Any], ProviderConfigForClient, Dict[str, Any]], Awaitable[None]]] = None, generate_fake_email: Optional[Callable[[str, str, Dict[str, Any]], Awaitable[str]]] = None, validate_access_token: Optional[Callable[[str, ProviderConfigForClient, Dict[str, Any]], Awaitable[None]]] = None) -
-
-
-
- -Expand source code - -
class ProviderConfigForClient(ProviderClientConfig, CommonProviderConfig):
-    def __init__(
-        self,
-        # ProviderClientConfig:
-        client_id: str,
-        client_secret: Optional[str] = None,
-        client_type: Optional[str] = None,
-        scope: Optional[List[str]] = None,
-        force_pkce: Optional[bool] = None,
-        additional_config: Optional[Dict[str, Any]] = None,
-        # CommonProviderConfig:
-        third_party_id: str = "temp",
-        name: Optional[str] = None,
-        authorization_endpoint: Optional[str] = None,
-        authorization_endpoint_query_params: Optional[
-            Dict[str, Union[str, None]]
-        ] = None,
-        token_endpoint: Optional[str] = None,
-        token_endpoint_body_params: Optional[Dict[str, Union[str, None]]] = None,
-        user_info_endpoint: Optional[str] = None,
-        user_info_endpoint_query_params: Optional[Dict[str, Union[str, None]]] = None,
-        user_info_endpoint_headers: Optional[Dict[str, Union[str, None]]] = None,
-        jwks_uri: Optional[str] = None,
-        oidc_discovery_endpoint: Optional[str] = None,
-        user_info_map: Optional[UserInfoMap] = None,
-        require_email: Optional[bool] = None,
-        validate_id_token_payload: Optional[
-            Callable[
-                [Dict[str, Any], ProviderConfigForClient, Dict[str, Any]],
-                Awaitable[None],
-            ]
-        ] = None,
-        generate_fake_email: Optional[
-            Callable[[str, str, Dict[str, Any]], Awaitable[str]]
-        ] = None,
-        validate_access_token: Optional[
-            Callable[
-                [str, ProviderConfigForClient, Dict[str, Any]],
-                Awaitable[None],
-            ]
-        ] = None,
-    ):
-        ProviderClientConfig.__init__(
-            self,
-            client_id,
-            client_secret,
-            client_type,
-            scope,
-            force_pkce,
-            additional_config,
-        )
-        CommonProviderConfig.__init__(
-            self,
-            third_party_id,
-            name,
-            authorization_endpoint,
-            authorization_endpoint_query_params,
-            token_endpoint,
-            token_endpoint_body_params,
-            user_info_endpoint,
-            user_info_endpoint_query_params,
-            user_info_endpoint_headers,
-            jwks_uri,
-            oidc_discovery_endpoint,
-            user_info_map,
-            require_email,
-            validate_id_token_payload,
-            generate_fake_email,
-            validate_access_token,
-        )
-
-    def to_json(self) -> Dict[str, Any]:
-        d1 = ProviderClientConfig.to_json(self)
-        d2 = CommonProviderConfig.to_json(self)
-        return {**d1, **d2}
-
-

Ancestors

- -

Methods

-
-
-def to_json(self) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
def to_json(self) -> Dict[str, Any]:
-    d1 = ProviderClientConfig.to_json(self)
-    d2 = CommonProviderConfig.to_json(self)
-    return {**d1, **d2}
-
-
-
-
-
-class ProviderInput -(config: ProviderConfig, include_in_non_public_tenants_by_default: bool = False, override: Optional[Callable[[Provider], Provider]] = None) -
-
-
-
- -Expand source code - -
class ProviderInput:
-    def __init__(
-        self,
-        config: ProviderConfig,
-        include_in_non_public_tenants_by_default: bool = False,
-        override: Optional[Callable[[Provider], Provider]] = None,
-    ):
-        self.config = config
-        self.include_in_non_public_tenants_by_default = (
-            include_in_non_public_tenants_by_default
-        )
-        self.override = override
-
-
-
-class RedirectUriInfo -(redirect_uri_on_provider_dashboard: str, redirect_uri_query_params: Dict[str, Any], pkce_code_verifier: Optional[str] = None) -
-
-
-
- -Expand source code - -
class RedirectUriInfo:
-    def __init__(
-        self,
-        redirect_uri_on_provider_dashboard: str,
-        redirect_uri_query_params: Dict[str, Any],
-        pkce_code_verifier: Optional[str] = None,
-    ):
-        self.redirect_uri_on_provider_dashboard = redirect_uri_on_provider_dashboard
-        self.redirect_uri_query_params = redirect_uri_query_params
-        self.pkce_code_verifier = pkce_code_verifier
-
-
-
-class UserFields -(user_id: Optional[str] = None, email: Optional[str] = None, email_verified: Optional[str] = None) -
-
-
-
- -Expand source code - -
class UserFields:
-    def __init__(
-        self,
-        user_id: Optional[str] = None,
-        email: Optional[str] = None,
-        email_verified: Optional[str] = None,
-    ):
-        self.user_id = user_id
-        self.email = email
-        self.email_verified = email_verified
-
-    def to_json(self) -> Dict[str, Any]:
-        res = {
-            "userId": self.user_id,
-            "email": self.email,
-            "emailVerified": self.email_verified,
-        }
-
-        return {k: v for k, v in res.items() if v is not None}
-
-

Methods

-
-
-def to_json(self) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
def to_json(self) -> Dict[str, Any]:
-    res = {
-        "userId": self.user_id,
-        "email": self.email,
-        "emailVerified": self.email_verified,
-    }
-
-    return {k: v for k, v in res.items() if v is not None}
-
-
-
-
-
-class UserInfoMap -(from_id_token_payload: Optional[UserFields] = None, from_user_info_api: Optional[UserFields] = None) -
-
-
-
- -Expand source code - -
class UserInfoMap:
-    def __init__(
-        self,
-        from_id_token_payload: Optional[UserFields] = None,
-        from_user_info_api: Optional[UserFields] = None,
-    ):
-        self.from_id_token_payload = from_id_token_payload
-        self.from_user_info_api = from_user_info_api
-
-    def to_json(self) -> Dict[str, Any]:
-        res: Dict[str, Any] = {}
-        if self.from_id_token_payload:
-            res["fromIdTokenPayload"] = self.from_id_token_payload.to_json()
-        if self.from_user_info_api:
-            res["fromUserInfoAPI"] = self.from_user_info_api.to_json()
-        return res
-
-

Methods

-
-
-def to_json(self) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
def to_json(self) -> Dict[str, Any]:
-    res: Dict[str, Any] = {}
-    if self.from_id_token_payload:
-        res["fromIdTokenPayload"] = self.from_id_token_payload.to_json()
-    if self.from_user_info_api:
-        res["fromUserInfoAPI"] = self.from_user_info_api.to_json()
-    return res
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/thirdparty/providers/active_directory.html b/html/supertokens_python/recipe/thirdparty/providers/active_directory.html deleted file mode 100644 index 838fb2681..000000000 --- a/html/supertokens_python/recipe/thirdparty/providers/active_directory.html +++ /dev/null @@ -1,245 +0,0 @@ - - - - - - -supertokens_python.recipe.thirdparty.providers.active_directory API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.thirdparty.providers.active_directory

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from typing import Any, Dict, Optional
-from .custom import GenericProvider, NewProvider
-from ..provider import (
-    Provider,
-    ProviderConfigForClient,
-    ProviderInput,
-)
-from .utils import normalise_oidc_endpoint_to_include_well_known
-
-
-class ActiveDirectoryImpl(GenericProvider):
-    async def get_config_for_client_type(
-        self, client_type: Optional[str], user_context: Dict[str, Any]
-    ) -> ProviderConfigForClient:
-        config = await super().get_config_for_client_type(client_type, user_context)
-
-        if (
-            config.additional_config is None
-            or config.additional_config.get("directoryId") is None
-        ):
-            if not config.oidc_discovery_endpoint:
-                raise Exception(
-                    "Please provide the directoryId in the additionalConfig of the Active Directory provider."
-                )
-        else:
-            config.oidc_discovery_endpoint = f"https://login.microsoftonline.com/{config.additional_config['directoryId']}/v2.0/.well-known/openid-configuration"
-
-        # The config could be coming from core where we didn't add the well-known previously
-        config.oidc_discovery_endpoint = normalise_oidc_endpoint_to_include_well_known(
-            config.oidc_discovery_endpoint
-        )
-
-        if config.scope is None:
-            config.scope = ["openid", "email"]
-
-        # TODO: Implement client assertion if required
-
-        return config
-
-
-def ActiveDirectory(
-    input: ProviderInput,  # pylint: disable=redefined-builtin
-) -> Provider:
-    if not input.config.name:
-        input.config.name = "Active Directory"
-
-    return NewProvider(input, ActiveDirectoryImpl)
-
-
-
-
-
-
-
-

Functions

-
-
-def ActiveDirectory(input: ProviderInput) ‑> Provider -
-
-
-
- -Expand source code - -
def ActiveDirectory(
-    input: ProviderInput,  # pylint: disable=redefined-builtin
-) -> Provider:
-    if not input.config.name:
-        input.config.name = "Active Directory"
-
-    return NewProvider(input, ActiveDirectoryImpl)
-
-
-
-
-
-

Classes

-
-
-class ActiveDirectoryImpl -(provider_config: ProviderConfig) -
-
-
-
- -Expand source code - -
class ActiveDirectoryImpl(GenericProvider):
-    async def get_config_for_client_type(
-        self, client_type: Optional[str], user_context: Dict[str, Any]
-    ) -> ProviderConfigForClient:
-        config = await super().get_config_for_client_type(client_type, user_context)
-
-        if (
-            config.additional_config is None
-            or config.additional_config.get("directoryId") is None
-        ):
-            if not config.oidc_discovery_endpoint:
-                raise Exception(
-                    "Please provide the directoryId in the additionalConfig of the Active Directory provider."
-                )
-        else:
-            config.oidc_discovery_endpoint = f"https://login.microsoftonline.com/{config.additional_config['directoryId']}/v2.0/.well-known/openid-configuration"
-
-        # The config could be coming from core where we didn't add the well-known previously
-        config.oidc_discovery_endpoint = normalise_oidc_endpoint_to_include_well_known(
-            config.oidc_discovery_endpoint
-        )
-
-        if config.scope is None:
-            config.scope = ["openid", "email"]
-
-        # TODO: Implement client assertion if required
-
-        return config
-
-

Ancestors

- -

Methods

-
-
-async def get_config_for_client_type(self, client_type: Optional[str], user_context: Dict[str, Any]) ‑> ProviderConfigForClient -
-
-
-
- -Expand source code - -
async def get_config_for_client_type(
-    self, client_type: Optional[str], user_context: Dict[str, Any]
-) -> ProviderConfigForClient:
-    config = await super().get_config_for_client_type(client_type, user_context)
-
-    if (
-        config.additional_config is None
-        or config.additional_config.get("directoryId") is None
-    ):
-        if not config.oidc_discovery_endpoint:
-            raise Exception(
-                "Please provide the directoryId in the additionalConfig of the Active Directory provider."
-            )
-    else:
-        config.oidc_discovery_endpoint = f"https://login.microsoftonline.com/{config.additional_config['directoryId']}/v2.0/.well-known/openid-configuration"
-
-    # The config could be coming from core where we didn't add the well-known previously
-    config.oidc_discovery_endpoint = normalise_oidc_endpoint_to_include_well_known(
-        config.oidc_discovery_endpoint
-    )
-
-    if config.scope is None:
-        config.scope = ["openid", "email"]
-
-    # TODO: Implement client assertion if required
-
-    return config
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/thirdparty/providers/apple.html b/html/supertokens_python/recipe/thirdparty/providers/apple.html deleted file mode 100644 index 9985692b8..000000000 --- a/html/supertokens_python/recipe/thirdparty/providers/apple.html +++ /dev/null @@ -1,299 +0,0 @@ - - - - - - -supertokens_python.recipe.thirdparty.providers.apple API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.thirdparty.providers.apple

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from re import sub
-from typing import Any, Dict, Optional
-from jwt import encode  # type: ignore
-from time import time
-
-from .custom import GenericProvider, NewProvider
-from ..provider import Provider, ProviderConfigForClient, ProviderInput
-from .utils import (
-    get_actual_client_id_from_development_client_id,
-    normalise_oidc_endpoint_to_include_well_known,
-)
-
-
-class AppleImpl(GenericProvider):
-    async def get_config_for_client_type(
-        self, client_type: Optional[str], user_context: Dict[str, Any]
-    ) -> ProviderConfigForClient:
-        config = await super().get_config_for_client_type(client_type, user_context)
-
-        if config.scope is None:
-            config.scope = ["openid", "email"]
-
-        if not config.client_secret:
-            config.client_secret = await self._get_client_secret(config)
-
-        if not config.oidc_discovery_endpoint:
-            raise Exception("should never happen")
-
-        # The config could be coming from core where we didn't add the well-known previously
-        config.oidc_discovery_endpoint = normalise_oidc_endpoint_to_include_well_known(
-            config.oidc_discovery_endpoint
-        )
-
-        return config
-
-    async def _get_client_secret(  # pylint: disable=no-self-use
-        self, config: ProviderConfigForClient
-    ) -> str:
-        if (
-            config.additional_config is None
-            or config.additional_config.get("keyId") is None
-            or config.additional_config.get("teamId") is None
-            or config.additional_config.get("privateKey") is None
-        ):
-            raise Exception(
-                "Please ensure that keyId, teamId and privateKey are provided in the additionalConfig"
-            )
-
-        payload: Dict[str, Any] = {
-            "iss": config.additional_config.get("teamId"),
-            "iat": time(),
-            "exp": time() + (86400 * 180),  # 6 months
-            "aud": "https://appleid.apple.com",
-            "sub": get_actual_client_id_from_development_client_id(config.client_id),
-        }
-        headers = {"kid": config.additional_config.get("keyId")}
-        return encode(  # type: ignore
-            payload,
-            sub(r"\\n", "\n", config.additional_config.get("privateKey")),  # type: ignore
-            algorithm="ES256",
-            headers=headers,
-        )  # type: ignore
-
-
-def Apple(input: ProviderInput) -> Provider:  # pylint: disable=redefined-builtin
-    if not input.config.name:
-        input.config.name = "Apple"
-
-    if not input.config.oidc_discovery_endpoint:
-        input.config.oidc_discovery_endpoint = (
-            "https://appleid.apple.com/.well-known/openid-configuration"
-        )
-
-    input.config.authorization_endpoint_query_params = {
-        "response_mode": "form_post",
-        **(input.config.authorization_endpoint_query_params or {}),
-    }
-
-    return NewProvider(input, AppleImpl)
-
-
-
-
-
-
-
-

Functions

-
-
-def Apple(input: ProviderInput) ‑> Provider -
-
-
-
- -Expand source code - -
def Apple(input: ProviderInput) -> Provider:  # pylint: disable=redefined-builtin
-    if not input.config.name:
-        input.config.name = "Apple"
-
-    if not input.config.oidc_discovery_endpoint:
-        input.config.oidc_discovery_endpoint = (
-            "https://appleid.apple.com/.well-known/openid-configuration"
-        )
-
-    input.config.authorization_endpoint_query_params = {
-        "response_mode": "form_post",
-        **(input.config.authorization_endpoint_query_params or {}),
-    }
-
-    return NewProvider(input, AppleImpl)
-
-
-
-
-
-

Classes

-
-
-class AppleImpl -(provider_config: ProviderConfig) -
-
-
-
- -Expand source code - -
class AppleImpl(GenericProvider):
-    async def get_config_for_client_type(
-        self, client_type: Optional[str], user_context: Dict[str, Any]
-    ) -> ProviderConfigForClient:
-        config = await super().get_config_for_client_type(client_type, user_context)
-
-        if config.scope is None:
-            config.scope = ["openid", "email"]
-
-        if not config.client_secret:
-            config.client_secret = await self._get_client_secret(config)
-
-        if not config.oidc_discovery_endpoint:
-            raise Exception("should never happen")
-
-        # The config could be coming from core where we didn't add the well-known previously
-        config.oidc_discovery_endpoint = normalise_oidc_endpoint_to_include_well_known(
-            config.oidc_discovery_endpoint
-        )
-
-        return config
-
-    async def _get_client_secret(  # pylint: disable=no-self-use
-        self, config: ProviderConfigForClient
-    ) -> str:
-        if (
-            config.additional_config is None
-            or config.additional_config.get("keyId") is None
-            or config.additional_config.get("teamId") is None
-            or config.additional_config.get("privateKey") is None
-        ):
-            raise Exception(
-                "Please ensure that keyId, teamId and privateKey are provided in the additionalConfig"
-            )
-
-        payload: Dict[str, Any] = {
-            "iss": config.additional_config.get("teamId"),
-            "iat": time(),
-            "exp": time() + (86400 * 180),  # 6 months
-            "aud": "https://appleid.apple.com",
-            "sub": get_actual_client_id_from_development_client_id(config.client_id),
-        }
-        headers = {"kid": config.additional_config.get("keyId")}
-        return encode(  # type: ignore
-            payload,
-            sub(r"\\n", "\n", config.additional_config.get("privateKey")),  # type: ignore
-            algorithm="ES256",
-            headers=headers,
-        )  # type: ignore
-
-

Ancestors

- -

Methods

-
-
-async def get_config_for_client_type(self, client_type: Optional[str], user_context: Dict[str, Any]) ‑> ProviderConfigForClient -
-
-
-
- -Expand source code - -
async def get_config_for_client_type(
-    self, client_type: Optional[str], user_context: Dict[str, Any]
-) -> ProviderConfigForClient:
-    config = await super().get_config_for_client_type(client_type, user_context)
-
-    if config.scope is None:
-        config.scope = ["openid", "email"]
-
-    if not config.client_secret:
-        config.client_secret = await self._get_client_secret(config)
-
-    if not config.oidc_discovery_endpoint:
-        raise Exception("should never happen")
-
-    # The config could be coming from core where we didn't add the well-known previously
-    config.oidc_discovery_endpoint = normalise_oidc_endpoint_to_include_well_known(
-        config.oidc_discovery_endpoint
-    )
-
-    return config
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/thirdparty/providers/bitbucket.html b/html/supertokens_python/recipe/thirdparty/providers/bitbucket.html deleted file mode 100644 index 1dbde3000..000000000 --- a/html/supertokens_python/recipe/thirdparty/providers/bitbucket.html +++ /dev/null @@ -1,378 +0,0 @@ - - - - - - -supertokens_python.recipe.thirdparty.providers.bitbucket API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.thirdparty.providers.bitbucket

-
-
-
- -Expand source code - -
# Copyright (c) 2023, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from __future__ import annotations
-
-from typing import Dict, Any, Optional
-
-from supertokens_python.recipe.thirdparty.provider import (
-    ProviderConfigForClient,
-    ProviderInput,
-    Provider,
-)
-from .custom import GenericProvider, NewProvider
-
-from .utils import do_get_request
-from ..types import RawUserInfoFromProvider, UserInfo, UserInfoEmail
-
-
-class BitbucketImpl(GenericProvider):
-    async def get_config_for_client_type(
-        self, client_type: Optional[str], user_context: Dict[str, Any]
-    ) -> ProviderConfigForClient:
-        config = await super().get_config_for_client_type(client_type, user_context)
-
-        if config.scope is None:
-            config.scope = ["account", "email"]
-
-        return config
-
-    async def get_user_info(
-        self, oauth_tokens: Dict[str, Any], user_context: Dict[str, Any]
-    ) -> UserInfo:
-        _ = user_context
-        access_token = oauth_tokens.get("access_token")
-        if access_token is None:
-            raise Exception("Access token not found")
-
-        headers = {
-            "Authorization": f"Bearer {access_token}",
-        }
-
-        raw_user_info_from_provider = RawUserInfoFromProvider({}, {})
-
-        user_info_from_access_token = await do_get_request(
-            "https://api.bitbucket.org/2.0/user",
-            query_params=None,
-            headers=headers,
-        )
-
-        raw_user_info_from_provider.from_user_info_api = user_info_from_access_token
-
-        user_info_from_email = await do_get_request(
-            "https://api.bitbucket.org/2.0/user/emails",
-            query_params=None,
-            headers=headers,
-        )
-
-        if raw_user_info_from_provider.from_id_token_payload is None:
-            # Actually this should never happen but python type
-            # checker is not agreeing so doing this:
-            raw_user_info_from_provider.from_id_token_payload = {}
-
-        raw_user_info_from_provider.from_id_token_payload[
-            "email"
-        ] = user_info_from_email
-
-        email = None
-        is_verified = False
-        for email_info in user_info_from_email["values"]:
-            if email_info["is_primary"]:
-                email = email_info["email"]
-                is_verified = email_info["is_confirmed"]
-
-        return UserInfo(
-            third_party_user_id=raw_user_info_from_provider.from_user_info_api["uuid"],
-            email=None if email is None else UserInfoEmail(email, is_verified),
-            raw_user_info_from_provider=raw_user_info_from_provider,
-        )
-
-
-def Bitbucket(input: ProviderInput) -> Provider:  # pylint: disable=redefined-builtin
-    if not input.config.name:
-        input.config.name = "Bitbucket"
-
-    if not input.config.authorization_endpoint:
-        input.config.authorization_endpoint = (
-            "https://bitbucket.org/site/oauth2/authorize"
-        )
-
-    if not input.config.token_endpoint:
-        input.config.token_endpoint = "https://bitbucket.org/site/oauth2/access_token"
-
-    if input.config.authorization_endpoint_query_params is None:
-        input.config.authorization_endpoint_query_params = {
-            "audience": "api.atlassian.com",
-        }
-
-    return NewProvider(input, BitbucketImpl)
-
-
-
-
-
-
-
-

Functions

-
-
-def Bitbucket(input: ProviderInput) ‑> Provider -
-
-
-
- -Expand source code - -
def Bitbucket(input: ProviderInput) -> Provider:  # pylint: disable=redefined-builtin
-    if not input.config.name:
-        input.config.name = "Bitbucket"
-
-    if not input.config.authorization_endpoint:
-        input.config.authorization_endpoint = (
-            "https://bitbucket.org/site/oauth2/authorize"
-        )
-
-    if not input.config.token_endpoint:
-        input.config.token_endpoint = "https://bitbucket.org/site/oauth2/access_token"
-
-    if input.config.authorization_endpoint_query_params is None:
-        input.config.authorization_endpoint_query_params = {
-            "audience": "api.atlassian.com",
-        }
-
-    return NewProvider(input, BitbucketImpl)
-
-
-
-
-
-

Classes

-
-
-class BitbucketImpl -(provider_config: ProviderConfig) -
-
-
-
- -Expand source code - -
class BitbucketImpl(GenericProvider):
-    async def get_config_for_client_type(
-        self, client_type: Optional[str], user_context: Dict[str, Any]
-    ) -> ProviderConfigForClient:
-        config = await super().get_config_for_client_type(client_type, user_context)
-
-        if config.scope is None:
-            config.scope = ["account", "email"]
-
-        return config
-
-    async def get_user_info(
-        self, oauth_tokens: Dict[str, Any], user_context: Dict[str, Any]
-    ) -> UserInfo:
-        _ = user_context
-        access_token = oauth_tokens.get("access_token")
-        if access_token is None:
-            raise Exception("Access token not found")
-
-        headers = {
-            "Authorization": f"Bearer {access_token}",
-        }
-
-        raw_user_info_from_provider = RawUserInfoFromProvider({}, {})
-
-        user_info_from_access_token = await do_get_request(
-            "https://api.bitbucket.org/2.0/user",
-            query_params=None,
-            headers=headers,
-        )
-
-        raw_user_info_from_provider.from_user_info_api = user_info_from_access_token
-
-        user_info_from_email = await do_get_request(
-            "https://api.bitbucket.org/2.0/user/emails",
-            query_params=None,
-            headers=headers,
-        )
-
-        if raw_user_info_from_provider.from_id_token_payload is None:
-            # Actually this should never happen but python type
-            # checker is not agreeing so doing this:
-            raw_user_info_from_provider.from_id_token_payload = {}
-
-        raw_user_info_from_provider.from_id_token_payload[
-            "email"
-        ] = user_info_from_email
-
-        email = None
-        is_verified = False
-        for email_info in user_info_from_email["values"]:
-            if email_info["is_primary"]:
-                email = email_info["email"]
-                is_verified = email_info["is_confirmed"]
-
-        return UserInfo(
-            third_party_user_id=raw_user_info_from_provider.from_user_info_api["uuid"],
-            email=None if email is None else UserInfoEmail(email, is_verified),
-            raw_user_info_from_provider=raw_user_info_from_provider,
-        )
-
-

Ancestors

- -

Methods

-
-
-async def get_config_for_client_type(self, client_type: Optional[str], user_context: Dict[str, Any]) ‑> ProviderConfigForClient -
-
-
-
- -Expand source code - -
async def get_config_for_client_type(
-    self, client_type: Optional[str], user_context: Dict[str, Any]
-) -> ProviderConfigForClient:
-    config = await super().get_config_for_client_type(client_type, user_context)
-
-    if config.scope is None:
-        config.scope = ["account", "email"]
-
-    return config
-
-
-
-async def get_user_info(self, oauth_tokens: Dict[str, Any], user_context: Dict[str, Any]) ‑> UserInfo -
-
-
-
- -Expand source code - -
async def get_user_info(
-    self, oauth_tokens: Dict[str, Any], user_context: Dict[str, Any]
-) -> UserInfo:
-    _ = user_context
-    access_token = oauth_tokens.get("access_token")
-    if access_token is None:
-        raise Exception("Access token not found")
-
-    headers = {
-        "Authorization": f"Bearer {access_token}",
-    }
-
-    raw_user_info_from_provider = RawUserInfoFromProvider({}, {})
-
-    user_info_from_access_token = await do_get_request(
-        "https://api.bitbucket.org/2.0/user",
-        query_params=None,
-        headers=headers,
-    )
-
-    raw_user_info_from_provider.from_user_info_api = user_info_from_access_token
-
-    user_info_from_email = await do_get_request(
-        "https://api.bitbucket.org/2.0/user/emails",
-        query_params=None,
-        headers=headers,
-    )
-
-    if raw_user_info_from_provider.from_id_token_payload is None:
-        # Actually this should never happen but python type
-        # checker is not agreeing so doing this:
-        raw_user_info_from_provider.from_id_token_payload = {}
-
-    raw_user_info_from_provider.from_id_token_payload[
-        "email"
-    ] = user_info_from_email
-
-    email = None
-    is_verified = False
-    for email_info in user_info_from_email["values"]:
-        if email_info["is_primary"]:
-            email = email_info["email"]
-            is_verified = email_info["is_confirmed"]
-
-    return UserInfo(
-        third_party_user_id=raw_user_info_from_provider.from_user_info_api["uuid"],
-        email=None if email is None else UserInfoEmail(email, is_verified),
-        raw_user_info_from_provider=raw_user_info_from_provider,
-    )
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/thirdparty/providers/boxy_saml.html b/html/supertokens_python/recipe/thirdparty/providers/boxy_saml.html deleted file mode 100644 index bd705ee2f..000000000 --- a/html/supertokens_python/recipe/thirdparty/providers/boxy_saml.html +++ /dev/null @@ -1,248 +0,0 @@ - - - - - - -supertokens_python.recipe.thirdparty.providers.boxy_saml API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.thirdparty.providers.boxy_saml

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from typing import Any, Dict, Optional
-from .custom import GenericProvider, NewProvider
-from ..provider import (
-    Provider,
-    ProviderConfigForClient,
-    ProviderInput,
-    UserFields,
-    UserInfoMap,
-)
-
-
-class BoxySAMLImpl(GenericProvider):
-    async def get_config_for_client_type(
-        self, client_type: Optional[str], user_context: Dict[str, Any]
-    ) -> ProviderConfigForClient:
-        config = await super().get_config_for_client_type(client_type, user_context)
-
-        boxy_url = (
-            config.additional_config.get("boxyURL")
-            if config.additional_config is not None
-            else None
-        )
-        if boxy_url:
-            if not config.authorization_endpoint:
-                config.authorization_endpoint = f"{boxy_url}/api/oauth/authorize"
-
-            if not config.token_endpoint:
-                config.token_endpoint = f"{boxy_url}/api/oauth/token"
-
-            if not config.user_info_endpoint:
-                config.user_info_endpoint = f"{boxy_url}/api/oauth/userinfo"
-
-        return config
-
-
-def BoxySAML(input: ProviderInput) -> Provider:  # pylint: disable=redefined-builtin
-    if not input.config.name:
-        input.config.name = "SAML"
-
-    if input.config.user_info_map is None:
-        input.config.user_info_map = UserInfoMap(UserFields(), UserFields())
-
-    if input.config.user_info_map.from_user_info_api is None:
-        input.config.user_info_map.from_user_info_api = UserFields()
-
-    if input.config.user_info_map.from_user_info_api.user_id is None:
-        input.config.user_info_map.from_user_info_api.user_id = "id"
-
-    if input.config.user_info_map.from_user_info_api.email is None:
-        input.config.user_info_map.from_user_info_api.email = "email"
-
-    return NewProvider(input, BoxySAMLImpl)
-
-
-
-
-
-
-
-

Functions

-
-
-def BoxySAML(input: ProviderInput) ‑> Provider -
-
-
-
- -Expand source code - -
def BoxySAML(input: ProviderInput) -> Provider:  # pylint: disable=redefined-builtin
-    if not input.config.name:
-        input.config.name = "SAML"
-
-    if input.config.user_info_map is None:
-        input.config.user_info_map = UserInfoMap(UserFields(), UserFields())
-
-    if input.config.user_info_map.from_user_info_api is None:
-        input.config.user_info_map.from_user_info_api = UserFields()
-
-    if input.config.user_info_map.from_user_info_api.user_id is None:
-        input.config.user_info_map.from_user_info_api.user_id = "id"
-
-    if input.config.user_info_map.from_user_info_api.email is None:
-        input.config.user_info_map.from_user_info_api.email = "email"
-
-    return NewProvider(input, BoxySAMLImpl)
-
-
-
-
-
-

Classes

-
-
-class BoxySAMLImpl -(provider_config: ProviderConfig) -
-
-
-
- -Expand source code - -
class BoxySAMLImpl(GenericProvider):
-    async def get_config_for_client_type(
-        self, client_type: Optional[str], user_context: Dict[str, Any]
-    ) -> ProviderConfigForClient:
-        config = await super().get_config_for_client_type(client_type, user_context)
-
-        boxy_url = (
-            config.additional_config.get("boxyURL")
-            if config.additional_config is not None
-            else None
-        )
-        if boxy_url:
-            if not config.authorization_endpoint:
-                config.authorization_endpoint = f"{boxy_url}/api/oauth/authorize"
-
-            if not config.token_endpoint:
-                config.token_endpoint = f"{boxy_url}/api/oauth/token"
-
-            if not config.user_info_endpoint:
-                config.user_info_endpoint = f"{boxy_url}/api/oauth/userinfo"
-
-        return config
-
-

Ancestors

- -

Methods

-
-
-async def get_config_for_client_type(self, client_type: Optional[str], user_context: Dict[str, Any]) ‑> ProviderConfigForClient -
-
-
-
- -Expand source code - -
async def get_config_for_client_type(
-    self, client_type: Optional[str], user_context: Dict[str, Any]
-) -> ProviderConfigForClient:
-    config = await super().get_config_for_client_type(client_type, user_context)
-
-    boxy_url = (
-        config.additional_config.get("boxyURL")
-        if config.additional_config is not None
-        else None
-    )
-    if boxy_url:
-        if not config.authorization_endpoint:
-            config.authorization_endpoint = f"{boxy_url}/api/oauth/authorize"
-
-        if not config.token_endpoint:
-            config.token_endpoint = f"{boxy_url}/api/oauth/token"
-
-        if not config.user_info_endpoint:
-            config.user_info_endpoint = f"{boxy_url}/api/oauth/userinfo"
-
-    return config
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/thirdparty/providers/config_utils.html b/html/supertokens_python/recipe/thirdparty/providers/config_utils.html deleted file mode 100644 index c348916d4..000000000 --- a/html/supertokens_python/recipe/thirdparty/providers/config_utils.html +++ /dev/null @@ -1,684 +0,0 @@ - - - - - - -supertokens_python.recipe.thirdparty.providers.config_utils API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.thirdparty.providers.config_utils

-
-
-
- -Expand source code - -
from typing import List, Dict, Optional, Any
-
-from supertokens_python.normalised_url_domain import NormalisedURLDomain
-from supertokens_python.normalised_url_path import NormalisedURLPath
-from .active_directory import ActiveDirectory
-from .apple import Apple
-from .bitbucket import Bitbucket
-from .boxy_saml import BoxySAML
-from .discord import Discord
-from .facebook import Facebook
-from .github import Github
-from .gitlab import Gitlab
-from .google_workspaces import GoogleWorkspaces
-from .google import Google
-from .linkedin import Linkedin
-from .twitter import Twitter
-from .okta import Okta
-from .custom import NewProvider
-from .utils import do_get_request
-
-from ..provider import (
-    ProviderConfig,
-    ProviderConfigForClient,
-    ProviderInput,
-    Provider,
-    UserFields,
-    UserInfoMap,
-)
-
-
-def merge_config(
-    config_from_static: ProviderConfig, config_from_core: ProviderConfig
-) -> ProviderConfig:
-    result = ProviderConfig(
-        third_party_id=config_from_static.third_party_id,
-        name=(
-            config_from_static.name
-            if config_from_core.name is None
-            else config_from_core.name
-        ),
-        authorization_endpoint=(
-            config_from_static.authorization_endpoint
-            if config_from_core.authorization_endpoint is None
-            else config_from_core.authorization_endpoint
-        ),
-        authorization_endpoint_query_params=(
-            config_from_static.authorization_endpoint_query_params
-            if config_from_core.authorization_endpoint_query_params is None
-            else config_from_core.authorization_endpoint_query_params
-        ),
-        token_endpoint=(
-            config_from_static.token_endpoint
-            if config_from_core.token_endpoint is None
-            else config_from_core.token_endpoint
-        ),
-        token_endpoint_body_params=(
-            config_from_static.token_endpoint_body_params
-            if config_from_core.token_endpoint_body_params is None
-            else config_from_core.token_endpoint_body_params
-        ),
-        user_info_endpoint=(
-            config_from_static.user_info_endpoint
-            if config_from_core.user_info_endpoint is None
-            else config_from_core.user_info_endpoint
-        ),
-        user_info_endpoint_headers=(
-            config_from_static.user_info_endpoint_headers
-            if config_from_core.user_info_endpoint_headers is None
-            else config_from_core.user_info_endpoint_headers
-        ),
-        user_info_endpoint_query_params=(
-            config_from_static.user_info_endpoint_query_params
-            if config_from_core.user_info_endpoint_query_params is None
-            else config_from_core.user_info_endpoint_query_params
-        ),
-        jwks_uri=(
-            config_from_static.jwks_uri
-            if config_from_core.jwks_uri is None
-            else config_from_core.jwks_uri
-        ),
-        oidc_discovery_endpoint=(
-            config_from_static.oidc_discovery_endpoint
-            if config_from_core.oidc_discovery_endpoint is None
-            else config_from_core.oidc_discovery_endpoint
-        ),
-        require_email=config_from_static.require_email,
-        user_info_map=config_from_static.user_info_map,
-        generate_fake_email=config_from_static.generate_fake_email,
-        validate_id_token_payload=config_from_static.validate_id_token_payload,
-        validate_access_token=config_from_static.validate_access_token,
-    )
-
-    if result.user_info_map is None:
-        result.user_info_map = UserInfoMap(UserFields(), UserFields())
-
-    if result.user_info_map.from_user_info_api is None:
-        result.user_info_map.from_user_info_api = UserFields()
-    if result.user_info_map.from_id_token_payload is None:
-        result.user_info_map.from_id_token_payload = UserFields()
-
-    if config_from_core.user_info_map is not None:
-        if config_from_core.user_info_map.from_user_info_api is None:
-            config_from_core.user_info_map.from_user_info_api = UserFields()
-        if config_from_core.user_info_map.from_id_token_payload is None:
-            config_from_core.user_info_map.from_id_token_payload = UserFields()
-
-        if config_from_core.user_info_map.from_id_token_payload.user_id is not None:
-            result.user_info_map.from_id_token_payload.user_id = (
-                config_from_core.user_info_map.from_id_token_payload.user_id
-            )
-        if config_from_core.user_info_map.from_id_token_payload.email is not None:
-            result.user_info_map.from_id_token_payload.email = (
-                config_from_core.user_info_map.from_id_token_payload.email
-            )
-        if (
-            config_from_core.user_info_map.from_id_token_payload.email_verified
-            is not None
-        ):
-            result.user_info_map.from_id_token_payload.email_verified = (
-                config_from_core.user_info_map.from_id_token_payload.email_verified
-            )
-
-        if config_from_core.user_info_map.from_user_info_api.user_id is not None:
-            result.user_info_map.from_user_info_api.user_id = (
-                config_from_core.user_info_map.from_user_info_api.user_id
-            )
-        if config_from_core.user_info_map.from_user_info_api.email is not None:
-            result.user_info_map.from_user_info_api.email = (
-                config_from_core.user_info_map.from_user_info_api.email
-            )
-        if config_from_core.user_info_map.from_user_info_api.email_verified is not None:
-            result.user_info_map.from_user_info_api.email_verified = (
-                config_from_core.user_info_map.from_user_info_api.email_verified
-            )
-
-    merged_clients = (config_from_static.clients or [])[:]  # Make a copy
-    core_config_clients = config_from_core.clients or []
-
-    for core_client in core_config_clients:
-        found = False
-        for idx, static_client in enumerate(merged_clients):
-            if static_client.client_type == core_client.client_type:
-                merged_clients[idx] = core_client
-                found = True
-                break
-
-        if not found:
-            merged_clients.append(core_client)
-
-    result.clients = merged_clients
-
-    return result
-
-
-def merge_providers_from_core_and_static(
-    provider_configs_from_core: List[ProviderConfig],
-    provider_inputs_from_static: List[ProviderInput],
-    include_all_providers: bool,
-) -> List[ProviderInput]:
-    merged_providers: List[ProviderInput] = []
-
-    if len(provider_configs_from_core) == 0:
-        for config in filter(
-            lambda provider: include_all_providers
-            or provider.include_in_non_public_tenants_by_default,
-            provider_inputs_from_static,
-        ):
-            merged_providers.append(config)
-    else:
-        for provider_config_from_core in provider_configs_from_core:
-            merged_provider_input = ProviderInput(provider_config_from_core)
-
-            for provider_input_from_static in provider_inputs_from_static:
-                if (
-                    provider_input_from_static.config.third_party_id
-                    == provider_config_from_core.third_party_id
-                ):
-                    merged_provider_input.config = merge_config(
-                        provider_input_from_static.config, provider_config_from_core
-                    )
-                    merged_provider_input.override = provider_input_from_static.override
-                    break
-
-            merged_providers.append(merged_provider_input)
-
-    return merged_providers
-
-
-def create_provider(provider_input: ProviderInput) -> Provider:
-    if provider_input.config.third_party_id.startswith("active-directory"):
-        return ActiveDirectory(provider_input)
-    if provider_input.config.third_party_id.startswith("apple"):
-        return Apple(provider_input)
-    if provider_input.config.third_party_id.startswith("bitbucket"):
-        return Bitbucket(provider_input)
-    if provider_input.config.third_party_id.startswith("discord"):
-        return Discord(provider_input)
-    if provider_input.config.third_party_id.startswith("facebook"):
-        return Facebook(provider_input)
-    if provider_input.config.third_party_id.startswith("github"):
-        return Github(provider_input)
-    if provider_input.config.third_party_id.startswith("gitlab"):
-        return Gitlab(provider_input)
-    if provider_input.config.third_party_id.startswith("google-workspaces"):
-        return GoogleWorkspaces(provider_input)
-    if provider_input.config.third_party_id.startswith("google"):
-        return Google(provider_input)
-    if provider_input.config.third_party_id.startswith("okta"):
-        return Okta(provider_input)
-    if provider_input.config.third_party_id.startswith("linkedin"):
-        return Linkedin(provider_input)
-    if provider_input.config.third_party_id.startswith("twitter"):
-        return Twitter(provider_input)
-    if provider_input.config.third_party_id.startswith("boxy-saml"):
-        return BoxySAML(provider_input)
-
-    return NewProvider(provider_input)
-
-
-OIDC_INFO_MAP: Dict[str, Any] = {}
-
-
-async def get_oidc_discovery_info(issuer: str):
-    if issuer in OIDC_INFO_MAP:
-        return OIDC_INFO_MAP[issuer]
-
-    ndomain = NormalisedURLDomain(issuer)
-    npath = NormalisedURLPath(issuer)
-
-    oidc_info = await do_get_request(
-        ndomain.get_as_string_dangerous() + npath.get_as_string_dangerous()
-    )
-    OIDC_INFO_MAP[issuer] = oidc_info
-
-    return oidc_info
-
-
-async def discover_oidc_endpoints(
-    config: ProviderConfigForClient,
-) -> ProviderConfigForClient:
-    if config.oidc_discovery_endpoint is None:
-        return config
-
-    oidc_info = await get_oidc_discovery_info(config.oidc_discovery_endpoint)
-    if (
-        oidc_info.get("authorization_endpoint") is not None
-        and config.authorization_endpoint is None
-    ):
-        config.authorization_endpoint = oidc_info["authorization_endpoint"]
-
-    if oidc_info.get("token_endpoint") is not None and config.token_endpoint is None:
-        config.token_endpoint = oidc_info["token_endpoint"]
-
-    if (
-        oidc_info.get("userinfo_endpoint") is not None
-        and config.user_info_endpoint is None
-    ):
-        config.user_info_endpoint = oidc_info["userinfo_endpoint"]
-
-    if oidc_info.get("jwks_uri") is not None and config.jwks_uri is None:
-        config.jwks_uri = oidc_info["jwks_uri"]
-
-    return config
-
-
-async def fetch_and_set_config(
-    provider_instance: Provider,
-    client_type: Optional[str],
-    user_context: Dict[str, Any],
-):
-    config = await provider_instance.get_config_for_client_type(
-        client_type, user_context
-    )
-    config = await discover_oidc_endpoints(config)
-    provider_instance.config = config
-
-
-async def find_and_create_provider_instance(
-    providers: List[ProviderInput],
-    third_party_id: str,
-    client_type: Optional[str],
-    user_context: Dict[str, Any],
-) -> Optional[Provider]:
-    for provider_input in providers:
-        if provider_input.config.third_party_id == third_party_id:
-            provider_instance = create_provider(provider_input)
-            await fetch_and_set_config(provider_instance, client_type, user_context)
-            return provider_instance
-
-    return None
-
-
-
-
-
-
-
-

Functions

-
-
-def create_provider(provider_input: ProviderInput) ‑> Provider -
-
-
-
- -Expand source code - -
def create_provider(provider_input: ProviderInput) -> Provider:
-    if provider_input.config.third_party_id.startswith("active-directory"):
-        return ActiveDirectory(provider_input)
-    if provider_input.config.third_party_id.startswith("apple"):
-        return Apple(provider_input)
-    if provider_input.config.third_party_id.startswith("bitbucket"):
-        return Bitbucket(provider_input)
-    if provider_input.config.third_party_id.startswith("discord"):
-        return Discord(provider_input)
-    if provider_input.config.third_party_id.startswith("facebook"):
-        return Facebook(provider_input)
-    if provider_input.config.third_party_id.startswith("github"):
-        return Github(provider_input)
-    if provider_input.config.third_party_id.startswith("gitlab"):
-        return Gitlab(provider_input)
-    if provider_input.config.third_party_id.startswith("google-workspaces"):
-        return GoogleWorkspaces(provider_input)
-    if provider_input.config.third_party_id.startswith("google"):
-        return Google(provider_input)
-    if provider_input.config.third_party_id.startswith("okta"):
-        return Okta(provider_input)
-    if provider_input.config.third_party_id.startswith("linkedin"):
-        return Linkedin(provider_input)
-    if provider_input.config.third_party_id.startswith("twitter"):
-        return Twitter(provider_input)
-    if provider_input.config.third_party_id.startswith("boxy-saml"):
-        return BoxySAML(provider_input)
-
-    return NewProvider(provider_input)
-
-
-
-async def discover_oidc_endpoints(config: ProviderConfigForClient) ‑> ProviderConfigForClient -
-
-
-
- -Expand source code - -
async def discover_oidc_endpoints(
-    config: ProviderConfigForClient,
-) -> ProviderConfigForClient:
-    if config.oidc_discovery_endpoint is None:
-        return config
-
-    oidc_info = await get_oidc_discovery_info(config.oidc_discovery_endpoint)
-    if (
-        oidc_info.get("authorization_endpoint") is not None
-        and config.authorization_endpoint is None
-    ):
-        config.authorization_endpoint = oidc_info["authorization_endpoint"]
-
-    if oidc_info.get("token_endpoint") is not None and config.token_endpoint is None:
-        config.token_endpoint = oidc_info["token_endpoint"]
-
-    if (
-        oidc_info.get("userinfo_endpoint") is not None
-        and config.user_info_endpoint is None
-    ):
-        config.user_info_endpoint = oidc_info["userinfo_endpoint"]
-
-    if oidc_info.get("jwks_uri") is not None and config.jwks_uri is None:
-        config.jwks_uri = oidc_info["jwks_uri"]
-
-    return config
-
-
-
-async def fetch_and_set_config(provider_instance: Provider, client_type: Optional[str], user_context: Dict[str, Any]) -
-
-
-
- -Expand source code - -
async def fetch_and_set_config(
-    provider_instance: Provider,
-    client_type: Optional[str],
-    user_context: Dict[str, Any],
-):
-    config = await provider_instance.get_config_for_client_type(
-        client_type, user_context
-    )
-    config = await discover_oidc_endpoints(config)
-    provider_instance.config = config
-
-
-
-async def find_and_create_provider_instance(providers: List[ProviderInput], third_party_id: str, client_type: Optional[str], user_context: Dict[str, Any]) ‑> Optional[Provider] -
-
-
-
- -Expand source code - -
async def find_and_create_provider_instance(
-    providers: List[ProviderInput],
-    third_party_id: str,
-    client_type: Optional[str],
-    user_context: Dict[str, Any],
-) -> Optional[Provider]:
-    for provider_input in providers:
-        if provider_input.config.third_party_id == third_party_id:
-            provider_instance = create_provider(provider_input)
-            await fetch_and_set_config(provider_instance, client_type, user_context)
-            return provider_instance
-
-    return None
-
-
-
-async def get_oidc_discovery_info(issuer: str) -
-
-
-
- -Expand source code - -
async def get_oidc_discovery_info(issuer: str):
-    if issuer in OIDC_INFO_MAP:
-        return OIDC_INFO_MAP[issuer]
-
-    ndomain = NormalisedURLDomain(issuer)
-    npath = NormalisedURLPath(issuer)
-
-    oidc_info = await do_get_request(
-        ndomain.get_as_string_dangerous() + npath.get_as_string_dangerous()
-    )
-    OIDC_INFO_MAP[issuer] = oidc_info
-
-    return oidc_info
-
-
-
-def merge_config(config_from_static: ProviderConfig, config_from_core: ProviderConfig) ‑> ProviderConfig -
-
-
-
- -Expand source code - -
def merge_config(
-    config_from_static: ProviderConfig, config_from_core: ProviderConfig
-) -> ProviderConfig:
-    result = ProviderConfig(
-        third_party_id=config_from_static.third_party_id,
-        name=(
-            config_from_static.name
-            if config_from_core.name is None
-            else config_from_core.name
-        ),
-        authorization_endpoint=(
-            config_from_static.authorization_endpoint
-            if config_from_core.authorization_endpoint is None
-            else config_from_core.authorization_endpoint
-        ),
-        authorization_endpoint_query_params=(
-            config_from_static.authorization_endpoint_query_params
-            if config_from_core.authorization_endpoint_query_params is None
-            else config_from_core.authorization_endpoint_query_params
-        ),
-        token_endpoint=(
-            config_from_static.token_endpoint
-            if config_from_core.token_endpoint is None
-            else config_from_core.token_endpoint
-        ),
-        token_endpoint_body_params=(
-            config_from_static.token_endpoint_body_params
-            if config_from_core.token_endpoint_body_params is None
-            else config_from_core.token_endpoint_body_params
-        ),
-        user_info_endpoint=(
-            config_from_static.user_info_endpoint
-            if config_from_core.user_info_endpoint is None
-            else config_from_core.user_info_endpoint
-        ),
-        user_info_endpoint_headers=(
-            config_from_static.user_info_endpoint_headers
-            if config_from_core.user_info_endpoint_headers is None
-            else config_from_core.user_info_endpoint_headers
-        ),
-        user_info_endpoint_query_params=(
-            config_from_static.user_info_endpoint_query_params
-            if config_from_core.user_info_endpoint_query_params is None
-            else config_from_core.user_info_endpoint_query_params
-        ),
-        jwks_uri=(
-            config_from_static.jwks_uri
-            if config_from_core.jwks_uri is None
-            else config_from_core.jwks_uri
-        ),
-        oidc_discovery_endpoint=(
-            config_from_static.oidc_discovery_endpoint
-            if config_from_core.oidc_discovery_endpoint is None
-            else config_from_core.oidc_discovery_endpoint
-        ),
-        require_email=config_from_static.require_email,
-        user_info_map=config_from_static.user_info_map,
-        generate_fake_email=config_from_static.generate_fake_email,
-        validate_id_token_payload=config_from_static.validate_id_token_payload,
-        validate_access_token=config_from_static.validate_access_token,
-    )
-
-    if result.user_info_map is None:
-        result.user_info_map = UserInfoMap(UserFields(), UserFields())
-
-    if result.user_info_map.from_user_info_api is None:
-        result.user_info_map.from_user_info_api = UserFields()
-    if result.user_info_map.from_id_token_payload is None:
-        result.user_info_map.from_id_token_payload = UserFields()
-
-    if config_from_core.user_info_map is not None:
-        if config_from_core.user_info_map.from_user_info_api is None:
-            config_from_core.user_info_map.from_user_info_api = UserFields()
-        if config_from_core.user_info_map.from_id_token_payload is None:
-            config_from_core.user_info_map.from_id_token_payload = UserFields()
-
-        if config_from_core.user_info_map.from_id_token_payload.user_id is not None:
-            result.user_info_map.from_id_token_payload.user_id = (
-                config_from_core.user_info_map.from_id_token_payload.user_id
-            )
-        if config_from_core.user_info_map.from_id_token_payload.email is not None:
-            result.user_info_map.from_id_token_payload.email = (
-                config_from_core.user_info_map.from_id_token_payload.email
-            )
-        if (
-            config_from_core.user_info_map.from_id_token_payload.email_verified
-            is not None
-        ):
-            result.user_info_map.from_id_token_payload.email_verified = (
-                config_from_core.user_info_map.from_id_token_payload.email_verified
-            )
-
-        if config_from_core.user_info_map.from_user_info_api.user_id is not None:
-            result.user_info_map.from_user_info_api.user_id = (
-                config_from_core.user_info_map.from_user_info_api.user_id
-            )
-        if config_from_core.user_info_map.from_user_info_api.email is not None:
-            result.user_info_map.from_user_info_api.email = (
-                config_from_core.user_info_map.from_user_info_api.email
-            )
-        if config_from_core.user_info_map.from_user_info_api.email_verified is not None:
-            result.user_info_map.from_user_info_api.email_verified = (
-                config_from_core.user_info_map.from_user_info_api.email_verified
-            )
-
-    merged_clients = (config_from_static.clients or [])[:]  # Make a copy
-    core_config_clients = config_from_core.clients or []
-
-    for core_client in core_config_clients:
-        found = False
-        for idx, static_client in enumerate(merged_clients):
-            if static_client.client_type == core_client.client_type:
-                merged_clients[idx] = core_client
-                found = True
-                break
-
-        if not found:
-            merged_clients.append(core_client)
-
-    result.clients = merged_clients
-
-    return result
-
-
-
-def merge_providers_from_core_and_static(provider_configs_from_core: List[ProviderConfig], provider_inputs_from_static: List[ProviderInput], include_all_providers: bool) ‑> List[ProviderInput] -
-
-
-
- -Expand source code - -
def merge_providers_from_core_and_static(
-    provider_configs_from_core: List[ProviderConfig],
-    provider_inputs_from_static: List[ProviderInput],
-    include_all_providers: bool,
-) -> List[ProviderInput]:
-    merged_providers: List[ProviderInput] = []
-
-    if len(provider_configs_from_core) == 0:
-        for config in filter(
-            lambda provider: include_all_providers
-            or provider.include_in_non_public_tenants_by_default,
-            provider_inputs_from_static,
-        ):
-            merged_providers.append(config)
-    else:
-        for provider_config_from_core in provider_configs_from_core:
-            merged_provider_input = ProviderInput(provider_config_from_core)
-
-            for provider_input_from_static in provider_inputs_from_static:
-                if (
-                    provider_input_from_static.config.third_party_id
-                    == provider_config_from_core.third_party_id
-                ):
-                    merged_provider_input.config = merge_config(
-                        provider_input_from_static.config, provider_config_from_core
-                    )
-                    merged_provider_input.override = provider_input_from_static.override
-                    break
-
-            merged_providers.append(merged_provider_input)
-
-    return merged_providers
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/thirdparty/providers/custom.html b/html/supertokens_python/recipe/thirdparty/providers/custom.html deleted file mode 100644 index a533be40f..000000000 --- a/html/supertokens_python/recipe/thirdparty/providers/custom.html +++ /dev/null @@ -1,1253 +0,0 @@ - - - - - - -supertokens_python.recipe.thirdparty.providers.custom API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.thirdparty.providers.custom

-
-
-
- -Expand source code - -
from typing import Any, Callable, Dict, List, Optional, Union
-from urllib.parse import parse_qs, urlencode, urlparse
-
-from httpx import AsyncClient
-
-from jwt import decode  # type: ignore
-from jwt.algorithms import RSAAlgorithm
-import pkce
-
-from supertokens_python.recipe.thirdparty.exceptions import ClientTypeNotFoundError
-from supertokens_python.recipe.thirdparty.providers.utils import (
-    DEV_OAUTH_AUTHORIZATION_URL,
-    DEV_OAUTH_REDIRECT_URL,
-    do_get_request,
-    do_post_request,
-    get_actual_client_id_from_development_client_id,
-    is_using_oauth_development_client_id,
-    DEV_KEY_IDENTIFIER,
-    DEV_OAUTH_CLIENT_IDS,
-)
-
-from ..types import RawUserInfoFromProvider, UserInfo, UserInfoEmail
-from ..provider import (
-    AuthorisationRedirect,
-    Provider,
-    ProviderClientConfig,
-    ProviderConfig,
-    ProviderConfigForClient,
-    ProviderInput,
-    RedirectUriInfo,
-    UserFields,
-    UserInfoMap,
-)
-
-
-def get_provider_config_for_client(
-    config: ProviderConfig, client_config: ProviderClientConfig
-) -> ProviderConfigForClient:
-    return ProviderConfigForClient(
-        # ProviderClientConfig
-        client_id=client_config.client_id,
-        client_secret=client_config.client_secret,
-        client_type=client_config.client_type,
-        scope=client_config.scope,
-        force_pkce=client_config.force_pkce,
-        additional_config=client_config.additional_config,
-        # CommonProviderConfig
-        third_party_id=config.third_party_id,
-        name=config.name,
-        authorization_endpoint=config.authorization_endpoint,
-        authorization_endpoint_query_params=config.authorization_endpoint_query_params,
-        token_endpoint=config.token_endpoint,
-        token_endpoint_body_params=config.token_endpoint_body_params,
-        user_info_endpoint=config.user_info_endpoint,
-        user_info_endpoint_query_params=config.user_info_endpoint_query_params,
-        user_info_endpoint_headers=config.user_info_endpoint_headers,
-        jwks_uri=config.jwks_uri,
-        oidc_discovery_endpoint=config.oidc_discovery_endpoint,
-        user_info_map=config.user_info_map,
-        require_email=config.require_email,
-        validate_id_token_payload=config.validate_id_token_payload,
-        generate_fake_email=config.generate_fake_email,
-        validate_access_token=config.validate_access_token,
-    )
-
-
-def access_field(obj: Any, key: str) -> Any:
-    key_parts = key.split(".")
-    for part in key_parts:
-        if isinstance(obj, dict):
-            obj = obj.get(part)  # type: ignore
-        else:
-            return None
-
-    return obj
-
-
-def get_supertokens_user_info_result_from_raw_user_info(
-    config: ProviderConfigForClient,
-    raw_user_info_from_provider: RawUserInfoFromProvider,
-) -> UserInfo:
-    third_party_user_id = ""
-
-    if config.user_info_map is None:
-        raise Exception("user info map is missing")
-
-    if config.user_info_map.from_user_info_api is None:
-        config.user_info_map.from_user_info_api = UserFields()
-    if config.user_info_map.from_id_token_payload is None:
-        config.user_info_map.from_id_token_payload = UserFields()
-
-    if config.user_info_map.from_user_info_api.user_id is not None:
-        user_id = access_field(
-            raw_user_info_from_provider.from_user_info_api,
-            config.user_info_map.from_user_info_api.user_id,
-        )
-        if user_id is not None:
-            third_party_user_id = str(user_id)
-
-    if config.user_info_map.from_id_token_payload.user_id is not None:
-        user_id = access_field(
-            raw_user_info_from_provider.from_id_token_payload,
-            config.user_info_map.from_id_token_payload.user_id,
-        )
-        if user_id is not None:
-            third_party_user_id = str(user_id)
-
-    if third_party_user_id == "":
-        raise Exception("third party user id is missing")
-
-    result = UserInfo(
-        third_party_user_id=third_party_user_id,
-    )
-
-    email = ""
-
-    if config.user_info_map.from_user_info_api.email is not None:
-        email_val = access_field(
-            raw_user_info_from_provider.from_user_info_api,
-            config.user_info_map.from_user_info_api.email,
-        )
-        if email_val is not None:
-            email = email_val
-
-    if config.user_info_map.from_id_token_payload.email is not None:
-        email_val = access_field(
-            raw_user_info_from_provider.from_id_token_payload,
-            config.user_info_map.from_id_token_payload.email,
-        )
-        if email_val is not None:
-            email = email_val
-
-    if email != "":
-        result.email = UserInfoEmail(email, False)
-
-        if config.user_info_map.from_user_info_api.email_verified is not None:
-            email_verified = access_field(
-                raw_user_info_from_provider.from_user_info_api,
-                config.user_info_map.from_user_info_api.email_verified,
-            )
-            if email_verified is not None:
-                result.email.is_verified = str(email_verified).lower() == "true"
-
-        if config.user_info_map.from_id_token_payload.email_verified is not None:
-            email_verified = access_field(
-                raw_user_info_from_provider.from_id_token_payload,
-                config.user_info_map.from_id_token_payload.email_verified,
-            )
-            if email_verified is not None:
-                result.email.is_verified = str(email_verified).lower() == "true"
-
-    return result
-
-
-async def verify_id_token_from_jwks_endpoint_and_get_payload(
-    id_token: str, jwks_uri: str, audience: str
-):
-    public_keys: List[RSAAlgorithm] = []
-    async with AsyncClient(timeout=30.0) as client:
-        response = await client.get(jwks_uri)  # type:ignore
-        key_payload = response.json()
-        for key in key_payload["keys"]:
-            public_keys.append(RSAAlgorithm.from_jwk(key))  # type: ignore
-
-    err = Exception("id token verification failed")
-    for key in public_keys:
-        try:
-            return decode(jwt=id_token, key=key, audience=[audience], algorithms=["RS256"])  # type: ignore
-        except Exception as e:
-            err = e
-    raise err
-
-
-def merge_into_dict(src: Dict[str, Any], dest: Dict[str, Any]) -> Dict[str, Any]:
-    res = dest.copy()
-    for k, v in src.items():
-        if v is None:
-            if k in res:
-                del res[k]
-        else:
-            res[k] = v
-
-    return res
-
-
-def is_using_development_client_id(client_id: str) -> bool:
-    return client_id.startswith(DEV_KEY_IDENTIFIER) or client_id in DEV_OAUTH_CLIENT_IDS
-
-
-class GenericProvider(Provider):
-    def __init__(self, provider_config: ProviderConfig):
-        self.input_config = input_config = self._normalize_input(provider_config)
-
-        provider_config_for_client = ProviderConfigForClient(
-            # Will automatically get replaced with correct value
-            # in get_provider_config_for_client
-            # when fetch_and_set_config function runs
-            client_id="temp",
-            client_secret=None,
-            client_type=None,
-            scope=None,
-            force_pkce=False,
-            additional_config=None,
-            name=input_config.name,
-            authorization_endpoint=input_config.authorization_endpoint,
-            authorization_endpoint_query_params=input_config.authorization_endpoint_query_params,
-            token_endpoint=input_config.token_endpoint,
-            token_endpoint_body_params=input_config.token_endpoint_body_params,
-            user_info_endpoint=input_config.user_info_endpoint,
-            user_info_endpoint_query_params=input_config.user_info_endpoint_query_params,
-            user_info_endpoint_headers=input_config.user_info_endpoint_headers,
-            jwks_uri=input_config.jwks_uri,
-            oidc_discovery_endpoint=input_config.oidc_discovery_endpoint,
-            user_info_map=input_config.user_info_map,
-            require_email=input_config.require_email,
-            validate_id_token_payload=input_config.validate_id_token_payload,
-            generate_fake_email=input_config.generate_fake_email,
-        )
-        super().__init__(input_config.third_party_id, provider_config_for_client)
-
-    def _normalize_input(  # pylint: disable=no-self-use
-        self, input_config: ProviderConfig
-    ) -> ProviderConfig:
-        if input_config.user_info_map is None:
-            input_config.user_info_map = UserInfoMap(
-                from_id_token_payload=UserFields(),
-                from_user_info_api=UserFields(),
-            )
-        if input_config.user_info_map.from_user_info_api is None:
-            input_config.user_info_map.from_user_info_api = UserFields()
-        if input_config.user_info_map.from_id_token_payload is None:
-            input_config.user_info_map.from_id_token_payload = UserFields()
-
-        # These are safe defaults common to most providers. Each provider
-        # implementations override these as necessary
-        if input_config.user_info_map.from_id_token_payload.user_id is None:
-            input_config.user_info_map.from_id_token_payload.user_id = "sub"
-
-        if input_config.user_info_map.from_id_token_payload.email is None:
-            input_config.user_info_map.from_id_token_payload.email = "email"
-
-        if input_config.user_info_map.from_id_token_payload.email_verified is None:
-            input_config.user_info_map.from_id_token_payload.email_verified = (
-                "email_verified"
-            )
-
-        if input_config.user_info_map.from_user_info_api.user_id is None:
-            input_config.user_info_map.from_user_info_api.user_id = "sub"
-
-        if input_config.user_info_map.from_user_info_api.email is None:
-            input_config.user_info_map.from_user_info_api.email = "email"
-
-        if input_config.user_info_map.from_user_info_api.email_verified is None:
-            input_config.user_info_map.from_user_info_api.email_verified = (
-                "email_verified"
-            )
-
-        if input_config.generate_fake_email is None:
-
-            async def default_generate_fake_email(
-                _tenant_id: str, third_party_user_id: str, _: Dict[str, Any]
-            ) -> str:
-                return f"{third_party_user_id}.{input_config.third_party_id}@stfakeemail.supertokens.com"
-
-            input_config.generate_fake_email = default_generate_fake_email
-
-        return input_config
-
-    async def get_config_for_client_type(
-        self, client_type: Optional[str], user_context: Dict[str, Any]
-    ) -> ProviderConfigForClient:
-        if client_type is None:
-            if self.input_config.clients is None or len(self.input_config.clients) != 1:
-                raise ClientTypeNotFoundError(
-                    "please provide exactly one client config or pass clientType or tenantId"
-                )
-
-            return get_provider_config_for_client(
-                self.input_config, self.input_config.clients[0]
-            )
-
-        if self.input_config.clients is not None:
-            for client in self.input_config.clients:
-                if client.client_type == client_type:
-                    return get_provider_config_for_client(self.input_config, client)
-
-        raise ClientTypeNotFoundError(
-            f"Could not find client config for clientType: {client_type}"
-        )
-
-    async def get_authorisation_redirect_url(
-        self,
-        redirect_uri_on_provider_dashboard: str,
-        user_context: Dict[str, Any],
-    ) -> AuthorisationRedirect:
-        query_params: Dict[str, str] = {
-            "client_id": self.config.client_id,
-            "redirect_uri": redirect_uri_on_provider_dashboard,
-            "response_type": "code",
-        }
-
-        if self.config.scope is not None:
-            query_params["scope"] = " ".join(self.config.scope)
-
-        pkce_code_verifier: Union[str, None] = None
-
-        if self.config.client_secret is None or self.config.force_pkce:
-            code_verifier, code_challenge = pkce.generate_pkce_pair(64)
-            query_params["code_challenge"] = code_challenge
-            query_params["code_challenge_method"] = "S256"
-            pkce_code_verifier = code_verifier
-
-        if self.config.authorization_endpoint_query_params is not None:
-            for k, v in self.config.authorization_endpoint_query_params.items():
-                if v is None:
-                    del query_params[k]
-                else:
-                    query_params[k] = v
-
-        if self.config.authorization_endpoint is None:
-            raise Exception(
-                "ThirdParty provider's authorizationEndpoint is not configured."
-            )
-
-        url: str = self.config.authorization_endpoint
-
-        # Transformation needed for dev keys BEGIN
-        if is_using_oauth_development_client_id(self.config.client_id):
-            query_params["client_id"] = get_actual_client_id_from_development_client_id(
-                self.config.client_id
-            )
-            query_params["actual_redirect_uri"] = url
-            url = DEV_OAUTH_AUTHORIZATION_URL
-        # Transformation needed for dev keys END
-
-        url_obj = urlparse(url)
-        qparams = parse_qs(url_obj.query)
-        for k, v in query_params.items():
-            qparams[k] = [v]
-
-        url = url_obj._replace(query=urlencode(qparams, doseq=True)).geturl()
-
-        return AuthorisationRedirect(url, pkce_code_verifier)
-
-    async def exchange_auth_code_for_oauth_tokens(
-        self, redirect_uri_info: RedirectUriInfo, user_context: Dict[str, Any]
-    ) -> Dict[str, Any]:
-        if self.config.token_endpoint is None:
-            raise Exception("ThirdParty provider's tokenEndpoint is not configured.")
-
-        token_api_url = self.config.token_endpoint
-        access_token_params: Dict[str, str] = {
-            "client_id": self.config.client_id,
-            "redirect_uri": redirect_uri_info.redirect_uri_on_provider_dashboard,
-            "code": redirect_uri_info.redirect_uri_query_params["code"],
-            "grant_type": "authorization_code",
-        }
-        if self.config.client_secret is not None:
-            access_token_params["client_secret"] = self.config.client_secret
-
-        if redirect_uri_info.pkce_code_verifier is not None:
-            access_token_params["code_verifier"] = redirect_uri_info.pkce_code_verifier
-
-        if self.config.token_endpoint_body_params is not None:
-            access_token_params = merge_into_dict(
-                self.config.token_endpoint_body_params, access_token_params
-            )
-
-        # Transformation needed for dev keys BEGIN
-        if is_using_oauth_development_client_id(self.config.client_id):
-            access_token_params[
-                "client_id"
-            ] = get_actual_client_id_from_development_client_id(self.config.client_id)
-            access_token_params["redirect_uri"] = DEV_OAUTH_REDIRECT_URL
-        # Transformation needed for dev keys END
-
-        _, body = await do_post_request(token_api_url, access_token_params)
-        return body
-
-    async def get_user_info(
-        self, oauth_tokens: Dict[str, Any], user_context: Dict[str, Any]
-    ) -> UserInfo:
-        access_token: Union[str, None] = oauth_tokens.get("access_token")
-        id_token: Union[str, None] = oauth_tokens.get("id_token")
-
-        raw_user_info_from_provider = RawUserInfoFromProvider({}, {})
-        if id_token is not None and self.config.jwks_uri is not None:
-            raw_user_info_from_provider.from_id_token_payload = (
-                await verify_id_token_from_jwks_endpoint_and_get_payload(
-                    id_token,
-                    self.config.jwks_uri,
-                    get_actual_client_id_from_development_client_id(
-                        self.config.client_id
-                    ),
-                )
-            )
-
-            if self.config.validate_id_token_payload is not None:
-                await self.config.validate_id_token_payload(
-                    raw_user_info_from_provider.from_id_token_payload,
-                    self.config,
-                    user_context,
-                )
-
-        if self.config.validate_access_token is not None and access_token is not None:
-            await self.config.validate_access_token(
-                access_token, self.config, user_context
-            )
-
-        if access_token is not None and self.config.user_info_endpoint is not None:
-            headers: Dict[str, str] = {"Authorization": f"Bearer {access_token}"}
-            query_params: Dict[str, str] = {}
-
-            if self.config.user_info_endpoint_headers is not None:
-                headers = merge_into_dict(
-                    self.config.user_info_endpoint_headers, headers
-                )
-
-            if self.config.user_info_endpoint_query_params is not None:
-                query_params = merge_into_dict(
-                    self.config.user_info_endpoint_query_params, query_params
-                )
-
-            raw_user_info_from_provider.from_user_info_api = await do_get_request(
-                self.config.user_info_endpoint, query_params, headers
-            )
-
-        user_info_result = get_supertokens_user_info_result_from_raw_user_info(
-            self.config, raw_user_info_from_provider
-        )
-
-        return UserInfo(
-            third_party_user_id=user_info_result.third_party_user_id,
-            email=user_info_result.email,
-            raw_user_info_from_provider=raw_user_info_from_provider,
-        )
-
-
-def NewProvider(
-    input: ProviderInput,  # pylint: disable=redefined-builtin
-    base_class: Callable[[ProviderConfig], Provider] = GenericProvider,
-) -> Provider:
-    provider_instance = base_class(input.config)
-    if input.override is not None:
-        provider_instance = input.override(provider_instance)
-
-    return provider_instance
-
-
-
-
-
-
-
-

Functions

-
-
-def NewProvider(input: ProviderInput, base_class: Callable[[ProviderConfig], Provider] = supertokens_python.recipe.thirdparty.providers.custom.GenericProvider) ‑> Provider -
-
-
-
- -Expand source code - -
def NewProvider(
-    input: ProviderInput,  # pylint: disable=redefined-builtin
-    base_class: Callable[[ProviderConfig], Provider] = GenericProvider,
-) -> Provider:
-    provider_instance = base_class(input.config)
-    if input.override is not None:
-        provider_instance = input.override(provider_instance)
-
-    return provider_instance
-
-
-
-def access_field(obj: Any, key: str) ‑> Any -
-
-
-
- -Expand source code - -
def access_field(obj: Any, key: str) -> Any:
-    key_parts = key.split(".")
-    for part in key_parts:
-        if isinstance(obj, dict):
-            obj = obj.get(part)  # type: ignore
-        else:
-            return None
-
-    return obj
-
-
-
-def get_provider_config_for_client(config: ProviderConfig, client_config: ProviderClientConfig) ‑> ProviderConfigForClient -
-
-
-
- -Expand source code - -
def get_provider_config_for_client(
-    config: ProviderConfig, client_config: ProviderClientConfig
-) -> ProviderConfigForClient:
-    return ProviderConfigForClient(
-        # ProviderClientConfig
-        client_id=client_config.client_id,
-        client_secret=client_config.client_secret,
-        client_type=client_config.client_type,
-        scope=client_config.scope,
-        force_pkce=client_config.force_pkce,
-        additional_config=client_config.additional_config,
-        # CommonProviderConfig
-        third_party_id=config.third_party_id,
-        name=config.name,
-        authorization_endpoint=config.authorization_endpoint,
-        authorization_endpoint_query_params=config.authorization_endpoint_query_params,
-        token_endpoint=config.token_endpoint,
-        token_endpoint_body_params=config.token_endpoint_body_params,
-        user_info_endpoint=config.user_info_endpoint,
-        user_info_endpoint_query_params=config.user_info_endpoint_query_params,
-        user_info_endpoint_headers=config.user_info_endpoint_headers,
-        jwks_uri=config.jwks_uri,
-        oidc_discovery_endpoint=config.oidc_discovery_endpoint,
-        user_info_map=config.user_info_map,
-        require_email=config.require_email,
-        validate_id_token_payload=config.validate_id_token_payload,
-        generate_fake_email=config.generate_fake_email,
-        validate_access_token=config.validate_access_token,
-    )
-
-
-
-def get_supertokens_user_info_result_from_raw_user_info(config: ProviderConfigForClient, raw_user_info_from_provider: RawUserInfoFromProvider) ‑> UserInfo -
-
-
-
- -Expand source code - -
def get_supertokens_user_info_result_from_raw_user_info(
-    config: ProviderConfigForClient,
-    raw_user_info_from_provider: RawUserInfoFromProvider,
-) -> UserInfo:
-    third_party_user_id = ""
-
-    if config.user_info_map is None:
-        raise Exception("user info map is missing")
-
-    if config.user_info_map.from_user_info_api is None:
-        config.user_info_map.from_user_info_api = UserFields()
-    if config.user_info_map.from_id_token_payload is None:
-        config.user_info_map.from_id_token_payload = UserFields()
-
-    if config.user_info_map.from_user_info_api.user_id is not None:
-        user_id = access_field(
-            raw_user_info_from_provider.from_user_info_api,
-            config.user_info_map.from_user_info_api.user_id,
-        )
-        if user_id is not None:
-            third_party_user_id = str(user_id)
-
-    if config.user_info_map.from_id_token_payload.user_id is not None:
-        user_id = access_field(
-            raw_user_info_from_provider.from_id_token_payload,
-            config.user_info_map.from_id_token_payload.user_id,
-        )
-        if user_id is not None:
-            third_party_user_id = str(user_id)
-
-    if third_party_user_id == "":
-        raise Exception("third party user id is missing")
-
-    result = UserInfo(
-        third_party_user_id=third_party_user_id,
-    )
-
-    email = ""
-
-    if config.user_info_map.from_user_info_api.email is not None:
-        email_val = access_field(
-            raw_user_info_from_provider.from_user_info_api,
-            config.user_info_map.from_user_info_api.email,
-        )
-        if email_val is not None:
-            email = email_val
-
-    if config.user_info_map.from_id_token_payload.email is not None:
-        email_val = access_field(
-            raw_user_info_from_provider.from_id_token_payload,
-            config.user_info_map.from_id_token_payload.email,
-        )
-        if email_val is not None:
-            email = email_val
-
-    if email != "":
-        result.email = UserInfoEmail(email, False)
-
-        if config.user_info_map.from_user_info_api.email_verified is not None:
-            email_verified = access_field(
-                raw_user_info_from_provider.from_user_info_api,
-                config.user_info_map.from_user_info_api.email_verified,
-            )
-            if email_verified is not None:
-                result.email.is_verified = str(email_verified).lower() == "true"
-
-        if config.user_info_map.from_id_token_payload.email_verified is not None:
-            email_verified = access_field(
-                raw_user_info_from_provider.from_id_token_payload,
-                config.user_info_map.from_id_token_payload.email_verified,
-            )
-            if email_verified is not None:
-                result.email.is_verified = str(email_verified).lower() == "true"
-
-    return result
-
-
-
-def is_using_development_client_id(client_id: str) ‑> bool -
-
-
-
- -Expand source code - -
def is_using_development_client_id(client_id: str) -> bool:
-    return client_id.startswith(DEV_KEY_IDENTIFIER) or client_id in DEV_OAUTH_CLIENT_IDS
-
-
-
-def merge_into_dict(src: Dict[str, Any], dest: Dict[str, Any]) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
def merge_into_dict(src: Dict[str, Any], dest: Dict[str, Any]) -> Dict[str, Any]:
-    res = dest.copy()
-    for k, v in src.items():
-        if v is None:
-            if k in res:
-                del res[k]
-        else:
-            res[k] = v
-
-    return res
-
-
-
-async def verify_id_token_from_jwks_endpoint_and_get_payload(id_token: str, jwks_uri: str, audience: str) -
-
-
-
- -Expand source code - -
async def verify_id_token_from_jwks_endpoint_and_get_payload(
-    id_token: str, jwks_uri: str, audience: str
-):
-    public_keys: List[RSAAlgorithm] = []
-    async with AsyncClient(timeout=30.0) as client:
-        response = await client.get(jwks_uri)  # type:ignore
-        key_payload = response.json()
-        for key in key_payload["keys"]:
-            public_keys.append(RSAAlgorithm.from_jwk(key))  # type: ignore
-
-    err = Exception("id token verification failed")
-    for key in public_keys:
-        try:
-            return decode(jwt=id_token, key=key, audience=[audience], algorithms=["RS256"])  # type: ignore
-        except Exception as e:
-            err = e
-    raise err
-
-
-
-
-
-

Classes

-
-
-class GenericProvider -(provider_config: ProviderConfig) -
-
-
-
- -Expand source code - -
class GenericProvider(Provider):
-    def __init__(self, provider_config: ProviderConfig):
-        self.input_config = input_config = self._normalize_input(provider_config)
-
-        provider_config_for_client = ProviderConfigForClient(
-            # Will automatically get replaced with correct value
-            # in get_provider_config_for_client
-            # when fetch_and_set_config function runs
-            client_id="temp",
-            client_secret=None,
-            client_type=None,
-            scope=None,
-            force_pkce=False,
-            additional_config=None,
-            name=input_config.name,
-            authorization_endpoint=input_config.authorization_endpoint,
-            authorization_endpoint_query_params=input_config.authorization_endpoint_query_params,
-            token_endpoint=input_config.token_endpoint,
-            token_endpoint_body_params=input_config.token_endpoint_body_params,
-            user_info_endpoint=input_config.user_info_endpoint,
-            user_info_endpoint_query_params=input_config.user_info_endpoint_query_params,
-            user_info_endpoint_headers=input_config.user_info_endpoint_headers,
-            jwks_uri=input_config.jwks_uri,
-            oidc_discovery_endpoint=input_config.oidc_discovery_endpoint,
-            user_info_map=input_config.user_info_map,
-            require_email=input_config.require_email,
-            validate_id_token_payload=input_config.validate_id_token_payload,
-            generate_fake_email=input_config.generate_fake_email,
-        )
-        super().__init__(input_config.third_party_id, provider_config_for_client)
-
-    def _normalize_input(  # pylint: disable=no-self-use
-        self, input_config: ProviderConfig
-    ) -> ProviderConfig:
-        if input_config.user_info_map is None:
-            input_config.user_info_map = UserInfoMap(
-                from_id_token_payload=UserFields(),
-                from_user_info_api=UserFields(),
-            )
-        if input_config.user_info_map.from_user_info_api is None:
-            input_config.user_info_map.from_user_info_api = UserFields()
-        if input_config.user_info_map.from_id_token_payload is None:
-            input_config.user_info_map.from_id_token_payload = UserFields()
-
-        # These are safe defaults common to most providers. Each provider
-        # implementations override these as necessary
-        if input_config.user_info_map.from_id_token_payload.user_id is None:
-            input_config.user_info_map.from_id_token_payload.user_id = "sub"
-
-        if input_config.user_info_map.from_id_token_payload.email is None:
-            input_config.user_info_map.from_id_token_payload.email = "email"
-
-        if input_config.user_info_map.from_id_token_payload.email_verified is None:
-            input_config.user_info_map.from_id_token_payload.email_verified = (
-                "email_verified"
-            )
-
-        if input_config.user_info_map.from_user_info_api.user_id is None:
-            input_config.user_info_map.from_user_info_api.user_id = "sub"
-
-        if input_config.user_info_map.from_user_info_api.email is None:
-            input_config.user_info_map.from_user_info_api.email = "email"
-
-        if input_config.user_info_map.from_user_info_api.email_verified is None:
-            input_config.user_info_map.from_user_info_api.email_verified = (
-                "email_verified"
-            )
-
-        if input_config.generate_fake_email is None:
-
-            async def default_generate_fake_email(
-                _tenant_id: str, third_party_user_id: str, _: Dict[str, Any]
-            ) -> str:
-                return f"{third_party_user_id}.{input_config.third_party_id}@stfakeemail.supertokens.com"
-
-            input_config.generate_fake_email = default_generate_fake_email
-
-        return input_config
-
-    async def get_config_for_client_type(
-        self, client_type: Optional[str], user_context: Dict[str, Any]
-    ) -> ProviderConfigForClient:
-        if client_type is None:
-            if self.input_config.clients is None or len(self.input_config.clients) != 1:
-                raise ClientTypeNotFoundError(
-                    "please provide exactly one client config or pass clientType or tenantId"
-                )
-
-            return get_provider_config_for_client(
-                self.input_config, self.input_config.clients[0]
-            )
-
-        if self.input_config.clients is not None:
-            for client in self.input_config.clients:
-                if client.client_type == client_type:
-                    return get_provider_config_for_client(self.input_config, client)
-
-        raise ClientTypeNotFoundError(
-            f"Could not find client config for clientType: {client_type}"
-        )
-
-    async def get_authorisation_redirect_url(
-        self,
-        redirect_uri_on_provider_dashboard: str,
-        user_context: Dict[str, Any],
-    ) -> AuthorisationRedirect:
-        query_params: Dict[str, str] = {
-            "client_id": self.config.client_id,
-            "redirect_uri": redirect_uri_on_provider_dashboard,
-            "response_type": "code",
-        }
-
-        if self.config.scope is not None:
-            query_params["scope"] = " ".join(self.config.scope)
-
-        pkce_code_verifier: Union[str, None] = None
-
-        if self.config.client_secret is None or self.config.force_pkce:
-            code_verifier, code_challenge = pkce.generate_pkce_pair(64)
-            query_params["code_challenge"] = code_challenge
-            query_params["code_challenge_method"] = "S256"
-            pkce_code_verifier = code_verifier
-
-        if self.config.authorization_endpoint_query_params is not None:
-            for k, v in self.config.authorization_endpoint_query_params.items():
-                if v is None:
-                    del query_params[k]
-                else:
-                    query_params[k] = v
-
-        if self.config.authorization_endpoint is None:
-            raise Exception(
-                "ThirdParty provider's authorizationEndpoint is not configured."
-            )
-
-        url: str = self.config.authorization_endpoint
-
-        # Transformation needed for dev keys BEGIN
-        if is_using_oauth_development_client_id(self.config.client_id):
-            query_params["client_id"] = get_actual_client_id_from_development_client_id(
-                self.config.client_id
-            )
-            query_params["actual_redirect_uri"] = url
-            url = DEV_OAUTH_AUTHORIZATION_URL
-        # Transformation needed for dev keys END
-
-        url_obj = urlparse(url)
-        qparams = parse_qs(url_obj.query)
-        for k, v in query_params.items():
-            qparams[k] = [v]
-
-        url = url_obj._replace(query=urlencode(qparams, doseq=True)).geturl()
-
-        return AuthorisationRedirect(url, pkce_code_verifier)
-
-    async def exchange_auth_code_for_oauth_tokens(
-        self, redirect_uri_info: RedirectUriInfo, user_context: Dict[str, Any]
-    ) -> Dict[str, Any]:
-        if self.config.token_endpoint is None:
-            raise Exception("ThirdParty provider's tokenEndpoint is not configured.")
-
-        token_api_url = self.config.token_endpoint
-        access_token_params: Dict[str, str] = {
-            "client_id": self.config.client_id,
-            "redirect_uri": redirect_uri_info.redirect_uri_on_provider_dashboard,
-            "code": redirect_uri_info.redirect_uri_query_params["code"],
-            "grant_type": "authorization_code",
-        }
-        if self.config.client_secret is not None:
-            access_token_params["client_secret"] = self.config.client_secret
-
-        if redirect_uri_info.pkce_code_verifier is not None:
-            access_token_params["code_verifier"] = redirect_uri_info.pkce_code_verifier
-
-        if self.config.token_endpoint_body_params is not None:
-            access_token_params = merge_into_dict(
-                self.config.token_endpoint_body_params, access_token_params
-            )
-
-        # Transformation needed for dev keys BEGIN
-        if is_using_oauth_development_client_id(self.config.client_id):
-            access_token_params[
-                "client_id"
-            ] = get_actual_client_id_from_development_client_id(self.config.client_id)
-            access_token_params["redirect_uri"] = DEV_OAUTH_REDIRECT_URL
-        # Transformation needed for dev keys END
-
-        _, body = await do_post_request(token_api_url, access_token_params)
-        return body
-
-    async def get_user_info(
-        self, oauth_tokens: Dict[str, Any], user_context: Dict[str, Any]
-    ) -> UserInfo:
-        access_token: Union[str, None] = oauth_tokens.get("access_token")
-        id_token: Union[str, None] = oauth_tokens.get("id_token")
-
-        raw_user_info_from_provider = RawUserInfoFromProvider({}, {})
-        if id_token is not None and self.config.jwks_uri is not None:
-            raw_user_info_from_provider.from_id_token_payload = (
-                await verify_id_token_from_jwks_endpoint_and_get_payload(
-                    id_token,
-                    self.config.jwks_uri,
-                    get_actual_client_id_from_development_client_id(
-                        self.config.client_id
-                    ),
-                )
-            )
-
-            if self.config.validate_id_token_payload is not None:
-                await self.config.validate_id_token_payload(
-                    raw_user_info_from_provider.from_id_token_payload,
-                    self.config,
-                    user_context,
-                )
-
-        if self.config.validate_access_token is not None and access_token is not None:
-            await self.config.validate_access_token(
-                access_token, self.config, user_context
-            )
-
-        if access_token is not None and self.config.user_info_endpoint is not None:
-            headers: Dict[str, str] = {"Authorization": f"Bearer {access_token}"}
-            query_params: Dict[str, str] = {}
-
-            if self.config.user_info_endpoint_headers is not None:
-                headers = merge_into_dict(
-                    self.config.user_info_endpoint_headers, headers
-                )
-
-            if self.config.user_info_endpoint_query_params is not None:
-                query_params = merge_into_dict(
-                    self.config.user_info_endpoint_query_params, query_params
-                )
-
-            raw_user_info_from_provider.from_user_info_api = await do_get_request(
-                self.config.user_info_endpoint, query_params, headers
-            )
-
-        user_info_result = get_supertokens_user_info_result_from_raw_user_info(
-            self.config, raw_user_info_from_provider
-        )
-
-        return UserInfo(
-            third_party_user_id=user_info_result.third_party_user_id,
-            email=user_info_result.email,
-            raw_user_info_from_provider=raw_user_info_from_provider,
-        )
-
-

Ancestors

- -

Subclasses

- -

Methods

-
-
-async def exchange_auth_code_for_oauth_tokens(self, redirect_uri_info: RedirectUriInfo, user_context: Dict[str, Any]) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
async def exchange_auth_code_for_oauth_tokens(
-    self, redirect_uri_info: RedirectUriInfo, user_context: Dict[str, Any]
-) -> Dict[str, Any]:
-    if self.config.token_endpoint is None:
-        raise Exception("ThirdParty provider's tokenEndpoint is not configured.")
-
-    token_api_url = self.config.token_endpoint
-    access_token_params: Dict[str, str] = {
-        "client_id": self.config.client_id,
-        "redirect_uri": redirect_uri_info.redirect_uri_on_provider_dashboard,
-        "code": redirect_uri_info.redirect_uri_query_params["code"],
-        "grant_type": "authorization_code",
-    }
-    if self.config.client_secret is not None:
-        access_token_params["client_secret"] = self.config.client_secret
-
-    if redirect_uri_info.pkce_code_verifier is not None:
-        access_token_params["code_verifier"] = redirect_uri_info.pkce_code_verifier
-
-    if self.config.token_endpoint_body_params is not None:
-        access_token_params = merge_into_dict(
-            self.config.token_endpoint_body_params, access_token_params
-        )
-
-    # Transformation needed for dev keys BEGIN
-    if is_using_oauth_development_client_id(self.config.client_id):
-        access_token_params[
-            "client_id"
-        ] = get_actual_client_id_from_development_client_id(self.config.client_id)
-        access_token_params["redirect_uri"] = DEV_OAUTH_REDIRECT_URL
-    # Transformation needed for dev keys END
-
-    _, body = await do_post_request(token_api_url, access_token_params)
-    return body
-
-
-
-async def get_authorisation_redirect_url(self, redirect_uri_on_provider_dashboard: str, user_context: Dict[str, Any]) ‑> AuthorisationRedirect -
-
-
-
- -Expand source code - -
async def get_authorisation_redirect_url(
-    self,
-    redirect_uri_on_provider_dashboard: str,
-    user_context: Dict[str, Any],
-) -> AuthorisationRedirect:
-    query_params: Dict[str, str] = {
-        "client_id": self.config.client_id,
-        "redirect_uri": redirect_uri_on_provider_dashboard,
-        "response_type": "code",
-    }
-
-    if self.config.scope is not None:
-        query_params["scope"] = " ".join(self.config.scope)
-
-    pkce_code_verifier: Union[str, None] = None
-
-    if self.config.client_secret is None or self.config.force_pkce:
-        code_verifier, code_challenge = pkce.generate_pkce_pair(64)
-        query_params["code_challenge"] = code_challenge
-        query_params["code_challenge_method"] = "S256"
-        pkce_code_verifier = code_verifier
-
-    if self.config.authorization_endpoint_query_params is not None:
-        for k, v in self.config.authorization_endpoint_query_params.items():
-            if v is None:
-                del query_params[k]
-            else:
-                query_params[k] = v
-
-    if self.config.authorization_endpoint is None:
-        raise Exception(
-            "ThirdParty provider's authorizationEndpoint is not configured."
-        )
-
-    url: str = self.config.authorization_endpoint
-
-    # Transformation needed for dev keys BEGIN
-    if is_using_oauth_development_client_id(self.config.client_id):
-        query_params["client_id"] = get_actual_client_id_from_development_client_id(
-            self.config.client_id
-        )
-        query_params["actual_redirect_uri"] = url
-        url = DEV_OAUTH_AUTHORIZATION_URL
-    # Transformation needed for dev keys END
-
-    url_obj = urlparse(url)
-    qparams = parse_qs(url_obj.query)
-    for k, v in query_params.items():
-        qparams[k] = [v]
-
-    url = url_obj._replace(query=urlencode(qparams, doseq=True)).geturl()
-
-    return AuthorisationRedirect(url, pkce_code_verifier)
-
-
-
-async def get_config_for_client_type(self, client_type: Optional[str], user_context: Dict[str, Any]) ‑> ProviderConfigForClient -
-
-
-
- -Expand source code - -
async def get_config_for_client_type(
-    self, client_type: Optional[str], user_context: Dict[str, Any]
-) -> ProviderConfigForClient:
-    if client_type is None:
-        if self.input_config.clients is None or len(self.input_config.clients) != 1:
-            raise ClientTypeNotFoundError(
-                "please provide exactly one client config or pass clientType or tenantId"
-            )
-
-        return get_provider_config_for_client(
-            self.input_config, self.input_config.clients[0]
-        )
-
-    if self.input_config.clients is not None:
-        for client in self.input_config.clients:
-            if client.client_type == client_type:
-                return get_provider_config_for_client(self.input_config, client)
-
-    raise ClientTypeNotFoundError(
-        f"Could not find client config for clientType: {client_type}"
-    )
-
-
-
-async def get_user_info(self, oauth_tokens: Dict[str, Any], user_context: Dict[str, Any]) ‑> UserInfo -
-
-
-
- -Expand source code - -
async def get_user_info(
-    self, oauth_tokens: Dict[str, Any], user_context: Dict[str, Any]
-) -> UserInfo:
-    access_token: Union[str, None] = oauth_tokens.get("access_token")
-    id_token: Union[str, None] = oauth_tokens.get("id_token")
-
-    raw_user_info_from_provider = RawUserInfoFromProvider({}, {})
-    if id_token is not None and self.config.jwks_uri is not None:
-        raw_user_info_from_provider.from_id_token_payload = (
-            await verify_id_token_from_jwks_endpoint_and_get_payload(
-                id_token,
-                self.config.jwks_uri,
-                get_actual_client_id_from_development_client_id(
-                    self.config.client_id
-                ),
-            )
-        )
-
-        if self.config.validate_id_token_payload is not None:
-            await self.config.validate_id_token_payload(
-                raw_user_info_from_provider.from_id_token_payload,
-                self.config,
-                user_context,
-            )
-
-    if self.config.validate_access_token is not None and access_token is not None:
-        await self.config.validate_access_token(
-            access_token, self.config, user_context
-        )
-
-    if access_token is not None and self.config.user_info_endpoint is not None:
-        headers: Dict[str, str] = {"Authorization": f"Bearer {access_token}"}
-        query_params: Dict[str, str] = {}
-
-        if self.config.user_info_endpoint_headers is not None:
-            headers = merge_into_dict(
-                self.config.user_info_endpoint_headers, headers
-            )
-
-        if self.config.user_info_endpoint_query_params is not None:
-            query_params = merge_into_dict(
-                self.config.user_info_endpoint_query_params, query_params
-            )
-
-        raw_user_info_from_provider.from_user_info_api = await do_get_request(
-            self.config.user_info_endpoint, query_params, headers
-        )
-
-    user_info_result = get_supertokens_user_info_result_from_raw_user_info(
-        self.config, raw_user_info_from_provider
-    )
-
-    return UserInfo(
-        third_party_user_id=user_info_result.third_party_user_id,
-        email=user_info_result.email,
-        raw_user_info_from_provider=raw_user_info_from_provider,
-    )
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/thirdparty/providers/discord.html b/html/supertokens_python/recipe/thirdparty/providers/discord.html deleted file mode 100644 index 1a71d6e81..000000000 --- a/html/supertokens_python/recipe/thirdparty/providers/discord.html +++ /dev/null @@ -1,240 +0,0 @@ - - - - - - -supertokens_python.recipe.thirdparty.providers.discord API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.thirdparty.providers.discord

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from typing import Any, Dict, Optional
-from ..provider import (
-    Provider,
-    ProviderConfigForClient,
-    ProviderInput,
-    UserFields,
-    UserInfoMap,
-)
-
-from .custom import (
-    GenericProvider,
-    NewProvider,
-)
-
-
-class DiscordImpl(GenericProvider):
-    async def get_config_for_client_type(
-        self, client_type: Optional[str], user_context: Dict[str, Any]
-    ) -> ProviderConfigForClient:
-        config = await super().get_config_for_client_type(client_type, user_context)
-
-        if config.scope is None:
-            config.scope = ["identify", "email"]
-
-        return config
-
-
-def Discord(input: ProviderInput) -> Provider:  # pylint: disable=redefined-builtin
-    if not input.config.name:
-        input.config.name = "Discord"
-
-    if not input.config.authorization_endpoint:
-        input.config.authorization_endpoint = "https://discord.com/api/oauth2/authorize"
-
-    if not input.config.token_endpoint:
-        input.config.token_endpoint = "https://discord.com/api/oauth2/token"
-
-    if not input.config.user_info_endpoint:
-        input.config.user_info_endpoint = "https://discord.com/api/users/@me"
-
-    if input.config.user_info_map is None:
-        input.config.user_info_map = UserInfoMap(UserFields(), UserFields())
-
-    if input.config.user_info_map.from_user_info_api is None:
-        input.config.user_info_map.from_user_info_api = UserFields()
-
-    if input.config.user_info_map.from_user_info_api.user_id is None:
-        input.config.user_info_map.from_user_info_api.user_id = "id"
-
-    if input.config.user_info_map.from_user_info_api.email is None:
-        input.config.user_info_map.from_user_info_api.email = "email"
-
-    if input.config.user_info_map.from_user_info_api.email_verified is None:
-        input.config.user_info_map.from_user_info_api.email_verified = "verified"
-
-    return NewProvider(input, DiscordImpl)
-
-
-
-
-
-
-
-

Functions

-
-
-def Discord(input: ProviderInput) ‑> Provider -
-
-
-
- -Expand source code - -
def Discord(input: ProviderInput) -> Provider:  # pylint: disable=redefined-builtin
-    if not input.config.name:
-        input.config.name = "Discord"
-
-    if not input.config.authorization_endpoint:
-        input.config.authorization_endpoint = "https://discord.com/api/oauth2/authorize"
-
-    if not input.config.token_endpoint:
-        input.config.token_endpoint = "https://discord.com/api/oauth2/token"
-
-    if not input.config.user_info_endpoint:
-        input.config.user_info_endpoint = "https://discord.com/api/users/@me"
-
-    if input.config.user_info_map is None:
-        input.config.user_info_map = UserInfoMap(UserFields(), UserFields())
-
-    if input.config.user_info_map.from_user_info_api is None:
-        input.config.user_info_map.from_user_info_api = UserFields()
-
-    if input.config.user_info_map.from_user_info_api.user_id is None:
-        input.config.user_info_map.from_user_info_api.user_id = "id"
-
-    if input.config.user_info_map.from_user_info_api.email is None:
-        input.config.user_info_map.from_user_info_api.email = "email"
-
-    if input.config.user_info_map.from_user_info_api.email_verified is None:
-        input.config.user_info_map.from_user_info_api.email_verified = "verified"
-
-    return NewProvider(input, DiscordImpl)
-
-
-
-
-
-

Classes

-
-
-class DiscordImpl -(provider_config: ProviderConfig) -
-
-
-
- -Expand source code - -
class DiscordImpl(GenericProvider):
-    async def get_config_for_client_type(
-        self, client_type: Optional[str], user_context: Dict[str, Any]
-    ) -> ProviderConfigForClient:
-        config = await super().get_config_for_client_type(client_type, user_context)
-
-        if config.scope is None:
-            config.scope = ["identify", "email"]
-
-        return config
-
-

Ancestors

- -

Methods

-
-
-async def get_config_for_client_type(self, client_type: Optional[str], user_context: Dict[str, Any]) ‑> ProviderConfigForClient -
-
-
-
- -Expand source code - -
async def get_config_for_client_type(
-    self, client_type: Optional[str], user_context: Dict[str, Any]
-) -> ProviderConfigForClient:
-    config = await super().get_config_for_client_type(client_type, user_context)
-
-    if config.scope is None:
-        config.scope = ["identify", "email"]
-
-    return config
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/thirdparty/providers/facebook.html b/html/supertokens_python/recipe/thirdparty/providers/facebook.html deleted file mode 100644 index a31e496b4..000000000 --- a/html/supertokens_python/recipe/thirdparty/providers/facebook.html +++ /dev/null @@ -1,282 +0,0 @@ - - - - - - -supertokens_python.recipe.thirdparty.providers.facebook API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.thirdparty.providers.facebook

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from typing import Any, Dict, Optional
-
-from supertokens_python.recipe.thirdparty.types import UserInfo
-from ..provider import (
-    Provider,
-    ProviderConfigForClient,
-    ProviderInput,
-    UserFields,
-    UserInfoMap,
-)
-
-from .custom import (
-    GenericProvider,
-    NewProvider,
-)
-
-
-class FacebookImpl(GenericProvider):
-    async def get_config_for_client_type(
-        self, client_type: Optional[str], user_context: Dict[str, Any]
-    ) -> ProviderConfigForClient:
-        config = await super().get_config_for_client_type(client_type, user_context)
-
-        if config.scope is None:
-            config.scope = ["email"]
-
-        return config
-
-    async def get_user_info(
-        self, oauth_tokens: Dict[str, Any], user_context: Dict[str, Any]
-    ) -> UserInfo:
-        self.config.user_info_endpoint_query_params = {
-            "access_token": str(oauth_tokens["access_token"]),
-            "fields": "id,email",
-            "format": "json",
-            **(self.config.user_info_endpoint_query_params or {}),
-        }
-        return await super().get_user_info(oauth_tokens, user_context)
-
-
-def Facebook(input: ProviderInput) -> Provider:  # pylint: disable=redefined-builtin
-    if not input.config.name:
-        input.config.name = "Facebook"
-
-    if not input.config.authorization_endpoint:
-        input.config.authorization_endpoint = (
-            "https://www.facebook.com/v12.0/dialog/oauth"
-        )
-
-    if not input.config.token_endpoint:
-        input.config.token_endpoint = (
-            "https://graph.facebook.com/v12.0/oauth/access_token"
-        )
-
-    if not input.config.user_info_endpoint:
-        input.config.user_info_endpoint = "https://graph.facebook.com/me"
-
-    if input.config.user_info_map is None:
-        input.config.user_info_map = UserInfoMap(UserFields(), UserFields())
-
-    if input.config.user_info_map.from_user_info_api is None:
-        input.config.user_info_map.from_user_info_api = UserFields()
-
-    if input.config.user_info_map.from_user_info_api.user_id is None:
-        input.config.user_info_map.from_user_info_api.user_id = "id"
-
-    return NewProvider(input, FacebookImpl)
-
-
-
-
-
-
-
-

Functions

-
-
-def Facebook(input: ProviderInput) ‑> Provider -
-
-
-
- -Expand source code - -
def Facebook(input: ProviderInput) -> Provider:  # pylint: disable=redefined-builtin
-    if not input.config.name:
-        input.config.name = "Facebook"
-
-    if not input.config.authorization_endpoint:
-        input.config.authorization_endpoint = (
-            "https://www.facebook.com/v12.0/dialog/oauth"
-        )
-
-    if not input.config.token_endpoint:
-        input.config.token_endpoint = (
-            "https://graph.facebook.com/v12.0/oauth/access_token"
-        )
-
-    if not input.config.user_info_endpoint:
-        input.config.user_info_endpoint = "https://graph.facebook.com/me"
-
-    if input.config.user_info_map is None:
-        input.config.user_info_map = UserInfoMap(UserFields(), UserFields())
-
-    if input.config.user_info_map.from_user_info_api is None:
-        input.config.user_info_map.from_user_info_api = UserFields()
-
-    if input.config.user_info_map.from_user_info_api.user_id is None:
-        input.config.user_info_map.from_user_info_api.user_id = "id"
-
-    return NewProvider(input, FacebookImpl)
-
-
-
-
-
-

Classes

-
-
-class FacebookImpl -(provider_config: ProviderConfig) -
-
-
-
- -Expand source code - -
class FacebookImpl(GenericProvider):
-    async def get_config_for_client_type(
-        self, client_type: Optional[str], user_context: Dict[str, Any]
-    ) -> ProviderConfigForClient:
-        config = await super().get_config_for_client_type(client_type, user_context)
-
-        if config.scope is None:
-            config.scope = ["email"]
-
-        return config
-
-    async def get_user_info(
-        self, oauth_tokens: Dict[str, Any], user_context: Dict[str, Any]
-    ) -> UserInfo:
-        self.config.user_info_endpoint_query_params = {
-            "access_token": str(oauth_tokens["access_token"]),
-            "fields": "id,email",
-            "format": "json",
-            **(self.config.user_info_endpoint_query_params or {}),
-        }
-        return await super().get_user_info(oauth_tokens, user_context)
-
-

Ancestors

- -

Methods

-
-
-async def get_config_for_client_type(self, client_type: Optional[str], user_context: Dict[str, Any]) ‑> ProviderConfigForClient -
-
-
-
- -Expand source code - -
async def get_config_for_client_type(
-    self, client_type: Optional[str], user_context: Dict[str, Any]
-) -> ProviderConfigForClient:
-    config = await super().get_config_for_client_type(client_type, user_context)
-
-    if config.scope is None:
-        config.scope = ["email"]
-
-    return config
-
-
-
-async def get_user_info(self, oauth_tokens: Dict[str, Any], user_context: Dict[str, Any]) ‑> UserInfo -
-
-
-
- -Expand source code - -
async def get_user_info(
-    self, oauth_tokens: Dict[str, Any], user_context: Dict[str, Any]
-) -> UserInfo:
-    self.config.user_info_endpoint_query_params = {
-        "access_token": str(oauth_tokens["access_token"]),
-        "fields": "id,email",
-        "format": "json",
-        **(self.config.user_info_endpoint_query_params or {}),
-    }
-    return await super().get_user_info(oauth_tokens, user_context)
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/thirdparty/providers/github.html b/html/supertokens_python/recipe/thirdparty/providers/github.html deleted file mode 100644 index d7d727ef2..000000000 --- a/html/supertokens_python/recipe/thirdparty/providers/github.html +++ /dev/null @@ -1,357 +0,0 @@ - - - - - - -supertokens_python.recipe.thirdparty.providers.github API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.thirdparty.providers.github

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-import base64
-from typing import Any, Dict, List, Optional
-
-from supertokens_python.recipe.thirdparty.providers.utils import (
-    do_get_request,
-    do_post_request,
-)
-from supertokens_python.recipe.thirdparty.types import UserInfo, UserInfoEmail
-
-from .custom import GenericProvider, NewProvider
-from ..provider import Provider, ProviderConfigForClient, ProviderInput
-
-
-class GithubImpl(GenericProvider):
-    async def get_config_for_client_type(
-        self, client_type: Optional[str], user_context: Dict[str, Any]
-    ) -> ProviderConfigForClient:
-        config = await super().get_config_for_client_type(client_type, user_context)
-
-        if config.scope is None:
-            config.scope = ["read:user", "user:email"]
-
-        return config
-
-    async def get_user_info(
-        self, oauth_tokens: Dict[str, Any], user_context: Dict[str, Any]
-    ) -> UserInfo:
-        headers = {
-            "Authorization": f"Bearer {oauth_tokens.get('access_token')}",
-            "Accept": "application/vnd.github.v3+json",
-        }
-
-        raw_response = {}
-
-        email_info: List[Any] = await do_get_request("https://api.github.com/user/emails", headers=headers)  # type: ignore
-        user_info = await do_get_request("https://api.github.com/user", headers=headers)
-
-        raw_response["emails"] = email_info
-        raw_response["user"] = user_info
-
-        result = UserInfo(
-            third_party_user_id=str(user_info.get("id")),
-        )
-
-        for info in email_info:
-            if info.get("primary"):
-                result.email = UserInfoEmail(
-                    email=info.get("email"), is_verified=info.get("verified")
-                )
-
-        return result
-
-
-def Github(input: ProviderInput) -> Provider:  # pylint: disable=redefined-builtin
-    if not input.config.name:
-        input.config.name = "Github"
-
-    if not input.config.authorization_endpoint:
-        input.config.authorization_endpoint = "https://github.com/login/oauth/authorize"
-
-    if not input.config.token_endpoint:
-        input.config.token_endpoint = "https://github.com/login/oauth/access_token"
-
-    if input.config.validate_access_token is None:
-        input.config.validate_access_token = validate_access_token
-
-    return NewProvider(input, GithubImpl)
-
-
-async def validate_access_token(
-    access_token: str, config: ProviderConfigForClient, _: Dict[str, Any]
-):
-    client_secret = "" if config.client_secret is None else config.client_secret
-    basic_auth_token = base64.b64encode(
-        f"{config.client_id}:{client_secret}".encode()
-    ).decode()
-
-    url = f"https://api.github.com/applications/{config.client_id}/token"
-    headers = {
-        "Authorization": f"Basic {basic_auth_token}",
-        "Content-Type": "application/json",
-    }
-
-    status, body = await do_post_request(url, {"access_token": access_token}, headers)
-    if status != 200:
-        raise ValueError("Invalid access token")
-
-    if "app" not in body or body["app"].get("client_id") != config.client_id:
-        raise ValueError("Access token does not belong to your application")
-
-
-
-
-
-
-
-

Functions

-
-
-def Github(input: ProviderInput) ‑> Provider -
-
-
-
- -Expand source code - -
def Github(input: ProviderInput) -> Provider:  # pylint: disable=redefined-builtin
-    if not input.config.name:
-        input.config.name = "Github"
-
-    if not input.config.authorization_endpoint:
-        input.config.authorization_endpoint = "https://github.com/login/oauth/authorize"
-
-    if not input.config.token_endpoint:
-        input.config.token_endpoint = "https://github.com/login/oauth/access_token"
-
-    if input.config.validate_access_token is None:
-        input.config.validate_access_token = validate_access_token
-
-    return NewProvider(input, GithubImpl)
-
-
-
-async def validate_access_token(access_token: str, config: ProviderConfigForClient, _: Dict[str, Any]) -
-
-
-
- -Expand source code - -
async def validate_access_token(
-    access_token: str, config: ProviderConfigForClient, _: Dict[str, Any]
-):
-    client_secret = "" if config.client_secret is None else config.client_secret
-    basic_auth_token = base64.b64encode(
-        f"{config.client_id}:{client_secret}".encode()
-    ).decode()
-
-    url = f"https://api.github.com/applications/{config.client_id}/token"
-    headers = {
-        "Authorization": f"Basic {basic_auth_token}",
-        "Content-Type": "application/json",
-    }
-
-    status, body = await do_post_request(url, {"access_token": access_token}, headers)
-    if status != 200:
-        raise ValueError("Invalid access token")
-
-    if "app" not in body or body["app"].get("client_id") != config.client_id:
-        raise ValueError("Access token does not belong to your application")
-
-
-
-
-
-

Classes

-
-
-class GithubImpl -(provider_config: ProviderConfig) -
-
-
-
- -Expand source code - -
class GithubImpl(GenericProvider):
-    async def get_config_for_client_type(
-        self, client_type: Optional[str], user_context: Dict[str, Any]
-    ) -> ProviderConfigForClient:
-        config = await super().get_config_for_client_type(client_type, user_context)
-
-        if config.scope is None:
-            config.scope = ["read:user", "user:email"]
-
-        return config
-
-    async def get_user_info(
-        self, oauth_tokens: Dict[str, Any], user_context: Dict[str, Any]
-    ) -> UserInfo:
-        headers = {
-            "Authorization": f"Bearer {oauth_tokens.get('access_token')}",
-            "Accept": "application/vnd.github.v3+json",
-        }
-
-        raw_response = {}
-
-        email_info: List[Any] = await do_get_request("https://api.github.com/user/emails", headers=headers)  # type: ignore
-        user_info = await do_get_request("https://api.github.com/user", headers=headers)
-
-        raw_response["emails"] = email_info
-        raw_response["user"] = user_info
-
-        result = UserInfo(
-            third_party_user_id=str(user_info.get("id")),
-        )
-
-        for info in email_info:
-            if info.get("primary"):
-                result.email = UserInfoEmail(
-                    email=info.get("email"), is_verified=info.get("verified")
-                )
-
-        return result
-
-

Ancestors

- -

Methods

-
-
-async def get_config_for_client_type(self, client_type: Optional[str], user_context: Dict[str, Any]) ‑> ProviderConfigForClient -
-
-
-
- -Expand source code - -
async def get_config_for_client_type(
-    self, client_type: Optional[str], user_context: Dict[str, Any]
-) -> ProviderConfigForClient:
-    config = await super().get_config_for_client_type(client_type, user_context)
-
-    if config.scope is None:
-        config.scope = ["read:user", "user:email"]
-
-    return config
-
-
-
-async def get_user_info(self, oauth_tokens: Dict[str, Any], user_context: Dict[str, Any]) ‑> UserInfo -
-
-
-
- -Expand source code - -
async def get_user_info(
-    self, oauth_tokens: Dict[str, Any], user_context: Dict[str, Any]
-) -> UserInfo:
-    headers = {
-        "Authorization": f"Bearer {oauth_tokens.get('access_token')}",
-        "Accept": "application/vnd.github.v3+json",
-    }
-
-    raw_response = {}
-
-    email_info: List[Any] = await do_get_request("https://api.github.com/user/emails", headers=headers)  # type: ignore
-    user_info = await do_get_request("https://api.github.com/user", headers=headers)
-
-    raw_response["emails"] = email_info
-    raw_response["user"] = user_info
-
-    result = UserInfo(
-        third_party_user_id=str(user_info.get("id")),
-    )
-
-    for info in email_info:
-        if info.get("primary"):
-            result.email = UserInfoEmail(
-                email=info.get("email"), is_verified=info.get("verified")
-            )
-
-    return result
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/thirdparty/providers/gitlab.html b/html/supertokens_python/recipe/thirdparty/providers/gitlab.html deleted file mode 100644 index 8e3b51155..000000000 --- a/html/supertokens_python/recipe/thirdparty/providers/gitlab.html +++ /dev/null @@ -1,261 +0,0 @@ - - - - - - -supertokens_python.recipe.thirdparty.providers.gitlab API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.thirdparty.providers.gitlab

-
-
-
- -Expand source code - -
# Copyright (c) 2023, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from __future__ import annotations
-
-from typing import Optional, Dict, Any
-
-from supertokens_python.recipe.thirdparty.provider import (
-    Provider,
-    ProviderConfigForClient,
-)
-from .custom import GenericProvider, NewProvider
-from ..provider import Provider, ProviderInput
-from .utils import normalise_oidc_endpoint_to_include_well_known
-from supertokens_python.normalised_url_domain import NormalisedURLDomain
-from supertokens_python.normalised_url_path import NormalisedURLPath
-
-
-class GitlabImpl(GenericProvider):
-    async def get_config_for_client_type(
-        self, client_type: Optional[str], user_context: Dict[str, Any]
-    ) -> ProviderConfigForClient:
-        config = await super().get_config_for_client_type(client_type, user_context)
-
-        if config.scope is None:
-            config.scope = ["openid", "email"]
-
-        if (
-            config.additional_config is not None
-            and config.additional_config.get("gitlabBaseUrl") is not None
-        ):
-            gitlab_base_url = config.additional_config["gitlabBaseUrl"]
-            oidc_domain = NormalisedURLDomain(gitlab_base_url)
-            oidc_path = NormalisedURLPath("/.well-known/openid-configuration")
-            config.oidc_discovery_endpoint = (
-                oidc_domain.get_as_string_dangerous()
-                + oidc_path.get_as_string_dangerous()
-            )
-
-        if not config.oidc_discovery_endpoint:
-            raise Exception("should never come here")
-
-        # The config could be coming from core where we didn't add the well-known previously
-        config.oidc_discovery_endpoint = normalise_oidc_endpoint_to_include_well_known(
-            config.oidc_discovery_endpoint
-        )
-
-        return config
-
-
-def Gitlab(input: ProviderInput) -> Provider:  # pylint: disable=redefined-builtin
-    if not input.config.name:
-        input.config.name = "Gitlab"
-
-    if not input.config.oidc_discovery_endpoint:
-        input.config.oidc_discovery_endpoint = (
-            "https://gitlab.com/.well-known/openid-configuration"
-        )
-
-    return NewProvider(input, GitlabImpl)
-
-
-
-
-
-
-
-

Functions

-
-
-def Gitlab(input: ProviderInput) ‑> Provider -
-
-
-
- -Expand source code - -
def Gitlab(input: ProviderInput) -> Provider:  # pylint: disable=redefined-builtin
-    if not input.config.name:
-        input.config.name = "Gitlab"
-
-    if not input.config.oidc_discovery_endpoint:
-        input.config.oidc_discovery_endpoint = (
-            "https://gitlab.com/.well-known/openid-configuration"
-        )
-
-    return NewProvider(input, GitlabImpl)
-
-
-
-
-
-

Classes

-
-
-class GitlabImpl -(provider_config: ProviderConfig) -
-
-
-
- -Expand source code - -
class GitlabImpl(GenericProvider):
-    async def get_config_for_client_type(
-        self, client_type: Optional[str], user_context: Dict[str, Any]
-    ) -> ProviderConfigForClient:
-        config = await super().get_config_for_client_type(client_type, user_context)
-
-        if config.scope is None:
-            config.scope = ["openid", "email"]
-
-        if (
-            config.additional_config is not None
-            and config.additional_config.get("gitlabBaseUrl") is not None
-        ):
-            gitlab_base_url = config.additional_config["gitlabBaseUrl"]
-            oidc_domain = NormalisedURLDomain(gitlab_base_url)
-            oidc_path = NormalisedURLPath("/.well-known/openid-configuration")
-            config.oidc_discovery_endpoint = (
-                oidc_domain.get_as_string_dangerous()
-                + oidc_path.get_as_string_dangerous()
-            )
-
-        if not config.oidc_discovery_endpoint:
-            raise Exception("should never come here")
-
-        # The config could be coming from core where we didn't add the well-known previously
-        config.oidc_discovery_endpoint = normalise_oidc_endpoint_to_include_well_known(
-            config.oidc_discovery_endpoint
-        )
-
-        return config
-
-

Ancestors

- -

Methods

-
-
-async def get_config_for_client_type(self, client_type: Optional[str], user_context: Dict[str, Any]) ‑> ProviderConfigForClient -
-
-
-
- -Expand source code - -
async def get_config_for_client_type(
-    self, client_type: Optional[str], user_context: Dict[str, Any]
-) -> ProviderConfigForClient:
-    config = await super().get_config_for_client_type(client_type, user_context)
-
-    if config.scope is None:
-        config.scope = ["openid", "email"]
-
-    if (
-        config.additional_config is not None
-        and config.additional_config.get("gitlabBaseUrl") is not None
-    ):
-        gitlab_base_url = config.additional_config["gitlabBaseUrl"]
-        oidc_domain = NormalisedURLDomain(gitlab_base_url)
-        oidc_path = NormalisedURLPath("/.well-known/openid-configuration")
-        config.oidc_discovery_endpoint = (
-            oidc_domain.get_as_string_dangerous()
-            + oidc_path.get_as_string_dangerous()
-        )
-
-    if not config.oidc_discovery_endpoint:
-        raise Exception("should never come here")
-
-    # The config could be coming from core where we didn't add the well-known previously
-    config.oidc_discovery_endpoint = normalise_oidc_endpoint_to_include_well_known(
-        config.oidc_discovery_endpoint
-    )
-
-    return config
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/thirdparty/providers/google.html b/html/supertokens_python/recipe/thirdparty/providers/google.html deleted file mode 100644 index dec9afe5e..000000000 --- a/html/supertokens_python/recipe/thirdparty/providers/google.html +++ /dev/null @@ -1,262 +0,0 @@ - - - - - - -supertokens_python.recipe.thirdparty.providers.google API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.thirdparty.providers.google

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-from typing import Any, Callable, Dict, Optional
-
-from ..provider import (
-    Provider,
-    ProviderConfig,
-    ProviderConfigForClient,
-    ProviderInput,
-    UserFields,
-    UserInfoMap,
-)
-
-from .custom import (
-    GenericProvider,
-    NewProvider,
-)
-from .utils import normalise_oidc_endpoint_to_include_well_known
-
-
-class GoogleImpl(GenericProvider):
-    async def get_config_for_client_type(
-        self, client_type: Optional[str], user_context: Dict[str, Any]
-    ) -> ProviderConfigForClient:
-        config = await super().get_config_for_client_type(client_type, user_context)
-
-        if config.scope is None:
-            config.scope = ["openid", "email"]
-
-        if not config.oidc_discovery_endpoint:
-            raise Exception("should never happen")
-
-        # The config could be coming from core where we didn't add the well-known previously
-        config.oidc_discovery_endpoint = normalise_oidc_endpoint_to_include_well_known(
-            config.oidc_discovery_endpoint
-        )
-
-        return config
-
-
-def Google(
-    input: ProviderInput,  # pylint: disable=redefined-builtin
-    base_class: Callable[[ProviderConfig], GoogleImpl] = GoogleImpl,
-) -> Provider:
-    if not input.config.name:
-        input.config.name = "Google"
-
-    if not input.config.oidc_discovery_endpoint:
-        input.config.oidc_discovery_endpoint = (
-            "https://accounts.google.com/.well-known/openid-configuration"
-        )
-
-    if input.config.user_info_map is None:
-        input.config.user_info_map = UserInfoMap(UserFields(), UserFields())
-
-    if input.config.authorization_endpoint_query_params is None:
-        input.config.authorization_endpoint_query_params = {}
-
-    input.config.authorization_endpoint_query_params = {
-        "included_grant_scopes": "true",
-        "access_type": "offline",
-        **input.config.authorization_endpoint_query_params,
-    }
-
-    return NewProvider(input, base_class)
-
-
-
-
-
-
-
-

Functions

-
-
-def Google(input: ProviderInput, base_class: Callable[[ProviderConfig], GoogleImpl] = supertokens_python.recipe.thirdparty.providers.google.GoogleImpl) ‑> Provider -
-
-
-
- -Expand source code - -
def Google(
-    input: ProviderInput,  # pylint: disable=redefined-builtin
-    base_class: Callable[[ProviderConfig], GoogleImpl] = GoogleImpl,
-) -> Provider:
-    if not input.config.name:
-        input.config.name = "Google"
-
-    if not input.config.oidc_discovery_endpoint:
-        input.config.oidc_discovery_endpoint = (
-            "https://accounts.google.com/.well-known/openid-configuration"
-        )
-
-    if input.config.user_info_map is None:
-        input.config.user_info_map = UserInfoMap(UserFields(), UserFields())
-
-    if input.config.authorization_endpoint_query_params is None:
-        input.config.authorization_endpoint_query_params = {}
-
-    input.config.authorization_endpoint_query_params = {
-        "included_grant_scopes": "true",
-        "access_type": "offline",
-        **input.config.authorization_endpoint_query_params,
-    }
-
-    return NewProvider(input, base_class)
-
-
-
-
-
-

Classes

-
-
-class GoogleImpl -(provider_config: ProviderConfig) -
-
-
-
- -Expand source code - -
class GoogleImpl(GenericProvider):
-    async def get_config_for_client_type(
-        self, client_type: Optional[str], user_context: Dict[str, Any]
-    ) -> ProviderConfigForClient:
-        config = await super().get_config_for_client_type(client_type, user_context)
-
-        if config.scope is None:
-            config.scope = ["openid", "email"]
-
-        if not config.oidc_discovery_endpoint:
-            raise Exception("should never happen")
-
-        # The config could be coming from core where we didn't add the well-known previously
-        config.oidc_discovery_endpoint = normalise_oidc_endpoint_to_include_well_known(
-            config.oidc_discovery_endpoint
-        )
-
-        return config
-
-

Ancestors

- -

Subclasses

- -

Methods

-
-
-async def get_config_for_client_type(self, client_type: Optional[str], user_context: Dict[str, Any]) ‑> ProviderConfigForClient -
-
-
-
- -Expand source code - -
async def get_config_for_client_type(
-    self, client_type: Optional[str], user_context: Dict[str, Any]
-) -> ProviderConfigForClient:
-    config = await super().get_config_for_client_type(client_type, user_context)
-
-    if config.scope is None:
-        config.scope = ["openid", "email"]
-
-    if not config.oidc_discovery_endpoint:
-        raise Exception("should never happen")
-
-    # The config could be coming from core where we didn't add the well-known previously
-    config.oidc_discovery_endpoint = normalise_oidc_endpoint_to_include_well_known(
-        config.oidc_discovery_endpoint
-    )
-
-    return config
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/thirdparty/providers/google_workspaces.html b/html/supertokens_python/recipe/thirdparty/providers/google_workspaces.html deleted file mode 100644 index 8a0176aac..000000000 --- a/html/supertokens_python/recipe/thirdparty/providers/google_workspaces.html +++ /dev/null @@ -1,241 +0,0 @@ - - - - - - -supertokens_python.recipe.thirdparty.providers.google_workspaces API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.thirdparty.providers.google_workspaces

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-from typing import Any, Dict, Optional
-
-from .google import Google, GoogleImpl
-
-from ..provider import (
-    Provider,
-    ProviderConfigForClient,
-    ProviderInput,
-)
-
-
-class GoogleWorkspacesImpl(GoogleImpl):
-    async def get_config_for_client_type(
-        self, client_type: Optional[str], user_context: Dict[str, Any]
-    ) -> ProviderConfigForClient:
-        config = await super().get_config_for_client_type(client_type, user_context)
-
-        if config.additional_config is None:
-            config.additional_config = {}
-
-        config.authorization_endpoint_query_params = {
-            "hd": str(config.additional_config.get("hd", "*")),
-            **(config.authorization_endpoint_query_params or {}),
-        }
-
-        return config
-
-
-def GoogleWorkspaces(
-    input: ProviderInput,  # pylint: disable=redefined-builtin
-) -> Provider:
-    if not input.config.name:
-        input.config.name = "Google Workspaces"
-
-    if input.config.validate_id_token_payload is None:
-
-        async def default_validate_id_token_payload(
-            id_token_payload: Dict[str, Any],
-            config: ProviderConfigForClient,
-            _user_context: Dict[str, Any],
-        ):
-            if (config.additional_config or {}).get("hd", "*") != "*":
-                if (config.additional_config or {}).get("hd") != id_token_payload.get(
-                    "hd"
-                ):
-                    raise Exception(
-                        "the value for hd claim in the id token does not match the value provided in the config"
-                    )
-
-        input.config.validate_id_token_payload = default_validate_id_token_payload
-
-    return Google(input, GoogleWorkspacesImpl)
-
-
-
-
-
-
-
-

Functions

-
-
-def GoogleWorkspaces(input: ProviderInput) ‑> Provider -
-
-
-
- -Expand source code - -
def GoogleWorkspaces(
-    input: ProviderInput,  # pylint: disable=redefined-builtin
-) -> Provider:
-    if not input.config.name:
-        input.config.name = "Google Workspaces"
-
-    if input.config.validate_id_token_payload is None:
-
-        async def default_validate_id_token_payload(
-            id_token_payload: Dict[str, Any],
-            config: ProviderConfigForClient,
-            _user_context: Dict[str, Any],
-        ):
-            if (config.additional_config or {}).get("hd", "*") != "*":
-                if (config.additional_config or {}).get("hd") != id_token_payload.get(
-                    "hd"
-                ):
-                    raise Exception(
-                        "the value for hd claim in the id token does not match the value provided in the config"
-                    )
-
-        input.config.validate_id_token_payload = default_validate_id_token_payload
-
-    return Google(input, GoogleWorkspacesImpl)
-
-
-
-
-
-

Classes

-
-
-class GoogleWorkspacesImpl -(provider_config: ProviderConfig) -
-
-
-
- -Expand source code - -
class GoogleWorkspacesImpl(GoogleImpl):
-    async def get_config_for_client_type(
-        self, client_type: Optional[str], user_context: Dict[str, Any]
-    ) -> ProviderConfigForClient:
-        config = await super().get_config_for_client_type(client_type, user_context)
-
-        if config.additional_config is None:
-            config.additional_config = {}
-
-        config.authorization_endpoint_query_params = {
-            "hd": str(config.additional_config.get("hd", "*")),
-            **(config.authorization_endpoint_query_params or {}),
-        }
-
-        return config
-
-

Ancestors

- -

Methods

-
-
-async def get_config_for_client_type(self, client_type: Optional[str], user_context: Dict[str, Any]) ‑> ProviderConfigForClient -
-
-
-
- -Expand source code - -
async def get_config_for_client_type(
-    self, client_type: Optional[str], user_context: Dict[str, Any]
-) -> ProviderConfigForClient:
-    config = await super().get_config_for_client_type(client_type, user_context)
-
-    if config.additional_config is None:
-        config.additional_config = {}
-
-    config.authorization_endpoint_query_params = {
-        "hd": str(config.additional_config.get("hd", "*")),
-        **(config.authorization_endpoint_query_params or {}),
-    }
-
-    return config
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/thirdparty/providers/index.html b/html/supertokens_python/recipe/thirdparty/providers/index.html deleted file mode 100644 index 35af277d9..000000000 --- a/html/supertokens_python/recipe/thirdparty/providers/index.html +++ /dev/null @@ -1,158 +0,0 @@ - - - - - - -supertokens_python.recipe.thirdparty.providers API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.thirdparty.providers

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-
-
-

Sub-modules

-
-
supertokens_python.recipe.thirdparty.providers.active_directory
-
-
-
-
supertokens_python.recipe.thirdparty.providers.apple
-
-
-
-
supertokens_python.recipe.thirdparty.providers.bitbucket
-
-
-
-
supertokens_python.recipe.thirdparty.providers.boxy_saml
-
-
-
-
supertokens_python.recipe.thirdparty.providers.config_utils
-
-
-
-
supertokens_python.recipe.thirdparty.providers.custom
-
-
-
-
supertokens_python.recipe.thirdparty.providers.discord
-
-
-
-
supertokens_python.recipe.thirdparty.providers.facebook
-
-
-
-
supertokens_python.recipe.thirdparty.providers.github
-
-
-
-
supertokens_python.recipe.thirdparty.providers.gitlab
-
-
-
-
supertokens_python.recipe.thirdparty.providers.google
-
-
-
-
supertokens_python.recipe.thirdparty.providers.google_workspaces
-
-
-
-
supertokens_python.recipe.thirdparty.providers.linkedin
-
-
-
-
supertokens_python.recipe.thirdparty.providers.okta
-
-
-
-
supertokens_python.recipe.thirdparty.providers.twitter
-
-
-
-
supertokens_python.recipe.thirdparty.providers.utils
-
-
-
-
-
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/thirdparty/providers/linkedin.html b/html/supertokens_python/recipe/thirdparty/providers/linkedin.html deleted file mode 100644 index ac96778d6..000000000 --- a/html/supertokens_python/recipe/thirdparty/providers/linkedin.html +++ /dev/null @@ -1,340 +0,0 @@ - - - - - - -supertokens_python.recipe.thirdparty.providers.linkedin API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.thirdparty.providers.linkedin

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from typing import Any, Dict, Optional, Union
-from supertokens_python.recipe.thirdparty.providers.utils import do_get_request
-
-from supertokens_python.recipe.thirdparty.types import (
-    RawUserInfoFromProvider,
-    UserInfo,
-    UserInfoEmail,
-)
-from ..provider import (
-    Provider,
-    ProviderConfigForClient,
-    ProviderInput,
-    UserFields,
-    UserInfoMap,
-)
-
-from .custom import (
-    GenericProvider,
-    NewProvider,
-)
-
-
-class LinkedinImpl(GenericProvider):
-    async def get_config_for_client_type(
-        self, client_type: Optional[str], user_context: Dict[str, Any]
-    ) -> ProviderConfigForClient:
-        config = await super().get_config_for_client_type(client_type, user_context)
-
-        if config.scope is None:
-            # https://learn.microsoft.com/en-us/linkedin/consumer/integrations/self-serve/sign-in-with-linkedin-v2?context=linkedin%2Fconsumer%2Fcontext#authenticating-members
-            config.scope = ["openid", "profile", "email"]
-
-        return config
-
-    async def get_user_info(
-        self, oauth_tokens: Dict[str, Any], user_context: Dict[str, Any]
-    ) -> UserInfo:
-        access_token: Union[str, None] = oauth_tokens.get("access_token")
-
-        if access_token is None:
-            raise Exception("Access token not found")
-
-        headers = {
-            "Authorization": f"Bearer {access_token}",
-        }
-
-        raw_user_info_from_provider = RawUserInfoFromProvider({}, {})
-        # https://learn.microsoft.com/en-us/linkedin/consumer/integrations/self-serve/sign-in-with-linkedin-v2?context=linkedin%2Fconsumer%2Fcontext#sample-api-response
-        user_info = await do_get_request(
-            "https://api.linkedin.com/v2/userinfo", headers=headers
-        )
-        raw_user_info_from_provider.from_user_info_api = user_info
-
-        return UserInfo(
-            third_party_user_id=raw_user_info_from_provider.from_user_info_api.get("sub"),  # type: ignore
-            email=UserInfoEmail(
-                email=raw_user_info_from_provider.from_user_info_api.get("email"),  # type: ignore
-                is_verified=raw_user_info_from_provider.from_user_info_api.get("email_verified"),  # type: ignore
-            ),
-        )
-
-
-def Linkedin(input: ProviderInput) -> Provider:  # pylint: disable=redefined-builtin
-    if not input.config.name:
-        input.config.name = "Linkedin"
-
-    if not input.config.authorization_endpoint:
-        input.config.authorization_endpoint = (
-            "https://www.linkedin.com/oauth/v2/authorization"
-        )
-
-    if not input.config.token_endpoint:
-        input.config.token_endpoint = "https://www.linkedin.com/oauth/v2/accessToken"
-
-    if input.config.user_info_map is None:
-        input.config.user_info_map = UserInfoMap(UserFields(), UserFields())
-
-    if input.config.user_info_map.from_user_info_api is None:
-        input.config.user_info_map.from_user_info_api = UserFields()
-
-    if input.config.user_info_map.from_user_info_api.user_id is None:
-        input.config.user_info_map.from_user_info_api.user_id = "id"
-
-    if input.config.user_info_map.from_user_info_api.email is None:
-        input.config.user_info_map.from_user_info_api.email = "email"
-
-    if input.config.user_info_map.from_user_info_api.email_verified is None:
-        input.config.user_info_map.from_user_info_api.email = "verified"
-
-    return NewProvider(input, LinkedinImpl)
-
-
-
-
-
-
-
-

Functions

-
-
-def Linkedin(input: ProviderInput) ‑> Provider -
-
-
-
- -Expand source code - -
def Linkedin(input: ProviderInput) -> Provider:  # pylint: disable=redefined-builtin
-    if not input.config.name:
-        input.config.name = "Linkedin"
-
-    if not input.config.authorization_endpoint:
-        input.config.authorization_endpoint = (
-            "https://www.linkedin.com/oauth/v2/authorization"
-        )
-
-    if not input.config.token_endpoint:
-        input.config.token_endpoint = "https://www.linkedin.com/oauth/v2/accessToken"
-
-    if input.config.user_info_map is None:
-        input.config.user_info_map = UserInfoMap(UserFields(), UserFields())
-
-    if input.config.user_info_map.from_user_info_api is None:
-        input.config.user_info_map.from_user_info_api = UserFields()
-
-    if input.config.user_info_map.from_user_info_api.user_id is None:
-        input.config.user_info_map.from_user_info_api.user_id = "id"
-
-    if input.config.user_info_map.from_user_info_api.email is None:
-        input.config.user_info_map.from_user_info_api.email = "email"
-
-    if input.config.user_info_map.from_user_info_api.email_verified is None:
-        input.config.user_info_map.from_user_info_api.email = "verified"
-
-    return NewProvider(input, LinkedinImpl)
-
-
-
-
-
-

Classes

-
-
-class LinkedinImpl -(provider_config: ProviderConfig) -
-
-
-
- -Expand source code - -
class LinkedinImpl(GenericProvider):
-    async def get_config_for_client_type(
-        self, client_type: Optional[str], user_context: Dict[str, Any]
-    ) -> ProviderConfigForClient:
-        config = await super().get_config_for_client_type(client_type, user_context)
-
-        if config.scope is None:
-            # https://learn.microsoft.com/en-us/linkedin/consumer/integrations/self-serve/sign-in-with-linkedin-v2?context=linkedin%2Fconsumer%2Fcontext#authenticating-members
-            config.scope = ["openid", "profile", "email"]
-
-        return config
-
-    async def get_user_info(
-        self, oauth_tokens: Dict[str, Any], user_context: Dict[str, Any]
-    ) -> UserInfo:
-        access_token: Union[str, None] = oauth_tokens.get("access_token")
-
-        if access_token is None:
-            raise Exception("Access token not found")
-
-        headers = {
-            "Authorization": f"Bearer {access_token}",
-        }
-
-        raw_user_info_from_provider = RawUserInfoFromProvider({}, {})
-        # https://learn.microsoft.com/en-us/linkedin/consumer/integrations/self-serve/sign-in-with-linkedin-v2?context=linkedin%2Fconsumer%2Fcontext#sample-api-response
-        user_info = await do_get_request(
-            "https://api.linkedin.com/v2/userinfo", headers=headers
-        )
-        raw_user_info_from_provider.from_user_info_api = user_info
-
-        return UserInfo(
-            third_party_user_id=raw_user_info_from_provider.from_user_info_api.get("sub"),  # type: ignore
-            email=UserInfoEmail(
-                email=raw_user_info_from_provider.from_user_info_api.get("email"),  # type: ignore
-                is_verified=raw_user_info_from_provider.from_user_info_api.get("email_verified"),  # type: ignore
-            ),
-        )
-
-

Ancestors

- -

Methods

-
-
-async def get_config_for_client_type(self, client_type: Optional[str], user_context: Dict[str, Any]) ‑> ProviderConfigForClient -
-
-
-
- -Expand source code - -
async def get_config_for_client_type(
-    self, client_type: Optional[str], user_context: Dict[str, Any]
-) -> ProviderConfigForClient:
-    config = await super().get_config_for_client_type(client_type, user_context)
-
-    if config.scope is None:
-        # https://learn.microsoft.com/en-us/linkedin/consumer/integrations/self-serve/sign-in-with-linkedin-v2?context=linkedin%2Fconsumer%2Fcontext#authenticating-members
-        config.scope = ["openid", "profile", "email"]
-
-    return config
-
-
-
-async def get_user_info(self, oauth_tokens: Dict[str, Any], user_context: Dict[str, Any]) ‑> UserInfo -
-
-
-
- -Expand source code - -
async def get_user_info(
-    self, oauth_tokens: Dict[str, Any], user_context: Dict[str, Any]
-) -> UserInfo:
-    access_token: Union[str, None] = oauth_tokens.get("access_token")
-
-    if access_token is None:
-        raise Exception("Access token not found")
-
-    headers = {
-        "Authorization": f"Bearer {access_token}",
-    }
-
-    raw_user_info_from_provider = RawUserInfoFromProvider({}, {})
-    # https://learn.microsoft.com/en-us/linkedin/consumer/integrations/self-serve/sign-in-with-linkedin-v2?context=linkedin%2Fconsumer%2Fcontext#sample-api-response
-    user_info = await do_get_request(
-        "https://api.linkedin.com/v2/userinfo", headers=headers
-    )
-    raw_user_info_from_provider.from_user_info_api = user_info
-
-    return UserInfo(
-        third_party_user_id=raw_user_info_from_provider.from_user_info_api.get("sub"),  # type: ignore
-        email=UserInfoEmail(
-            email=raw_user_info_from_provider.from_user_info_api.get("email"),  # type: ignore
-            is_verified=raw_user_info_from_provider.from_user_info_api.get("email_verified"),  # type: ignore
-        ),
-    )
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/thirdparty/providers/okta.html b/html/supertokens_python/recipe/thirdparty/providers/okta.html deleted file mode 100644 index e5e6b16b6..000000000 --- a/html/supertokens_python/recipe/thirdparty/providers/okta.html +++ /dev/null @@ -1,270 +0,0 @@ - - - - - - -supertokens_python.recipe.thirdparty.providers.okta API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.thirdparty.providers.okta

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from typing import Any, Dict, Optional
-from .custom import GenericProvider, NewProvider
-from ..provider import (
-    Provider,
-    ProviderConfigForClient,
-    ProviderInput,
-)
-from .utils import normalise_oidc_endpoint_to_include_well_known
-from supertokens_python.normalised_url_domain import NormalisedURLDomain
-from supertokens_python.normalised_url_path import NormalisedURLPath
-
-
-class OktaImpl(GenericProvider):
-    async def get_config_for_client_type(
-        self, client_type: Optional[str], user_context: Dict[str, Any]
-    ) -> ProviderConfigForClient:
-        config = await super().get_config_for_client_type(client_type, user_context)
-
-        if (
-            config.additional_config is None
-            or config.additional_config.get("oktaDomain") is None
-        ):
-            if not config.oidc_discovery_endpoint:
-                raise Exception(
-                    "Please provide the oktaDomain in the additionalConfig of the Okta provider."
-                )
-        else:
-            okta_domain = config.additional_config["oktaDomain"]
-            oidc_domain = NormalisedURLDomain(okta_domain)
-            oidc_path = NormalisedURLPath("/.well-known/openid-configuration")
-            config.oidc_discovery_endpoint = (
-                oidc_domain.get_as_string_dangerous()
-                + oidc_path.get_as_string_dangerous()
-            )
-
-        if not config.oidc_discovery_endpoint:
-            raise Exception("should never happen")
-
-        # The config could be coming from core where we didn't add the well-known previously
-        config.oidc_discovery_endpoint = normalise_oidc_endpoint_to_include_well_known(
-            config.oidc_discovery_endpoint
-        )
-
-        if config.scope is None:
-            config.scope = ["openid", "email"]
-
-        # TODO later if required, client assertion impl
-
-        return config
-
-
-def Okta(input: ProviderInput) -> Provider:  # pylint: disable=redefined-builtin
-    if not input.config.name:
-        input.config.name = "Okta"
-
-    return NewProvider(input, OktaImpl)
-
-
-
-
-
-
-
-

Functions

-
-
-def Okta(input: ProviderInput) ‑> Provider -
-
-
-
- -Expand source code - -
def Okta(input: ProviderInput) -> Provider:  # pylint: disable=redefined-builtin
-    if not input.config.name:
-        input.config.name = "Okta"
-
-    return NewProvider(input, OktaImpl)
-
-
-
-
-
-

Classes

-
-
-class OktaImpl -(provider_config: ProviderConfig) -
-
-
-
- -Expand source code - -
class OktaImpl(GenericProvider):
-    async def get_config_for_client_type(
-        self, client_type: Optional[str], user_context: Dict[str, Any]
-    ) -> ProviderConfigForClient:
-        config = await super().get_config_for_client_type(client_type, user_context)
-
-        if (
-            config.additional_config is None
-            or config.additional_config.get("oktaDomain") is None
-        ):
-            if not config.oidc_discovery_endpoint:
-                raise Exception(
-                    "Please provide the oktaDomain in the additionalConfig of the Okta provider."
-                )
-        else:
-            okta_domain = config.additional_config["oktaDomain"]
-            oidc_domain = NormalisedURLDomain(okta_domain)
-            oidc_path = NormalisedURLPath("/.well-known/openid-configuration")
-            config.oidc_discovery_endpoint = (
-                oidc_domain.get_as_string_dangerous()
-                + oidc_path.get_as_string_dangerous()
-            )
-
-        if not config.oidc_discovery_endpoint:
-            raise Exception("should never happen")
-
-        # The config could be coming from core where we didn't add the well-known previously
-        config.oidc_discovery_endpoint = normalise_oidc_endpoint_to_include_well_known(
-            config.oidc_discovery_endpoint
-        )
-
-        if config.scope is None:
-            config.scope = ["openid", "email"]
-
-        # TODO later if required, client assertion impl
-
-        return config
-
-

Ancestors

- -

Methods

-
-
-async def get_config_for_client_type(self, client_type: Optional[str], user_context: Dict[str, Any]) ‑> ProviderConfigForClient -
-
-
-
- -Expand source code - -
async def get_config_for_client_type(
-    self, client_type: Optional[str], user_context: Dict[str, Any]
-) -> ProviderConfigForClient:
-    config = await super().get_config_for_client_type(client_type, user_context)
-
-    if (
-        config.additional_config is None
-        or config.additional_config.get("oktaDomain") is None
-    ):
-        if not config.oidc_discovery_endpoint:
-            raise Exception(
-                "Please provide the oktaDomain in the additionalConfig of the Okta provider."
-            )
-    else:
-        okta_domain = config.additional_config["oktaDomain"]
-        oidc_domain = NormalisedURLDomain(okta_domain)
-        oidc_path = NormalisedURLPath("/.well-known/openid-configuration")
-        config.oidc_discovery_endpoint = (
-            oidc_domain.get_as_string_dangerous()
-            + oidc_path.get_as_string_dangerous()
-        )
-
-    if not config.oidc_discovery_endpoint:
-        raise Exception("should never happen")
-
-    # The config could be coming from core where we didn't add the well-known previously
-    config.oidc_discovery_endpoint = normalise_oidc_endpoint_to_include_well_known(
-        config.oidc_discovery_endpoint
-    )
-
-    if config.scope is None:
-        config.scope = ["openid", "email"]
-
-    # TODO later if required, client assertion impl
-
-    return config
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/thirdparty/providers/twitter.html b/html/supertokens_python/recipe/thirdparty/providers/twitter.html deleted file mode 100644 index 85a5b2435..000000000 --- a/html/supertokens_python/recipe/thirdparty/providers/twitter.html +++ /dev/null @@ -1,385 +0,0 @@ - - - - - - -supertokens_python.recipe.thirdparty.providers.twitter API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.thirdparty.providers.twitter

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from base64 import b64encode
-from typing import Any, Dict, Optional
-from supertokens_python.recipe.thirdparty.provider import RedirectUriInfo
-from supertokens_python.recipe.thirdparty.providers.utils import (
-    do_post_request,
-    DEV_OAUTH_REDIRECT_URL,
-    get_actual_client_id_from_development_client_id,
-)
-from ..provider import (
-    Provider,
-    ProviderConfigForClient,
-    ProviderInput,
-    UserFields,
-    UserInfoMap,
-)
-
-from .custom import (
-    GenericProvider,
-    NewProvider,
-    is_using_development_client_id,
-)
-
-
-class TwitterImpl(GenericProvider):
-    async def get_config_for_client_type(
-        self, client_type: Optional[str], user_context: Dict[str, Any]
-    ) -> ProviderConfigForClient:
-        config = await super().get_config_for_client_type(client_type, user_context)
-
-        if config.scope is None:
-            config.scope = ["users.read", "tweet.read"]
-
-        if config.force_pkce is None:
-            config.force_pkce = True
-
-        return config
-
-    async def exchange_auth_code_for_oauth_tokens(
-        self, redirect_uri_info: RedirectUriInfo, user_context: Dict[str, Any]
-    ) -> Dict[str, Any]:
-
-        client_id = self.config.client_id
-        redirect_uri = redirect_uri_info.redirect_uri_on_provider_dashboard
-
-        # We need to do this because we don't call the original implementation
-        # Transformation needed for dev keys BEGIN
-        if is_using_development_client_id(self.config.client_id):
-            client_id = get_actual_client_id_from_development_client_id(
-                self.config.client_id
-            )
-            redirect_uri = DEV_OAUTH_REDIRECT_URL
-        # Transformation needed for dev keys END
-
-        credentials = client_id + ":" + (self.config.client_secret or "")
-        auth_token = b64encode(credentials.encode()).decode()
-
-        twitter_oauth_tokens_params: Dict[str, Any] = {
-            "grant_type": "authorization_code",
-            "client_id": client_id,
-            "code_verifier": redirect_uri_info.pkce_code_verifier,
-            "redirect_uri": redirect_uri,
-            "code": redirect_uri_info.redirect_uri_query_params["code"],
-        }
-
-        twitter_oauth_tokens_params = {
-            **twitter_oauth_tokens_params,
-            **(self.config.token_endpoint_body_params or {}),
-        }
-
-        assert self.config.token_endpoint is not None
-
-        _, body = await do_post_request(
-            self.config.token_endpoint,
-            body_params=twitter_oauth_tokens_params,
-            headers={"Authorization": f"Basic {auth_token}"},
-        )
-        return body
-
-
-def Twitter(input: ProviderInput) -> Provider:  # pylint: disable=redefined-builtin
-    if not input.config.name:
-        input.config.name = "Twitter"
-
-    if not input.config.authorization_endpoint:
-        input.config.authorization_endpoint = "https://twitter.com/i/oauth2/authorize"
-
-    if not input.config.token_endpoint:
-        input.config.token_endpoint = "https://api.twitter.com/2/oauth2/token"
-
-    if not input.config.user_info_endpoint:
-        input.config.user_info_endpoint = "https://api.twitter.com/2/users/me"
-
-    if input.config.require_email is None:
-        input.config.require_email = False
-
-    if input.config.user_info_map is None:
-        input.config.user_info_map = UserInfoMap(UserFields(), UserFields())
-
-    if input.config.user_info_map.from_user_info_api is None:
-        input.config.user_info_map.from_user_info_api = UserFields()
-
-    if input.config.user_info_map.from_user_info_api.user_id is None:
-        input.config.user_info_map.from_user_info_api.user_id = "data.id"
-
-    return NewProvider(input, TwitterImpl)
-
-
-
-
-
-
-
-

Functions

-
-
-def Twitter(input: ProviderInput) ‑> Provider -
-
-
-
- -Expand source code - -
def Twitter(input: ProviderInput) -> Provider:  # pylint: disable=redefined-builtin
-    if not input.config.name:
-        input.config.name = "Twitter"
-
-    if not input.config.authorization_endpoint:
-        input.config.authorization_endpoint = "https://twitter.com/i/oauth2/authorize"
-
-    if not input.config.token_endpoint:
-        input.config.token_endpoint = "https://api.twitter.com/2/oauth2/token"
-
-    if not input.config.user_info_endpoint:
-        input.config.user_info_endpoint = "https://api.twitter.com/2/users/me"
-
-    if input.config.require_email is None:
-        input.config.require_email = False
-
-    if input.config.user_info_map is None:
-        input.config.user_info_map = UserInfoMap(UserFields(), UserFields())
-
-    if input.config.user_info_map.from_user_info_api is None:
-        input.config.user_info_map.from_user_info_api = UserFields()
-
-    if input.config.user_info_map.from_user_info_api.user_id is None:
-        input.config.user_info_map.from_user_info_api.user_id = "data.id"
-
-    return NewProvider(input, TwitterImpl)
-
-
-
-
-
-

Classes

-
-
-class TwitterImpl -(provider_config: ProviderConfig) -
-
-
-
- -Expand source code - -
class TwitterImpl(GenericProvider):
-    async def get_config_for_client_type(
-        self, client_type: Optional[str], user_context: Dict[str, Any]
-    ) -> ProviderConfigForClient:
-        config = await super().get_config_for_client_type(client_type, user_context)
-
-        if config.scope is None:
-            config.scope = ["users.read", "tweet.read"]
-
-        if config.force_pkce is None:
-            config.force_pkce = True
-
-        return config
-
-    async def exchange_auth_code_for_oauth_tokens(
-        self, redirect_uri_info: RedirectUriInfo, user_context: Dict[str, Any]
-    ) -> Dict[str, Any]:
-
-        client_id = self.config.client_id
-        redirect_uri = redirect_uri_info.redirect_uri_on_provider_dashboard
-
-        # We need to do this because we don't call the original implementation
-        # Transformation needed for dev keys BEGIN
-        if is_using_development_client_id(self.config.client_id):
-            client_id = get_actual_client_id_from_development_client_id(
-                self.config.client_id
-            )
-            redirect_uri = DEV_OAUTH_REDIRECT_URL
-        # Transformation needed for dev keys END
-
-        credentials = client_id + ":" + (self.config.client_secret or "")
-        auth_token = b64encode(credentials.encode()).decode()
-
-        twitter_oauth_tokens_params: Dict[str, Any] = {
-            "grant_type": "authorization_code",
-            "client_id": client_id,
-            "code_verifier": redirect_uri_info.pkce_code_verifier,
-            "redirect_uri": redirect_uri,
-            "code": redirect_uri_info.redirect_uri_query_params["code"],
-        }
-
-        twitter_oauth_tokens_params = {
-            **twitter_oauth_tokens_params,
-            **(self.config.token_endpoint_body_params or {}),
-        }
-
-        assert self.config.token_endpoint is not None
-
-        _, body = await do_post_request(
-            self.config.token_endpoint,
-            body_params=twitter_oauth_tokens_params,
-            headers={"Authorization": f"Basic {auth_token}"},
-        )
-        return body
-
-

Ancestors

- -

Methods

-
-
-async def exchange_auth_code_for_oauth_tokens(self, redirect_uri_info: RedirectUriInfo, user_context: Dict[str, Any]) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
async def exchange_auth_code_for_oauth_tokens(
-    self, redirect_uri_info: RedirectUriInfo, user_context: Dict[str, Any]
-) -> Dict[str, Any]:
-
-    client_id = self.config.client_id
-    redirect_uri = redirect_uri_info.redirect_uri_on_provider_dashboard
-
-    # We need to do this because we don't call the original implementation
-    # Transformation needed for dev keys BEGIN
-    if is_using_development_client_id(self.config.client_id):
-        client_id = get_actual_client_id_from_development_client_id(
-            self.config.client_id
-        )
-        redirect_uri = DEV_OAUTH_REDIRECT_URL
-    # Transformation needed for dev keys END
-
-    credentials = client_id + ":" + (self.config.client_secret or "")
-    auth_token = b64encode(credentials.encode()).decode()
-
-    twitter_oauth_tokens_params: Dict[str, Any] = {
-        "grant_type": "authorization_code",
-        "client_id": client_id,
-        "code_verifier": redirect_uri_info.pkce_code_verifier,
-        "redirect_uri": redirect_uri,
-        "code": redirect_uri_info.redirect_uri_query_params["code"],
-    }
-
-    twitter_oauth_tokens_params = {
-        **twitter_oauth_tokens_params,
-        **(self.config.token_endpoint_body_params or {}),
-    }
-
-    assert self.config.token_endpoint is not None
-
-    _, body = await do_post_request(
-        self.config.token_endpoint,
-        body_params=twitter_oauth_tokens_params,
-        headers={"Authorization": f"Basic {auth_token}"},
-    )
-    return body
-
-
-
-async def get_config_for_client_type(self, client_type: Optional[str], user_context: Dict[str, Any]) ‑> ProviderConfigForClient -
-
-
-
- -Expand source code - -
async def get_config_for_client_type(
-    self, client_type: Optional[str], user_context: Dict[str, Any]
-) -> ProviderConfigForClient:
-    config = await super().get_config_for_client_type(client_type, user_context)
-
-    if config.scope is None:
-        config.scope = ["users.read", "tweet.read"]
-
-    if config.force_pkce is None:
-        config.force_pkce = True
-
-    return config
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/thirdparty/providers/utils.html b/html/supertokens_python/recipe/thirdparty/providers/utils.html deleted file mode 100644 index 3655535d2..000000000 --- a/html/supertokens_python/recipe/thirdparty/providers/utils.html +++ /dev/null @@ -1,277 +0,0 @@ - - - - - - -supertokens_python.recipe.thirdparty.providers.utils API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.thirdparty.providers.utils

-
-
-
- -Expand source code - -
from typing import Any, Dict, Optional, Tuple
-
-from httpx import AsyncClient
-
-from supertokens_python.logger import log_debug_message
-from supertokens_python.normalised_url_domain import NormalisedURLDomain
-from supertokens_python.normalised_url_path import NormalisedURLPath
-
-DEV_OAUTH_CLIENT_IDS = [
-    "1060725074195-kmeum4crr01uirfl2op9kd5acmi9jutn.apps.googleusercontent.com",
-    # google client id
-    "467101b197249757c71f",  # github client id
-]
-DEV_KEY_IDENTIFIER = "4398792-"
-DEV_OAUTH_AUTHORIZATION_URL = "https://supertokens.io/dev/oauth/redirect-to-provider"
-DEV_OAUTH_REDIRECT_URL = "https://supertokens.io/dev/oauth/redirect-to-app"
-
-
-def is_using_oauth_development_client_id(client_id: str):
-    return client_id.startswith(DEV_KEY_IDENTIFIER) or client_id in DEV_OAUTH_CLIENT_IDS
-
-
-def get_actual_client_id_from_development_client_id(client_id: str) -> str:
-    if client_id.startswith(DEV_KEY_IDENTIFIER):
-        return client_id.split(DEV_KEY_IDENTIFIER, 1)[1]
-    return client_id
-
-
-async def do_get_request(
-    url: str,
-    query_params: Optional[Dict[str, str]] = None,
-    headers: Optional[Dict[str, str]] = None,
-) -> Dict[str, Any]:
-    if query_params is None:
-        query_params = {}
-    if headers is None:
-        headers = {}
-
-    async with AsyncClient(timeout=30.0) as client:
-        res = await client.get(url, params=query_params, headers=headers)  # type:ignore
-
-        log_debug_message(
-            "Received response with status %s and body %s", res.status_code, res.text
-        )
-
-        return res.json()
-
-
-async def do_post_request(
-    url: str,
-    body_params: Optional[Dict[str, str]] = None,
-    headers: Optional[Dict[str, str]] = None,
-) -> Tuple[int, Dict[str, Any]]:
-    if body_params is None:
-        body_params = {}
-    if headers is None:
-        headers = {}
-
-    headers["content-type"] = "application/x-www-form-urlencoded"
-    headers["accept"] = "application/json"
-
-    async with AsyncClient(timeout=30.0) as client:
-        res = await client.post(url, data=body_params, headers=headers)  # type:ignore
-        log_debug_message(
-            "Received response with status %s and body %s", res.status_code, res.text
-        )
-        return res.status_code, res.json()
-
-
-def normalise_oidc_endpoint_to_include_well_known(url: str) -> str:
-    # We call this only for built-in providers that use OIDC.
-    # We no longer generically add well-known in the custom provider
-    if url.endswith("/.well-known/openid-configuration"):
-        return url
-
-    try:
-        normalised_domain = NormalisedURLDomain(url)
-        normalised_path = NormalisedURLPath(url)
-
-        normalised_path = normalised_path.append(
-            NormalisedURLPath("/.well-known/openid-configuration")
-        )
-    except Exception:
-        return url  # Return original URL if normalization fails
-
-    return (
-        normalised_domain.get_as_string_dangerous()
-        + normalised_path.get_as_string_dangerous()
-    )
-
-
-
-
-
-
-
-

Functions

-
-
-async def do_get_request(url: str, query_params: Optional[Dict[str, str]] = None, headers: Optional[Dict[str, str]] = None) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
async def do_get_request(
-    url: str,
-    query_params: Optional[Dict[str, str]] = None,
-    headers: Optional[Dict[str, str]] = None,
-) -> Dict[str, Any]:
-    if query_params is None:
-        query_params = {}
-    if headers is None:
-        headers = {}
-
-    async with AsyncClient(timeout=30.0) as client:
-        res = await client.get(url, params=query_params, headers=headers)  # type:ignore
-
-        log_debug_message(
-            "Received response with status %s and body %s", res.status_code, res.text
-        )
-
-        return res.json()
-
-
-
-async def do_post_request(url: str, body_params: Optional[Dict[str, str]] = None, headers: Optional[Dict[str, str]] = None) ‑> Tuple[int, Dict[str, Any]] -
-
-
-
- -Expand source code - -
async def do_post_request(
-    url: str,
-    body_params: Optional[Dict[str, str]] = None,
-    headers: Optional[Dict[str, str]] = None,
-) -> Tuple[int, Dict[str, Any]]:
-    if body_params is None:
-        body_params = {}
-    if headers is None:
-        headers = {}
-
-    headers["content-type"] = "application/x-www-form-urlencoded"
-    headers["accept"] = "application/json"
-
-    async with AsyncClient(timeout=30.0) as client:
-        res = await client.post(url, data=body_params, headers=headers)  # type:ignore
-        log_debug_message(
-            "Received response with status %s and body %s", res.status_code, res.text
-        )
-        return res.status_code, res.json()
-
-
-
-def get_actual_client_id_from_development_client_id(client_id: str) ‑> str -
-
-
-
- -Expand source code - -
def get_actual_client_id_from_development_client_id(client_id: str) -> str:
-    if client_id.startswith(DEV_KEY_IDENTIFIER):
-        return client_id.split(DEV_KEY_IDENTIFIER, 1)[1]
-    return client_id
-
-
-
-def is_using_oauth_development_client_id(client_id: str) -
-
-
-
- -Expand source code - -
def is_using_oauth_development_client_id(client_id: str):
-    return client_id.startswith(DEV_KEY_IDENTIFIER) or client_id in DEV_OAUTH_CLIENT_IDS
-
-
-
-def normalise_oidc_endpoint_to_include_well_known(url: str) ‑> str -
-
-
-
- -Expand source code - -
def normalise_oidc_endpoint_to_include_well_known(url: str) -> str:
-    # We call this only for built-in providers that use OIDC.
-    # We no longer generically add well-known in the custom provider
-    if url.endswith("/.well-known/openid-configuration"):
-        return url
-
-    try:
-        normalised_domain = NormalisedURLDomain(url)
-        normalised_path = NormalisedURLPath(url)
-
-        normalised_path = normalised_path.append(
-            NormalisedURLPath("/.well-known/openid-configuration")
-        )
-    except Exception:
-        return url  # Return original URL if normalization fails
-
-    return (
-        normalised_domain.get_as_string_dangerous()
-        + normalised_path.get_as_string_dangerous()
-    )
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/thirdparty/recipe.html b/html/supertokens_python/recipe/thirdparty/recipe.html deleted file mode 100644 index 04bb9cd8f..000000000 --- a/html/supertokens_python/recipe/thirdparty/recipe.html +++ /dev/null @@ -1,711 +0,0 @@ - - - - - - -supertokens_python.recipe.thirdparty.recipe API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.thirdparty.recipe

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from os import environ
-from typing import TYPE_CHECKING, Any, Dict, List, Union
-
-from supertokens_python.normalised_url_path import NormalisedURLPath
-from supertokens_python.querier import Querier
-from supertokens_python.recipe_module import APIHandled, RecipeModule
-
-from .api.implementation import APIImplementation
-from .interfaces import APIInterface, APIOptions, RecipeInterface
-from .recipe_implementation import RecipeImplementation
-from ..emailverification.interfaces import GetEmailForUserIdOkResult, UnknownUserIdError
-from ...post_init_callbacks import PostSTInitCallbacks
-
-if TYPE_CHECKING:
-    from supertokens_python.framework.request import BaseRequest
-    from supertokens_python.framework.response import BaseResponse
-    from supertokens_python.supertokens import AppInfo
-    from .utils import SignInAndUpFeature, InputOverrideConfig
-
-from supertokens_python.exceptions import SuperTokensError, raise_general_exception
-from supertokens_python.recipe.emailverification.recipe import EmailVerificationRecipe
-from supertokens_python.recipe.multitenancy.recipe import MultitenancyRecipe
-
-from .api import (
-    handle_apple_redirect_api,
-    handle_authorisation_url_api,
-    handle_sign_in_up_api,
-)
-from .constants import APPLE_REDIRECT_HANDLER, AUTHORISATIONURL, SIGNINUP
-from .exceptions import SuperTokensThirdPartyError
-from .types import ThirdPartyIngredients
-from .utils import validate_and_normalise_user_input
-
-
-class ThirdPartyRecipe(RecipeModule):
-    recipe_id = "thirdparty"
-    __instance = None
-
-    def __init__(
-        self,
-        recipe_id: str,
-        app_info: AppInfo,
-        sign_in_and_up_feature: SignInAndUpFeature,
-        _ingredients: ThirdPartyIngredients,
-        override: Union[InputOverrideConfig, None] = None,
-    ):
-        super().__init__(recipe_id, app_info)
-        self.config = validate_and_normalise_user_input(
-            sign_in_and_up_feature,
-            override,
-        )
-        self.providers = self.config.sign_in_and_up_feature.providers
-        recipe_implementation = RecipeImplementation(
-            Querier.get_instance(recipe_id), self.providers
-        )
-        self.recipe_implementation: RecipeInterface = (
-            recipe_implementation
-            if self.config.override.functions is None
-            else self.config.override.functions(recipe_implementation)
-        )
-        api_implementation = APIImplementation()
-        self.api_implementation: APIInterface = (
-            api_implementation
-            if self.config.override.apis is None
-            else self.config.override.apis(api_implementation)
-        )
-
-        def callback():
-            ev_recipe = EmailVerificationRecipe.get_instance_optional()
-            if ev_recipe:
-                ev_recipe.add_get_email_for_user_id_func(self.get_email_for_user_id)
-
-            mt_recipe = MultitenancyRecipe.get_instance_optional()
-            if mt_recipe:
-                mt_recipe.static_third_party_providers = self.providers
-
-        PostSTInitCallbacks.add_post_init_callback(callback)
-
-    def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
-        return isinstance(err, SuperTokensError) and (
-            isinstance(err, SuperTokensThirdPartyError)
-        )
-
-    def get_apis_handled(self) -> List[APIHandled]:
-        return [
-            APIHandled(
-                NormalisedURLPath(SIGNINUP),
-                "post",
-                SIGNINUP,
-                self.api_implementation.disable_sign_in_up_post,
-            ),
-            APIHandled(
-                NormalisedURLPath(AUTHORISATIONURL),
-                "get",
-                AUTHORISATIONURL,
-                self.api_implementation.disable_authorisation_url_get,
-            ),
-            APIHandled(
-                NormalisedURLPath(APPLE_REDIRECT_HANDLER),
-                "post",
-                APPLE_REDIRECT_HANDLER,
-                self.api_implementation.disable_apple_redirect_handler_post,
-            ),
-        ]
-
-    async def handle_api_request(
-        self,
-        request_id: str,
-        tenant_id: str,
-        request: BaseRequest,
-        path: NormalisedURLPath,
-        method: str,
-        response: BaseResponse,
-        user_context: Dict[str, Any],
-    ):
-        api_options = APIOptions(
-            request,
-            response,
-            self.recipe_id,
-            self.config,
-            self.recipe_implementation,
-            self.providers,
-            self.app_info,
-        )
-
-        if request_id == SIGNINUP:
-            return await handle_sign_in_up_api(
-                self.api_implementation, tenant_id, api_options, user_context
-            )
-        if request_id == AUTHORISATIONURL:
-            return await handle_authorisation_url_api(
-                self.api_implementation, tenant_id, api_options, user_context
-            )
-        if request_id == APPLE_REDIRECT_HANDLER:
-            return await handle_apple_redirect_api(
-                self.api_implementation, api_options, user_context
-            )
-
-        return None
-
-    async def handle_error(
-        self,
-        request: BaseRequest,
-        err: SuperTokensError,
-        response: BaseResponse,
-        user_context: Dict[str, Any],
-    ) -> BaseResponse:  # type: ignore
-        if isinstance(err, SuperTokensThirdPartyError):
-            raise err
-
-    def get_all_cors_headers(self) -> List[str]:
-        return []
-
-    @staticmethod
-    def init(
-        sign_in_and_up_feature: SignInAndUpFeature,
-        override: Union[InputOverrideConfig, None] = None,
-    ):
-        def func(app_info: AppInfo):
-            if ThirdPartyRecipe.__instance is None:
-                ingredients = ThirdPartyIngredients()
-                ThirdPartyRecipe.__instance = ThirdPartyRecipe(
-                    ThirdPartyRecipe.recipe_id,
-                    app_info,
-                    sign_in_and_up_feature,
-                    ingredients,
-                    override,
-                )
-                return ThirdPartyRecipe.__instance
-            raise_general_exception(
-                "ThirdParty recipe has already been initialised. Please check your code for bugs."
-            )
-
-        return func
-
-    @staticmethod
-    def get_instance() -> ThirdPartyRecipe:
-        if ThirdPartyRecipe.__instance is not None:
-            return ThirdPartyRecipe.__instance
-        raise_general_exception(
-            "Initialisation not done. Did you forget to call the SuperTokens.init function?"
-        )
-
-    @staticmethod
-    def reset():
-        if ("SUPERTOKENS_ENV" not in environ) or (
-            environ["SUPERTOKENS_ENV"] != "testing"
-        ):
-            raise_general_exception("calling testing function in non testing env")
-        ThirdPartyRecipe.__instance = None
-
-    # instance functions below...............
-
-    async def get_email_for_user_id(self, user_id: str, user_context: Dict[str, Any]):
-        user_info = await self.recipe_implementation.get_user_by_id(
-            user_id, user_context
-        )
-        if user_info is not None:
-            return GetEmailForUserIdOkResult(user_info.email)
-
-        return UnknownUserIdError()
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class ThirdPartyRecipe -(recipe_id: str, app_info: AppInfo, sign_in_and_up_feature: SignInAndUpFeature, _ingredients: ThirdPartyIngredients, override: Union[InputOverrideConfig, None] = None) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class ThirdPartyRecipe(RecipeModule):
-    recipe_id = "thirdparty"
-    __instance = None
-
-    def __init__(
-        self,
-        recipe_id: str,
-        app_info: AppInfo,
-        sign_in_and_up_feature: SignInAndUpFeature,
-        _ingredients: ThirdPartyIngredients,
-        override: Union[InputOverrideConfig, None] = None,
-    ):
-        super().__init__(recipe_id, app_info)
-        self.config = validate_and_normalise_user_input(
-            sign_in_and_up_feature,
-            override,
-        )
-        self.providers = self.config.sign_in_and_up_feature.providers
-        recipe_implementation = RecipeImplementation(
-            Querier.get_instance(recipe_id), self.providers
-        )
-        self.recipe_implementation: RecipeInterface = (
-            recipe_implementation
-            if self.config.override.functions is None
-            else self.config.override.functions(recipe_implementation)
-        )
-        api_implementation = APIImplementation()
-        self.api_implementation: APIInterface = (
-            api_implementation
-            if self.config.override.apis is None
-            else self.config.override.apis(api_implementation)
-        )
-
-        def callback():
-            ev_recipe = EmailVerificationRecipe.get_instance_optional()
-            if ev_recipe:
-                ev_recipe.add_get_email_for_user_id_func(self.get_email_for_user_id)
-
-            mt_recipe = MultitenancyRecipe.get_instance_optional()
-            if mt_recipe:
-                mt_recipe.static_third_party_providers = self.providers
-
-        PostSTInitCallbacks.add_post_init_callback(callback)
-
-    def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
-        return isinstance(err, SuperTokensError) and (
-            isinstance(err, SuperTokensThirdPartyError)
-        )
-
-    def get_apis_handled(self) -> List[APIHandled]:
-        return [
-            APIHandled(
-                NormalisedURLPath(SIGNINUP),
-                "post",
-                SIGNINUP,
-                self.api_implementation.disable_sign_in_up_post,
-            ),
-            APIHandled(
-                NormalisedURLPath(AUTHORISATIONURL),
-                "get",
-                AUTHORISATIONURL,
-                self.api_implementation.disable_authorisation_url_get,
-            ),
-            APIHandled(
-                NormalisedURLPath(APPLE_REDIRECT_HANDLER),
-                "post",
-                APPLE_REDIRECT_HANDLER,
-                self.api_implementation.disable_apple_redirect_handler_post,
-            ),
-        ]
-
-    async def handle_api_request(
-        self,
-        request_id: str,
-        tenant_id: str,
-        request: BaseRequest,
-        path: NormalisedURLPath,
-        method: str,
-        response: BaseResponse,
-        user_context: Dict[str, Any],
-    ):
-        api_options = APIOptions(
-            request,
-            response,
-            self.recipe_id,
-            self.config,
-            self.recipe_implementation,
-            self.providers,
-            self.app_info,
-        )
-
-        if request_id == SIGNINUP:
-            return await handle_sign_in_up_api(
-                self.api_implementation, tenant_id, api_options, user_context
-            )
-        if request_id == AUTHORISATIONURL:
-            return await handle_authorisation_url_api(
-                self.api_implementation, tenant_id, api_options, user_context
-            )
-        if request_id == APPLE_REDIRECT_HANDLER:
-            return await handle_apple_redirect_api(
-                self.api_implementation, api_options, user_context
-            )
-
-        return None
-
-    async def handle_error(
-        self,
-        request: BaseRequest,
-        err: SuperTokensError,
-        response: BaseResponse,
-        user_context: Dict[str, Any],
-    ) -> BaseResponse:  # type: ignore
-        if isinstance(err, SuperTokensThirdPartyError):
-            raise err
-
-    def get_all_cors_headers(self) -> List[str]:
-        return []
-
-    @staticmethod
-    def init(
-        sign_in_and_up_feature: SignInAndUpFeature,
-        override: Union[InputOverrideConfig, None] = None,
-    ):
-        def func(app_info: AppInfo):
-            if ThirdPartyRecipe.__instance is None:
-                ingredients = ThirdPartyIngredients()
-                ThirdPartyRecipe.__instance = ThirdPartyRecipe(
-                    ThirdPartyRecipe.recipe_id,
-                    app_info,
-                    sign_in_and_up_feature,
-                    ingredients,
-                    override,
-                )
-                return ThirdPartyRecipe.__instance
-            raise_general_exception(
-                "ThirdParty recipe has already been initialised. Please check your code for bugs."
-            )
-
-        return func
-
-    @staticmethod
-    def get_instance() -> ThirdPartyRecipe:
-        if ThirdPartyRecipe.__instance is not None:
-            return ThirdPartyRecipe.__instance
-        raise_general_exception(
-            "Initialisation not done. Did you forget to call the SuperTokens.init function?"
-        )
-
-    @staticmethod
-    def reset():
-        if ("SUPERTOKENS_ENV" not in environ) or (
-            environ["SUPERTOKENS_ENV"] != "testing"
-        ):
-            raise_general_exception("calling testing function in non testing env")
-        ThirdPartyRecipe.__instance = None
-
-    # instance functions below...............
-
-    async def get_email_for_user_id(self, user_id: str, user_context: Dict[str, Any]):
-        user_info = await self.recipe_implementation.get_user_by_id(
-            user_id, user_context
-        )
-        if user_info is not None:
-            return GetEmailForUserIdOkResult(user_info.email)
-
-        return UnknownUserIdError()
-
-

Ancestors

- -

Class variables

-
-
var get_tenant_id : Optional[Callable[[str, Dict[str, Any]], Awaitable[str]]]
-
-
-
-
var recipe_id
-
-
-
-
-

Static methods

-
-
-def get_instance() ‑> ThirdPartyRecipe -
-
-
-
- -Expand source code - -
@staticmethod
-def get_instance() -> ThirdPartyRecipe:
-    if ThirdPartyRecipe.__instance is not None:
-        return ThirdPartyRecipe.__instance
-    raise_general_exception(
-        "Initialisation not done. Did you forget to call the SuperTokens.init function?"
-    )
-
-
-
-def init(sign_in_and_up_feature: SignInAndUpFeature, override: Union[InputOverrideConfig, None] = None) -
-
-
-
- -Expand source code - -
@staticmethod
-def init(
-    sign_in_and_up_feature: SignInAndUpFeature,
-    override: Union[InputOverrideConfig, None] = None,
-):
-    def func(app_info: AppInfo):
-        if ThirdPartyRecipe.__instance is None:
-            ingredients = ThirdPartyIngredients()
-            ThirdPartyRecipe.__instance = ThirdPartyRecipe(
-                ThirdPartyRecipe.recipe_id,
-                app_info,
-                sign_in_and_up_feature,
-                ingredients,
-                override,
-            )
-            return ThirdPartyRecipe.__instance
-        raise_general_exception(
-            "ThirdParty recipe has already been initialised. Please check your code for bugs."
-        )
-
-    return func
-
-
-
-def reset() -
-
-
-
- -Expand source code - -
@staticmethod
-def reset():
-    if ("SUPERTOKENS_ENV" not in environ) or (
-        environ["SUPERTOKENS_ENV"] != "testing"
-    ):
-        raise_general_exception("calling testing function in non testing env")
-    ThirdPartyRecipe.__instance = None
-
-
-
-

Methods

-
-
-def get_all_cors_headers(self) ‑> List[str] -
-
-
-
- -Expand source code - -
def get_all_cors_headers(self) -> List[str]:
-    return []
-
-
-
-def get_apis_handled(self) ‑> List[APIHandled] -
-
-
-
- -Expand source code - -
def get_apis_handled(self) -> List[APIHandled]:
-    return [
-        APIHandled(
-            NormalisedURLPath(SIGNINUP),
-            "post",
-            SIGNINUP,
-            self.api_implementation.disable_sign_in_up_post,
-        ),
-        APIHandled(
-            NormalisedURLPath(AUTHORISATIONURL),
-            "get",
-            AUTHORISATIONURL,
-            self.api_implementation.disable_authorisation_url_get,
-        ),
-        APIHandled(
-            NormalisedURLPath(APPLE_REDIRECT_HANDLER),
-            "post",
-            APPLE_REDIRECT_HANDLER,
-            self.api_implementation.disable_apple_redirect_handler_post,
-        ),
-    ]
-
-
-
-async def get_email_for_user_id(self, user_id: str, user_context: Dict[str, Any]) -
-
-
-
- -Expand source code - -
async def get_email_for_user_id(self, user_id: str, user_context: Dict[str, Any]):
-    user_info = await self.recipe_implementation.get_user_by_id(
-        user_id, user_context
-    )
-    if user_info is not None:
-        return GetEmailForUserIdOkResult(user_info.email)
-
-    return UnknownUserIdError()
-
-
-
-async def handle_api_request(self, request_id: str, tenant_id: str, request: BaseRequest, path: NormalisedURLPath, method: str, response: BaseResponse, user_context: Dict[str, Any]) -
-
-
-
- -Expand source code - -
async def handle_api_request(
-    self,
-    request_id: str,
-    tenant_id: str,
-    request: BaseRequest,
-    path: NormalisedURLPath,
-    method: str,
-    response: BaseResponse,
-    user_context: Dict[str, Any],
-):
-    api_options = APIOptions(
-        request,
-        response,
-        self.recipe_id,
-        self.config,
-        self.recipe_implementation,
-        self.providers,
-        self.app_info,
-    )
-
-    if request_id == SIGNINUP:
-        return await handle_sign_in_up_api(
-            self.api_implementation, tenant_id, api_options, user_context
-        )
-    if request_id == AUTHORISATIONURL:
-        return await handle_authorisation_url_api(
-            self.api_implementation, tenant_id, api_options, user_context
-        )
-    if request_id == APPLE_REDIRECT_HANDLER:
-        return await handle_apple_redirect_api(
-            self.api_implementation, api_options, user_context
-        )
-
-    return None
-
-
-
-async def handle_error(self, request: BaseRequest, err: SuperTokensError, response: BaseResponse, user_context: Dict[str, Any]) ‑> BaseResponse -
-
-
-
- -Expand source code - -
async def handle_error(
-    self,
-    request: BaseRequest,
-    err: SuperTokensError,
-    response: BaseResponse,
-    user_context: Dict[str, Any],
-) -> BaseResponse:  # type: ignore
-    if isinstance(err, SuperTokensThirdPartyError):
-        raise err
-
-
-
-def is_error_from_this_recipe_based_on_instance(self, err: Exception) ‑> bool -
-
-
-
- -Expand source code - -
def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
-    return isinstance(err, SuperTokensError) and (
-        isinstance(err, SuperTokensThirdPartyError)
-    )
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/thirdparty/recipe_implementation.html b/html/supertokens_python/recipe/thirdparty/recipe_implementation.html deleted file mode 100644 index bc2bf5f56..000000000 --- a/html/supertokens_python/recipe/thirdparty/recipe_implementation.html +++ /dev/null @@ -1,723 +0,0 @@ - - - - - - -supertokens_python.recipe.thirdparty.recipe_implementation API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.thirdparty.recipe_implementation

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union
-
-from supertokens_python.normalised_url_path import NormalisedURLPath
-from supertokens_python.recipe.multitenancy.constants import DEFAULT_TENANT_ID
-from supertokens_python.recipe.multitenancy.recipe import MultitenancyRecipe
-from supertokens_python.recipe.thirdparty.provider import ProviderInput
-from supertokens_python.recipe.thirdparty.providers.config_utils import (
-    find_and_create_provider_instance,
-    merge_providers_from_core_and_static,
-)
-
-if TYPE_CHECKING:
-    from supertokens_python.querier import Querier
-
-from .interfaces import (
-    ManuallyCreateOrUpdateUserOkResult,
-    RecipeInterface,
-    SignInUpOkResult,
-)
-from .types import RawUserInfoFromProvider, ThirdPartyInfo, User
-
-
-class RecipeImplementation(RecipeInterface):
-    def __init__(self, querier: Querier, providers: List[ProviderInput]):
-        super().__init__()
-        self.querier = querier
-        self.providers = providers
-
-    async def get_user_by_id(
-        self, user_id: str, user_context: Dict[str, Any]
-    ) -> Union[User, None]:
-        params = {"userId": user_id}
-        response = await self.querier.send_get_request(
-            NormalisedURLPath("/recipe/user"),
-            params,
-            user_context=user_context,
-        )
-        if "status" in response and response["status"] == "OK":
-            return User(
-                response["user"]["id"],
-                response["user"]["email"],
-                response["user"]["timeJoined"],
-                response["user"]["tenantIds"],
-                ThirdPartyInfo(
-                    response["user"]["thirdParty"]["userId"],
-                    response["user"]["thirdParty"]["id"],
-                ),
-            )
-        return None
-
-    async def get_users_by_email(
-        self, email: str, tenant_id: str, user_context: Dict[str, Any]
-    ) -> List[User]:
-        response = await self.querier.send_get_request(
-            NormalisedURLPath(f"{tenant_id}/recipe/users/by-email"),
-            {"email": email},
-            user_context=user_context,
-        )
-        users: List[User] = []
-        users_list: List[Dict[str, Any]] = (
-            response["users"] if "users" in response else []
-        )
-        for user in users_list:
-            users.append(
-                User(
-                    user["id"],
-                    user["email"],
-                    user["timeJoined"],
-                    user["tenantIds"],
-                    ThirdPartyInfo(
-                        user["thirdParty"]["userId"], user["thirdParty"]["id"]
-                    ),
-                )
-            )
-        return users
-
-    async def get_user_by_thirdparty_info(
-        self,
-        third_party_id: str,
-        third_party_user_id: str,
-        tenant_id: str,
-        user_context: Dict[str, Any],
-    ) -> Union[User, None]:
-        params = {
-            "thirdPartyId": third_party_id,
-            "thirdPartyUserId": third_party_user_id,
-        }
-        response = await self.querier.send_get_request(
-            NormalisedURLPath(f"{tenant_id}/recipe/user"),
-            params,
-            user_context=user_context,
-        )
-        if "status" in response and response["status"] == "OK":
-            return User(
-                response["user"]["id"],
-                response["user"]["email"],
-                response["user"]["timeJoined"],
-                response["user"]["tenantIds"],
-                ThirdPartyInfo(
-                    response["user"]["thirdParty"]["userId"],
-                    response["user"]["thirdParty"]["id"],
-                ),
-            )
-        return None
-
-    async def sign_in_up(
-        self,
-        third_party_id: str,
-        third_party_user_id: str,
-        email: str,
-        oauth_tokens: Dict[str, Any],
-        raw_user_info_from_provider: RawUserInfoFromProvider,
-        tenant_id: str,
-        user_context: Dict[str, Any],
-    ) -> SignInUpOkResult:
-        data = {
-            "thirdPartyId": third_party_id,
-            "thirdPartyUserId": third_party_user_id,
-            "email": {"id": email},
-        }
-        response = await self.querier.send_post_request(
-            NormalisedURLPath(f"{tenant_id}/recipe/signinup"),
-            data,
-            user_context=user_context,
-        )
-        return SignInUpOkResult(
-            User(
-                response["user"]["id"],
-                response["user"]["email"],
-                response["user"]["timeJoined"],
-                response["user"]["tenantIds"],
-                ThirdPartyInfo(
-                    response["user"]["thirdParty"]["userId"],
-                    response["user"]["thirdParty"]["id"],
-                ),
-            ),
-            response["createdNewUser"],
-            oauth_tokens,
-            raw_user_info_from_provider,
-        )
-
-    async def manually_create_or_update_user(
-        self,
-        third_party_id: str,
-        third_party_user_id: str,
-        email: str,
-        tenant_id: str,
-        user_context: Dict[str, Any],
-    ) -> ManuallyCreateOrUpdateUserOkResult:
-        data = {
-            "thirdPartyId": third_party_id,
-            "thirdPartyUserId": third_party_user_id,
-            "email": {"id": email},
-        }
-        response = await self.querier.send_post_request(
-            NormalisedURLPath(f"{tenant_id}/recipe/signinup"),
-            data,
-            user_context=user_context,
-        )
-        return ManuallyCreateOrUpdateUserOkResult(
-            User(
-                response["user"]["id"],
-                response["user"]["email"],
-                response["user"]["timeJoined"],
-                response["user"]["tenantIds"],
-                ThirdPartyInfo(
-                    response["user"]["thirdParty"]["userId"],
-                    response["user"]["thirdParty"]["id"],
-                ),
-            ),
-            response["createdNewUser"],
-        )
-
-    async def get_provider(
-        self,
-        third_party_id: str,
-        client_type: Optional[str],
-        tenant_id: str,
-        user_context: Dict[str, Any],
-    ):
-        mt_recipe = MultitenancyRecipe.get_instance()
-        tenant_config = await mt_recipe.recipe_implementation.get_tenant(
-            tenant_id=tenant_id,
-            user_context=user_context,
-        )
-
-        if tenant_config is None:
-            raise Exception("Tenant not found")
-
-        merged_providers = merge_providers_from_core_and_static(
-            provider_configs_from_core=tenant_config.third_party.providers,
-            provider_inputs_from_static=self.providers,
-            include_all_providers=tenant_id == DEFAULT_TENANT_ID,
-        )
-
-        provider = await find_and_create_provider_instance(
-            merged_providers, third_party_id, client_type, user_context
-        )
-
-        return provider
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class RecipeImplementation -(querier: Querier, providers: List[ProviderInput]) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class RecipeImplementation(RecipeInterface):
-    def __init__(self, querier: Querier, providers: List[ProviderInput]):
-        super().__init__()
-        self.querier = querier
-        self.providers = providers
-
-    async def get_user_by_id(
-        self, user_id: str, user_context: Dict[str, Any]
-    ) -> Union[User, None]:
-        params = {"userId": user_id}
-        response = await self.querier.send_get_request(
-            NormalisedURLPath("/recipe/user"),
-            params,
-            user_context=user_context,
-        )
-        if "status" in response and response["status"] == "OK":
-            return User(
-                response["user"]["id"],
-                response["user"]["email"],
-                response["user"]["timeJoined"],
-                response["user"]["tenantIds"],
-                ThirdPartyInfo(
-                    response["user"]["thirdParty"]["userId"],
-                    response["user"]["thirdParty"]["id"],
-                ),
-            )
-        return None
-
-    async def get_users_by_email(
-        self, email: str, tenant_id: str, user_context: Dict[str, Any]
-    ) -> List[User]:
-        response = await self.querier.send_get_request(
-            NormalisedURLPath(f"{tenant_id}/recipe/users/by-email"),
-            {"email": email},
-            user_context=user_context,
-        )
-        users: List[User] = []
-        users_list: List[Dict[str, Any]] = (
-            response["users"] if "users" in response else []
-        )
-        for user in users_list:
-            users.append(
-                User(
-                    user["id"],
-                    user["email"],
-                    user["timeJoined"],
-                    user["tenantIds"],
-                    ThirdPartyInfo(
-                        user["thirdParty"]["userId"], user["thirdParty"]["id"]
-                    ),
-                )
-            )
-        return users
-
-    async def get_user_by_thirdparty_info(
-        self,
-        third_party_id: str,
-        third_party_user_id: str,
-        tenant_id: str,
-        user_context: Dict[str, Any],
-    ) -> Union[User, None]:
-        params = {
-            "thirdPartyId": third_party_id,
-            "thirdPartyUserId": third_party_user_id,
-        }
-        response = await self.querier.send_get_request(
-            NormalisedURLPath(f"{tenant_id}/recipe/user"),
-            params,
-            user_context=user_context,
-        )
-        if "status" in response and response["status"] == "OK":
-            return User(
-                response["user"]["id"],
-                response["user"]["email"],
-                response["user"]["timeJoined"],
-                response["user"]["tenantIds"],
-                ThirdPartyInfo(
-                    response["user"]["thirdParty"]["userId"],
-                    response["user"]["thirdParty"]["id"],
-                ),
-            )
-        return None
-
-    async def sign_in_up(
-        self,
-        third_party_id: str,
-        third_party_user_id: str,
-        email: str,
-        oauth_tokens: Dict[str, Any],
-        raw_user_info_from_provider: RawUserInfoFromProvider,
-        tenant_id: str,
-        user_context: Dict[str, Any],
-    ) -> SignInUpOkResult:
-        data = {
-            "thirdPartyId": third_party_id,
-            "thirdPartyUserId": third_party_user_id,
-            "email": {"id": email},
-        }
-        response = await self.querier.send_post_request(
-            NormalisedURLPath(f"{tenant_id}/recipe/signinup"),
-            data,
-            user_context=user_context,
-        )
-        return SignInUpOkResult(
-            User(
-                response["user"]["id"],
-                response["user"]["email"],
-                response["user"]["timeJoined"],
-                response["user"]["tenantIds"],
-                ThirdPartyInfo(
-                    response["user"]["thirdParty"]["userId"],
-                    response["user"]["thirdParty"]["id"],
-                ),
-            ),
-            response["createdNewUser"],
-            oauth_tokens,
-            raw_user_info_from_provider,
-        )
-
-    async def manually_create_or_update_user(
-        self,
-        third_party_id: str,
-        third_party_user_id: str,
-        email: str,
-        tenant_id: str,
-        user_context: Dict[str, Any],
-    ) -> ManuallyCreateOrUpdateUserOkResult:
-        data = {
-            "thirdPartyId": third_party_id,
-            "thirdPartyUserId": third_party_user_id,
-            "email": {"id": email},
-        }
-        response = await self.querier.send_post_request(
-            NormalisedURLPath(f"{tenant_id}/recipe/signinup"),
-            data,
-            user_context=user_context,
-        )
-        return ManuallyCreateOrUpdateUserOkResult(
-            User(
-                response["user"]["id"],
-                response["user"]["email"],
-                response["user"]["timeJoined"],
-                response["user"]["tenantIds"],
-                ThirdPartyInfo(
-                    response["user"]["thirdParty"]["userId"],
-                    response["user"]["thirdParty"]["id"],
-                ),
-            ),
-            response["createdNewUser"],
-        )
-
-    async def get_provider(
-        self,
-        third_party_id: str,
-        client_type: Optional[str],
-        tenant_id: str,
-        user_context: Dict[str, Any],
-    ):
-        mt_recipe = MultitenancyRecipe.get_instance()
-        tenant_config = await mt_recipe.recipe_implementation.get_tenant(
-            tenant_id=tenant_id,
-            user_context=user_context,
-        )
-
-        if tenant_config is None:
-            raise Exception("Tenant not found")
-
-        merged_providers = merge_providers_from_core_and_static(
-            provider_configs_from_core=tenant_config.third_party.providers,
-            provider_inputs_from_static=self.providers,
-            include_all_providers=tenant_id == DEFAULT_TENANT_ID,
-        )
-
-        provider = await find_and_create_provider_instance(
-            merged_providers, third_party_id, client_type, user_context
-        )
-
-        return provider
-
-

Ancestors

- -

Methods

-
-
-async def get_provider(self, third_party_id: str, client_type: Optional[str], tenant_id: str, user_context: Dict[str, Any]) -
-
-
-
- -Expand source code - -
async def get_provider(
-    self,
-    third_party_id: str,
-    client_type: Optional[str],
-    tenant_id: str,
-    user_context: Dict[str, Any],
-):
-    mt_recipe = MultitenancyRecipe.get_instance()
-    tenant_config = await mt_recipe.recipe_implementation.get_tenant(
-        tenant_id=tenant_id,
-        user_context=user_context,
-    )
-
-    if tenant_config is None:
-        raise Exception("Tenant not found")
-
-    merged_providers = merge_providers_from_core_and_static(
-        provider_configs_from_core=tenant_config.third_party.providers,
-        provider_inputs_from_static=self.providers,
-        include_all_providers=tenant_id == DEFAULT_TENANT_ID,
-    )
-
-    provider = await find_and_create_provider_instance(
-        merged_providers, third_party_id, client_type, user_context
-    )
-
-    return provider
-
-
-
-async def get_user_by_id(self, user_id: str, user_context: Dict[str, Any]) ‑> Optional[User] -
-
-
-
- -Expand source code - -
async def get_user_by_id(
-    self, user_id: str, user_context: Dict[str, Any]
-) -> Union[User, None]:
-    params = {"userId": user_id}
-    response = await self.querier.send_get_request(
-        NormalisedURLPath("/recipe/user"),
-        params,
-        user_context=user_context,
-    )
-    if "status" in response and response["status"] == "OK":
-        return User(
-            response["user"]["id"],
-            response["user"]["email"],
-            response["user"]["timeJoined"],
-            response["user"]["tenantIds"],
-            ThirdPartyInfo(
-                response["user"]["thirdParty"]["userId"],
-                response["user"]["thirdParty"]["id"],
-            ),
-        )
-    return None
-
-
-
-async def get_user_by_thirdparty_info(self, third_party_id: str, third_party_user_id: str, tenant_id: str, user_context: Dict[str, Any]) ‑> Optional[User] -
-
-
-
- -Expand source code - -
async def get_user_by_thirdparty_info(
-    self,
-    third_party_id: str,
-    third_party_user_id: str,
-    tenant_id: str,
-    user_context: Dict[str, Any],
-) -> Union[User, None]:
-    params = {
-        "thirdPartyId": third_party_id,
-        "thirdPartyUserId": third_party_user_id,
-    }
-    response = await self.querier.send_get_request(
-        NormalisedURLPath(f"{tenant_id}/recipe/user"),
-        params,
-        user_context=user_context,
-    )
-    if "status" in response and response["status"] == "OK":
-        return User(
-            response["user"]["id"],
-            response["user"]["email"],
-            response["user"]["timeJoined"],
-            response["user"]["tenantIds"],
-            ThirdPartyInfo(
-                response["user"]["thirdParty"]["userId"],
-                response["user"]["thirdParty"]["id"],
-            ),
-        )
-    return None
-
-
-
-async def get_users_by_email(self, email: str, tenant_id: str, user_context: Dict[str, Any]) ‑> List[User] -
-
-
-
- -Expand source code - -
async def get_users_by_email(
-    self, email: str, tenant_id: str, user_context: Dict[str, Any]
-) -> List[User]:
-    response = await self.querier.send_get_request(
-        NormalisedURLPath(f"{tenant_id}/recipe/users/by-email"),
-        {"email": email},
-        user_context=user_context,
-    )
-    users: List[User] = []
-    users_list: List[Dict[str, Any]] = (
-        response["users"] if "users" in response else []
-    )
-    for user in users_list:
-        users.append(
-            User(
-                user["id"],
-                user["email"],
-                user["timeJoined"],
-                user["tenantIds"],
-                ThirdPartyInfo(
-                    user["thirdParty"]["userId"], user["thirdParty"]["id"]
-                ),
-            )
-        )
-    return users
-
-
-
-async def manually_create_or_update_user(self, third_party_id: str, third_party_user_id: str, email: str, tenant_id: str, user_context: Dict[str, Any]) ‑> ManuallyCreateOrUpdateUserOkResult -
-
-
-
- -Expand source code - -
async def manually_create_or_update_user(
-    self,
-    third_party_id: str,
-    third_party_user_id: str,
-    email: str,
-    tenant_id: str,
-    user_context: Dict[str, Any],
-) -> ManuallyCreateOrUpdateUserOkResult:
-    data = {
-        "thirdPartyId": third_party_id,
-        "thirdPartyUserId": third_party_user_id,
-        "email": {"id": email},
-    }
-    response = await self.querier.send_post_request(
-        NormalisedURLPath(f"{tenant_id}/recipe/signinup"),
-        data,
-        user_context=user_context,
-    )
-    return ManuallyCreateOrUpdateUserOkResult(
-        User(
-            response["user"]["id"],
-            response["user"]["email"],
-            response["user"]["timeJoined"],
-            response["user"]["tenantIds"],
-            ThirdPartyInfo(
-                response["user"]["thirdParty"]["userId"],
-                response["user"]["thirdParty"]["id"],
-            ),
-        ),
-        response["createdNewUser"],
-    )
-
-
-
-async def sign_in_up(self, third_party_id: str, third_party_user_id: str, email: str, oauth_tokens: Dict[str, Any], raw_user_info_from_provider: RawUserInfoFromProvider, tenant_id: str, user_context: Dict[str, Any]) ‑> SignInUpOkResult -
-
-
-
- -Expand source code - -
async def sign_in_up(
-    self,
-    third_party_id: str,
-    third_party_user_id: str,
-    email: str,
-    oauth_tokens: Dict[str, Any],
-    raw_user_info_from_provider: RawUserInfoFromProvider,
-    tenant_id: str,
-    user_context: Dict[str, Any],
-) -> SignInUpOkResult:
-    data = {
-        "thirdPartyId": third_party_id,
-        "thirdPartyUserId": third_party_user_id,
-        "email": {"id": email},
-    }
-    response = await self.querier.send_post_request(
-        NormalisedURLPath(f"{tenant_id}/recipe/signinup"),
-        data,
-        user_context=user_context,
-    )
-    return SignInUpOkResult(
-        User(
-            response["user"]["id"],
-            response["user"]["email"],
-            response["user"]["timeJoined"],
-            response["user"]["tenantIds"],
-            ThirdPartyInfo(
-                response["user"]["thirdParty"]["userId"],
-                response["user"]["thirdParty"]["id"],
-            ),
-        ),
-        response["createdNewUser"],
-        oauth_tokens,
-        raw_user_info_from_provider,
-    )
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/thirdparty/syncio/index.html b/html/supertokens_python/recipe/thirdparty/syncio/index.html deleted file mode 100644 index 85ca6a152..000000000 --- a/html/supertokens_python/recipe/thirdparty/syncio/index.html +++ /dev/null @@ -1,261 +0,0 @@ - - - - - - -supertokens_python.recipe.thirdparty.syncio API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.thirdparty.syncio

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from typing import Any, Dict, List, Optional, Union
-
-from supertokens_python.async_to_sync_wrapper import sync
-
-from ..types import User
-
-
-def get_user_by_id(
-    user_id: str, user_context: Union[None, Dict[str, Any]] = None
-) -> Union[User, None]:
-    from supertokens_python.recipe.thirdparty.asyncio import get_user_by_id
-
-    return sync(get_user_by_id(user_id, user_context))
-
-
-def get_users_by_email(
-    tenant_id: str,
-    email: str,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> List[User]:
-    from supertokens_python.recipe.thirdparty.asyncio import get_users_by_email
-
-    return sync(get_users_by_email(tenant_id, email, user_context))
-
-
-def get_user_by_third_party_info(
-    tenant_id: str,
-    third_party_id: str,
-    third_party_user_id: str,
-    user_context: Union[None, Dict[str, Any]] = None,
-):
-    from supertokens_python.recipe.thirdparty.asyncio import (
-        get_user_by_third_party_info,
-    )
-
-    return sync(
-        get_user_by_third_party_info(
-            tenant_id, third_party_id, third_party_user_id, user_context
-        )
-    )
-
-
-def manually_create_or_update_user(
-    tenant_id: str,
-    third_party_id: str,
-    third_party_user_id: str,
-    email: str,
-    user_context: Union[None, Dict[str, Any]] = None,
-):
-    from supertokens_python.recipe.thirdparty.asyncio import (
-        manually_create_or_update_user,
-    )
-
-    return sync(
-        manually_create_or_update_user(
-            tenant_id, third_party_id, third_party_user_id, email, user_context
-        )
-    )
-
-
-def get_provider(
-    tenant_id: str,
-    third_party_id: str,
-    client_type: Optional[str] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-):
-    from supertokens_python.recipe.thirdparty.asyncio import get_provider
-
-    return sync(get_provider(tenant_id, third_party_id, client_type, user_context))
-
-
-
-
-
-
-
-

Functions

-
-
-def get_provider(tenant_id: str, third_party_id: str, client_type: Optional[str] = None, user_context: Optional[Dict[str, Any]] = None) -
-
-
-
- -Expand source code - -
def get_provider(
-    tenant_id: str,
-    third_party_id: str,
-    client_type: Optional[str] = None,
-    user_context: Union[None, Dict[str, Any]] = None,
-):
-    from supertokens_python.recipe.thirdparty.asyncio import get_provider
-
-    return sync(get_provider(tenant_id, third_party_id, client_type, user_context))
-
-
-
-def get_user_by_id(user_id: str, user_context: Optional[Dict[str, Any]] = None) ‑> Optional[User] -
-
-
-
- -Expand source code - -
def get_user_by_id(
-    user_id: str, user_context: Union[None, Dict[str, Any]] = None
-) -> Union[User, None]:
-    from supertokens_python.recipe.thirdparty.asyncio import get_user_by_id
-
-    return sync(get_user_by_id(user_id, user_context))
-
-
-
-def get_user_by_third_party_info(tenant_id: str, third_party_id: str, third_party_user_id: str, user_context: Optional[Dict[str, Any]] = None) -
-
-
-
- -Expand source code - -
def get_user_by_third_party_info(
-    tenant_id: str,
-    third_party_id: str,
-    third_party_user_id: str,
-    user_context: Union[None, Dict[str, Any]] = None,
-):
-    from supertokens_python.recipe.thirdparty.asyncio import (
-        get_user_by_third_party_info,
-    )
-
-    return sync(
-        get_user_by_third_party_info(
-            tenant_id, third_party_id, third_party_user_id, user_context
-        )
-    )
-
-
-
-def get_users_by_email(tenant_id: str, email: str, user_context: Optional[Dict[str, Any]] = None) ‑> List[User] -
-
-
-
- -Expand source code - -
def get_users_by_email(
-    tenant_id: str,
-    email: str,
-    user_context: Union[None, Dict[str, Any]] = None,
-) -> List[User]:
-    from supertokens_python.recipe.thirdparty.asyncio import get_users_by_email
-
-    return sync(get_users_by_email(tenant_id, email, user_context))
-
-
-
-def manually_create_or_update_user(tenant_id: str, third_party_id: str, third_party_user_id: str, email: str, user_context: Optional[Dict[str, Any]] = None) -
-
-
-
- -Expand source code - -
def manually_create_or_update_user(
-    tenant_id: str,
-    third_party_id: str,
-    third_party_user_id: str,
-    email: str,
-    user_context: Union[None, Dict[str, Any]] = None,
-):
-    from supertokens_python.recipe.thirdparty.asyncio import (
-        manually_create_or_update_user,
-    )
-
-    return sync(
-        manually_create_or_update_user(
-            tenant_id, third_party_id, third_party_user_id, email, user_context
-        )
-    )
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/thirdparty/types.html b/html/supertokens_python/recipe/thirdparty/types.html deleted file mode 100644 index f1f46712c..000000000 --- a/html/supertokens_python/recipe/thirdparty/types.html +++ /dev/null @@ -1,408 +0,0 @@ - - - - - - -supertokens_python.recipe.thirdparty.types API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.thirdparty.types

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from typing import Any, Callable, Dict, List, Union, Optional
-
-from supertokens_python.framework.request import BaseRequest
-
-
-class ThirdPartyInfo:
-    def __init__(self, third_party_user_id: str, third_party_id: str):
-        self.user_id = third_party_user_id
-        self.id = third_party_id
-
-    def __eq__(self, other: object) -> bool:
-        return (
-            isinstance(other, self.__class__)
-            and self.user_id == other.user_id
-            and self.id == other.id
-        )
-
-
-class RawUserInfoFromProvider:
-    def __init__(
-        self,
-        from_id_token_payload: Optional[Dict[str, Any]],
-        from_user_info_api: Optional[Dict[str, Any]],
-    ):
-        self.from_id_token_payload = from_id_token_payload
-        self.from_user_info_api = from_user_info_api
-
-
-class User:
-    def __init__(
-        self,
-        user_id: str,
-        email: str,
-        time_joined: int,
-        tenant_ids: List[str],
-        third_party_info: ThirdPartyInfo,
-    ):
-        self.user_id: str = user_id
-        self.email: str = email
-        self.time_joined: int = time_joined
-        self.tenant_ids = tenant_ids
-        self.third_party_info: ThirdPartyInfo = third_party_info
-
-    def __eq__(self, other: object) -> bool:
-        return (
-            isinstance(other, self.__class__)
-            and self.user_id == other.user_id
-            and self.email == other.email
-            and self.time_joined == other.time_joined
-            and self.tenant_ids == other.tenant_ids
-            and self.third_party_info == other.third_party_info
-        )
-
-
-class UserInfoEmail:
-    def __init__(self, email: str, is_verified: bool):
-        self.id: str = email
-        self.is_verified: bool = is_verified
-
-
-class UserInfo:
-    def __init__(
-        self,
-        third_party_user_id: str,
-        email: Union[UserInfoEmail, None] = None,
-        raw_user_info_from_provider: Optional[RawUserInfoFromProvider] = None,
-    ):
-        self.third_party_user_id: str = third_party_user_id
-        self.email: Union[UserInfoEmail, None] = email
-        self.raw_user_info_from_provider = (
-            raw_user_info_from_provider or RawUserInfoFromProvider({}, {})
-        )
-
-
-class AccessTokenAPI:
-    def __init__(self, url: str, params: Dict[str, str]):
-        self.url = url
-        self.params = params
-
-
-class AuthorisationRedirectAPI:
-    def __init__(
-        self, url: str, params: Dict[str, Union[Callable[[BaseRequest], str], str]]
-    ):
-        self.url = url
-        self.params = params
-
-
-class SignInUpResponse:
-    def __init__(self, user: User, is_new_user: bool):
-        self.user = user
-        self.is_new_user = is_new_user
-
-
-class UsersResponse:
-    def __init__(self, users: List[User], next_pagination_token: Union[str, None]):
-        self.users = users
-        self.next_pagination_token = next_pagination_token
-
-
-class ThirdPartyIngredients:
-    pass
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class AccessTokenAPI -(url: str, params: Dict[str, str]) -
-
-
-
- -Expand source code - -
class AccessTokenAPI:
-    def __init__(self, url: str, params: Dict[str, str]):
-        self.url = url
-        self.params = params
-
-
-
-class AuthorisationRedirectAPI -(url: str, params: Dict[str, Union[Callable[[BaseRequest], str], str]]) -
-
-
-
- -Expand source code - -
class AuthorisationRedirectAPI:
-    def __init__(
-        self, url: str, params: Dict[str, Union[Callable[[BaseRequest], str], str]]
-    ):
-        self.url = url
-        self.params = params
-
-
-
-class RawUserInfoFromProvider -(from_id_token_payload: Optional[Dict[str, Any]], from_user_info_api: Optional[Dict[str, Any]]) -
-
-
-
- -Expand source code - -
class RawUserInfoFromProvider:
-    def __init__(
-        self,
-        from_id_token_payload: Optional[Dict[str, Any]],
-        from_user_info_api: Optional[Dict[str, Any]],
-    ):
-        self.from_id_token_payload = from_id_token_payload
-        self.from_user_info_api = from_user_info_api
-
-
-
-class SignInUpResponse -(user: User, is_new_user: bool) -
-
-
-
- -Expand source code - -
class SignInUpResponse:
-    def __init__(self, user: User, is_new_user: bool):
-        self.user = user
-        self.is_new_user = is_new_user
-
-
-
-class ThirdPartyInfo -(third_party_user_id: str, third_party_id: str) -
-
-
-
- -Expand source code - -
class ThirdPartyInfo:
-    def __init__(self, third_party_user_id: str, third_party_id: str):
-        self.user_id = third_party_user_id
-        self.id = third_party_id
-
-    def __eq__(self, other: object) -> bool:
-        return (
-            isinstance(other, self.__class__)
-            and self.user_id == other.user_id
-            and self.id == other.id
-        )
-
-
-
-class ThirdPartyIngredients -
-
-
-
- -Expand source code - -
class ThirdPartyIngredients:
-    pass
-
-
-
-class User -(user_id: str, email: str, time_joined: int, tenant_ids: List[str], third_party_info: ThirdPartyInfo) -
-
-
-
- -Expand source code - -
class User:
-    def __init__(
-        self,
-        user_id: str,
-        email: str,
-        time_joined: int,
-        tenant_ids: List[str],
-        third_party_info: ThirdPartyInfo,
-    ):
-        self.user_id: str = user_id
-        self.email: str = email
-        self.time_joined: int = time_joined
-        self.tenant_ids = tenant_ids
-        self.third_party_info: ThirdPartyInfo = third_party_info
-
-    def __eq__(self, other: object) -> bool:
-        return (
-            isinstance(other, self.__class__)
-            and self.user_id == other.user_id
-            and self.email == other.email
-            and self.time_joined == other.time_joined
-            and self.tenant_ids == other.tenant_ids
-            and self.third_party_info == other.third_party_info
-        )
-
-
-
-class UserInfo -(third_party_user_id: str, email: Optional[UserInfoEmail] = None, raw_user_info_from_provider: Optional[RawUserInfoFromProvider] = None) -
-
-
-
- -Expand source code - -
class UserInfo:
-    def __init__(
-        self,
-        third_party_user_id: str,
-        email: Union[UserInfoEmail, None] = None,
-        raw_user_info_from_provider: Optional[RawUserInfoFromProvider] = None,
-    ):
-        self.third_party_user_id: str = third_party_user_id
-        self.email: Union[UserInfoEmail, None] = email
-        self.raw_user_info_from_provider = (
-            raw_user_info_from_provider or RawUserInfoFromProvider({}, {})
-        )
-
-
-
-class UserInfoEmail -(email: str, is_verified: bool) -
-
-
-
- -Expand source code - -
class UserInfoEmail:
-    def __init__(self, email: str, is_verified: bool):
-        self.id: str = email
-        self.is_verified: bool = is_verified
-
-
-
-class UsersResponse -(users: List[User], next_pagination_token: Optional[str]) -
-
-
-
- -Expand source code - -
class UsersResponse:
-    def __init__(self, users: List[User], next_pagination_token: Union[str, None]):
-        self.users = users
-        self.next_pagination_token = next_pagination_token
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/thirdparty/utils.html b/html/supertokens_python/recipe/thirdparty/utils.html deleted file mode 100644 index 7c576643c..000000000 --- a/html/supertokens_python/recipe/thirdparty/utils.html +++ /dev/null @@ -1,362 +0,0 @@ - - - - - - -supertokens_python.recipe.thirdparty.utils API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.thirdparty.utils

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from typing import TYPE_CHECKING, Any, Callable, Dict, List, Set, Union, Optional
-
-from supertokens_python.exceptions import raise_bad_input_exception
-from supertokens_python.recipe.thirdparty.provider import ProviderInput
-
-from .interfaces import APIInterface, RecipeInterface
-
-if TYPE_CHECKING:
-    from .provider import ProviderInput
-
-from jwt import PyJWKClient, decode  # type: ignore
-
-
-class SignInAndUpFeature:
-    def __init__(self, providers: Optional[List[ProviderInput]] = None):
-        if providers is None:
-            providers = []
-
-        third_party_id_set: Set[str] = set()
-
-        for provider in providers:
-            third_party_id = provider.config.third_party_id
-
-            if third_party_id in third_party_id_set:
-                raise_bad_input_exception(
-                    "The providers array has multiple entries for the same third party provider."
-                )
-
-            third_party_id_set.add(third_party_id)
-
-        self.providers = providers
-
-
-class InputOverrideConfig:
-    def __init__(
-        self,
-        functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None,
-        apis: Union[Callable[[APIInterface], APIInterface], None] = None,
-    ):
-        self.functions = functions
-        self.apis = apis
-
-
-class OverrideConfig:
-    def __init__(
-        self,
-        functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None,
-        apis: Union[Callable[[APIInterface], APIInterface], None] = None,
-    ):
-        self.functions = functions
-        self.apis = apis
-
-
-class ThirdPartyConfig:
-    def __init__(
-        self,
-        sign_in_and_up_feature: SignInAndUpFeature,
-        override: OverrideConfig,
-    ):
-        self.sign_in_and_up_feature = sign_in_and_up_feature
-        self.override = override
-
-
-def validate_and_normalise_user_input(
-    sign_in_and_up_feature: SignInAndUpFeature,
-    override: Union[InputOverrideConfig, None] = None,
-) -> ThirdPartyConfig:
-    if not isinstance(sign_in_and_up_feature, SignInAndUpFeature):  # type: ignore
-        raise ValueError(
-            "sign_in_and_up_feature must be an instance of SignInAndUpFeature"
-        )
-
-    if override is not None and not isinstance(override, InputOverrideConfig):  # type: ignore
-        raise ValueError("override must be an instance of InputOverrideConfig or None")
-
-    if override is None:
-        override = InputOverrideConfig()
-
-    return ThirdPartyConfig(
-        sign_in_and_up_feature,
-        OverrideConfig(functions=override.functions, apis=override.apis),
-    )
-
-
-def verify_id_token_from_jwks_endpoint(
-    id_token: str, jwks_uri: str, audience: str, issuers: List[str]
-) -> Dict[str, Any]:
-    jwks_client = PyJWKClient(jwks_uri)
-    signing_key = jwks_client.get_signing_key_from_jwt(id_token)
-
-    data: Dict[str, Any] = decode(  # type: ignore
-        id_token,
-        signing_key.key,  # type: ignore
-        algorithms=["RS256"],
-        audience=audience,
-        options={"verify_exp": False},
-    )
-
-    issuer_found = False
-    for issuer in issuers:
-        if data["iss"] == issuer:
-            issuer_found = True
-
-    if not issuer_found:
-        raise Exception("no required issuer found")
-
-    return data
-
-
-
-
-
-
-
-

Functions

-
-
-def validate_and_normalise_user_input(sign_in_and_up_feature: SignInAndUpFeature, override: Union[InputOverrideConfig, None] = None) ‑> ThirdPartyConfig -
-
-
-
- -Expand source code - -
def validate_and_normalise_user_input(
-    sign_in_and_up_feature: SignInAndUpFeature,
-    override: Union[InputOverrideConfig, None] = None,
-) -> ThirdPartyConfig:
-    if not isinstance(sign_in_and_up_feature, SignInAndUpFeature):  # type: ignore
-        raise ValueError(
-            "sign_in_and_up_feature must be an instance of SignInAndUpFeature"
-        )
-
-    if override is not None and not isinstance(override, InputOverrideConfig):  # type: ignore
-        raise ValueError("override must be an instance of InputOverrideConfig or None")
-
-    if override is None:
-        override = InputOverrideConfig()
-
-    return ThirdPartyConfig(
-        sign_in_and_up_feature,
-        OverrideConfig(functions=override.functions, apis=override.apis),
-    )
-
-
-
-def verify_id_token_from_jwks_endpoint(id_token: str, jwks_uri: str, audience: str, issuers: List[str]) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
def verify_id_token_from_jwks_endpoint(
-    id_token: str, jwks_uri: str, audience: str, issuers: List[str]
-) -> Dict[str, Any]:
-    jwks_client = PyJWKClient(jwks_uri)
-    signing_key = jwks_client.get_signing_key_from_jwt(id_token)
-
-    data: Dict[str, Any] = decode(  # type: ignore
-        id_token,
-        signing_key.key,  # type: ignore
-        algorithms=["RS256"],
-        audience=audience,
-        options={"verify_exp": False},
-    )
-
-    issuer_found = False
-    for issuer in issuers:
-        if data["iss"] == issuer:
-            issuer_found = True
-
-    if not issuer_found:
-        raise Exception("no required issuer found")
-
-    return data
-
-
-
-
-
-

Classes

-
-
-class InputOverrideConfig -(functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None, apis: Union[Callable[[APIInterface], APIInterface], None] = None) -
-
-
-
- -Expand source code - -
class InputOverrideConfig:
-    def __init__(
-        self,
-        functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None,
-        apis: Union[Callable[[APIInterface], APIInterface], None] = None,
-    ):
-        self.functions = functions
-        self.apis = apis
-
-
-
-class OverrideConfig -(functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None, apis: Union[Callable[[APIInterface], APIInterface], None] = None) -
-
-
-
- -Expand source code - -
class OverrideConfig:
-    def __init__(
-        self,
-        functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None,
-        apis: Union[Callable[[APIInterface], APIInterface], None] = None,
-    ):
-        self.functions = functions
-        self.apis = apis
-
-
-
-class SignInAndUpFeature -(providers: Optional[List[ProviderInput]] = None) -
-
-
-
- -Expand source code - -
class SignInAndUpFeature:
-    def __init__(self, providers: Optional[List[ProviderInput]] = None):
-        if providers is None:
-            providers = []
-
-        third_party_id_set: Set[str] = set()
-
-        for provider in providers:
-            third_party_id = provider.config.third_party_id
-
-            if third_party_id in third_party_id_set:
-                raise_bad_input_exception(
-                    "The providers array has multiple entries for the same third party provider."
-                )
-
-            third_party_id_set.add(third_party_id)
-
-        self.providers = providers
-
-
-
-class ThirdPartyConfig -(sign_in_and_up_feature: SignInAndUpFeature, override: OverrideConfig) -
-
-
-
- -Expand source code - -
class ThirdPartyConfig:
-    def __init__(
-        self,
-        sign_in_and_up_feature: SignInAndUpFeature,
-        override: OverrideConfig,
-    ):
-        self.sign_in_and_up_feature = sign_in_and_up_feature
-        self.override = override
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/usermetadata/asyncio/index.html b/html/supertokens_python/recipe/usermetadata/asyncio/index.html deleted file mode 100644 index 6c8452ec1..000000000 --- a/html/supertokens_python/recipe/usermetadata/asyncio/index.html +++ /dev/null @@ -1,166 +0,0 @@ - - - - - - -supertokens_python.recipe.usermetadata.asyncio API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.usermetadata.asyncio

-
-
-
- -Expand source code - -
from typing import Any, Dict, Union
-
-from supertokens_python.recipe.usermetadata.recipe import UserMetadataRecipe
-
-
-async def get_user_metadata(
-    user_id: str, user_context: Union[Dict[str, Any], None] = None
-):
-    if user_context is None:
-        user_context = {}
-    return (
-        await UserMetadataRecipe.get_instance().recipe_implementation.get_user_metadata(
-            user_id, user_context
-        )
-    )
-
-
-async def update_user_metadata(
-    user_id: str,
-    metadata_update: Dict[str, Any],
-    user_context: Union[Dict[str, Any], None] = None,
-):
-    if user_context is None:
-        user_context = {}
-    return await UserMetadataRecipe.get_instance().recipe_implementation.update_user_metadata(
-        user_id, metadata_update, user_context
-    )
-
-
-async def clear_user_metadata(
-    user_id: str, user_context: Union[Dict[str, Any], None] = None
-):
-    if user_context is None:
-        user_context = {}
-    return await UserMetadataRecipe.get_instance().recipe_implementation.clear_user_metadata(
-        user_id, user_context
-    )
-
-
-
-
-
-
-
-

Functions

-
-
-async def clear_user_metadata(user_id: str, user_context: Optional[Dict[str, Any]] = None) -
-
-
-
- -Expand source code - -
async def clear_user_metadata(
-    user_id: str, user_context: Union[Dict[str, Any], None] = None
-):
-    if user_context is None:
-        user_context = {}
-    return await UserMetadataRecipe.get_instance().recipe_implementation.clear_user_metadata(
-        user_id, user_context
-    )
-
-
-
-async def get_user_metadata(user_id: str, user_context: Optional[Dict[str, Any]] = None) -
-
-
-
- -Expand source code - -
async def get_user_metadata(
-    user_id: str, user_context: Union[Dict[str, Any], None] = None
-):
-    if user_context is None:
-        user_context = {}
-    return (
-        await UserMetadataRecipe.get_instance().recipe_implementation.get_user_metadata(
-            user_id, user_context
-        )
-    )
-
-
-
-async def update_user_metadata(user_id: str, metadata_update: Dict[str, Any], user_context: Optional[Dict[str, Any]] = None) -
-
-
-
- -Expand source code - -
async def update_user_metadata(
-    user_id: str,
-    metadata_update: Dict[str, Any],
-    user_context: Union[Dict[str, Any], None] = None,
-):
-    if user_context is None:
-        user_context = {}
-    return await UserMetadataRecipe.get_instance().recipe_implementation.update_user_metadata(
-        user_id, metadata_update, user_context
-    )
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/usermetadata/exceptions.html b/html/supertokens_python/recipe/usermetadata/exceptions.html deleted file mode 100644 index 0bfbdfd01..000000000 --- a/html/supertokens_python/recipe/usermetadata/exceptions.html +++ /dev/null @@ -1,107 +0,0 @@ - - - - - - -supertokens_python.recipe.usermetadata.exceptions API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.usermetadata.exceptions

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from supertokens_python.exceptions import SuperTokensError
-
-
-class SuperTokensUserMetadataError(SuperTokensError):
-    pass
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class SuperTokensUserMetadataError -(*args, **kwargs) -
-
-

Common base class for all non-exit exceptions.

-
- -Expand source code - -
class SuperTokensUserMetadataError(SuperTokensError):
-    pass
-
-

Ancestors

- -
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/usermetadata/index.html b/html/supertokens_python/recipe/usermetadata/index.html deleted file mode 100644 index f2b634495..000000000 --- a/html/supertokens_python/recipe/usermetadata/index.html +++ /dev/null @@ -1,153 +0,0 @@ - - - - - - -supertokens_python.recipe.usermetadata API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.usermetadata

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from typing import TYPE_CHECKING, Callable, Union
-
-from . import utils
-from .recipe import UserMetadataRecipe
-
-if TYPE_CHECKING:
-    from supertokens_python.supertokens import AppInfo
-
-    from ...recipe_module import RecipeModule
-
-
-def init(
-    override: Union[utils.InputOverrideConfig, None] = None
-) -> Callable[[AppInfo], RecipeModule]:
-    return UserMetadataRecipe.init(override)
-
-
-
-

Sub-modules

-
-
supertokens_python.recipe.usermetadata.asyncio
-
-
-
-
supertokens_python.recipe.usermetadata.exceptions
-
-
-
-
supertokens_python.recipe.usermetadata.interfaces
-
-
-
-
supertokens_python.recipe.usermetadata.recipe
-
-
-
-
supertokens_python.recipe.usermetadata.recipe_implementation
-
-
-
-
supertokens_python.recipe.usermetadata.syncio
-
-
-
-
supertokens_python.recipe.usermetadata.utils
-
-
-
-
-
-
-
-
-

Functions

-
-
-def init(override: Union[InputOverrideConfig, None] = None) ‑> Callable[[AppInfo], RecipeModule] -
-
-
-
- -Expand source code - -
def init(
-    override: Union[utils.InputOverrideConfig, None] = None
-) -> Callable[[AppInfo], RecipeModule]:
-    return UserMetadataRecipe.init(override)
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/usermetadata/interfaces.html b/html/supertokens_python/recipe/usermetadata/interfaces.html deleted file mode 100644 index 93724418b..000000000 --- a/html/supertokens_python/recipe/usermetadata/interfaces.html +++ /dev/null @@ -1,266 +0,0 @@ - - - - - - -supertokens_python.recipe.usermetadata.interfaces API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.usermetadata.interfaces

-
-
-
- -Expand source code - -
from abc import ABC, abstractmethod
-from typing import Any, Dict
-
-
-class MetadataResult(ABC):
-    def __init__(self, metadata: Dict[str, Any]):
-        self.metadata = metadata
-
-
-class ClearUserMetadataResult:
-    pass
-
-
-class RecipeInterface(ABC):
-    @abstractmethod
-    async def get_user_metadata(
-        self, user_id: str, user_context: Dict[str, Any]
-    ) -> MetadataResult:
-        pass
-
-    @abstractmethod
-    async def update_user_metadata(
-        self,
-        user_id: str,
-        metadata_update: Dict[str, Any],
-        user_context: Dict[str, Any],
-    ) -> MetadataResult:
-        pass
-
-    @abstractmethod
-    async def clear_user_metadata(
-        self, user_id: str, user_context: Dict[str, Any]
-    ) -> ClearUserMetadataResult:
-        pass
-
-
-class APIInterface(ABC):
-    pass
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class APIInterface -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class APIInterface(ABC):
-    pass
-
-

Ancestors

-
    -
  • abc.ABC
  • -
-
-
-class ClearUserMetadataResult -
-
-
-
- -Expand source code - -
class ClearUserMetadataResult:
-    pass
-
-
-
-class MetadataResult -(metadata: Dict[str, Any]) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class MetadataResult(ABC):
-    def __init__(self, metadata: Dict[str, Any]):
-        self.metadata = metadata
-
-

Ancestors

-
    -
  • abc.ABC
  • -
-
-
-class RecipeInterface -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class RecipeInterface(ABC):
-    @abstractmethod
-    async def get_user_metadata(
-        self, user_id: str, user_context: Dict[str, Any]
-    ) -> MetadataResult:
-        pass
-
-    @abstractmethod
-    async def update_user_metadata(
-        self,
-        user_id: str,
-        metadata_update: Dict[str, Any],
-        user_context: Dict[str, Any],
-    ) -> MetadataResult:
-        pass
-
-    @abstractmethod
-    async def clear_user_metadata(
-        self, user_id: str, user_context: Dict[str, Any]
-    ) -> ClearUserMetadataResult:
-        pass
-
-

Ancestors

-
    -
  • abc.ABC
  • -
-

Subclasses

- -

Methods

-
-
-async def clear_user_metadata(self, user_id: str, user_context: Dict[str, Any]) ‑> ClearUserMetadataResult -
-
-
-
- -Expand source code - -
@abstractmethod
-async def clear_user_metadata(
-    self, user_id: str, user_context: Dict[str, Any]
-) -> ClearUserMetadataResult:
-    pass
-
-
-
-async def get_user_metadata(self, user_id: str, user_context: Dict[str, Any]) ‑> MetadataResult -
-
-
-
- -Expand source code - -
@abstractmethod
-async def get_user_metadata(
-    self, user_id: str, user_context: Dict[str, Any]
-) -> MetadataResult:
-    pass
-
-
-
-async def update_user_metadata(self, user_id: str, metadata_update: Dict[str, Any], user_context: Dict[str, Any]) ‑> MetadataResult -
-
-
-
- -Expand source code - -
@abstractmethod
-async def update_user_metadata(
-    self,
-    user_id: str,
-    metadata_update: Dict[str, Any],
-    user_context: Dict[str, Any],
-) -> MetadataResult:
-    pass
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/usermetadata/recipe.html b/html/supertokens_python/recipe/usermetadata/recipe.html deleted file mode 100644 index 246077d73..000000000 --- a/html/supertokens_python/recipe/usermetadata/recipe.html +++ /dev/null @@ -1,458 +0,0 @@ - - - - - - -supertokens_python.recipe.usermetadata.recipe API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.usermetadata.recipe

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from __future__ import annotations
-
-from os import environ
-from typing import List, Union, Optional, Dict, Any
-
-from supertokens_python.exceptions import SuperTokensError, raise_general_exception
-from supertokens_python.framework import BaseRequest, BaseResponse
-from supertokens_python.normalised_url_path import NormalisedURLPath
-from supertokens_python.querier import Querier
-from supertokens_python.recipe.usermetadata.exceptions import (
-    SuperTokensUserMetadataError,
-)
-from supertokens_python.recipe.usermetadata.recipe_implementation import (
-    RecipeImplementation,
-)
-from supertokens_python.recipe.usermetadata.utils import (
-    validate_and_normalise_user_input,
-)
-from supertokens_python.recipe_module import APIHandled, RecipeModule
-from supertokens_python.supertokens import AppInfo
-
-from .utils import InputOverrideConfig
-
-
-class UserMetadataRecipe(RecipeModule):
-    recipe_id = "usermetadata"
-    __instance = None
-
-    def __init__(
-        self,
-        recipe_id: str,
-        app_info: AppInfo,
-        override: Union[InputOverrideConfig, None] = None,
-    ):
-        super().__init__(recipe_id, app_info)
-        self.config = validate_and_normalise_user_input(self, app_info, override)
-        recipe_implementation = RecipeImplementation(Querier.get_instance(recipe_id))
-        self.recipe_implementation = (
-            recipe_implementation
-            if self.config.override.functions is None
-            else self.config.override.functions(recipe_implementation)
-        )
-
-    def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
-        return isinstance(err, SuperTokensError) and (
-            isinstance(err, SuperTokensUserMetadataError)
-        )
-
-    def get_apis_handled(self) -> List[APIHandled]:
-        return []
-
-    async def handle_api_request(
-        self,
-        request_id: str,
-        tenant_id: Optional[str],
-        request: BaseRequest,
-        path: NormalisedURLPath,
-        method: str,
-        response: BaseResponse,
-        user_context: Dict[str, Any],
-    ) -> Union[BaseResponse, None]:
-        raise Exception("Should never come here")
-
-    async def handle_error(
-        self,
-        request: BaseRequest,
-        err: SuperTokensError,
-        response: BaseResponse,
-        user_context: Dict[str, Any],
-    ) -> BaseResponse:
-        raise err
-
-    def get_all_cors_headers(self) -> List[str]:
-        return []
-
-    @staticmethod
-    def init(override: Union[InputOverrideConfig, None] = None):
-        def func(app_info: AppInfo):
-            if UserMetadataRecipe.__instance is None:
-                UserMetadataRecipe.__instance = UserMetadataRecipe(
-                    UserMetadataRecipe.recipe_id, app_info, override
-                )
-                return UserMetadataRecipe.__instance
-            raise Exception(
-                None,
-                "Usermetadata recipe has already been initialised. Please check your code for bugs.",
-            )
-
-        return func
-
-    @staticmethod
-    def reset():
-        if ("SUPERTOKENS_ENV" not in environ) or (
-            environ["SUPERTOKENS_ENV"] != "testing"
-        ):
-            raise_general_exception("calling testing function in non testing env")
-        UserMetadataRecipe.__instance = None
-
-    @staticmethod
-    def get_instance() -> UserMetadataRecipe:
-        if UserMetadataRecipe.__instance is not None:
-            return UserMetadataRecipe.__instance
-        raise_general_exception(
-            "Initialisation not done. Did you forget to call the SuperTokens.init function?"
-        )
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class UserMetadataRecipe -(recipe_id: str, app_info: AppInfo, override: Union[InputOverrideConfig, None] = None) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class UserMetadataRecipe(RecipeModule):
-    recipe_id = "usermetadata"
-    __instance = None
-
-    def __init__(
-        self,
-        recipe_id: str,
-        app_info: AppInfo,
-        override: Union[InputOverrideConfig, None] = None,
-    ):
-        super().__init__(recipe_id, app_info)
-        self.config = validate_and_normalise_user_input(self, app_info, override)
-        recipe_implementation = RecipeImplementation(Querier.get_instance(recipe_id))
-        self.recipe_implementation = (
-            recipe_implementation
-            if self.config.override.functions is None
-            else self.config.override.functions(recipe_implementation)
-        )
-
-    def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
-        return isinstance(err, SuperTokensError) and (
-            isinstance(err, SuperTokensUserMetadataError)
-        )
-
-    def get_apis_handled(self) -> List[APIHandled]:
-        return []
-
-    async def handle_api_request(
-        self,
-        request_id: str,
-        tenant_id: Optional[str],
-        request: BaseRequest,
-        path: NormalisedURLPath,
-        method: str,
-        response: BaseResponse,
-        user_context: Dict[str, Any],
-    ) -> Union[BaseResponse, None]:
-        raise Exception("Should never come here")
-
-    async def handle_error(
-        self,
-        request: BaseRequest,
-        err: SuperTokensError,
-        response: BaseResponse,
-        user_context: Dict[str, Any],
-    ) -> BaseResponse:
-        raise err
-
-    def get_all_cors_headers(self) -> List[str]:
-        return []
-
-    @staticmethod
-    def init(override: Union[InputOverrideConfig, None] = None):
-        def func(app_info: AppInfo):
-            if UserMetadataRecipe.__instance is None:
-                UserMetadataRecipe.__instance = UserMetadataRecipe(
-                    UserMetadataRecipe.recipe_id, app_info, override
-                )
-                return UserMetadataRecipe.__instance
-            raise Exception(
-                None,
-                "Usermetadata recipe has already been initialised. Please check your code for bugs.",
-            )
-
-        return func
-
-    @staticmethod
-    def reset():
-        if ("SUPERTOKENS_ENV" not in environ) or (
-            environ["SUPERTOKENS_ENV"] != "testing"
-        ):
-            raise_general_exception("calling testing function in non testing env")
-        UserMetadataRecipe.__instance = None
-
-    @staticmethod
-    def get_instance() -> UserMetadataRecipe:
-        if UserMetadataRecipe.__instance is not None:
-            return UserMetadataRecipe.__instance
-        raise_general_exception(
-            "Initialisation not done. Did you forget to call the SuperTokens.init function?"
-        )
-
-

Ancestors

- -

Class variables

-
-
var get_tenant_id : Optional[Callable[[str, Dict[str, Any]], Awaitable[str]]]
-
-
-
-
var recipe_id
-
-
-
-
-

Static methods

-
-
-def get_instance() ‑> UserMetadataRecipe -
-
-
-
- -Expand source code - -
@staticmethod
-def get_instance() -> UserMetadataRecipe:
-    if UserMetadataRecipe.__instance is not None:
-        return UserMetadataRecipe.__instance
-    raise_general_exception(
-        "Initialisation not done. Did you forget to call the SuperTokens.init function?"
-    )
-
-
-
-def init(override: Union[InputOverrideConfig, None] = None) -
-
-
-
- -Expand source code - -
@staticmethod
-def init(override: Union[InputOverrideConfig, None] = None):
-    def func(app_info: AppInfo):
-        if UserMetadataRecipe.__instance is None:
-            UserMetadataRecipe.__instance = UserMetadataRecipe(
-                UserMetadataRecipe.recipe_id, app_info, override
-            )
-            return UserMetadataRecipe.__instance
-        raise Exception(
-            None,
-            "Usermetadata recipe has already been initialised. Please check your code for bugs.",
-        )
-
-    return func
-
-
-
-def reset() -
-
-
-
- -Expand source code - -
@staticmethod
-def reset():
-    if ("SUPERTOKENS_ENV" not in environ) or (
-        environ["SUPERTOKENS_ENV"] != "testing"
-    ):
-        raise_general_exception("calling testing function in non testing env")
-    UserMetadataRecipe.__instance = None
-
-
-
-

Methods

-
-
-def get_all_cors_headers(self) ‑> List[str] -
-
-
-
- -Expand source code - -
def get_all_cors_headers(self) -> List[str]:
-    return []
-
-
-
-def get_apis_handled(self) ‑> List[APIHandled] -
-
-
-
- -Expand source code - -
def get_apis_handled(self) -> List[APIHandled]:
-    return []
-
-
-
-async def handle_api_request(self, request_id: str, tenant_id: Optional[str], request: BaseRequest, path: NormalisedURLPath, method: str, response: BaseResponse, user_context: Dict[str, Any]) ‑> Optional[BaseResponse] -
-
-
-
- -Expand source code - -
async def handle_api_request(
-    self,
-    request_id: str,
-    tenant_id: Optional[str],
-    request: BaseRequest,
-    path: NormalisedURLPath,
-    method: str,
-    response: BaseResponse,
-    user_context: Dict[str, Any],
-) -> Union[BaseResponse, None]:
-    raise Exception("Should never come here")
-
-
-
-async def handle_error(self, request: BaseRequest, err: SuperTokensError, response: BaseResponse, user_context: Dict[str, Any]) ‑> BaseResponse -
-
-
-
- -Expand source code - -
async def handle_error(
-    self,
-    request: BaseRequest,
-    err: SuperTokensError,
-    response: BaseResponse,
-    user_context: Dict[str, Any],
-) -> BaseResponse:
-    raise err
-
-
-
-def is_error_from_this_recipe_based_on_instance(self, err: Exception) ‑> bool -
-
-
-
- -Expand source code - -
def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
-    return isinstance(err, SuperTokensError) and (
-        isinstance(err, SuperTokensUserMetadataError)
-    )
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/usermetadata/recipe_implementation.html b/html/supertokens_python/recipe/usermetadata/recipe_implementation.html deleted file mode 100644 index 77a612637..000000000 --- a/html/supertokens_python/recipe/usermetadata/recipe_implementation.html +++ /dev/null @@ -1,263 +0,0 @@ - - - - - - -supertokens_python.recipe.usermetadata.recipe_implementation API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.usermetadata.recipe_implementation

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-
-from typing import Any, Dict
-
-from supertokens_python.normalised_url_path import NormalisedURLPath
-from supertokens_python.querier import Querier
-
-from .interfaces import ClearUserMetadataResult, MetadataResult, RecipeInterface
-
-
-class RecipeImplementation(RecipeInterface):
-    def __init__(self, querier: Querier):
-        super().__init__()
-        self.querier = querier
-
-    async def get_user_metadata(
-        self, user_id: str, user_context: Dict[str, Any]
-    ) -> MetadataResult:
-        params = {"userId": user_id}
-        response = await self.querier.send_get_request(
-            NormalisedURLPath("/recipe/user/metadata"),
-            params,
-            user_context=user_context,
-        )
-        return MetadataResult(metadata=response["metadata"])
-
-    async def update_user_metadata(
-        self,
-        user_id: str,
-        metadata_update: Dict[str, Any],
-        user_context: Dict[str, Any],
-    ) -> MetadataResult:
-        params = {"userId": user_id, "metadataUpdate": metadata_update}
-        response = await self.querier.send_put_request(
-            NormalisedURLPath("/recipe/user/metadata"),
-            params,
-            user_context=user_context,
-        )
-        return MetadataResult(metadata=response["metadata"])
-
-    async def clear_user_metadata(
-        self, user_id: str, user_context: Dict[str, Any]
-    ) -> ClearUserMetadataResult:
-        params = {"userId": user_id}
-        await self.querier.send_post_request(
-            NormalisedURLPath("/recipe/user/metadata/remove"),
-            params,
-            user_context=user_context,
-        )
-        return ClearUserMetadataResult()
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class RecipeImplementation -(querier: Querier) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class RecipeImplementation(RecipeInterface):
-    def __init__(self, querier: Querier):
-        super().__init__()
-        self.querier = querier
-
-    async def get_user_metadata(
-        self, user_id: str, user_context: Dict[str, Any]
-    ) -> MetadataResult:
-        params = {"userId": user_id}
-        response = await self.querier.send_get_request(
-            NormalisedURLPath("/recipe/user/metadata"),
-            params,
-            user_context=user_context,
-        )
-        return MetadataResult(metadata=response["metadata"])
-
-    async def update_user_metadata(
-        self,
-        user_id: str,
-        metadata_update: Dict[str, Any],
-        user_context: Dict[str, Any],
-    ) -> MetadataResult:
-        params = {"userId": user_id, "metadataUpdate": metadata_update}
-        response = await self.querier.send_put_request(
-            NormalisedURLPath("/recipe/user/metadata"),
-            params,
-            user_context=user_context,
-        )
-        return MetadataResult(metadata=response["metadata"])
-
-    async def clear_user_metadata(
-        self, user_id: str, user_context: Dict[str, Any]
-    ) -> ClearUserMetadataResult:
-        params = {"userId": user_id}
-        await self.querier.send_post_request(
-            NormalisedURLPath("/recipe/user/metadata/remove"),
-            params,
-            user_context=user_context,
-        )
-        return ClearUserMetadataResult()
-
-

Ancestors

- -

Methods

-
-
-async def clear_user_metadata(self, user_id: str, user_context: Dict[str, Any]) ‑> ClearUserMetadataResult -
-
-
-
- -Expand source code - -
async def clear_user_metadata(
-    self, user_id: str, user_context: Dict[str, Any]
-) -> ClearUserMetadataResult:
-    params = {"userId": user_id}
-    await self.querier.send_post_request(
-        NormalisedURLPath("/recipe/user/metadata/remove"),
-        params,
-        user_context=user_context,
-    )
-    return ClearUserMetadataResult()
-
-
-
-async def get_user_metadata(self, user_id: str, user_context: Dict[str, Any]) ‑> MetadataResult -
-
-
-
- -Expand source code - -
async def get_user_metadata(
-    self, user_id: str, user_context: Dict[str, Any]
-) -> MetadataResult:
-    params = {"userId": user_id}
-    response = await self.querier.send_get_request(
-        NormalisedURLPath("/recipe/user/metadata"),
-        params,
-        user_context=user_context,
-    )
-    return MetadataResult(metadata=response["metadata"])
-
-
-
-async def update_user_metadata(self, user_id: str, metadata_update: Dict[str, Any], user_context: Dict[str, Any]) ‑> MetadataResult -
-
-
-
- -Expand source code - -
async def update_user_metadata(
-    self,
-    user_id: str,
-    metadata_update: Dict[str, Any],
-    user_context: Dict[str, Any],
-) -> MetadataResult:
-    params = {"userId": user_id, "metadataUpdate": metadata_update}
-    response = await self.querier.send_put_request(
-        NormalisedURLPath("/recipe/user/metadata"),
-        params,
-        user_context=user_context,
-    )
-    return MetadataResult(metadata=response["metadata"])
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/usermetadata/syncio/index.html b/html/supertokens_python/recipe/usermetadata/syncio/index.html deleted file mode 100644 index 9422e3bc1..000000000 --- a/html/supertokens_python/recipe/usermetadata/syncio/index.html +++ /dev/null @@ -1,142 +0,0 @@ - - - - - - -supertokens_python.recipe.usermetadata.syncio API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.usermetadata.syncio

-
-
-
- -Expand source code - -
from typing import Any, Dict, Union
-
-from supertokens_python.async_to_sync_wrapper import sync
-
-
-def get_user_metadata(user_id: str, user_context: Union[Dict[str, Any], None] = None):
-    from supertokens_python.recipe.usermetadata.asyncio import get_user_metadata
-
-    return sync(get_user_metadata(user_id, user_context))
-
-
-def update_user_metadata(
-    user_id: str,
-    metadata_update: Dict[str, Any],
-    user_context: Union[Dict[str, Any], None] = None,
-):
-    from supertokens_python.recipe.usermetadata.asyncio import update_user_metadata
-
-    return sync(update_user_metadata(user_id, metadata_update, user_context))
-
-
-def clear_user_metadata(user_id: str, user_context: Union[Dict[str, Any], None] = None):
-    from supertokens_python.recipe.usermetadata.asyncio import clear_user_metadata
-
-    return sync(clear_user_metadata(user_id, user_context))
-
-
-
-
-
-
-
-

Functions

-
-
-def clear_user_metadata(user_id: str, user_context: Optional[Dict[str, Any]] = None) -
-
-
-
- -Expand source code - -
def clear_user_metadata(user_id: str, user_context: Union[Dict[str, Any], None] = None):
-    from supertokens_python.recipe.usermetadata.asyncio import clear_user_metadata
-
-    return sync(clear_user_metadata(user_id, user_context))
-
-
-
-def get_user_metadata(user_id: str, user_context: Optional[Dict[str, Any]] = None) -
-
-
-
- -Expand source code - -
def get_user_metadata(user_id: str, user_context: Union[Dict[str, Any], None] = None):
-    from supertokens_python.recipe.usermetadata.asyncio import get_user_metadata
-
-    return sync(get_user_metadata(user_id, user_context))
-
-
-
-def update_user_metadata(user_id: str, metadata_update: Dict[str, Any], user_context: Optional[Dict[str, Any]] = None) -
-
-
-
- -Expand source code - -
def update_user_metadata(
-    user_id: str,
-    metadata_update: Dict[str, Any],
-    user_context: Union[Dict[str, Any], None] = None,
-):
-    from supertokens_python.recipe.usermetadata.asyncio import update_user_metadata
-
-    return sync(update_user_metadata(user_id, metadata_update, user_context))
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/usermetadata/utils.html b/html/supertokens_python/recipe/usermetadata/utils.html deleted file mode 100644 index 691fd3763..000000000 --- a/html/supertokens_python/recipe/usermetadata/utils.html +++ /dev/null @@ -1,192 +0,0 @@ - - - - - - -supertokens_python.recipe.usermetadata.utils API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.usermetadata.utils

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from __future__ import annotations
-
-from typing import TYPE_CHECKING, Callable, Union
-
-from supertokens_python.recipe.usermetadata.interfaces import (
-    APIInterface,
-    RecipeInterface,
-)
-from supertokens_python.supertokens import AppInfo
-
-if TYPE_CHECKING:
-    from supertokens_python.recipe.usermetadata.recipe import UserMetadataRecipe
-
-
-class InputOverrideConfig:
-    def __init__(
-        self,
-        functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None,
-        apis: Union[Callable[[APIInterface], APIInterface], None] = None,
-    ):
-        self.functions = functions
-        self.apis = apis
-
-
-class UserMetadataConfig:
-    def __init__(self, override: InputOverrideConfig) -> None:
-        self.override = override
-
-
-def validate_and_normalise_user_input(
-    _recipe: UserMetadataRecipe,
-    _app_info: AppInfo,
-    override: Union[InputOverrideConfig, None] = None,
-) -> UserMetadataConfig:
-    if override is not None and not isinstance(override, InputOverrideConfig):  # type: ignore
-        raise ValueError("override must be an instance of InputOverrideConfig or None")
-
-    if override is None:
-        override = InputOverrideConfig()
-
-    return UserMetadataConfig(override=override)
-
-
-
-
-
-
-
-

Functions

-
-
-def validate_and_normalise_user_input(_recipe: UserMetadataRecipe, _app_info: AppInfo, override: Union[InputOverrideConfig, None] = None) ‑> UserMetadataConfig -
-
-
-
- -Expand source code - -
def validate_and_normalise_user_input(
-    _recipe: UserMetadataRecipe,
-    _app_info: AppInfo,
-    override: Union[InputOverrideConfig, None] = None,
-) -> UserMetadataConfig:
-    if override is not None and not isinstance(override, InputOverrideConfig):  # type: ignore
-        raise ValueError("override must be an instance of InputOverrideConfig or None")
-
-    if override is None:
-        override = InputOverrideConfig()
-
-    return UserMetadataConfig(override=override)
-
-
-
-
-
-

Classes

-
-
-class InputOverrideConfig -(functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None, apis: Union[Callable[[APIInterface], APIInterface], None] = None) -
-
-
-
- -Expand source code - -
class InputOverrideConfig:
-    def __init__(
-        self,
-        functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None,
-        apis: Union[Callable[[APIInterface], APIInterface], None] = None,
-    ):
-        self.functions = functions
-        self.apis = apis
-
-
-
-class UserMetadataConfig -(override: InputOverrideConfig) -
-
-
-
- -Expand source code - -
class UserMetadataConfig:
-    def __init__(self, override: InputOverrideConfig) -> None:
-        self.override = override
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/userroles/asyncio/index.html b/html/supertokens_python/recipe/userroles/asyncio/index.html deleted file mode 100644 index 402fcd11f..000000000 --- a/html/supertokens_python/recipe/userroles/asyncio/index.html +++ /dev/null @@ -1,397 +0,0 @@ - - - - - - -supertokens_python.recipe.userroles.asyncio API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.userroles.asyncio

-
-
-
- -Expand source code - -
from typing import Any, Dict, List, Union
-
-from supertokens_python.recipe.userroles.interfaces import (
-    AddRoleToUserOkResult,
-    CreateNewRoleOrAddPermissionsOkResult,
-    DeleteRoleOkResult,
-    GetAllRolesOkResult,
-    GetPermissionsForRoleOkResult,
-    GetRolesForUserOkResult,
-    GetRolesThatHavePermissionOkResult,
-    GetUsersThatHaveRoleOkResult,
-    RemovePermissionsFromRoleOkResult,
-    RemoveUserRoleOkResult,
-    UnknownRoleError,
-)
-from supertokens_python.recipe.userroles.recipe import UserRolesRecipe
-
-
-async def add_role_to_user(
-    tenant_id: str,
-    user_id: str,
-    role: str,
-    user_context: Union[Dict[str, Any], None] = None,
-) -> Union[AddRoleToUserOkResult, UnknownRoleError]:
-    if user_context is None:
-        user_context = {}
-    return await UserRolesRecipe.get_instance().recipe_implementation.add_role_to_user(
-        user_id, role, tenant_id, user_context
-    )
-
-
-async def remove_user_role(
-    tenant_id: str,
-    user_id: str,
-    role: str,
-    user_context: Union[Dict[str, Any], None] = None,
-) -> Union[RemoveUserRoleOkResult, UnknownRoleError]:
-    if user_context is None:
-        user_context = {}
-    return await UserRolesRecipe.get_instance().recipe_implementation.remove_user_role(
-        user_id, role, tenant_id, user_context
-    )
-
-
-async def get_roles_for_user(
-    tenant_id: str, user_id: str, user_context: Union[Dict[str, Any], None] = None
-) -> GetRolesForUserOkResult:
-    if user_context is None:
-        user_context = {}
-    return (
-        await UserRolesRecipe.get_instance().recipe_implementation.get_roles_for_user(
-            user_id, tenant_id, user_context
-        )
-    )
-
-
-async def get_users_that_have_role(
-    tenant_id: str, role: str, user_context: Union[Dict[str, Any], None] = None
-) -> Union[GetUsersThatHaveRoleOkResult, UnknownRoleError]:
-    if user_context is None:
-        user_context = {}
-    return await UserRolesRecipe.get_instance().recipe_implementation.get_users_that_have_role(
-        role, tenant_id, user_context
-    )
-
-
-async def create_new_role_or_add_permissions(
-    role: str, permissions: List[str], user_context: Union[Dict[str, Any], None] = None
-) -> CreateNewRoleOrAddPermissionsOkResult:
-    if user_context is None:
-        user_context = {}
-    return await UserRolesRecipe.get_instance().recipe_implementation.create_new_role_or_add_permissions(
-        role, permissions, user_context
-    )
-
-
-async def get_permissions_for_role(
-    role: str, user_context: Union[Dict[str, Any], None] = None
-) -> Union[GetPermissionsForRoleOkResult, UnknownRoleError]:
-    if user_context is None:
-        user_context = {}
-    return await UserRolesRecipe.get_instance().recipe_implementation.get_permissions_for_role(
-        role, user_context
-    )
-
-
-async def remove_permissions_from_role(
-    role: str, permissions: List[str], user_context: Union[Dict[str, Any], None] = None
-) -> Union[RemovePermissionsFromRoleOkResult, UnknownRoleError]:
-    if user_context is None:
-        user_context = {}
-    return await UserRolesRecipe.get_instance().recipe_implementation.remove_permissions_from_role(
-        role, permissions, user_context
-    )
-
-
-async def get_roles_that_have_permission(
-    permission: str, user_context: Union[Dict[str, Any], None] = None
-) -> GetRolesThatHavePermissionOkResult:
-    if user_context is None:
-        user_context = {}
-    return await UserRolesRecipe.get_instance().recipe_implementation.get_roles_that_have_permission(
-        permission, user_context
-    )
-
-
-async def delete_role(
-    role: str, user_context: Union[Dict[str, Any], None] = None
-) -> DeleteRoleOkResult:
-    if user_context is None:
-        user_context = {}
-    return await UserRolesRecipe.get_instance().recipe_implementation.delete_role(
-        role, user_context
-    )
-
-
-async def get_all_roles(
-    user_context: Union[Dict[str, Any], None] = None
-) -> GetAllRolesOkResult:
-    if user_context is None:
-        user_context = {}
-    return await UserRolesRecipe.get_instance().recipe_implementation.get_all_roles(
-        user_context
-    )
-
-
-
-
-
-
-
-

Functions

-
-
-async def add_role_to_user(tenant_id: str, user_id: str, role: str, user_context: Optional[Dict[str, Any]] = None) ‑> Union[AddRoleToUserOkResultUnknownRoleError] -
-
-
-
- -Expand source code - -
async def add_role_to_user(
-    tenant_id: str,
-    user_id: str,
-    role: str,
-    user_context: Union[Dict[str, Any], None] = None,
-) -> Union[AddRoleToUserOkResult, UnknownRoleError]:
-    if user_context is None:
-        user_context = {}
-    return await UserRolesRecipe.get_instance().recipe_implementation.add_role_to_user(
-        user_id, role, tenant_id, user_context
-    )
-
-
-
-async def create_new_role_or_add_permissions(role: str, permissions: List[str], user_context: Optional[Dict[str, Any]] = None) ‑> CreateNewRoleOrAddPermissionsOkResult -
-
-
-
- -Expand source code - -
async def create_new_role_or_add_permissions(
-    role: str, permissions: List[str], user_context: Union[Dict[str, Any], None] = None
-) -> CreateNewRoleOrAddPermissionsOkResult:
-    if user_context is None:
-        user_context = {}
-    return await UserRolesRecipe.get_instance().recipe_implementation.create_new_role_or_add_permissions(
-        role, permissions, user_context
-    )
-
-
-
-async def delete_role(role: str, user_context: Optional[Dict[str, Any]] = None) ‑> DeleteRoleOkResult -
-
-
-
- -Expand source code - -
async def delete_role(
-    role: str, user_context: Union[Dict[str, Any], None] = None
-) -> DeleteRoleOkResult:
-    if user_context is None:
-        user_context = {}
-    return await UserRolesRecipe.get_instance().recipe_implementation.delete_role(
-        role, user_context
-    )
-
-
-
-async def get_all_roles(user_context: Optional[Dict[str, Any]] = None) ‑> GetAllRolesOkResult -
-
-
-
- -Expand source code - -
async def get_all_roles(
-    user_context: Union[Dict[str, Any], None] = None
-) -> GetAllRolesOkResult:
-    if user_context is None:
-        user_context = {}
-    return await UserRolesRecipe.get_instance().recipe_implementation.get_all_roles(
-        user_context
-    )
-
-
-
-async def get_permissions_for_role(role: str, user_context: Optional[Dict[str, Any]] = None) ‑> Union[GetPermissionsForRoleOkResultUnknownRoleError] -
-
-
-
- -Expand source code - -
async def get_permissions_for_role(
-    role: str, user_context: Union[Dict[str, Any], None] = None
-) -> Union[GetPermissionsForRoleOkResult, UnknownRoleError]:
-    if user_context is None:
-        user_context = {}
-    return await UserRolesRecipe.get_instance().recipe_implementation.get_permissions_for_role(
-        role, user_context
-    )
-
-
-
-async def get_roles_for_user(tenant_id: str, user_id: str, user_context: Optional[Dict[str, Any]] = None) ‑> GetRolesForUserOkResult -
-
-
-
- -Expand source code - -
async def get_roles_for_user(
-    tenant_id: str, user_id: str, user_context: Union[Dict[str, Any], None] = None
-) -> GetRolesForUserOkResult:
-    if user_context is None:
-        user_context = {}
-    return (
-        await UserRolesRecipe.get_instance().recipe_implementation.get_roles_for_user(
-            user_id, tenant_id, user_context
-        )
-    )
-
-
-
-async def get_roles_that_have_permission(permission: str, user_context: Optional[Dict[str, Any]] = None) ‑> GetRolesThatHavePermissionOkResult -
-
-
-
- -Expand source code - -
async def get_roles_that_have_permission(
-    permission: str, user_context: Union[Dict[str, Any], None] = None
-) -> GetRolesThatHavePermissionOkResult:
-    if user_context is None:
-        user_context = {}
-    return await UserRolesRecipe.get_instance().recipe_implementation.get_roles_that_have_permission(
-        permission, user_context
-    )
-
-
-
-async def get_users_that_have_role(tenant_id: str, role: str, user_context: Optional[Dict[str, Any]] = None) ‑> Union[GetUsersThatHaveRoleOkResultUnknownRoleError] -
-
-
-
- -Expand source code - -
async def get_users_that_have_role(
-    tenant_id: str, role: str, user_context: Union[Dict[str, Any], None] = None
-) -> Union[GetUsersThatHaveRoleOkResult, UnknownRoleError]:
-    if user_context is None:
-        user_context = {}
-    return await UserRolesRecipe.get_instance().recipe_implementation.get_users_that_have_role(
-        role, tenant_id, user_context
-    )
-
-
-
-async def remove_permissions_from_role(role: str, permissions: List[str], user_context: Optional[Dict[str, Any]] = None) ‑> Union[RemovePermissionsFromRoleOkResultUnknownRoleError] -
-
-
-
- -Expand source code - -
async def remove_permissions_from_role(
-    role: str, permissions: List[str], user_context: Union[Dict[str, Any], None] = None
-) -> Union[RemovePermissionsFromRoleOkResult, UnknownRoleError]:
-    if user_context is None:
-        user_context = {}
-    return await UserRolesRecipe.get_instance().recipe_implementation.remove_permissions_from_role(
-        role, permissions, user_context
-    )
-
-
-
-async def remove_user_role(tenant_id: str, user_id: str, role: str, user_context: Optional[Dict[str, Any]] = None) ‑> Union[RemoveUserRoleOkResultUnknownRoleError] -
-
-
-
- -Expand source code - -
async def remove_user_role(
-    tenant_id: str,
-    user_id: str,
-    role: str,
-    user_context: Union[Dict[str, Any], None] = None,
-) -> Union[RemoveUserRoleOkResult, UnknownRoleError]:
-    if user_context is None:
-        user_context = {}
-    return await UserRolesRecipe.get_instance().recipe_implementation.remove_user_role(
-        user_id, role, tenant_id, user_context
-    )
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/userroles/exceptions.html b/html/supertokens_python/recipe/userroles/exceptions.html deleted file mode 100644 index d10b26e96..000000000 --- a/html/supertokens_python/recipe/userroles/exceptions.html +++ /dev/null @@ -1,107 +0,0 @@ - - - - - - -supertokens_python.recipe.userroles.exceptions API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.userroles.exceptions

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from supertokens_python.exceptions import SuperTokensError
-
-
-class SuperTokensUserRolesError(SuperTokensError):
-    pass
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class SuperTokensUserRolesError -(*args, **kwargs) -
-
-

Common base class for all non-exit exceptions.

-
- -Expand source code - -
class SuperTokensUserRolesError(SuperTokensError):
-    pass
-
-

Ancestors

- -
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/userroles/index.html b/html/supertokens_python/recipe/userroles/index.html deleted file mode 100644 index 791eb9c3b..000000000 --- a/html/supertokens_python/recipe/userroles/index.html +++ /dev/null @@ -1,169 +0,0 @@ - - - - - - -supertokens_python.recipe.userroles API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.userroles

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from __future__ import annotations
-
-from typing import TYPE_CHECKING, Callable, Union, Optional
-
-from . import utils
-from .recipe import UserRolesRecipe
-from . import recipe
-
-PermissionClaim = recipe.PermissionClaim
-UserRoleClaim = recipe.UserRoleClaim
-
-if TYPE_CHECKING:
-    from supertokens_python.supertokens import AppInfo
-
-    from ...recipe_module import RecipeModule
-
-
-def init(
-    skip_adding_roles_to_access_token: Optional[bool] = None,
-    skip_adding_permissions_to_access_token: Optional[bool] = None,
-    override: Union[utils.InputOverrideConfig, None] = None,
-) -> Callable[[AppInfo], RecipeModule]:
-    return UserRolesRecipe.init(
-        skip_adding_roles_to_access_token,
-        skip_adding_permissions_to_access_token,
-        override,
-    )
-
-
-
-

Sub-modules

-
-
supertokens_python.recipe.userroles.asyncio
-
-
-
-
supertokens_python.recipe.userroles.exceptions
-
-
-
-
supertokens_python.recipe.userroles.interfaces
-
-
-
-
supertokens_python.recipe.userroles.recipe
-
-
-
-
supertokens_python.recipe.userroles.recipe_implementation
-
-
-
-
supertokens_python.recipe.userroles.syncio
-
-
-
-
supertokens_python.recipe.userroles.utils
-
-
-
-
-
-
-
-
-

Functions

-
-
-def init(skip_adding_roles_to_access_token: Optional[bool] = None, skip_adding_permissions_to_access_token: Optional[bool] = None, override: Union[InputOverrideConfig, None] = None) ‑> Callable[[AppInfo], RecipeModule] -
-
-
-
- -Expand source code - -
def init(
-    skip_adding_roles_to_access_token: Optional[bool] = None,
-    skip_adding_permissions_to_access_token: Optional[bool] = None,
-    override: Union[utils.InputOverrideConfig, None] = None,
-) -> Callable[[AppInfo], RecipeModule]:
-    return UserRolesRecipe.init(
-        skip_adding_roles_to_access_token,
-        skip_adding_permissions_to_access_token,
-        override,
-    )
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/userroles/interfaces.html b/html/supertokens_python/recipe/userroles/interfaces.html deleted file mode 100644 index b3db4927f..000000000 --- a/html/supertokens_python/recipe/userroles/interfaces.html +++ /dev/null @@ -1,677 +0,0 @@ - - - - - - -supertokens_python.recipe.userroles.interfaces API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.userroles.interfaces

-
-
-
- -Expand source code - -
from abc import ABC, abstractmethod
-from typing import Any, Dict, List, Union
-
-
-class AddRoleToUserOkResult:
-    def __init__(self, did_user_already_have_role: bool):
-        self.did_user_already_have_role = did_user_already_have_role
-
-
-class UnknownRoleError:
-    pass
-
-
-class RemoveUserRoleOkResult:
-    def __init__(self, did_user_have_role: bool):
-        self.did_user_have_role = did_user_have_role
-
-
-class GetRolesForUserOkResult:
-    def __init__(self, roles: List[str]):
-        self.roles = roles
-
-
-class GetUsersThatHaveRoleOkResult:
-    def __init__(self, users: List[str]):
-        self.users = users
-
-
-class CreateNewRoleOrAddPermissionsOkResult:
-    def __init__(self, created_new_role: bool):
-        self.created_new_role = created_new_role
-
-
-class GetPermissionsForRoleOkResult:
-    def __init__(self, permissions: List[str]):
-        self.permissions = permissions
-
-
-class RemovePermissionsFromRoleOkResult:
-    pass
-
-
-class GetRolesThatHavePermissionOkResult:
-    def __init__(self, roles: List[str]):
-        self.roles = roles
-
-
-class DeleteRoleOkResult:
-    def __init__(self, did_role_exist: bool):
-        self.did_role_exist = did_role_exist
-
-
-class GetAllRolesOkResult:
-    def __init__(self, roles: List[str]):
-        self.roles = roles
-
-
-class RecipeInterface(ABC):
-    @abstractmethod
-    async def add_role_to_user(
-        self,
-        user_id: str,
-        role: str,
-        tenant_id: str,
-        user_context: Dict[str, Any],
-    ) -> Union[AddRoleToUserOkResult, UnknownRoleError]:
-        pass
-
-    @abstractmethod
-    async def remove_user_role(
-        self,
-        user_id: str,
-        role: str,
-        tenant_id: str,
-        user_context: Dict[str, Any],
-    ) -> Union[RemoveUserRoleOkResult, UnknownRoleError]:
-        pass
-
-    @abstractmethod
-    async def get_roles_for_user(
-        self, user_id: str, tenant_id: str, user_context: Dict[str, Any]
-    ) -> GetRolesForUserOkResult:
-        pass
-
-    @abstractmethod
-    async def get_users_that_have_role(
-        self, role: str, tenant_id: str, user_context: Dict[str, Any]
-    ) -> Union[GetUsersThatHaveRoleOkResult, UnknownRoleError]:
-        pass
-
-    @abstractmethod
-    async def create_new_role_or_add_permissions(
-        self, role: str, permissions: List[str], user_context: Dict[str, Any]
-    ) -> CreateNewRoleOrAddPermissionsOkResult:
-        pass
-
-    @abstractmethod
-    async def get_permissions_for_role(
-        self, role: str, user_context: Dict[str, Any]
-    ) -> Union[GetPermissionsForRoleOkResult, UnknownRoleError]:
-        pass
-
-    @abstractmethod
-    async def remove_permissions_from_role(
-        self, role: str, permissions: List[str], user_context: Dict[str, Any]
-    ) -> Union[RemovePermissionsFromRoleOkResult, UnknownRoleError]:
-        pass
-
-    @abstractmethod
-    async def get_roles_that_have_permission(
-        self, permission: str, user_context: Dict[str, Any]
-    ) -> GetRolesThatHavePermissionOkResult:
-        pass
-
-    @abstractmethod
-    async def delete_role(
-        self, role: str, user_context: Dict[str, Any]
-    ) -> DeleteRoleOkResult:
-        pass
-
-    @abstractmethod
-    async def get_all_roles(self, user_context: Dict[str, Any]) -> GetAllRolesOkResult:
-        pass
-
-
-class APIInterface(ABC):
-    pass
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class APIInterface -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class APIInterface(ABC):
-    pass
-
-

Ancestors

-
    -
  • abc.ABC
  • -
-
-
-class AddRoleToUserOkResult -(did_user_already_have_role: bool) -
-
-
-
- -Expand source code - -
class AddRoleToUserOkResult:
-    def __init__(self, did_user_already_have_role: bool):
-        self.did_user_already_have_role = did_user_already_have_role
-
-
-
-class CreateNewRoleOrAddPermissionsOkResult -(created_new_role: bool) -
-
-
-
- -Expand source code - -
class CreateNewRoleOrAddPermissionsOkResult:
-    def __init__(self, created_new_role: bool):
-        self.created_new_role = created_new_role
-
-
-
-class DeleteRoleOkResult -(did_role_exist: bool) -
-
-
-
- -Expand source code - -
class DeleteRoleOkResult:
-    def __init__(self, did_role_exist: bool):
-        self.did_role_exist = did_role_exist
-
-
-
-class GetAllRolesOkResult -(roles: List[str]) -
-
-
-
- -Expand source code - -
class GetAllRolesOkResult:
-    def __init__(self, roles: List[str]):
-        self.roles = roles
-
-
-
-class GetPermissionsForRoleOkResult -(permissions: List[str]) -
-
-
-
- -Expand source code - -
class GetPermissionsForRoleOkResult:
-    def __init__(self, permissions: List[str]):
-        self.permissions = permissions
-
-
-
-class GetRolesForUserOkResult -(roles: List[str]) -
-
-
-
- -Expand source code - -
class GetRolesForUserOkResult:
-    def __init__(self, roles: List[str]):
-        self.roles = roles
-
-
-
-class GetRolesThatHavePermissionOkResult -(roles: List[str]) -
-
-
-
- -Expand source code - -
class GetRolesThatHavePermissionOkResult:
-    def __init__(self, roles: List[str]):
-        self.roles = roles
-
-
-
-class GetUsersThatHaveRoleOkResult -(users: List[str]) -
-
-
-
- -Expand source code - -
class GetUsersThatHaveRoleOkResult:
-    def __init__(self, users: List[str]):
-        self.users = users
-
-
-
-class RecipeInterface -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class RecipeInterface(ABC):
-    @abstractmethod
-    async def add_role_to_user(
-        self,
-        user_id: str,
-        role: str,
-        tenant_id: str,
-        user_context: Dict[str, Any],
-    ) -> Union[AddRoleToUserOkResult, UnknownRoleError]:
-        pass
-
-    @abstractmethod
-    async def remove_user_role(
-        self,
-        user_id: str,
-        role: str,
-        tenant_id: str,
-        user_context: Dict[str, Any],
-    ) -> Union[RemoveUserRoleOkResult, UnknownRoleError]:
-        pass
-
-    @abstractmethod
-    async def get_roles_for_user(
-        self, user_id: str, tenant_id: str, user_context: Dict[str, Any]
-    ) -> GetRolesForUserOkResult:
-        pass
-
-    @abstractmethod
-    async def get_users_that_have_role(
-        self, role: str, tenant_id: str, user_context: Dict[str, Any]
-    ) -> Union[GetUsersThatHaveRoleOkResult, UnknownRoleError]:
-        pass
-
-    @abstractmethod
-    async def create_new_role_or_add_permissions(
-        self, role: str, permissions: List[str], user_context: Dict[str, Any]
-    ) -> CreateNewRoleOrAddPermissionsOkResult:
-        pass
-
-    @abstractmethod
-    async def get_permissions_for_role(
-        self, role: str, user_context: Dict[str, Any]
-    ) -> Union[GetPermissionsForRoleOkResult, UnknownRoleError]:
-        pass
-
-    @abstractmethod
-    async def remove_permissions_from_role(
-        self, role: str, permissions: List[str], user_context: Dict[str, Any]
-    ) -> Union[RemovePermissionsFromRoleOkResult, UnknownRoleError]:
-        pass
-
-    @abstractmethod
-    async def get_roles_that_have_permission(
-        self, permission: str, user_context: Dict[str, Any]
-    ) -> GetRolesThatHavePermissionOkResult:
-        pass
-
-    @abstractmethod
-    async def delete_role(
-        self, role: str, user_context: Dict[str, Any]
-    ) -> DeleteRoleOkResult:
-        pass
-
-    @abstractmethod
-    async def get_all_roles(self, user_context: Dict[str, Any]) -> GetAllRolesOkResult:
-        pass
-
-

Ancestors

-
    -
  • abc.ABC
  • -
-

Subclasses

- -

Methods

-
-
-async def add_role_to_user(self, user_id: str, role: str, tenant_id: str, user_context: Dict[str, Any]) ‑> Union[AddRoleToUserOkResultUnknownRoleError] -
-
-
-
- -Expand source code - -
@abstractmethod
-async def add_role_to_user(
-    self,
-    user_id: str,
-    role: str,
-    tenant_id: str,
-    user_context: Dict[str, Any],
-) -> Union[AddRoleToUserOkResult, UnknownRoleError]:
-    pass
-
-
-
-async def create_new_role_or_add_permissions(self, role: str, permissions: List[str], user_context: Dict[str, Any]) ‑> CreateNewRoleOrAddPermissionsOkResult -
-
-
-
- -Expand source code - -
@abstractmethod
-async def create_new_role_or_add_permissions(
-    self, role: str, permissions: List[str], user_context: Dict[str, Any]
-) -> CreateNewRoleOrAddPermissionsOkResult:
-    pass
-
-
-
-async def delete_role(self, role: str, user_context: Dict[str, Any]) ‑> DeleteRoleOkResult -
-
-
-
- -Expand source code - -
@abstractmethod
-async def delete_role(
-    self, role: str, user_context: Dict[str, Any]
-) -> DeleteRoleOkResult:
-    pass
-
-
-
-async def get_all_roles(self, user_context: Dict[str, Any]) ‑> GetAllRolesOkResult -
-
-
-
- -Expand source code - -
@abstractmethod
-async def get_all_roles(self, user_context: Dict[str, Any]) -> GetAllRolesOkResult:
-    pass
-
-
-
-async def get_permissions_for_role(self, role: str, user_context: Dict[str, Any]) ‑> Union[GetPermissionsForRoleOkResultUnknownRoleError] -
-
-
-
- -Expand source code - -
@abstractmethod
-async def get_permissions_for_role(
-    self, role: str, user_context: Dict[str, Any]
-) -> Union[GetPermissionsForRoleOkResult, UnknownRoleError]:
-    pass
-
-
-
-async def get_roles_for_user(self, user_id: str, tenant_id: str, user_context: Dict[str, Any]) ‑> GetRolesForUserOkResult -
-
-
-
- -Expand source code - -
@abstractmethod
-async def get_roles_for_user(
-    self, user_id: str, tenant_id: str, user_context: Dict[str, Any]
-) -> GetRolesForUserOkResult:
-    pass
-
-
-
-async def get_roles_that_have_permission(self, permission: str, user_context: Dict[str, Any]) ‑> GetRolesThatHavePermissionOkResult -
-
-
-
- -Expand source code - -
@abstractmethod
-async def get_roles_that_have_permission(
-    self, permission: str, user_context: Dict[str, Any]
-) -> GetRolesThatHavePermissionOkResult:
-    pass
-
-
-
-async def get_users_that_have_role(self, role: str, tenant_id: str, user_context: Dict[str, Any]) ‑> Union[GetUsersThatHaveRoleOkResultUnknownRoleError] -
-
-
-
- -Expand source code - -
@abstractmethod
-async def get_users_that_have_role(
-    self, role: str, tenant_id: str, user_context: Dict[str, Any]
-) -> Union[GetUsersThatHaveRoleOkResult, UnknownRoleError]:
-    pass
-
-
-
-async def remove_permissions_from_role(self, role: str, permissions: List[str], user_context: Dict[str, Any]) ‑> Union[RemovePermissionsFromRoleOkResultUnknownRoleError] -
-
-
-
- -Expand source code - -
@abstractmethod
-async def remove_permissions_from_role(
-    self, role: str, permissions: List[str], user_context: Dict[str, Any]
-) -> Union[RemovePermissionsFromRoleOkResult, UnknownRoleError]:
-    pass
-
-
-
-async def remove_user_role(self, user_id: str, role: str, tenant_id: str, user_context: Dict[str, Any]) ‑> Union[RemoveUserRoleOkResultUnknownRoleError] -
-
-
-
- -Expand source code - -
@abstractmethod
-async def remove_user_role(
-    self,
-    user_id: str,
-    role: str,
-    tenant_id: str,
-    user_context: Dict[str, Any],
-) -> Union[RemoveUserRoleOkResult, UnknownRoleError]:
-    pass
-
-
-
-
-
-class RemovePermissionsFromRoleOkResult -
-
-
-
- -Expand source code - -
class RemovePermissionsFromRoleOkResult:
-    pass
-
-
-
-class RemoveUserRoleOkResult -(did_user_have_role: bool) -
-
-
-
- -Expand source code - -
class RemoveUserRoleOkResult:
-    def __init__(self, did_user_have_role: bool):
-        self.did_user_have_role = did_user_have_role
-
-
-
-class UnknownRoleError -
-
-
-
- -Expand source code - -
class UnknownRoleError:
-    pass
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/userroles/recipe.html b/html/supertokens_python/recipe/userroles/recipe.html deleted file mode 100644 index ddceffa24..000000000 --- a/html/supertokens_python/recipe/userroles/recipe.html +++ /dev/null @@ -1,704 +0,0 @@ - - - - - - -supertokens_python.recipe.userroles.recipe API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.userroles.recipe

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from __future__ import annotations
-
-from os import environ
-from typing import Any, Dict, List, Optional, Set, Union
-
-from supertokens_python.exceptions import SuperTokensError, raise_general_exception
-from supertokens_python.framework import BaseRequest, BaseResponse
-from supertokens_python.normalised_url_path import NormalisedURLPath
-from supertokens_python.querier import Querier
-from supertokens_python.recipe.userroles.recipe_implementation import (
-    RecipeImplementation,
-)
-from supertokens_python.recipe.userroles.utils import validate_and_normalise_user_input
-from supertokens_python.recipe_module import APIHandled, RecipeModule
-from supertokens_python.supertokens import AppInfo
-
-from ...post_init_callbacks import PostSTInitCallbacks
-from ..session import SessionRecipe
-from ..session.claim_base_classes.primitive_array_claim import PrimitiveArrayClaim
-from .exceptions import SuperTokensUserRolesError
-from .interfaces import GetPermissionsForRoleOkResult
-from .utils import InputOverrideConfig
-
-
-class UserRolesRecipe(RecipeModule):
-    recipe_id = "userroles"
-    __instance = None
-
-    def __init__(
-        self,
-        recipe_id: str,
-        app_info: AppInfo,
-        skip_adding_roles_to_access_token: Optional[bool] = None,
-        skip_adding_permissions_to_access_token: Optional[bool] = None,
-        override: Union[InputOverrideConfig, None] = None,
-    ):
-        super().__init__(recipe_id, app_info)
-        self.config = validate_and_normalise_user_input(
-            self,
-            app_info,
-            skip_adding_roles_to_access_token,
-            skip_adding_permissions_to_access_token,
-            override,
-        )
-        recipe_implementation = RecipeImplementation(Querier.get_instance(recipe_id))
-        self.recipe_implementation = (
-            recipe_implementation
-            if self.config.override.functions is None
-            else self.config.override.functions(recipe_implementation)
-        )
-
-        def callback():
-            if self.config.skip_adding_roles_to_access_token is False:
-                SessionRecipe.get_instance().add_claim_from_other_recipe(UserRoleClaim)
-            if self.config.skip_adding_permissions_to_access_token is False:
-                SessionRecipe.get_instance().add_claim_from_other_recipe(
-                    PermissionClaim
-                )
-
-        PostSTInitCallbacks.add_post_init_callback(callback)
-
-    def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
-        return isinstance(err, SuperTokensError) and (
-            isinstance(err, SuperTokensUserRolesError)
-        )
-
-    def get_apis_handled(self) -> List[APIHandled]:
-        return []
-
-    async def handle_api_request(
-        self,
-        request_id: str,
-        tenant_id: Optional[str],
-        request: BaseRequest,
-        path: NormalisedURLPath,
-        method: str,
-        response: BaseResponse,
-        user_context: Dict[str, Any],
-    ) -> Union[BaseResponse, None]:
-        raise Exception("Should never come here")
-
-    async def handle_error(
-        self,
-        request: BaseRequest,
-        err: SuperTokensError,
-        response: BaseResponse,
-        user_context: Dict[str, Any],
-    ) -> BaseResponse:
-        raise err
-
-    def get_all_cors_headers(self) -> List[str]:
-        return []
-
-    @staticmethod
-    def init(
-        skip_adding_roles_to_access_token: Optional[bool] = None,
-        skip_adding_permissions_to_access_token: Optional[bool] = None,
-        override: Union[InputOverrideConfig, None] = None,
-    ):
-        def func(app_info: AppInfo):
-            if UserRolesRecipe.__instance is None:
-                UserRolesRecipe.__instance = UserRolesRecipe(
-                    UserRolesRecipe.recipe_id,
-                    app_info,
-                    skip_adding_roles_to_access_token,
-                    skip_adding_permissions_to_access_token,
-                    override,
-                )
-                return UserRolesRecipe.__instance
-            raise Exception(
-                None,
-                "UserRoles recipe has already been initialised. Please check your code for bugs.",
-            )
-
-        return func
-
-    @staticmethod
-    def reset():
-        if ("SUPERTOKENS_ENV" not in environ) or (
-            environ["SUPERTOKENS_ENV"] != "testing"
-        ):
-            raise_general_exception("calling testing function in non testing env")
-        UserRolesRecipe.__instance = None
-
-    @staticmethod
-    def get_instance() -> UserRolesRecipe:
-        if UserRolesRecipe.__instance is not None:
-            return UserRolesRecipe.__instance
-        raise_general_exception(
-            "Initialisation not done. Did you forget to call the SuperTokens.init or UserRoles.init function?"
-        )
-
-
-class PermissionClaimClass(PrimitiveArrayClaim[List[str]]):
-    def __init__(self) -> None:
-        key = "st-perm"
-        default_max_age_in_sec = 300
-
-        async def fetch_value(
-            user_id: str, tenant_id: str, user_context: Dict[str, Any]
-        ) -> List[str]:
-            recipe = UserRolesRecipe.get_instance()
-
-            user_roles = await recipe.recipe_implementation.get_roles_for_user(
-                user_id, tenant_id, user_context
-            )
-
-            user_permissions: Set[str] = set()
-
-            for role in user_roles.roles:
-                role_permissions = (
-                    await recipe.recipe_implementation.get_permissions_for_role(
-                        role, user_context
-                    )
-                )
-
-                if isinstance(role_permissions, GetPermissionsForRoleOkResult):
-                    for permission in role_permissions.permissions:
-                        user_permissions.add(permission)
-
-            return list(user_permissions)
-
-        super().__init__(key, fetch_value, default_max_age_in_sec)
-
-
-PermissionClaim = PermissionClaimClass()
-
-
-class UserRoleClaimClass(PrimitiveArrayClaim[List[str]]):
-    def __init__(self) -> None:
-        key = "st-role"
-        default_max_age_in_sec = 300
-
-        async def fetch_value(
-            user_id: str, tenant_id: str, user_context: Dict[str, Any]
-        ) -> List[str]:
-            recipe = UserRolesRecipe.get_instance()
-            res = await recipe.recipe_implementation.get_roles_for_user(
-                user_id, tenant_id, user_context
-            )
-            return res.roles
-
-        super().__init__(key, fetch_value, default_max_age_in_sec)
-
-
-UserRoleClaim = UserRoleClaimClass()
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class PermissionClaimClass -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-

Args

-
-
key
-
The key to use when storing the claim in the payload.
-
fetch_value
-
a method that fetches the current value of this claim for the user. -A None return value signifies that we don't want to update the claim payload and or the claim value is -not present in the database. For example, this can happen with a second factor auth claim, where we -don't want to add the claim to the session automatically
-
-
- -Expand source code - -
class PermissionClaimClass(PrimitiveArrayClaim[List[str]]):
-    def __init__(self) -> None:
-        key = "st-perm"
-        default_max_age_in_sec = 300
-
-        async def fetch_value(
-            user_id: str, tenant_id: str, user_context: Dict[str, Any]
-        ) -> List[str]:
-            recipe = UserRolesRecipe.get_instance()
-
-            user_roles = await recipe.recipe_implementation.get_roles_for_user(
-                user_id, tenant_id, user_context
-            )
-
-            user_permissions: Set[str] = set()
-
-            for role in user_roles.roles:
-                role_permissions = (
-                    await recipe.recipe_implementation.get_permissions_for_role(
-                        role, user_context
-                    )
-                )
-
-                if isinstance(role_permissions, GetPermissionsForRoleOkResult):
-                    for permission in role_permissions.permissions:
-                        user_permissions.add(permission)
-
-            return list(user_permissions)
-
-        super().__init__(key, fetch_value, default_max_age_in_sec)
-
-

Ancestors

- -

Inherited members

- -
-
-class UserRoleClaimClass -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-

Args

-
-
key
-
The key to use when storing the claim in the payload.
-
fetch_value
-
a method that fetches the current value of this claim for the user. -A None return value signifies that we don't want to update the claim payload and or the claim value is -not present in the database. For example, this can happen with a second factor auth claim, where we -don't want to add the claim to the session automatically
-
-
- -Expand source code - -
class UserRoleClaimClass(PrimitiveArrayClaim[List[str]]):
-    def __init__(self) -> None:
-        key = "st-role"
-        default_max_age_in_sec = 300
-
-        async def fetch_value(
-            user_id: str, tenant_id: str, user_context: Dict[str, Any]
-        ) -> List[str]:
-            recipe = UserRolesRecipe.get_instance()
-            res = await recipe.recipe_implementation.get_roles_for_user(
-                user_id, tenant_id, user_context
-            )
-            return res.roles
-
-        super().__init__(key, fetch_value, default_max_age_in_sec)
-
-

Ancestors

- -

Inherited members

- -
-
-class UserRolesRecipe -(recipe_id: str, app_info: AppInfo, skip_adding_roles_to_access_token: Optional[bool] = None, skip_adding_permissions_to_access_token: Optional[bool] = None, override: Union[InputOverrideConfig, None] = None) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class UserRolesRecipe(RecipeModule):
-    recipe_id = "userroles"
-    __instance = None
-
-    def __init__(
-        self,
-        recipe_id: str,
-        app_info: AppInfo,
-        skip_adding_roles_to_access_token: Optional[bool] = None,
-        skip_adding_permissions_to_access_token: Optional[bool] = None,
-        override: Union[InputOverrideConfig, None] = None,
-    ):
-        super().__init__(recipe_id, app_info)
-        self.config = validate_and_normalise_user_input(
-            self,
-            app_info,
-            skip_adding_roles_to_access_token,
-            skip_adding_permissions_to_access_token,
-            override,
-        )
-        recipe_implementation = RecipeImplementation(Querier.get_instance(recipe_id))
-        self.recipe_implementation = (
-            recipe_implementation
-            if self.config.override.functions is None
-            else self.config.override.functions(recipe_implementation)
-        )
-
-        def callback():
-            if self.config.skip_adding_roles_to_access_token is False:
-                SessionRecipe.get_instance().add_claim_from_other_recipe(UserRoleClaim)
-            if self.config.skip_adding_permissions_to_access_token is False:
-                SessionRecipe.get_instance().add_claim_from_other_recipe(
-                    PermissionClaim
-                )
-
-        PostSTInitCallbacks.add_post_init_callback(callback)
-
-    def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
-        return isinstance(err, SuperTokensError) and (
-            isinstance(err, SuperTokensUserRolesError)
-        )
-
-    def get_apis_handled(self) -> List[APIHandled]:
-        return []
-
-    async def handle_api_request(
-        self,
-        request_id: str,
-        tenant_id: Optional[str],
-        request: BaseRequest,
-        path: NormalisedURLPath,
-        method: str,
-        response: BaseResponse,
-        user_context: Dict[str, Any],
-    ) -> Union[BaseResponse, None]:
-        raise Exception("Should never come here")
-
-    async def handle_error(
-        self,
-        request: BaseRequest,
-        err: SuperTokensError,
-        response: BaseResponse,
-        user_context: Dict[str, Any],
-    ) -> BaseResponse:
-        raise err
-
-    def get_all_cors_headers(self) -> List[str]:
-        return []
-
-    @staticmethod
-    def init(
-        skip_adding_roles_to_access_token: Optional[bool] = None,
-        skip_adding_permissions_to_access_token: Optional[bool] = None,
-        override: Union[InputOverrideConfig, None] = None,
-    ):
-        def func(app_info: AppInfo):
-            if UserRolesRecipe.__instance is None:
-                UserRolesRecipe.__instance = UserRolesRecipe(
-                    UserRolesRecipe.recipe_id,
-                    app_info,
-                    skip_adding_roles_to_access_token,
-                    skip_adding_permissions_to_access_token,
-                    override,
-                )
-                return UserRolesRecipe.__instance
-            raise Exception(
-                None,
-                "UserRoles recipe has already been initialised. Please check your code for bugs.",
-            )
-
-        return func
-
-    @staticmethod
-    def reset():
-        if ("SUPERTOKENS_ENV" not in environ) or (
-            environ["SUPERTOKENS_ENV"] != "testing"
-        ):
-            raise_general_exception("calling testing function in non testing env")
-        UserRolesRecipe.__instance = None
-
-    @staticmethod
-    def get_instance() -> UserRolesRecipe:
-        if UserRolesRecipe.__instance is not None:
-            return UserRolesRecipe.__instance
-        raise_general_exception(
-            "Initialisation not done. Did you forget to call the SuperTokens.init or UserRoles.init function?"
-        )
-
-

Ancestors

- -

Class variables

-
-
var get_tenant_id : Optional[Callable[[str, Dict[str, Any]], Awaitable[str]]]
-
-
-
-
var recipe_id
-
-
-
-
-

Static methods

-
-
-def get_instance() ‑> UserRolesRecipe -
-
-
-
- -Expand source code - -
@staticmethod
-def get_instance() -> UserRolesRecipe:
-    if UserRolesRecipe.__instance is not None:
-        return UserRolesRecipe.__instance
-    raise_general_exception(
-        "Initialisation not done. Did you forget to call the SuperTokens.init or UserRoles.init function?"
-    )
-
-
-
-def init(skip_adding_roles_to_access_token: Optional[bool] = None, skip_adding_permissions_to_access_token: Optional[bool] = None, override: Union[InputOverrideConfig, None] = None) -
-
-
-
- -Expand source code - -
@staticmethod
-def init(
-    skip_adding_roles_to_access_token: Optional[bool] = None,
-    skip_adding_permissions_to_access_token: Optional[bool] = None,
-    override: Union[InputOverrideConfig, None] = None,
-):
-    def func(app_info: AppInfo):
-        if UserRolesRecipe.__instance is None:
-            UserRolesRecipe.__instance = UserRolesRecipe(
-                UserRolesRecipe.recipe_id,
-                app_info,
-                skip_adding_roles_to_access_token,
-                skip_adding_permissions_to_access_token,
-                override,
-            )
-            return UserRolesRecipe.__instance
-        raise Exception(
-            None,
-            "UserRoles recipe has already been initialised. Please check your code for bugs.",
-        )
-
-    return func
-
-
-
-def reset() -
-
-
-
- -Expand source code - -
@staticmethod
-def reset():
-    if ("SUPERTOKENS_ENV" not in environ) or (
-        environ["SUPERTOKENS_ENV"] != "testing"
-    ):
-        raise_general_exception("calling testing function in non testing env")
-    UserRolesRecipe.__instance = None
-
-
-
-

Methods

-
-
-def get_all_cors_headers(self) ‑> List[str] -
-
-
-
- -Expand source code - -
def get_all_cors_headers(self) -> List[str]:
-    return []
-
-
-
-def get_apis_handled(self) ‑> List[APIHandled] -
-
-
-
- -Expand source code - -
def get_apis_handled(self) -> List[APIHandled]:
-    return []
-
-
-
-async def handle_api_request(self, request_id: str, tenant_id: Optional[str], request: BaseRequest, path: NormalisedURLPath, method: str, response: BaseResponse, user_context: Dict[str, Any]) ‑> Optional[BaseResponse] -
-
-
-
- -Expand source code - -
async def handle_api_request(
-    self,
-    request_id: str,
-    tenant_id: Optional[str],
-    request: BaseRequest,
-    path: NormalisedURLPath,
-    method: str,
-    response: BaseResponse,
-    user_context: Dict[str, Any],
-) -> Union[BaseResponse, None]:
-    raise Exception("Should never come here")
-
-
-
-async def handle_error(self, request: BaseRequest, err: SuperTokensError, response: BaseResponse, user_context: Dict[str, Any]) ‑> BaseResponse -
-
-
-
- -Expand source code - -
async def handle_error(
-    self,
-    request: BaseRequest,
-    err: SuperTokensError,
-    response: BaseResponse,
-    user_context: Dict[str, Any],
-) -> BaseResponse:
-    raise err
-
-
-
-def is_error_from_this_recipe_based_on_instance(self, err: Exception) ‑> bool -
-
-
-
- -Expand source code - -
def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
-    return isinstance(err, SuperTokensError) and (
-        isinstance(err, SuperTokensUserRolesError)
-    )
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/userroles/recipe_implementation.html b/html/supertokens_python/recipe/userroles/recipe_implementation.html deleted file mode 100644 index cdb6d6f7f..000000000 --- a/html/supertokens_python/recipe/userroles/recipe_implementation.html +++ /dev/null @@ -1,641 +0,0 @@ - - - - - - -supertokens_python.recipe.userroles.recipe_implementation API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.userroles.recipe_implementation

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-
-from typing import Any, Dict, List, Union
-
-from supertokens_python.normalised_url_path import NormalisedURLPath
-from supertokens_python.querier import Querier
-
-from .interfaces import (
-    AddRoleToUserOkResult,
-    CreateNewRoleOrAddPermissionsOkResult,
-    DeleteRoleOkResult,
-    GetAllRolesOkResult,
-    GetPermissionsForRoleOkResult,
-    GetRolesForUserOkResult,
-    GetRolesThatHavePermissionOkResult,
-    GetUsersThatHaveRoleOkResult,
-    RecipeInterface,
-    RemovePermissionsFromRoleOkResult,
-    RemoveUserRoleOkResult,
-    UnknownRoleError,
-)
-
-
-class RecipeImplementation(RecipeInterface):
-    def __init__(self, querier: Querier):
-        super().__init__()
-        self.querier = querier
-
-    async def add_role_to_user(
-        self,
-        user_id: str,
-        role: str,
-        tenant_id: str,
-        user_context: Dict[str, Any],
-    ) -> Union[AddRoleToUserOkResult, UnknownRoleError]:
-        params = {"userId": user_id, "role": role}
-        response = await self.querier.send_put_request(
-            NormalisedURLPath(f"{tenant_id}/recipe/user/role"),
-            params,
-            user_context=user_context,
-        )
-        if response["status"] == "OK":
-            return AddRoleToUserOkResult(
-                did_user_already_have_role=response["didUserAlreadyHaveRole"]
-            )
-        return UnknownRoleError()
-
-    async def remove_user_role(
-        self,
-        user_id: str,
-        role: str,
-        tenant_id: str,
-        user_context: Dict[str, Any],
-    ) -> Union[RemoveUserRoleOkResult, UnknownRoleError]:
-        params = {"userId": user_id, "role": role}
-        response = await self.querier.send_post_request(
-            NormalisedURLPath(f"{tenant_id}/recipe/user/role/remove"),
-            params,
-            user_context=user_context,
-        )
-        if response["status"] == "OK":
-            return RemoveUserRoleOkResult(
-                did_user_have_role=response["didUserHaveRole"]
-            )
-        return UnknownRoleError()
-
-    async def get_roles_for_user(
-        self, user_id: str, tenant_id: str, user_context: Dict[str, Any]
-    ) -> GetRolesForUserOkResult:
-        params = {"userId": user_id}
-        response = await self.querier.send_get_request(
-            NormalisedURLPath(f"{tenant_id}/recipe/user/roles"),
-            params,
-            user_context=user_context,
-        )
-        return GetRolesForUserOkResult(roles=response["roles"])
-
-    async def get_users_that_have_role(
-        self, role: str, tenant_id: str, user_context: Dict[str, Any]
-    ) -> Union[GetUsersThatHaveRoleOkResult, UnknownRoleError]:
-        params = {"role": role}
-        response = await self.querier.send_get_request(
-            NormalisedURLPath(f"{tenant_id}/recipe/role/users"),
-            params,
-            user_context=user_context,
-        )
-        if response["status"] == "OK":
-            return GetUsersThatHaveRoleOkResult(users=response["users"])
-        return UnknownRoleError()
-
-    async def create_new_role_or_add_permissions(
-        self, role: str, permissions: List[str], user_context: Dict[str, Any]
-    ) -> CreateNewRoleOrAddPermissionsOkResult:
-        params = {"role": role, "permissions": permissions}
-        response = await self.querier.send_put_request(
-            NormalisedURLPath("/recipe/role"),
-            params,
-            user_context=user_context,
-        )
-        return CreateNewRoleOrAddPermissionsOkResult(
-            created_new_role=response["createdNewRole"]
-        )
-
-    async def get_permissions_for_role(
-        self, role: str, user_context: Dict[str, Any]
-    ) -> Union[GetPermissionsForRoleOkResult, UnknownRoleError]:
-        params = {"role": role}
-        response = await self.querier.send_get_request(
-            NormalisedURLPath("/recipe/role/permissions"),
-            params,
-            user_context=user_context,
-        )
-        if response["status"] == "OK":
-            return GetPermissionsForRoleOkResult(permissions=response["permissions"])
-        return UnknownRoleError()
-
-    async def remove_permissions_from_role(
-        self, role: str, permissions: List[str], user_context: Dict[str, Any]
-    ) -> Union[RemovePermissionsFromRoleOkResult, UnknownRoleError]:
-        params = {"role": role, "permissions": permissions}
-        response = await self.querier.send_post_request(
-            NormalisedURLPath("/recipe/role/permissions/remove"),
-            params,
-            user_context=user_context,
-        )
-        if response["status"] == "OK":
-            return RemovePermissionsFromRoleOkResult()
-        return UnknownRoleError()
-
-    async def get_roles_that_have_permission(
-        self, permission: str, user_context: Dict[str, Any]
-    ) -> GetRolesThatHavePermissionOkResult:
-        params = {"permission": permission}
-        response = await self.querier.send_get_request(
-            NormalisedURLPath("/recipe/permission/roles"),
-            params,
-            user_context=user_context,
-        )
-        return GetRolesThatHavePermissionOkResult(roles=response["roles"])
-
-    async def delete_role(
-        self, role: str, user_context: Dict[str, Any]
-    ) -> DeleteRoleOkResult:
-        params = {"role": role}
-        response = await self.querier.send_post_request(
-            NormalisedURLPath("/recipe/role/remove"),
-            params,
-            user_context=user_context,
-        )
-        return DeleteRoleOkResult(did_role_exist=response["didRoleExist"])
-
-    async def get_all_roles(self, user_context: Dict[str, Any]) -> GetAllRolesOkResult:
-        params = {}
-        response = await self.querier.send_get_request(
-            NormalisedURLPath("/recipe/roles"),
-            params,
-            user_context=user_context,
-        )
-        return GetAllRolesOkResult(roles=response["roles"])
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class RecipeImplementation -(querier: Querier) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class RecipeImplementation(RecipeInterface):
-    def __init__(self, querier: Querier):
-        super().__init__()
-        self.querier = querier
-
-    async def add_role_to_user(
-        self,
-        user_id: str,
-        role: str,
-        tenant_id: str,
-        user_context: Dict[str, Any],
-    ) -> Union[AddRoleToUserOkResult, UnknownRoleError]:
-        params = {"userId": user_id, "role": role}
-        response = await self.querier.send_put_request(
-            NormalisedURLPath(f"{tenant_id}/recipe/user/role"),
-            params,
-            user_context=user_context,
-        )
-        if response["status"] == "OK":
-            return AddRoleToUserOkResult(
-                did_user_already_have_role=response["didUserAlreadyHaveRole"]
-            )
-        return UnknownRoleError()
-
-    async def remove_user_role(
-        self,
-        user_id: str,
-        role: str,
-        tenant_id: str,
-        user_context: Dict[str, Any],
-    ) -> Union[RemoveUserRoleOkResult, UnknownRoleError]:
-        params = {"userId": user_id, "role": role}
-        response = await self.querier.send_post_request(
-            NormalisedURLPath(f"{tenant_id}/recipe/user/role/remove"),
-            params,
-            user_context=user_context,
-        )
-        if response["status"] == "OK":
-            return RemoveUserRoleOkResult(
-                did_user_have_role=response["didUserHaveRole"]
-            )
-        return UnknownRoleError()
-
-    async def get_roles_for_user(
-        self, user_id: str, tenant_id: str, user_context: Dict[str, Any]
-    ) -> GetRolesForUserOkResult:
-        params = {"userId": user_id}
-        response = await self.querier.send_get_request(
-            NormalisedURLPath(f"{tenant_id}/recipe/user/roles"),
-            params,
-            user_context=user_context,
-        )
-        return GetRolesForUserOkResult(roles=response["roles"])
-
-    async def get_users_that_have_role(
-        self, role: str, tenant_id: str, user_context: Dict[str, Any]
-    ) -> Union[GetUsersThatHaveRoleOkResult, UnknownRoleError]:
-        params = {"role": role}
-        response = await self.querier.send_get_request(
-            NormalisedURLPath(f"{tenant_id}/recipe/role/users"),
-            params,
-            user_context=user_context,
-        )
-        if response["status"] == "OK":
-            return GetUsersThatHaveRoleOkResult(users=response["users"])
-        return UnknownRoleError()
-
-    async def create_new_role_or_add_permissions(
-        self, role: str, permissions: List[str], user_context: Dict[str, Any]
-    ) -> CreateNewRoleOrAddPermissionsOkResult:
-        params = {"role": role, "permissions": permissions}
-        response = await self.querier.send_put_request(
-            NormalisedURLPath("/recipe/role"),
-            params,
-            user_context=user_context,
-        )
-        return CreateNewRoleOrAddPermissionsOkResult(
-            created_new_role=response["createdNewRole"]
-        )
-
-    async def get_permissions_for_role(
-        self, role: str, user_context: Dict[str, Any]
-    ) -> Union[GetPermissionsForRoleOkResult, UnknownRoleError]:
-        params = {"role": role}
-        response = await self.querier.send_get_request(
-            NormalisedURLPath("/recipe/role/permissions"),
-            params,
-            user_context=user_context,
-        )
-        if response["status"] == "OK":
-            return GetPermissionsForRoleOkResult(permissions=response["permissions"])
-        return UnknownRoleError()
-
-    async def remove_permissions_from_role(
-        self, role: str, permissions: List[str], user_context: Dict[str, Any]
-    ) -> Union[RemovePermissionsFromRoleOkResult, UnknownRoleError]:
-        params = {"role": role, "permissions": permissions}
-        response = await self.querier.send_post_request(
-            NormalisedURLPath("/recipe/role/permissions/remove"),
-            params,
-            user_context=user_context,
-        )
-        if response["status"] == "OK":
-            return RemovePermissionsFromRoleOkResult()
-        return UnknownRoleError()
-
-    async def get_roles_that_have_permission(
-        self, permission: str, user_context: Dict[str, Any]
-    ) -> GetRolesThatHavePermissionOkResult:
-        params = {"permission": permission}
-        response = await self.querier.send_get_request(
-            NormalisedURLPath("/recipe/permission/roles"),
-            params,
-            user_context=user_context,
-        )
-        return GetRolesThatHavePermissionOkResult(roles=response["roles"])
-
-    async def delete_role(
-        self, role: str, user_context: Dict[str, Any]
-    ) -> DeleteRoleOkResult:
-        params = {"role": role}
-        response = await self.querier.send_post_request(
-            NormalisedURLPath("/recipe/role/remove"),
-            params,
-            user_context=user_context,
-        )
-        return DeleteRoleOkResult(did_role_exist=response["didRoleExist"])
-
-    async def get_all_roles(self, user_context: Dict[str, Any]) -> GetAllRolesOkResult:
-        params = {}
-        response = await self.querier.send_get_request(
-            NormalisedURLPath("/recipe/roles"),
-            params,
-            user_context=user_context,
-        )
-        return GetAllRolesOkResult(roles=response["roles"])
-
-

Ancestors

- -

Methods

-
-
-async def add_role_to_user(self, user_id: str, role: str, tenant_id: str, user_context: Dict[str, Any]) ‑> Union[AddRoleToUserOkResultUnknownRoleError] -
-
-
-
- -Expand source code - -
async def add_role_to_user(
-    self,
-    user_id: str,
-    role: str,
-    tenant_id: str,
-    user_context: Dict[str, Any],
-) -> Union[AddRoleToUserOkResult, UnknownRoleError]:
-    params = {"userId": user_id, "role": role}
-    response = await self.querier.send_put_request(
-        NormalisedURLPath(f"{tenant_id}/recipe/user/role"),
-        params,
-        user_context=user_context,
-    )
-    if response["status"] == "OK":
-        return AddRoleToUserOkResult(
-            did_user_already_have_role=response["didUserAlreadyHaveRole"]
-        )
-    return UnknownRoleError()
-
-
-
-async def create_new_role_or_add_permissions(self, role: str, permissions: List[str], user_context: Dict[str, Any]) ‑> CreateNewRoleOrAddPermissionsOkResult -
-
-
-
- -Expand source code - -
async def create_new_role_or_add_permissions(
-    self, role: str, permissions: List[str], user_context: Dict[str, Any]
-) -> CreateNewRoleOrAddPermissionsOkResult:
-    params = {"role": role, "permissions": permissions}
-    response = await self.querier.send_put_request(
-        NormalisedURLPath("/recipe/role"),
-        params,
-        user_context=user_context,
-    )
-    return CreateNewRoleOrAddPermissionsOkResult(
-        created_new_role=response["createdNewRole"]
-    )
-
-
-
-async def delete_role(self, role: str, user_context: Dict[str, Any]) ‑> DeleteRoleOkResult -
-
-
-
- -Expand source code - -
async def delete_role(
-    self, role: str, user_context: Dict[str, Any]
-) -> DeleteRoleOkResult:
-    params = {"role": role}
-    response = await self.querier.send_post_request(
-        NormalisedURLPath("/recipe/role/remove"),
-        params,
-        user_context=user_context,
-    )
-    return DeleteRoleOkResult(did_role_exist=response["didRoleExist"])
-
-
-
-async def get_all_roles(self, user_context: Dict[str, Any]) ‑> GetAllRolesOkResult -
-
-
-
- -Expand source code - -
async def get_all_roles(self, user_context: Dict[str, Any]) -> GetAllRolesOkResult:
-    params = {}
-    response = await self.querier.send_get_request(
-        NormalisedURLPath("/recipe/roles"),
-        params,
-        user_context=user_context,
-    )
-    return GetAllRolesOkResult(roles=response["roles"])
-
-
-
-async def get_permissions_for_role(self, role: str, user_context: Dict[str, Any]) ‑> Union[GetPermissionsForRoleOkResultUnknownRoleError] -
-
-
-
- -Expand source code - -
async def get_permissions_for_role(
-    self, role: str, user_context: Dict[str, Any]
-) -> Union[GetPermissionsForRoleOkResult, UnknownRoleError]:
-    params = {"role": role}
-    response = await self.querier.send_get_request(
-        NormalisedURLPath("/recipe/role/permissions"),
-        params,
-        user_context=user_context,
-    )
-    if response["status"] == "OK":
-        return GetPermissionsForRoleOkResult(permissions=response["permissions"])
-    return UnknownRoleError()
-
-
-
-async def get_roles_for_user(self, user_id: str, tenant_id: str, user_context: Dict[str, Any]) ‑> GetRolesForUserOkResult -
-
-
-
- -Expand source code - -
async def get_roles_for_user(
-    self, user_id: str, tenant_id: str, user_context: Dict[str, Any]
-) -> GetRolesForUserOkResult:
-    params = {"userId": user_id}
-    response = await self.querier.send_get_request(
-        NormalisedURLPath(f"{tenant_id}/recipe/user/roles"),
-        params,
-        user_context=user_context,
-    )
-    return GetRolesForUserOkResult(roles=response["roles"])
-
-
-
-async def get_roles_that_have_permission(self, permission: str, user_context: Dict[str, Any]) ‑> GetRolesThatHavePermissionOkResult -
-
-
-
- -Expand source code - -
async def get_roles_that_have_permission(
-    self, permission: str, user_context: Dict[str, Any]
-) -> GetRolesThatHavePermissionOkResult:
-    params = {"permission": permission}
-    response = await self.querier.send_get_request(
-        NormalisedURLPath("/recipe/permission/roles"),
-        params,
-        user_context=user_context,
-    )
-    return GetRolesThatHavePermissionOkResult(roles=response["roles"])
-
-
-
-async def get_users_that_have_role(self, role: str, tenant_id: str, user_context: Dict[str, Any]) ‑> Union[GetUsersThatHaveRoleOkResultUnknownRoleError] -
-
-
-
- -Expand source code - -
async def get_users_that_have_role(
-    self, role: str, tenant_id: str, user_context: Dict[str, Any]
-) -> Union[GetUsersThatHaveRoleOkResult, UnknownRoleError]:
-    params = {"role": role}
-    response = await self.querier.send_get_request(
-        NormalisedURLPath(f"{tenant_id}/recipe/role/users"),
-        params,
-        user_context=user_context,
-    )
-    if response["status"] == "OK":
-        return GetUsersThatHaveRoleOkResult(users=response["users"])
-    return UnknownRoleError()
-
-
-
-async def remove_permissions_from_role(self, role: str, permissions: List[str], user_context: Dict[str, Any]) ‑> Union[RemovePermissionsFromRoleOkResultUnknownRoleError] -
-
-
-
- -Expand source code - -
async def remove_permissions_from_role(
-    self, role: str, permissions: List[str], user_context: Dict[str, Any]
-) -> Union[RemovePermissionsFromRoleOkResult, UnknownRoleError]:
-    params = {"role": role, "permissions": permissions}
-    response = await self.querier.send_post_request(
-        NormalisedURLPath("/recipe/role/permissions/remove"),
-        params,
-        user_context=user_context,
-    )
-    if response["status"] == "OK":
-        return RemovePermissionsFromRoleOkResult()
-    return UnknownRoleError()
-
-
-
-async def remove_user_role(self, user_id: str, role: str, tenant_id: str, user_context: Dict[str, Any]) ‑> Union[RemoveUserRoleOkResultUnknownRoleError] -
-
-
-
- -Expand source code - -
async def remove_user_role(
-    self,
-    user_id: str,
-    role: str,
-    tenant_id: str,
-    user_context: Dict[str, Any],
-) -> Union[RemoveUserRoleOkResult, UnknownRoleError]:
-    params = {"userId": user_id, "role": role}
-    response = await self.querier.send_post_request(
-        NormalisedURLPath(f"{tenant_id}/recipe/user/role/remove"),
-        params,
-        user_context=user_context,
-    )
-    if response["status"] == "OK":
-        return RemoveUserRoleOkResult(
-            did_user_have_role=response["didUserHaveRole"]
-        )
-    return UnknownRoleError()
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/userroles/syncio/index.html b/html/supertokens_python/recipe/userroles/syncio/index.html deleted file mode 100644 index 3760819c0..000000000 --- a/html/supertokens_python/recipe/userroles/syncio/index.html +++ /dev/null @@ -1,375 +0,0 @@ - - - - - - -supertokens_python.recipe.userroles.syncio API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.userroles.syncio

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from typing import Any, Dict, List, Union
-
-from supertokens_python.async_to_sync_wrapper import sync
-from supertokens_python.recipe.userroles.interfaces import (
-    AddRoleToUserOkResult,
-    CreateNewRoleOrAddPermissionsOkResult,
-    DeleteRoleOkResult,
-    GetAllRolesOkResult,
-    GetPermissionsForRoleOkResult,
-    GetRolesForUserOkResult,
-    GetRolesThatHavePermissionOkResult,
-    GetUsersThatHaveRoleOkResult,
-    RemovePermissionsFromRoleOkResult,
-    RemoveUserRoleOkResult,
-    UnknownRoleError,
-)
-
-
-def add_role_to_user(
-    tenant_id: str,
-    user_id: str,
-    role: str,
-    user_context: Union[Dict[str, Any], None] = None,
-) -> Union[AddRoleToUserOkResult, UnknownRoleError]:
-    from supertokens_python.recipe.userroles.asyncio import add_role_to_user
-
-    return sync(add_role_to_user(tenant_id, user_id, role, user_context))
-
-
-def remove_user_role(
-    tenant_id: str,
-    user_id: str,
-    role: str,
-    user_context: Union[Dict[str, Any], None] = None,
-) -> Union[RemoveUserRoleOkResult, UnknownRoleError]:
-    from supertokens_python.recipe.userroles.asyncio import remove_user_role
-
-    return sync(remove_user_role(tenant_id, user_id, role, user_context))
-
-
-def get_roles_for_user(
-    tenant_id: str, user_id: str, user_context: Union[Dict[str, Any], None] = None
-) -> GetRolesForUserOkResult:
-    from supertokens_python.recipe.userroles.asyncio import get_roles_for_user
-
-    return sync(get_roles_for_user(tenant_id, user_id, user_context))
-
-
-def get_users_that_have_role(
-    tenant_id: str, role: str, user_context: Union[Dict[str, Any], None] = None
-) -> Union[GetUsersThatHaveRoleOkResult, UnknownRoleError]:
-    from supertokens_python.recipe.userroles.asyncio import get_users_that_have_role
-
-    return sync(get_users_that_have_role(tenant_id, role, user_context))
-
-
-def create_new_role_or_add_permissions(
-    role: str, permissions: List[str], user_context: Union[Dict[str, Any], None] = None
-) -> CreateNewRoleOrAddPermissionsOkResult:
-    from supertokens_python.recipe.userroles.asyncio import (
-        create_new_role_or_add_permissions,
-    )
-
-    return sync(create_new_role_or_add_permissions(role, permissions, user_context))
-
-
-def get_permissions_for_role(
-    role: str, user_context: Union[Dict[str, Any], None] = None
-) -> Union[GetPermissionsForRoleOkResult, UnknownRoleError]:
-    from supertokens_python.recipe.userroles.asyncio import get_permissions_for_role
-
-    return sync(get_permissions_for_role(role, user_context))
-
-
-def remove_permissions_from_role(
-    role: str, permissions: List[str], user_context: Union[Dict[str, Any], None] = None
-) -> Union[RemovePermissionsFromRoleOkResult, UnknownRoleError]:
-    from supertokens_python.recipe.userroles.asyncio import remove_permissions_from_role
-
-    return sync(remove_permissions_from_role(role, permissions, user_context))
-
-
-def get_roles_that_have_permission(
-    permission: str, user_context: Union[Dict[str, Any], None] = None
-) -> GetRolesThatHavePermissionOkResult:
-    from supertokens_python.recipe.userroles.asyncio import (
-        get_roles_that_have_permission,
-    )
-
-    return sync(get_roles_that_have_permission(permission, user_context))
-
-
-def delete_role(
-    role: str, user_context: Union[Dict[str, Any], None] = None
-) -> DeleteRoleOkResult:
-    from supertokens_python.recipe.userroles.asyncio import delete_role
-
-    return sync(delete_role(role, user_context))
-
-
-def get_all_roles(
-    user_context: Union[Dict[str, Any], None] = None
-) -> GetAllRolesOkResult:
-    from supertokens_python.recipe.userroles.asyncio import get_all_roles
-
-    return sync(get_all_roles(user_context))
-
-
-
-
-
-
-
-

Functions

-
-
-def add_role_to_user(tenant_id: str, user_id: str, role: str, user_context: Optional[Dict[str, Any]] = None) ‑> Union[AddRoleToUserOkResultUnknownRoleError] -
-
-
-
- -Expand source code - -
def add_role_to_user(
-    tenant_id: str,
-    user_id: str,
-    role: str,
-    user_context: Union[Dict[str, Any], None] = None,
-) -> Union[AddRoleToUserOkResult, UnknownRoleError]:
-    from supertokens_python.recipe.userroles.asyncio import add_role_to_user
-
-    return sync(add_role_to_user(tenant_id, user_id, role, user_context))
-
-
-
-def create_new_role_or_add_permissions(role: str, permissions: List[str], user_context: Optional[Dict[str, Any]] = None) ‑> CreateNewRoleOrAddPermissionsOkResult -
-
-
-
- -Expand source code - -
def create_new_role_or_add_permissions(
-    role: str, permissions: List[str], user_context: Union[Dict[str, Any], None] = None
-) -> CreateNewRoleOrAddPermissionsOkResult:
-    from supertokens_python.recipe.userroles.asyncio import (
-        create_new_role_or_add_permissions,
-    )
-
-    return sync(create_new_role_or_add_permissions(role, permissions, user_context))
-
-
-
-def delete_role(role: str, user_context: Optional[Dict[str, Any]] = None) ‑> DeleteRoleOkResult -
-
-
-
- -Expand source code - -
def delete_role(
-    role: str, user_context: Union[Dict[str, Any], None] = None
-) -> DeleteRoleOkResult:
-    from supertokens_python.recipe.userroles.asyncio import delete_role
-
-    return sync(delete_role(role, user_context))
-
-
-
-def get_all_roles(user_context: Optional[Dict[str, Any]] = None) ‑> GetAllRolesOkResult -
-
-
-
- -Expand source code - -
def get_all_roles(
-    user_context: Union[Dict[str, Any], None] = None
-) -> GetAllRolesOkResult:
-    from supertokens_python.recipe.userroles.asyncio import get_all_roles
-
-    return sync(get_all_roles(user_context))
-
-
-
-def get_permissions_for_role(role: str, user_context: Optional[Dict[str, Any]] = None) ‑> Union[GetPermissionsForRoleOkResultUnknownRoleError] -
-
-
-
- -Expand source code - -
def get_permissions_for_role(
-    role: str, user_context: Union[Dict[str, Any], None] = None
-) -> Union[GetPermissionsForRoleOkResult, UnknownRoleError]:
-    from supertokens_python.recipe.userroles.asyncio import get_permissions_for_role
-
-    return sync(get_permissions_for_role(role, user_context))
-
-
-
-def get_roles_for_user(tenant_id: str, user_id: str, user_context: Optional[Dict[str, Any]] = None) ‑> GetRolesForUserOkResult -
-
-
-
- -Expand source code - -
def get_roles_for_user(
-    tenant_id: str, user_id: str, user_context: Union[Dict[str, Any], None] = None
-) -> GetRolesForUserOkResult:
-    from supertokens_python.recipe.userroles.asyncio import get_roles_for_user
-
-    return sync(get_roles_for_user(tenant_id, user_id, user_context))
-
-
-
-def get_roles_that_have_permission(permission: str, user_context: Optional[Dict[str, Any]] = None) ‑> GetRolesThatHavePermissionOkResult -
-
-
-
- -Expand source code - -
def get_roles_that_have_permission(
-    permission: str, user_context: Union[Dict[str, Any], None] = None
-) -> GetRolesThatHavePermissionOkResult:
-    from supertokens_python.recipe.userroles.asyncio import (
-        get_roles_that_have_permission,
-    )
-
-    return sync(get_roles_that_have_permission(permission, user_context))
-
-
-
-def get_users_that_have_role(tenant_id: str, role: str, user_context: Optional[Dict[str, Any]] = None) ‑> Union[GetUsersThatHaveRoleOkResultUnknownRoleError] -
-
-
-
- -Expand source code - -
def get_users_that_have_role(
-    tenant_id: str, role: str, user_context: Union[Dict[str, Any], None] = None
-) -> Union[GetUsersThatHaveRoleOkResult, UnknownRoleError]:
-    from supertokens_python.recipe.userroles.asyncio import get_users_that_have_role
-
-    return sync(get_users_that_have_role(tenant_id, role, user_context))
-
-
-
-def remove_permissions_from_role(role: str, permissions: List[str], user_context: Optional[Dict[str, Any]] = None) ‑> Union[RemovePermissionsFromRoleOkResultUnknownRoleError] -
-
-
-
- -Expand source code - -
def remove_permissions_from_role(
-    role: str, permissions: List[str], user_context: Union[Dict[str, Any], None] = None
-) -> Union[RemovePermissionsFromRoleOkResult, UnknownRoleError]:
-    from supertokens_python.recipe.userroles.asyncio import remove_permissions_from_role
-
-    return sync(remove_permissions_from_role(role, permissions, user_context))
-
-
-
-def remove_user_role(tenant_id: str, user_id: str, role: str, user_context: Optional[Dict[str, Any]] = None) ‑> Union[RemoveUserRoleOkResultUnknownRoleError] -
-
-
-
- -Expand source code - -
def remove_user_role(
-    tenant_id: str,
-    user_id: str,
-    role: str,
-    user_context: Union[Dict[str, Any], None] = None,
-) -> Union[RemoveUserRoleOkResult, UnknownRoleError]:
-    from supertokens_python.recipe.userroles.asyncio import remove_user_role
-
-    return sync(remove_user_role(tenant_id, user_id, role, user_context))
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe/userroles/utils.html b/html/supertokens_python/recipe/userroles/utils.html deleted file mode 100644 index 41d2f37f5..000000000 --- a/html/supertokens_python/recipe/userroles/utils.html +++ /dev/null @@ -1,229 +0,0 @@ - - - - - - -supertokens_python.recipe.userroles.utils API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe.userroles.utils

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from __future__ import annotations
-
-from typing import TYPE_CHECKING, Callable, Union, Optional
-
-from supertokens_python.recipe.userroles.interfaces import APIInterface, RecipeInterface
-from supertokens_python.supertokens import AppInfo
-
-if TYPE_CHECKING:
-    from supertokens_python.recipe.userroles.recipe import UserRolesRecipe
-
-
-class InputOverrideConfig:
-    def __init__(
-        self,
-        functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None,
-        apis: Union[Callable[[APIInterface], APIInterface], None] = None,
-    ):
-        self.functions = functions
-        self.apis = apis
-
-
-class UserRolesConfig:
-    def __init__(
-        self,
-        skip_adding_roles_to_access_token: bool,
-        skip_adding_permissions_to_access_token: bool,
-        override: InputOverrideConfig,
-    ) -> None:
-        self.skip_adding_roles_to_access_token = skip_adding_roles_to_access_token
-        self.skip_adding_permissions_to_access_token = (
-            skip_adding_permissions_to_access_token
-        )
-        self.override = override
-
-
-def validate_and_normalise_user_input(
-    _recipe: UserRolesRecipe,
-    _app_info: AppInfo,
-    skip_adding_roles_to_access_token: Optional[bool] = None,
-    skip_adding_permissions_to_access_token: Optional[bool] = None,
-    override: Union[InputOverrideConfig, None] = None,
-) -> UserRolesConfig:
-    if override is not None and not isinstance(override, InputOverrideConfig):  # type: ignore
-        raise ValueError("override must be an instance of InputOverrideConfig or None")
-
-    if override is None:
-        override = InputOverrideConfig()
-
-    if skip_adding_roles_to_access_token is None:
-        skip_adding_roles_to_access_token = False
-    if skip_adding_permissions_to_access_token is None:
-        skip_adding_permissions_to_access_token = False
-
-    return UserRolesConfig(
-        skip_adding_roles_to_access_token=skip_adding_roles_to_access_token,
-        skip_adding_permissions_to_access_token=skip_adding_permissions_to_access_token,
-        override=override,
-    )
-
-
-
-
-
-
-
-

Functions

-
-
-def validate_and_normalise_user_input(_recipe: UserRolesRecipe, _app_info: AppInfo, skip_adding_roles_to_access_token: Optional[bool] = None, skip_adding_permissions_to_access_token: Optional[bool] = None, override: Union[InputOverrideConfig, None] = None) ‑> UserRolesConfig -
-
-
-
- -Expand source code - -
def validate_and_normalise_user_input(
-    _recipe: UserRolesRecipe,
-    _app_info: AppInfo,
-    skip_adding_roles_to_access_token: Optional[bool] = None,
-    skip_adding_permissions_to_access_token: Optional[bool] = None,
-    override: Union[InputOverrideConfig, None] = None,
-) -> UserRolesConfig:
-    if override is not None and not isinstance(override, InputOverrideConfig):  # type: ignore
-        raise ValueError("override must be an instance of InputOverrideConfig or None")
-
-    if override is None:
-        override = InputOverrideConfig()
-
-    if skip_adding_roles_to_access_token is None:
-        skip_adding_roles_to_access_token = False
-    if skip_adding_permissions_to_access_token is None:
-        skip_adding_permissions_to_access_token = False
-
-    return UserRolesConfig(
-        skip_adding_roles_to_access_token=skip_adding_roles_to_access_token,
-        skip_adding_permissions_to_access_token=skip_adding_permissions_to_access_token,
-        override=override,
-    )
-
-
-
-
-
-

Classes

-
-
-class InputOverrideConfig -(functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None, apis: Union[Callable[[APIInterface], APIInterface], None] = None) -
-
-
-
- -Expand source code - -
class InputOverrideConfig:
-    def __init__(
-        self,
-        functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None,
-        apis: Union[Callable[[APIInterface], APIInterface], None] = None,
-    ):
-        self.functions = functions
-        self.apis = apis
-
-
-
-class UserRolesConfig -(skip_adding_roles_to_access_token: bool, skip_adding_permissions_to_access_token: bool, override: InputOverrideConfig) -
-
-
-
- -Expand source code - -
class UserRolesConfig:
-    def __init__(
-        self,
-        skip_adding_roles_to_access_token: bool,
-        skip_adding_permissions_to_access_token: bool,
-        override: InputOverrideConfig,
-    ) -> None:
-        self.skip_adding_roles_to_access_token = skip_adding_roles_to_access_token
-        self.skip_adding_permissions_to_access_token = (
-            skip_adding_permissions_to_access_token
-        )
-        self.override = override
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/recipe_module.html b/html/supertokens_python/recipe_module.html deleted file mode 100644 index 0245fac92..000000000 --- a/html/supertokens_python/recipe_module.html +++ /dev/null @@ -1,580 +0,0 @@ - - - - - - -supertokens_python.recipe_module API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.recipe_module

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from __future__ import annotations
-
-import abc
-import re
-from typing import TYPE_CHECKING, List, Union, Optional, Dict, Any, Callable, Awaitable
-from typing_extensions import Literal
-
-from .framework.response import BaseResponse
-
-if TYPE_CHECKING:
-    from supertokens_python.framework.request import BaseRequest
-    from .supertokens import AppInfo
-
-
-from .exceptions import SuperTokensError
-from .normalised_url_path import NormalisedURLPath
-
-
-class ApiIdWithTenantId:
-    def __init__(self, api_id: str, tenant_id: str):
-        self.api_id = api_id
-        self.tenant_id = tenant_id
-
-
-class RecipeModule(abc.ABC):
-    get_tenant_id: Optional[Callable[[str, Dict[str, Any]], Awaitable[str]]] = None
-
-    def __init__(self, recipe_id: str, app_info: AppInfo):
-        self.recipe_id = recipe_id
-        self.app_info = app_info
-
-    def get_recipe_id(self):
-        return self.recipe_id
-
-    def get_app_info(self):
-        return self.app_info
-
-    async def return_api_id_if_can_handle_request(
-        self, path: NormalisedURLPath, method: str, user_context: Dict[str, Any]
-    ) -> Union[ApiIdWithTenantId, None]:
-        from supertokens_python.recipe.multitenancy.constants import DEFAULT_TENANT_ID
-
-        apis_handled = self.get_apis_handled()
-
-        base_path_str = self.app_info.api_base_path.get_as_string_dangerous()
-        path_str = path.get_as_string_dangerous()
-        regex = rf"^{base_path_str}(?:/([a-zA-Z0-9-]+))?(/.*)$"
-
-        match = re.match(regex, path_str)
-        match_group_1 = match.group(1) if match is not None else None
-        match_group_2 = match.group(2) if match is not None else None
-
-        tenant_id: str = DEFAULT_TENANT_ID
-        remaining_path: Optional[NormalisedURLPath] = None
-
-        if (
-            match is not None
-            and isinstance(match_group_1, str)
-            and isinstance(match_group_2, str)
-        ):
-            tenant_id = match_group_1
-            remaining_path = NormalisedURLPath(match_group_2)
-
-        assert RecipeModule.get_tenant_id is not None
-        assert callable(RecipeModule.get_tenant_id)
-
-        for current_api in apis_handled:
-            if not current_api.disabled and current_api.method == method:
-                if self.app_info.api_base_path.append(
-                    current_api.path_without_api_base_path
-                ).equals(path):
-                    final_tenant_id = await RecipeModule.get_tenant_id(  # pylint: disable=not-callable
-                        DEFAULT_TENANT_ID, user_context
-                    )
-                    return ApiIdWithTenantId(current_api.request_id, final_tenant_id)
-
-                if remaining_path is not None and self.app_info.api_base_path.append(
-                    current_api.path_without_api_base_path
-                ).equals(self.app_info.api_base_path.append(remaining_path)):
-                    final_tenant_id = await RecipeModule.get_tenant_id(  # pylint: disable=not-callable
-                        tenant_id, user_context
-                    )
-                    return ApiIdWithTenantId(current_api.request_id, final_tenant_id)
-
-        return None
-
-    @abc.abstractmethod
-    def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
-        pass
-
-    @abc.abstractmethod
-    def get_apis_handled(self) -> List[APIHandled]:
-        pass
-
-    @abc.abstractmethod
-    async def handle_api_request(
-        self,
-        request_id: str,
-        tenant_id: str,
-        request: BaseRequest,
-        path: NormalisedURLPath,
-        method: str,
-        response: BaseResponse,
-        user_context: Dict[str, Any],
-    ) -> Union[BaseResponse, None]:
-        pass
-
-    @abc.abstractmethod
-    async def handle_error(
-        self,
-        request: BaseRequest,
-        err: SuperTokensError,
-        response: BaseResponse,
-        user_context: Dict[str, Any],
-    ) -> BaseResponse:
-        pass
-
-    @abc.abstractmethod
-    def get_all_cors_headers(self) -> List[str]:
-        pass
-
-
-class APIHandled:
-    def __init__(
-        self,
-        path_without_api_base_path: NormalisedURLPath,
-        method: Literal["post", "get", "delete", "put", "options", "trace"],
-        request_id: str,
-        disabled: bool,
-    ):
-        self.path_without_api_base_path = path_without_api_base_path
-        self.method = method
-        self.request_id = request_id
-        self.disabled = disabled
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class APIHandled -(path_without_api_base_path: NormalisedURLPath, method: "Literal['post', 'get', 'delete', 'put', 'options', 'trace']", request_id: str, disabled: bool) -
-
-
-
- -Expand source code - -
class APIHandled:
-    def __init__(
-        self,
-        path_without_api_base_path: NormalisedURLPath,
-        method: Literal["post", "get", "delete", "put", "options", "trace"],
-        request_id: str,
-        disabled: bool,
-    ):
-        self.path_without_api_base_path = path_without_api_base_path
-        self.method = method
-        self.request_id = request_id
-        self.disabled = disabled
-
-
-
-class ApiIdWithTenantId -(api_id: str, tenant_id: str) -
-
-
-
- -Expand source code - -
class ApiIdWithTenantId:
-    def __init__(self, api_id: str, tenant_id: str):
-        self.api_id = api_id
-        self.tenant_id = tenant_id
-
-
-
-class RecipeModule -(recipe_id: str, app_info: AppInfo) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class RecipeModule(abc.ABC):
-    get_tenant_id: Optional[Callable[[str, Dict[str, Any]], Awaitable[str]]] = None
-
-    def __init__(self, recipe_id: str, app_info: AppInfo):
-        self.recipe_id = recipe_id
-        self.app_info = app_info
-
-    def get_recipe_id(self):
-        return self.recipe_id
-
-    def get_app_info(self):
-        return self.app_info
-
-    async def return_api_id_if_can_handle_request(
-        self, path: NormalisedURLPath, method: str, user_context: Dict[str, Any]
-    ) -> Union[ApiIdWithTenantId, None]:
-        from supertokens_python.recipe.multitenancy.constants import DEFAULT_TENANT_ID
-
-        apis_handled = self.get_apis_handled()
-
-        base_path_str = self.app_info.api_base_path.get_as_string_dangerous()
-        path_str = path.get_as_string_dangerous()
-        regex = rf"^{base_path_str}(?:/([a-zA-Z0-9-]+))?(/.*)$"
-
-        match = re.match(regex, path_str)
-        match_group_1 = match.group(1) if match is not None else None
-        match_group_2 = match.group(2) if match is not None else None
-
-        tenant_id: str = DEFAULT_TENANT_ID
-        remaining_path: Optional[NormalisedURLPath] = None
-
-        if (
-            match is not None
-            and isinstance(match_group_1, str)
-            and isinstance(match_group_2, str)
-        ):
-            tenant_id = match_group_1
-            remaining_path = NormalisedURLPath(match_group_2)
-
-        assert RecipeModule.get_tenant_id is not None
-        assert callable(RecipeModule.get_tenant_id)
-
-        for current_api in apis_handled:
-            if not current_api.disabled and current_api.method == method:
-                if self.app_info.api_base_path.append(
-                    current_api.path_without_api_base_path
-                ).equals(path):
-                    final_tenant_id = await RecipeModule.get_tenant_id(  # pylint: disable=not-callable
-                        DEFAULT_TENANT_ID, user_context
-                    )
-                    return ApiIdWithTenantId(current_api.request_id, final_tenant_id)
-
-                if remaining_path is not None and self.app_info.api_base_path.append(
-                    current_api.path_without_api_base_path
-                ).equals(self.app_info.api_base_path.append(remaining_path)):
-                    final_tenant_id = await RecipeModule.get_tenant_id(  # pylint: disable=not-callable
-                        tenant_id, user_context
-                    )
-                    return ApiIdWithTenantId(current_api.request_id, final_tenant_id)
-
-        return None
-
-    @abc.abstractmethod
-    def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
-        pass
-
-    @abc.abstractmethod
-    def get_apis_handled(self) -> List[APIHandled]:
-        pass
-
-    @abc.abstractmethod
-    async def handle_api_request(
-        self,
-        request_id: str,
-        tenant_id: str,
-        request: BaseRequest,
-        path: NormalisedURLPath,
-        method: str,
-        response: BaseResponse,
-        user_context: Dict[str, Any],
-    ) -> Union[BaseResponse, None]:
-        pass
-
-    @abc.abstractmethod
-    async def handle_error(
-        self,
-        request: BaseRequest,
-        err: SuperTokensError,
-        response: BaseResponse,
-        user_context: Dict[str, Any],
-    ) -> BaseResponse:
-        pass
-
-    @abc.abstractmethod
-    def get_all_cors_headers(self) -> List[str]:
-        pass
-
-

Ancestors

-
    -
  • abc.ABC
  • -
-

Subclasses

- -

Class variables

-
-
var get_tenant_id : Optional[Callable[[str, Dict[str, Any]], Awaitable[str]]]
-
-
-
-
-

Methods

-
-
-def get_all_cors_headers(self) ‑> List[str] -
-
-
-
- -Expand source code - -
@abc.abstractmethod
-def get_all_cors_headers(self) -> List[str]:
-    pass
-
-
-
-def get_apis_handled(self) ‑> List[APIHandled] -
-
-
-
- -Expand source code - -
@abc.abstractmethod
-def get_apis_handled(self) -> List[APIHandled]:
-    pass
-
-
-
-def get_app_info(self) -
-
-
-
- -Expand source code - -
def get_app_info(self):
-    return self.app_info
-
-
-
-def get_recipe_id(self) -
-
-
-
- -Expand source code - -
def get_recipe_id(self):
-    return self.recipe_id
-
-
-
-async def handle_api_request(self, request_id: str, tenant_id: str, request: BaseRequest, path: NormalisedURLPath, method: str, response: BaseResponse, user_context: Dict[str, Any]) ‑> Union[BaseResponse, None] -
-
-
-
- -Expand source code - -
@abc.abstractmethod
-async def handle_api_request(
-    self,
-    request_id: str,
-    tenant_id: str,
-    request: BaseRequest,
-    path: NormalisedURLPath,
-    method: str,
-    response: BaseResponse,
-    user_context: Dict[str, Any],
-) -> Union[BaseResponse, None]:
-    pass
-
-
-
-async def handle_error(self, request: BaseRequest, err: SuperTokensError, response: BaseResponse, user_context: Dict[str, Any]) ‑> BaseResponse -
-
-
-
- -Expand source code - -
@abc.abstractmethod
-async def handle_error(
-    self,
-    request: BaseRequest,
-    err: SuperTokensError,
-    response: BaseResponse,
-    user_context: Dict[str, Any],
-) -> BaseResponse:
-    pass
-
-
-
-def is_error_from_this_recipe_based_on_instance(self, err: Exception) ‑> bool -
-
-
-
- -Expand source code - -
@abc.abstractmethod
-def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
-    pass
-
-
-
-async def return_api_id_if_can_handle_request(self, path: NormalisedURLPath, method: str, user_context: Dict[str, Any]) ‑> Optional[ApiIdWithTenantId] -
-
-
-
- -Expand source code - -
async def return_api_id_if_can_handle_request(
-    self, path: NormalisedURLPath, method: str, user_context: Dict[str, Any]
-) -> Union[ApiIdWithTenantId, None]:
-    from supertokens_python.recipe.multitenancy.constants import DEFAULT_TENANT_ID
-
-    apis_handled = self.get_apis_handled()
-
-    base_path_str = self.app_info.api_base_path.get_as_string_dangerous()
-    path_str = path.get_as_string_dangerous()
-    regex = rf"^{base_path_str}(?:/([a-zA-Z0-9-]+))?(/.*)$"
-
-    match = re.match(regex, path_str)
-    match_group_1 = match.group(1) if match is not None else None
-    match_group_2 = match.group(2) if match is not None else None
-
-    tenant_id: str = DEFAULT_TENANT_ID
-    remaining_path: Optional[NormalisedURLPath] = None
-
-    if (
-        match is not None
-        and isinstance(match_group_1, str)
-        and isinstance(match_group_2, str)
-    ):
-        tenant_id = match_group_1
-        remaining_path = NormalisedURLPath(match_group_2)
-
-    assert RecipeModule.get_tenant_id is not None
-    assert callable(RecipeModule.get_tenant_id)
-
-    for current_api in apis_handled:
-        if not current_api.disabled and current_api.method == method:
-            if self.app_info.api_base_path.append(
-                current_api.path_without_api_base_path
-            ).equals(path):
-                final_tenant_id = await RecipeModule.get_tenant_id(  # pylint: disable=not-callable
-                    DEFAULT_TENANT_ID, user_context
-                )
-                return ApiIdWithTenantId(current_api.request_id, final_tenant_id)
-
-            if remaining_path is not None and self.app_info.api_base_path.append(
-                current_api.path_without_api_base_path
-            ).equals(self.app_info.api_base_path.append(remaining_path)):
-                final_tenant_id = await RecipeModule.get_tenant_id(  # pylint: disable=not-callable
-                    tenant_id, user_context
-                )
-                return ApiIdWithTenantId(current_api.request_id, final_tenant_id)
-
-    return None
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/supertokens.html b/html/supertokens_python/supertokens.html deleted file mode 100644 index 980b644aa..000000000 --- a/html/supertokens_python/supertokens.html +++ /dev/null @@ -1,2276 +0,0 @@ - - - - - - -supertokens_python.supertokens API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.supertokens

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from __future__ import annotations
-
-from os import environ
-from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Set, Union, Tuple
-
-from typing_extensions import Literal
-
-from supertokens_python.logger import (
-    get_maybe_none_as_str,
-    log_debug_message,
-    enable_debug_logging,
-)
-
-
-from .constants import FDI_KEY_HEADER, RID_KEY_HEADER, USER_COUNT, USER_DELETE, USERS
-from .exceptions import SuperTokensError
-from .interfaces import (
-    CreateUserIdMappingOkResult,
-    DeleteUserIdMappingOkResult,
-    GetUserIdMappingOkResult,
-    UnknownMappingError,
-    UnknownSupertokensUserIDError,
-    UpdateOrDeleteUserIdMappingInfoOkResult,
-    UserIdMappingAlreadyExistsError,
-    UserIDTypes,
-)
-from .normalised_url_domain import NormalisedURLDomain
-from .normalised_url_path import NormalisedURLPath
-from .post_init_callbacks import PostSTInitCallbacks
-from .querier import Querier
-from .types import ThirdPartyInfo, User, UsersResponse
-from .utils import (
-    get_rid_from_header,
-    get_top_level_domain_for_same_site_resolution,
-    is_version_gte,
-    normalise_http_method,
-    send_non_200_response_with_message,
-)
-
-if TYPE_CHECKING:
-    from .recipe_module import RecipeModule
-    from supertokens_python.framework.request import BaseRequest
-    from supertokens_python.framework.response import BaseResponse
-    from supertokens_python.recipe.session import SessionContainer
-
-import json
-
-from .exceptions import BadInputError, GeneralError, raise_general_exception
-
-
-class SupertokensConfig:
-    def __init__(
-        self,
-        connection_uri: str,
-        api_key: Union[str, None] = None,
-        network_interceptor: Optional[
-            Callable[
-                [
-                    str,
-                    str,
-                    Dict[str, Any],
-                    Optional[Dict[str, Any]],
-                    Optional[Dict[str, Any]],
-                    Optional[Dict[str, Any]],
-                ],
-                Tuple[
-                    str,
-                    str,
-                    Dict[str, Any],
-                    Optional[Dict[str, Any]],
-                    Optional[Dict[str, Any]],
-                ],
-            ]
-        ] = None,
-        disable_core_call_cache: bool = False,
-    ):  # We keep this = None here because this is directly used by the user.
-        self.connection_uri = connection_uri
-        self.api_key = api_key
-        self.network_interceptor = network_interceptor
-        self.disable_core_call_cache = disable_core_call_cache
-
-
-class Host:
-    def __init__(self, domain: NormalisedURLDomain, base_path: NormalisedURLPath):
-        self.domain = domain
-        self.base_path = base_path
-
-
-class InputAppInfo:
-    def __init__(
-        self,
-        app_name: str,
-        api_domain: str,
-        api_gateway_path: str = "",
-        api_base_path: str = "/auth",
-        website_base_path: str = "/auth",
-        website_domain: Optional[str] = None,
-        origin: Optional[
-            Union[str, Callable[[Optional[BaseRequest], Dict[str, Any]], str]]
-        ] = None,
-    ):
-        self.app_name = app_name
-        self.api_gateway_path = api_gateway_path
-        self.api_domain = api_domain
-        self.website_domain = website_domain
-        self.origin = origin
-        self.api_base_path = api_base_path
-        self.website_base_path = website_base_path
-
-
-class AppInfo:
-    def __init__(
-        self,
-        app_name: str,
-        api_domain: str,
-        website_domain: Optional[str],
-        framework: Literal["fastapi", "flask", "django"],
-        api_gateway_path: str,
-        api_base_path: str,
-        website_base_path: str,
-        mode: Union[Literal["asgi", "wsgi"], None],
-        origin: Optional[
-            Union[str, Callable[[Optional[BaseRequest], Dict[str, Any]], str]]
-        ],
-    ):
-        self.app_name = app_name
-        self.api_gateway_path = NormalisedURLPath(api_gateway_path)
-        self.api_domain = NormalisedURLDomain(api_domain)
-        self.top_level_api_domain = get_top_level_domain_for_same_site_resolution(
-            self.api_domain.get_as_string_dangerous()
-        )
-        if website_domain is None and origin is None:
-            raise_general_exception(
-                "Please provide at least one of website_domain or origin"
-            )
-        self.__origin = origin
-        self.__website_domain = website_domain
-        self.api_base_path = self.api_gateway_path.append(
-            NormalisedURLPath(api_base_path)
-        )
-        self.website_base_path = NormalisedURLPath(website_base_path)
-        if mode is not None:
-            self.mode = mode
-        elif framework == "fastapi":
-            mode = "asgi"
-        else:
-            mode = "wsgi"
-        self.framework = framework
-        self.mode = mode
-
-    def get_top_level_website_domain(
-        self, request: Optional[BaseRequest], user_context: Dict[str, Any]
-    ) -> str:
-        return get_top_level_domain_for_same_site_resolution(
-            self.get_origin(request, user_context).get_as_string_dangerous()
-        )
-
-    def get_origin(self, request: Optional[BaseRequest], user_context: Dict[str, Any]):
-        origin = self.__origin
-        if origin is None:
-            origin = self.__website_domain
-
-        # This should not be possible because we check for either origin or websiteDomain above
-        if origin is None:
-            raise_general_exception("should never come here")
-
-        if callable(origin):
-            origin = origin(request, user_context)
-
-        return NormalisedURLDomain(origin)
-
-    def toJSON(self):
-        def defaultImpl(o: Any):
-            if isinstance(o, (NormalisedURLDomain, NormalisedURLPath)):
-                return o.get_as_string_dangerous()
-            return o.__dict__
-
-        return json.dumps(self, default=defaultImpl, sort_keys=True, indent=4)
-
-
-def manage_session_post_response(
-    session: SessionContainer, response: BaseResponse, user_context: Dict[str, Any]
-):
-    # Something similar happens in handle_error of session/recipe.py
-    for mutator in session.response_mutators:
-        mutator(response, user_context)
-
-
-class Supertokens:
-    __instance = None
-
-    def __init__(
-        self,
-        app_info: InputAppInfo,
-        framework: Literal["fastapi", "flask", "django"],
-        supertokens_config: SupertokensConfig,
-        recipe_list: List[Callable[[AppInfo], RecipeModule]],
-        mode: Optional[Literal["asgi", "wsgi"]],
-        telemetry: Optional[bool],
-        debug: Optional[bool],
-    ):
-        if not isinstance(app_info, InputAppInfo):  # type: ignore
-            raise ValueError("app_info must be an instance of InputAppInfo")
-
-        self.app_info = AppInfo(
-            app_info.app_name,
-            app_info.api_domain,
-            app_info.website_domain,
-            framework,
-            app_info.api_gateway_path,
-            app_info.api_base_path,
-            app_info.website_base_path,
-            mode,
-            app_info.origin,
-        )
-        self.supertokens_config = supertokens_config
-        if debug is True:
-            enable_debug_logging()
-        self._telemetry_status: str = "NONE"
-        log_debug_message(
-            "Started SuperTokens with debug logging (supertokens.init called)"
-        )
-        log_debug_message("app_info: %s", self.app_info.toJSON())
-        log_debug_message("framework: %s", framework)
-        hosts = list(
-            map(
-                lambda h: Host(
-                    NormalisedURLDomain(h.strip()), NormalisedURLPath(h.strip())
-                ),
-                filter(lambda x: x != "", supertokens_config.connection_uri.split(";")),
-            )
-        )
-        Querier.init(
-            hosts,
-            supertokens_config.api_key,
-            supertokens_config.network_interceptor,
-            supertokens_config.disable_core_call_cache,
-        )
-
-        if len(recipe_list) == 0:
-            raise_general_exception(
-                "Please provide at least one recipe to the supertokens.init function call"
-            )
-
-        from supertokens_python.recipe.multitenancy.recipe import MultitenancyRecipe
-
-        multitenancy_found = False
-
-        def make_recipe(recipe: Callable[[AppInfo], RecipeModule]) -> RecipeModule:
-            nonlocal multitenancy_found
-            recipe_module = recipe(self.app_info)
-            if recipe_module.get_recipe_id() == MultitenancyRecipe.recipe_id:
-                multitenancy_found = True
-            return recipe_module
-
-        self.recipe_modules: List[RecipeModule] = list(map(make_recipe, recipe_list))
-
-        if not multitenancy_found:
-            recipe = MultitenancyRecipe.init()(self.app_info)
-            self.recipe_modules.append(recipe)
-
-        self.telemetry = (
-            telemetry
-            if telemetry is not None
-            else (environ.get("TEST_MODE") != "testing")
-        )
-
-    @staticmethod
-    def init(
-        app_info: InputAppInfo,
-        framework: Literal["fastapi", "flask", "django"],
-        supertokens_config: SupertokensConfig,
-        recipe_list: List[Callable[[AppInfo], RecipeModule]],
-        mode: Optional[Literal["asgi", "wsgi"]],
-        telemetry: Optional[bool],
-        debug: Optional[bool],
-    ):
-        if Supertokens.__instance is None:
-            Supertokens.__instance = Supertokens(
-                app_info,
-                framework,
-                supertokens_config,
-                recipe_list,
-                mode,
-                telemetry,
-                debug,
-            )
-            PostSTInitCallbacks.run_post_init_callbacks()
-
-    @staticmethod
-    def reset():
-        if ("SUPERTOKENS_ENV" not in environ) or (
-            environ["SUPERTOKENS_ENV"] != "testing"
-        ):
-            raise_general_exception("calling testing function in non testing env")
-        Querier.reset()
-        Supertokens.__instance = None
-
-    @staticmethod
-    def get_instance() -> Supertokens:
-        if Supertokens.__instance is not None:
-            return Supertokens.__instance
-        raise_general_exception(
-            "Initialisation not done. Did you forget to call the SuperTokens.init function?"
-        )
-
-    def get_all_cors_headers(self) -> List[str]:
-        headers_set: Set[str] = set()
-        headers_set.add(RID_KEY_HEADER)
-        headers_set.add(FDI_KEY_HEADER)
-        for recipe in self.recipe_modules:
-            headers = recipe.get_all_cors_headers()
-            for header in headers:
-                headers_set.add(header)
-
-        return list(headers_set)
-
-    async def get_user_count(  # pylint: disable=no-self-use
-        self,
-        include_recipe_ids: Union[None, List[str]],
-        tenant_id: Optional[str] = None,
-        user_context: Optional[Dict[str, Any]] = None,
-    ) -> int:
-        querier = Querier.get_instance(None)
-        include_recipe_ids_str = None
-        if include_recipe_ids is not None:
-            include_recipe_ids_str = ",".join(include_recipe_ids)
-
-        response = await querier.send_get_request(
-            NormalisedURLPath(f"/{tenant_id or 'public'}{USER_COUNT}"),
-            {
-                "includeRecipeIds": include_recipe_ids_str,
-                "includeAllTenants": tenant_id is None,
-            },
-            user_context=user_context,
-        )
-
-        return int(response["count"])
-
-    async def delete_user(  # pylint: disable=no-self-use
-        self,
-        user_id: str,
-        user_context: Optional[Dict[str, Any]],
-    ) -> None:
-        querier = Querier.get_instance(None)
-
-        cdi_version = await querier.get_api_version(user_context)
-
-        if is_version_gte(cdi_version, "2.10"):
-            await querier.send_post_request(
-                NormalisedURLPath(USER_DELETE),
-                {"userId": user_id},
-                user_context=user_context,
-            )
-
-            return None
-        raise_general_exception("Please upgrade the SuperTokens core to >= 3.7.0")
-
-    async def get_users(  # pylint: disable=no-self-use
-        self,
-        tenant_id: str,
-        time_joined_order: Literal["ASC", "DESC"],
-        limit: Union[int, None],
-        pagination_token: Union[str, None],
-        include_recipe_ids: Union[None, List[str]],
-        query: Union[Dict[str, str], None],
-        user_context: Optional[Dict[str, Any]],
-    ) -> UsersResponse:
-
-        querier = Querier.get_instance(None)
-        params = {"timeJoinedOrder": time_joined_order}
-        if limit is not None:
-            params = {"limit": limit, **params}
-        if pagination_token is not None:
-            params = {"paginationToken": pagination_token, **params}
-
-        include_recipe_ids_str = None
-        if include_recipe_ids is not None:
-            include_recipe_ids_str = ",".join(include_recipe_ids)
-            params = {"includeRecipeIds": include_recipe_ids_str, **params}
-
-        if query is not None:
-            params = {**params, **query}
-
-        response = await querier.send_get_request(
-            NormalisedURLPath(f"/{tenant_id}{USERS}"), params, user_context=user_context
-        )
-        next_pagination_token = None
-        if "nextPaginationToken" in response:
-            next_pagination_token = response["nextPaginationToken"]
-        users_list = response["users"]
-        users: List[User] = []
-        for user in users_list:
-            recipe_id = user["recipeId"]
-            user_obj = user["user"]
-            third_party = None
-            if "thirdParty" in user_obj:
-                third_party = ThirdPartyInfo(
-                    user_obj["thirdParty"]["userId"], user_obj["thirdParty"]["id"]
-                )
-            email = None
-            if "email" in user_obj:
-                email = user_obj["email"]
-            phone_number = None
-            if "phoneNumber" in user_obj:
-                phone_number = user_obj["phoneNumber"]
-            users.append(
-                User(
-                    recipe_id,
-                    user_obj["id"],
-                    user_obj["timeJoined"],
-                    email,
-                    phone_number,
-                    third_party,
-                    user_obj["tenantIds"],
-                )
-            )
-
-        return UsersResponse(users, next_pagination_token)
-
-    async def create_user_id_mapping(  # pylint: disable=no-self-use
-        self,
-        supertokens_user_id: str,
-        external_user_id: str,
-        external_user_id_info: Optional[str],
-        force: Optional[bool],
-        user_context: Optional[Dict[str, Any]],
-    ) -> Union[
-        CreateUserIdMappingOkResult,
-        UnknownSupertokensUserIDError,
-        UserIdMappingAlreadyExistsError,
-    ]:
-        querier = Querier.get_instance(None)
-
-        cdi_version = await querier.get_api_version(user_context)
-
-        if is_version_gte(cdi_version, "2.15"):
-            body: Dict[str, Any] = {
-                "superTokensUserId": supertokens_user_id,
-                "externalUserId": external_user_id,
-                "externalUserIdInfo": external_user_id_info,
-            }
-            if force:
-                body["force"] = force
-
-            res = await querier.send_post_request(
-                NormalisedURLPath("/recipe/userid/map"), body, user_context=user_context
-            )
-            if res["status"] == "OK":
-                return CreateUserIdMappingOkResult()
-            if res["status"] == "UNKNOWN_SUPERTOKENS_USER_ID_ERROR":
-                return UnknownSupertokensUserIDError()
-            if res["status"] == "USER_ID_MAPPING_ALREADY_EXISTS_ERROR":
-                return UserIdMappingAlreadyExistsError(
-                    does_super_tokens_user_id_exist=res["doesSuperTokensUserIdExist"],
-                    does_external_user_id_exist=res["does_external_user_id_exist"],
-                )
-
-            raise_general_exception("Unknown response")
-
-        raise_general_exception("Please upgrade the SuperTokens core to >= 3.15.0")
-
-    async def get_user_id_mapping(  # pylint: disable=no-self-use
-        self,
-        user_id: str,
-        user_id_type: Optional[UserIDTypes],
-        user_context: Optional[Dict[str, Any]],
-    ) -> Union[GetUserIdMappingOkResult, UnknownMappingError]:
-        querier = Querier.get_instance(None)
-
-        cdi_version = await querier.get_api_version(user_context)
-
-        if is_version_gte(cdi_version, "2.15"):
-            body = {
-                "userId": user_id,
-            }
-            if user_id_type:
-                body["userIdType"] = user_id_type
-            res = await querier.send_get_request(
-                NormalisedURLPath("/recipe/userid/map"),
-                body,
-                user_context=user_context,
-            )
-            if res["status"] == "OK":
-                return GetUserIdMappingOkResult(
-                    supertokens_user_id=res["superTokensUserId"],
-                    external_user_id=res["externalUserId"],
-                    external_user_info=res.get("externalUserIdInfo"),
-                )
-            if res["status"] == "UNKNOWN_MAPPING_ERROR":
-                return UnknownMappingError()
-
-            raise_general_exception("Unknown response")
-
-        raise_general_exception("Please upgrade the SuperTokens core to >= 3.15.0")
-
-    async def delete_user_id_mapping(  # pylint: disable=no-self-use
-        self,
-        user_id: str,
-        user_id_type: Optional[UserIDTypes],
-        force: Optional[bool],
-        user_context: Optional[Dict[str, Any]],
-    ) -> DeleteUserIdMappingOkResult:
-        querier = Querier.get_instance(None)
-
-        cdi_version = await querier.get_api_version(user_context)
-
-        if is_version_gte(cdi_version, "2.15"):
-            body: Dict[str, Any] = {
-                "userId": user_id,
-                "userIdType": user_id_type,
-            }
-            if force:
-                body["force"] = force
-            res = await querier.send_post_request(
-                NormalisedURLPath("/recipe/userid/map/remove"),
-                body,
-                user_context=user_context,
-            )
-            if res["status"] == "OK":
-                return DeleteUserIdMappingOkResult(
-                    did_mapping_exist=res["didMappingExist"]
-                )
-
-            raise_general_exception("Unknown response")
-
-        raise_general_exception("Please upgrade the SuperTokens core to >= 3.15.0")
-
-    async def update_or_delete_user_id_mapping_info(  # pylint: disable=no-self-use
-        self,
-        user_id: str,
-        user_id_type: Optional[UserIDTypes],
-        external_user_id_info: Optional[str],
-        user_context: Optional[Dict[str, Any]],
-    ) -> Union[UpdateOrDeleteUserIdMappingInfoOkResult, UnknownMappingError]:
-        querier = Querier.get_instance(None)
-
-        cdi_version = await querier.get_api_version(user_context)
-
-        if is_version_gte(cdi_version, "2.15"):
-            res = await querier.send_post_request(
-                NormalisedURLPath("/recipe/userid/external-user-id-info"),
-                {
-                    "userId": user_id,
-                    "userIdType": user_id_type,
-                    "externalUserIdInfo": external_user_id_info,
-                },
-                user_context=user_context,
-            )
-            if res["status"] == "OK":
-                return UpdateOrDeleteUserIdMappingInfoOkResult()
-            if res["status"] == "UNKNOWN_MAPPING_ERROR":
-                return UnknownMappingError()
-
-            raise_general_exception("Unknown response")
-
-        raise_general_exception("Please upgrade the SuperTokens core to >= 3.15.0")
-
-    async def middleware(  # pylint: disable=no-self-use
-        self, request: BaseRequest, response: BaseResponse, user_context: Dict[str, Any]
-    ) -> Union[BaseResponse, None]:
-        log_debug_message("middleware: Started")
-        path = Supertokens.get_instance().app_info.api_gateway_path.append(
-            NormalisedURLPath(request.get_path())
-        )
-        method = normalise_http_method(request.method())
-
-        if not path.startswith(Supertokens.get_instance().app_info.api_base_path):
-            log_debug_message(
-                "middleware: Not handling because request path did not start with api base path. Request path: %s",
-                path.get_as_string_dangerous(),
-            )
-            return None
-        request_rid = get_rid_from_header(request)
-        log_debug_message(
-            "middleware: requestRID is: %s", get_maybe_none_as_str(request_rid)
-        )
-        if request_rid is not None and request_rid == "anti-csrf":
-            # see https://github.com/supertokens/supertokens-python/issues/54
-            request_rid = None
-
-        async def handle_without_rid():
-            for recipe in self.recipe_modules:
-                log_debug_message(
-                    "middleware: Checking recipe ID for match: %s with path: %s and method: %s",
-                    recipe.get_recipe_id(),
-                    path.get_as_string_dangerous(),
-                    method,
-                )
-                api_and_tenant_id = await recipe.return_api_id_if_can_handle_request(
-                    path, method, user_context
-                )
-                if api_and_tenant_id is not None:
-                    log_debug_message(
-                        "middleware: Request being handled by recipe. ID is: %s",
-                        api_and_tenant_id.api_id,
-                    )
-                    api_resp = await recipe.handle_api_request(
-                        api_and_tenant_id.api_id,
-                        api_and_tenant_id.tenant_id,
-                        request,
-                        path,
-                        method,
-                        response,
-                        user_context,
-                    )
-                    if api_resp is None:
-                        log_debug_message(
-                            "middleware: Not handled because API returned None"
-                        )
-                        return None
-                    log_debug_message("middleware: Ended")
-                    return api_resp
-            log_debug_message("middleware: Not handling because no recipe matched")
-            return None
-
-        if request_rid is not None:
-            matched_recipes = [
-                recipe
-                for recipe in self.recipe_modules
-                if recipe.get_recipe_id() == request_rid
-                or (
-                    request_rid == "thirdpartyemailpassword"
-                    and recipe.get_recipe_id() in ["thirdparty", "emailpassword"]
-                )
-                or (
-                    request_rid == "thirdpartypasswordless"
-                    and recipe.get_recipe_id() in ["thirdparty", "passwordless"]
-                )
-            ]
-
-            if len(matched_recipes) == 0:
-                log_debug_message(
-                    "middleware: Not handling based on rid match. Trying without rid."
-                )
-                return await handle_without_rid()
-
-            for recipe in matched_recipes:
-                log_debug_message(
-                    "middleware: Matched with recipe Ids: %s", recipe.get_recipe_id()
-                )
-
-            id_result = None
-            final_matched_recipe = None
-            for recipe in matched_recipes:
-                current_id_result = await recipe.return_api_id_if_can_handle_request(
-                    path, method, user_context
-                )
-                if current_id_result is not None:
-                    if id_result is not None:
-                        raise ValueError(
-                            "Two recipes have matched the same API path and method! This is a bug in the SDK. Please contact support."
-                        )
-                    final_matched_recipe = recipe
-                    id_result = current_id_result
-
-            if id_result is None or final_matched_recipe is None:
-                return await handle_without_rid()
-
-            log_debug_message(
-                "middleware: Request being handled by recipe. ID is: %s",
-                id_result.api_id,
-            )
-            request_handled = await final_matched_recipe.handle_api_request(
-                id_result.api_id,
-                id_result.tenant_id,
-                request,
-                path,
-                method,
-                response,
-                user_context,
-            )
-            if request_handled is None:
-                log_debug_message(
-                    "middleware: Not handled because API returned request_handled as None"
-                )
-                return None
-            log_debug_message("middleware: Ended")
-            return request_handled
-        return await handle_without_rid()
-
-    async def handle_supertokens_error(
-        self,
-        request: BaseRequest,
-        err: Exception,
-        response: BaseResponse,
-        user_context: Dict[str, Any],
-    ):
-        log_debug_message("errorHandler: Started")
-        log_debug_message(
-            "errorHandler: Error is from SuperTokens recipe. Message: %s", str(err)
-        )
-        if isinstance(err, GeneralError):
-            raise err
-
-        if isinstance(err, BadInputError):
-            log_debug_message("errorHandler: Sending 400 status code response")
-            return send_non_200_response_with_message(str(err), 400, response)
-
-        for recipe in self.recipe_modules:
-            log_debug_message(
-                "errorHandler: Checking recipe for match: %s", recipe.get_recipe_id()
-            )
-            if recipe.is_error_from_this_recipe_based_on_instance(err) and isinstance(
-                err, SuperTokensError
-            ):
-                log_debug_message(
-                    "errorHandler: Matched with recipeID: %s", recipe.get_recipe_id()
-                )
-                return await recipe.handle_error(request, err, response, user_context)
-        raise err
-
-    def get_request_from_user_context(  # pylint: disable=no-self-use
-        self,
-        user_context: Optional[Dict[str, Any]] = None,
-    ) -> Optional[BaseRequest]:
-        if user_context is None:
-            return None
-
-        if "_default" not in user_context:
-            return None
-
-        if not isinstance(user_context["_default"], dict):
-            return None
-
-        return user_context.get("_default", {}).get("request")
-
-
-def get_request_from_user_context(
-    user_context: Optional[Dict[str, Any]],
-) -> Optional[BaseRequest]:
-    return Supertokens.get_instance().get_request_from_user_context(user_context)
-
-
-
-
-
-
-
-

Functions

-
-
-def get_request_from_user_context(user_context: Optional[Dict[str, Any]]) ‑> Optional[BaseRequest] -
-
-
-
- -Expand source code - -
def get_request_from_user_context(
-    user_context: Optional[Dict[str, Any]],
-) -> Optional[BaseRequest]:
-    return Supertokens.get_instance().get_request_from_user_context(user_context)
-
-
-
-def manage_session_post_response(session: SessionContainer, response: BaseResponse, user_context: Dict[str, Any]) -
-
-
-
- -Expand source code - -
def manage_session_post_response(
-    session: SessionContainer, response: BaseResponse, user_context: Dict[str, Any]
-):
-    # Something similar happens in handle_error of session/recipe.py
-    for mutator in session.response_mutators:
-        mutator(response, user_context)
-
-
-
-
-
-

Classes

-
-
-class AppInfo -(app_name: str, api_domain: str, website_domain: Optional[str], framework: "Literal['fastapi', 'flask', 'django']", api_gateway_path: str, api_base_path: str, website_base_path: str, mode: "Union[Literal['asgi', 'wsgi'], None]", origin: Optional[Union[str, Callable[[Optional[BaseRequest], Dict[str, Any]], str]]]) -
-
-
-
- -Expand source code - -
class AppInfo:
-    def __init__(
-        self,
-        app_name: str,
-        api_domain: str,
-        website_domain: Optional[str],
-        framework: Literal["fastapi", "flask", "django"],
-        api_gateway_path: str,
-        api_base_path: str,
-        website_base_path: str,
-        mode: Union[Literal["asgi", "wsgi"], None],
-        origin: Optional[
-            Union[str, Callable[[Optional[BaseRequest], Dict[str, Any]], str]]
-        ],
-    ):
-        self.app_name = app_name
-        self.api_gateway_path = NormalisedURLPath(api_gateway_path)
-        self.api_domain = NormalisedURLDomain(api_domain)
-        self.top_level_api_domain = get_top_level_domain_for_same_site_resolution(
-            self.api_domain.get_as_string_dangerous()
-        )
-        if website_domain is None and origin is None:
-            raise_general_exception(
-                "Please provide at least one of website_domain or origin"
-            )
-        self.__origin = origin
-        self.__website_domain = website_domain
-        self.api_base_path = self.api_gateway_path.append(
-            NormalisedURLPath(api_base_path)
-        )
-        self.website_base_path = NormalisedURLPath(website_base_path)
-        if mode is not None:
-            self.mode = mode
-        elif framework == "fastapi":
-            mode = "asgi"
-        else:
-            mode = "wsgi"
-        self.framework = framework
-        self.mode = mode
-
-    def get_top_level_website_domain(
-        self, request: Optional[BaseRequest], user_context: Dict[str, Any]
-    ) -> str:
-        return get_top_level_domain_for_same_site_resolution(
-            self.get_origin(request, user_context).get_as_string_dangerous()
-        )
-
-    def get_origin(self, request: Optional[BaseRequest], user_context: Dict[str, Any]):
-        origin = self.__origin
-        if origin is None:
-            origin = self.__website_domain
-
-        # This should not be possible because we check for either origin or websiteDomain above
-        if origin is None:
-            raise_general_exception("should never come here")
-
-        if callable(origin):
-            origin = origin(request, user_context)
-
-        return NormalisedURLDomain(origin)
-
-    def toJSON(self):
-        def defaultImpl(o: Any):
-            if isinstance(o, (NormalisedURLDomain, NormalisedURLPath)):
-                return o.get_as_string_dangerous()
-            return o.__dict__
-
-        return json.dumps(self, default=defaultImpl, sort_keys=True, indent=4)
-
-

Methods

-
-
-def get_origin(self, request: Optional[BaseRequest], user_context: Dict[str, Any]) -
-
-
-
- -Expand source code - -
def get_origin(self, request: Optional[BaseRequest], user_context: Dict[str, Any]):
-    origin = self.__origin
-    if origin is None:
-        origin = self.__website_domain
-
-    # This should not be possible because we check for either origin or websiteDomain above
-    if origin is None:
-        raise_general_exception("should never come here")
-
-    if callable(origin):
-        origin = origin(request, user_context)
-
-    return NormalisedURLDomain(origin)
-
-
-
-def get_top_level_website_domain(self, request: Optional[BaseRequest], user_context: Dict[str, Any]) ‑> str -
-
-
-
- -Expand source code - -
def get_top_level_website_domain(
-    self, request: Optional[BaseRequest], user_context: Dict[str, Any]
-) -> str:
-    return get_top_level_domain_for_same_site_resolution(
-        self.get_origin(request, user_context).get_as_string_dangerous()
-    )
-
-
-
-def toJSON(self) -
-
-
-
- -Expand source code - -
def toJSON(self):
-    def defaultImpl(o: Any):
-        if isinstance(o, (NormalisedURLDomain, NormalisedURLPath)):
-            return o.get_as_string_dangerous()
-        return o.__dict__
-
-    return json.dumps(self, default=defaultImpl, sort_keys=True, indent=4)
-
-
-
-
-
-class Host -(domain: NormalisedURLDomain, base_path: NormalisedURLPath) -
-
-
-
- -Expand source code - -
class Host:
-    def __init__(self, domain: NormalisedURLDomain, base_path: NormalisedURLPath):
-        self.domain = domain
-        self.base_path = base_path
-
-
-
-class InputAppInfo -(app_name: str, api_domain: str, api_gateway_path: str = '', api_base_path: str = '/auth', website_base_path: str = '/auth', website_domain: Optional[str] = None, origin: Optional[Union[str, Callable[[Optional[BaseRequest], Dict[str, Any]], str]]] = None) -
-
-
-
- -Expand source code - -
class InputAppInfo:
-    def __init__(
-        self,
-        app_name: str,
-        api_domain: str,
-        api_gateway_path: str = "",
-        api_base_path: str = "/auth",
-        website_base_path: str = "/auth",
-        website_domain: Optional[str] = None,
-        origin: Optional[
-            Union[str, Callable[[Optional[BaseRequest], Dict[str, Any]], str]]
-        ] = None,
-    ):
-        self.app_name = app_name
-        self.api_gateway_path = api_gateway_path
-        self.api_domain = api_domain
-        self.website_domain = website_domain
-        self.origin = origin
-        self.api_base_path = api_base_path
-        self.website_base_path = website_base_path
-
-
-
-class Supertokens -(app_info: InputAppInfo, framework: "Literal['fastapi', 'flask', 'django']", supertokens_config: SupertokensConfig, recipe_list: List[Callable[[AppInfo], RecipeModule]], mode: "Optional[Literal['asgi', 'wsgi']]", telemetry: Optional[bool], debug: Optional[bool]) -
-
-
-
- -Expand source code - -
class Supertokens:
-    __instance = None
-
-    def __init__(
-        self,
-        app_info: InputAppInfo,
-        framework: Literal["fastapi", "flask", "django"],
-        supertokens_config: SupertokensConfig,
-        recipe_list: List[Callable[[AppInfo], RecipeModule]],
-        mode: Optional[Literal["asgi", "wsgi"]],
-        telemetry: Optional[bool],
-        debug: Optional[bool],
-    ):
-        if not isinstance(app_info, InputAppInfo):  # type: ignore
-            raise ValueError("app_info must be an instance of InputAppInfo")
-
-        self.app_info = AppInfo(
-            app_info.app_name,
-            app_info.api_domain,
-            app_info.website_domain,
-            framework,
-            app_info.api_gateway_path,
-            app_info.api_base_path,
-            app_info.website_base_path,
-            mode,
-            app_info.origin,
-        )
-        self.supertokens_config = supertokens_config
-        if debug is True:
-            enable_debug_logging()
-        self._telemetry_status: str = "NONE"
-        log_debug_message(
-            "Started SuperTokens with debug logging (supertokens.init called)"
-        )
-        log_debug_message("app_info: %s", self.app_info.toJSON())
-        log_debug_message("framework: %s", framework)
-        hosts = list(
-            map(
-                lambda h: Host(
-                    NormalisedURLDomain(h.strip()), NormalisedURLPath(h.strip())
-                ),
-                filter(lambda x: x != "", supertokens_config.connection_uri.split(";")),
-            )
-        )
-        Querier.init(
-            hosts,
-            supertokens_config.api_key,
-            supertokens_config.network_interceptor,
-            supertokens_config.disable_core_call_cache,
-        )
-
-        if len(recipe_list) == 0:
-            raise_general_exception(
-                "Please provide at least one recipe to the supertokens.init function call"
-            )
-
-        from supertokens_python.recipe.multitenancy.recipe import MultitenancyRecipe
-
-        multitenancy_found = False
-
-        def make_recipe(recipe: Callable[[AppInfo], RecipeModule]) -> RecipeModule:
-            nonlocal multitenancy_found
-            recipe_module = recipe(self.app_info)
-            if recipe_module.get_recipe_id() == MultitenancyRecipe.recipe_id:
-                multitenancy_found = True
-            return recipe_module
-
-        self.recipe_modules: List[RecipeModule] = list(map(make_recipe, recipe_list))
-
-        if not multitenancy_found:
-            recipe = MultitenancyRecipe.init()(self.app_info)
-            self.recipe_modules.append(recipe)
-
-        self.telemetry = (
-            telemetry
-            if telemetry is not None
-            else (environ.get("TEST_MODE") != "testing")
-        )
-
-    @staticmethod
-    def init(
-        app_info: InputAppInfo,
-        framework: Literal["fastapi", "flask", "django"],
-        supertokens_config: SupertokensConfig,
-        recipe_list: List[Callable[[AppInfo], RecipeModule]],
-        mode: Optional[Literal["asgi", "wsgi"]],
-        telemetry: Optional[bool],
-        debug: Optional[bool],
-    ):
-        if Supertokens.__instance is None:
-            Supertokens.__instance = Supertokens(
-                app_info,
-                framework,
-                supertokens_config,
-                recipe_list,
-                mode,
-                telemetry,
-                debug,
-            )
-            PostSTInitCallbacks.run_post_init_callbacks()
-
-    @staticmethod
-    def reset():
-        if ("SUPERTOKENS_ENV" not in environ) or (
-            environ["SUPERTOKENS_ENV"] != "testing"
-        ):
-            raise_general_exception("calling testing function in non testing env")
-        Querier.reset()
-        Supertokens.__instance = None
-
-    @staticmethod
-    def get_instance() -> Supertokens:
-        if Supertokens.__instance is not None:
-            return Supertokens.__instance
-        raise_general_exception(
-            "Initialisation not done. Did you forget to call the SuperTokens.init function?"
-        )
-
-    def get_all_cors_headers(self) -> List[str]:
-        headers_set: Set[str] = set()
-        headers_set.add(RID_KEY_HEADER)
-        headers_set.add(FDI_KEY_HEADER)
-        for recipe in self.recipe_modules:
-            headers = recipe.get_all_cors_headers()
-            for header in headers:
-                headers_set.add(header)
-
-        return list(headers_set)
-
-    async def get_user_count(  # pylint: disable=no-self-use
-        self,
-        include_recipe_ids: Union[None, List[str]],
-        tenant_id: Optional[str] = None,
-        user_context: Optional[Dict[str, Any]] = None,
-    ) -> int:
-        querier = Querier.get_instance(None)
-        include_recipe_ids_str = None
-        if include_recipe_ids is not None:
-            include_recipe_ids_str = ",".join(include_recipe_ids)
-
-        response = await querier.send_get_request(
-            NormalisedURLPath(f"/{tenant_id or 'public'}{USER_COUNT}"),
-            {
-                "includeRecipeIds": include_recipe_ids_str,
-                "includeAllTenants": tenant_id is None,
-            },
-            user_context=user_context,
-        )
-
-        return int(response["count"])
-
-    async def delete_user(  # pylint: disable=no-self-use
-        self,
-        user_id: str,
-        user_context: Optional[Dict[str, Any]],
-    ) -> None:
-        querier = Querier.get_instance(None)
-
-        cdi_version = await querier.get_api_version(user_context)
-
-        if is_version_gte(cdi_version, "2.10"):
-            await querier.send_post_request(
-                NormalisedURLPath(USER_DELETE),
-                {"userId": user_id},
-                user_context=user_context,
-            )
-
-            return None
-        raise_general_exception("Please upgrade the SuperTokens core to >= 3.7.0")
-
-    async def get_users(  # pylint: disable=no-self-use
-        self,
-        tenant_id: str,
-        time_joined_order: Literal["ASC", "DESC"],
-        limit: Union[int, None],
-        pagination_token: Union[str, None],
-        include_recipe_ids: Union[None, List[str]],
-        query: Union[Dict[str, str], None],
-        user_context: Optional[Dict[str, Any]],
-    ) -> UsersResponse:
-
-        querier = Querier.get_instance(None)
-        params = {"timeJoinedOrder": time_joined_order}
-        if limit is not None:
-            params = {"limit": limit, **params}
-        if pagination_token is not None:
-            params = {"paginationToken": pagination_token, **params}
-
-        include_recipe_ids_str = None
-        if include_recipe_ids is not None:
-            include_recipe_ids_str = ",".join(include_recipe_ids)
-            params = {"includeRecipeIds": include_recipe_ids_str, **params}
-
-        if query is not None:
-            params = {**params, **query}
-
-        response = await querier.send_get_request(
-            NormalisedURLPath(f"/{tenant_id}{USERS}"), params, user_context=user_context
-        )
-        next_pagination_token = None
-        if "nextPaginationToken" in response:
-            next_pagination_token = response["nextPaginationToken"]
-        users_list = response["users"]
-        users: List[User] = []
-        for user in users_list:
-            recipe_id = user["recipeId"]
-            user_obj = user["user"]
-            third_party = None
-            if "thirdParty" in user_obj:
-                third_party = ThirdPartyInfo(
-                    user_obj["thirdParty"]["userId"], user_obj["thirdParty"]["id"]
-                )
-            email = None
-            if "email" in user_obj:
-                email = user_obj["email"]
-            phone_number = None
-            if "phoneNumber" in user_obj:
-                phone_number = user_obj["phoneNumber"]
-            users.append(
-                User(
-                    recipe_id,
-                    user_obj["id"],
-                    user_obj["timeJoined"],
-                    email,
-                    phone_number,
-                    third_party,
-                    user_obj["tenantIds"],
-                )
-            )
-
-        return UsersResponse(users, next_pagination_token)
-
-    async def create_user_id_mapping(  # pylint: disable=no-self-use
-        self,
-        supertokens_user_id: str,
-        external_user_id: str,
-        external_user_id_info: Optional[str],
-        force: Optional[bool],
-        user_context: Optional[Dict[str, Any]],
-    ) -> Union[
-        CreateUserIdMappingOkResult,
-        UnknownSupertokensUserIDError,
-        UserIdMappingAlreadyExistsError,
-    ]:
-        querier = Querier.get_instance(None)
-
-        cdi_version = await querier.get_api_version(user_context)
-
-        if is_version_gte(cdi_version, "2.15"):
-            body: Dict[str, Any] = {
-                "superTokensUserId": supertokens_user_id,
-                "externalUserId": external_user_id,
-                "externalUserIdInfo": external_user_id_info,
-            }
-            if force:
-                body["force"] = force
-
-            res = await querier.send_post_request(
-                NormalisedURLPath("/recipe/userid/map"), body, user_context=user_context
-            )
-            if res["status"] == "OK":
-                return CreateUserIdMappingOkResult()
-            if res["status"] == "UNKNOWN_SUPERTOKENS_USER_ID_ERROR":
-                return UnknownSupertokensUserIDError()
-            if res["status"] == "USER_ID_MAPPING_ALREADY_EXISTS_ERROR":
-                return UserIdMappingAlreadyExistsError(
-                    does_super_tokens_user_id_exist=res["doesSuperTokensUserIdExist"],
-                    does_external_user_id_exist=res["does_external_user_id_exist"],
-                )
-
-            raise_general_exception("Unknown response")
-
-        raise_general_exception("Please upgrade the SuperTokens core to >= 3.15.0")
-
-    async def get_user_id_mapping(  # pylint: disable=no-self-use
-        self,
-        user_id: str,
-        user_id_type: Optional[UserIDTypes],
-        user_context: Optional[Dict[str, Any]],
-    ) -> Union[GetUserIdMappingOkResult, UnknownMappingError]:
-        querier = Querier.get_instance(None)
-
-        cdi_version = await querier.get_api_version(user_context)
-
-        if is_version_gte(cdi_version, "2.15"):
-            body = {
-                "userId": user_id,
-            }
-            if user_id_type:
-                body["userIdType"] = user_id_type
-            res = await querier.send_get_request(
-                NormalisedURLPath("/recipe/userid/map"),
-                body,
-                user_context=user_context,
-            )
-            if res["status"] == "OK":
-                return GetUserIdMappingOkResult(
-                    supertokens_user_id=res["superTokensUserId"],
-                    external_user_id=res["externalUserId"],
-                    external_user_info=res.get("externalUserIdInfo"),
-                )
-            if res["status"] == "UNKNOWN_MAPPING_ERROR":
-                return UnknownMappingError()
-
-            raise_general_exception("Unknown response")
-
-        raise_general_exception("Please upgrade the SuperTokens core to >= 3.15.0")
-
-    async def delete_user_id_mapping(  # pylint: disable=no-self-use
-        self,
-        user_id: str,
-        user_id_type: Optional[UserIDTypes],
-        force: Optional[bool],
-        user_context: Optional[Dict[str, Any]],
-    ) -> DeleteUserIdMappingOkResult:
-        querier = Querier.get_instance(None)
-
-        cdi_version = await querier.get_api_version(user_context)
-
-        if is_version_gte(cdi_version, "2.15"):
-            body: Dict[str, Any] = {
-                "userId": user_id,
-                "userIdType": user_id_type,
-            }
-            if force:
-                body["force"] = force
-            res = await querier.send_post_request(
-                NormalisedURLPath("/recipe/userid/map/remove"),
-                body,
-                user_context=user_context,
-            )
-            if res["status"] == "OK":
-                return DeleteUserIdMappingOkResult(
-                    did_mapping_exist=res["didMappingExist"]
-                )
-
-            raise_general_exception("Unknown response")
-
-        raise_general_exception("Please upgrade the SuperTokens core to >= 3.15.0")
-
-    async def update_or_delete_user_id_mapping_info(  # pylint: disable=no-self-use
-        self,
-        user_id: str,
-        user_id_type: Optional[UserIDTypes],
-        external_user_id_info: Optional[str],
-        user_context: Optional[Dict[str, Any]],
-    ) -> Union[UpdateOrDeleteUserIdMappingInfoOkResult, UnknownMappingError]:
-        querier = Querier.get_instance(None)
-
-        cdi_version = await querier.get_api_version(user_context)
-
-        if is_version_gte(cdi_version, "2.15"):
-            res = await querier.send_post_request(
-                NormalisedURLPath("/recipe/userid/external-user-id-info"),
-                {
-                    "userId": user_id,
-                    "userIdType": user_id_type,
-                    "externalUserIdInfo": external_user_id_info,
-                },
-                user_context=user_context,
-            )
-            if res["status"] == "OK":
-                return UpdateOrDeleteUserIdMappingInfoOkResult()
-            if res["status"] == "UNKNOWN_MAPPING_ERROR":
-                return UnknownMappingError()
-
-            raise_general_exception("Unknown response")
-
-        raise_general_exception("Please upgrade the SuperTokens core to >= 3.15.0")
-
-    async def middleware(  # pylint: disable=no-self-use
-        self, request: BaseRequest, response: BaseResponse, user_context: Dict[str, Any]
-    ) -> Union[BaseResponse, None]:
-        log_debug_message("middleware: Started")
-        path = Supertokens.get_instance().app_info.api_gateway_path.append(
-            NormalisedURLPath(request.get_path())
-        )
-        method = normalise_http_method(request.method())
-
-        if not path.startswith(Supertokens.get_instance().app_info.api_base_path):
-            log_debug_message(
-                "middleware: Not handling because request path did not start with api base path. Request path: %s",
-                path.get_as_string_dangerous(),
-            )
-            return None
-        request_rid = get_rid_from_header(request)
-        log_debug_message(
-            "middleware: requestRID is: %s", get_maybe_none_as_str(request_rid)
-        )
-        if request_rid is not None and request_rid == "anti-csrf":
-            # see https://github.com/supertokens/supertokens-python/issues/54
-            request_rid = None
-
-        async def handle_without_rid():
-            for recipe in self.recipe_modules:
-                log_debug_message(
-                    "middleware: Checking recipe ID for match: %s with path: %s and method: %s",
-                    recipe.get_recipe_id(),
-                    path.get_as_string_dangerous(),
-                    method,
-                )
-                api_and_tenant_id = await recipe.return_api_id_if_can_handle_request(
-                    path, method, user_context
-                )
-                if api_and_tenant_id is not None:
-                    log_debug_message(
-                        "middleware: Request being handled by recipe. ID is: %s",
-                        api_and_tenant_id.api_id,
-                    )
-                    api_resp = await recipe.handle_api_request(
-                        api_and_tenant_id.api_id,
-                        api_and_tenant_id.tenant_id,
-                        request,
-                        path,
-                        method,
-                        response,
-                        user_context,
-                    )
-                    if api_resp is None:
-                        log_debug_message(
-                            "middleware: Not handled because API returned None"
-                        )
-                        return None
-                    log_debug_message("middleware: Ended")
-                    return api_resp
-            log_debug_message("middleware: Not handling because no recipe matched")
-            return None
-
-        if request_rid is not None:
-            matched_recipes = [
-                recipe
-                for recipe in self.recipe_modules
-                if recipe.get_recipe_id() == request_rid
-                or (
-                    request_rid == "thirdpartyemailpassword"
-                    and recipe.get_recipe_id() in ["thirdparty", "emailpassword"]
-                )
-                or (
-                    request_rid == "thirdpartypasswordless"
-                    and recipe.get_recipe_id() in ["thirdparty", "passwordless"]
-                )
-            ]
-
-            if len(matched_recipes) == 0:
-                log_debug_message(
-                    "middleware: Not handling based on rid match. Trying without rid."
-                )
-                return await handle_without_rid()
-
-            for recipe in matched_recipes:
-                log_debug_message(
-                    "middleware: Matched with recipe Ids: %s", recipe.get_recipe_id()
-                )
-
-            id_result = None
-            final_matched_recipe = None
-            for recipe in matched_recipes:
-                current_id_result = await recipe.return_api_id_if_can_handle_request(
-                    path, method, user_context
-                )
-                if current_id_result is not None:
-                    if id_result is not None:
-                        raise ValueError(
-                            "Two recipes have matched the same API path and method! This is a bug in the SDK. Please contact support."
-                        )
-                    final_matched_recipe = recipe
-                    id_result = current_id_result
-
-            if id_result is None or final_matched_recipe is None:
-                return await handle_without_rid()
-
-            log_debug_message(
-                "middleware: Request being handled by recipe. ID is: %s",
-                id_result.api_id,
-            )
-            request_handled = await final_matched_recipe.handle_api_request(
-                id_result.api_id,
-                id_result.tenant_id,
-                request,
-                path,
-                method,
-                response,
-                user_context,
-            )
-            if request_handled is None:
-                log_debug_message(
-                    "middleware: Not handled because API returned request_handled as None"
-                )
-                return None
-            log_debug_message("middleware: Ended")
-            return request_handled
-        return await handle_without_rid()
-
-    async def handle_supertokens_error(
-        self,
-        request: BaseRequest,
-        err: Exception,
-        response: BaseResponse,
-        user_context: Dict[str, Any],
-    ):
-        log_debug_message("errorHandler: Started")
-        log_debug_message(
-            "errorHandler: Error is from SuperTokens recipe. Message: %s", str(err)
-        )
-        if isinstance(err, GeneralError):
-            raise err
-
-        if isinstance(err, BadInputError):
-            log_debug_message("errorHandler: Sending 400 status code response")
-            return send_non_200_response_with_message(str(err), 400, response)
-
-        for recipe in self.recipe_modules:
-            log_debug_message(
-                "errorHandler: Checking recipe for match: %s", recipe.get_recipe_id()
-            )
-            if recipe.is_error_from_this_recipe_based_on_instance(err) and isinstance(
-                err, SuperTokensError
-            ):
-                log_debug_message(
-                    "errorHandler: Matched with recipeID: %s", recipe.get_recipe_id()
-                )
-                return await recipe.handle_error(request, err, response, user_context)
-        raise err
-
-    def get_request_from_user_context(  # pylint: disable=no-self-use
-        self,
-        user_context: Optional[Dict[str, Any]] = None,
-    ) -> Optional[BaseRequest]:
-        if user_context is None:
-            return None
-
-        if "_default" not in user_context:
-            return None
-
-        if not isinstance(user_context["_default"], dict):
-            return None
-
-        return user_context.get("_default", {}).get("request")
-
-

Static methods

-
-
-def get_instance() ‑> Supertokens -
-
-
-
- -Expand source code - -
@staticmethod
-def get_instance() -> Supertokens:
-    if Supertokens.__instance is not None:
-        return Supertokens.__instance
-    raise_general_exception(
-        "Initialisation not done. Did you forget to call the SuperTokens.init function?"
-    )
-
-
-
-def init(app_info: InputAppInfo, framework: "Literal['fastapi', 'flask', 'django']", supertokens_config: SupertokensConfig, recipe_list: List[Callable[[AppInfo], RecipeModule]], mode: "Optional[Literal['asgi', 'wsgi']]", telemetry: Optional[bool], debug: Optional[bool]) -
-
-
-
- -Expand source code - -
@staticmethod
-def init(
-    app_info: InputAppInfo,
-    framework: Literal["fastapi", "flask", "django"],
-    supertokens_config: SupertokensConfig,
-    recipe_list: List[Callable[[AppInfo], RecipeModule]],
-    mode: Optional[Literal["asgi", "wsgi"]],
-    telemetry: Optional[bool],
-    debug: Optional[bool],
-):
-    if Supertokens.__instance is None:
-        Supertokens.__instance = Supertokens(
-            app_info,
-            framework,
-            supertokens_config,
-            recipe_list,
-            mode,
-            telemetry,
-            debug,
-        )
-        PostSTInitCallbacks.run_post_init_callbacks()
-
-
-
-def reset() -
-
-
-
- -Expand source code - -
@staticmethod
-def reset():
-    if ("SUPERTOKENS_ENV" not in environ) or (
-        environ["SUPERTOKENS_ENV"] != "testing"
-    ):
-        raise_general_exception("calling testing function in non testing env")
-    Querier.reset()
-    Supertokens.__instance = None
-
-
-
-

Methods

-
-
-async def create_user_id_mapping(self, supertokens_user_id: str, external_user_id: str, external_user_id_info: Optional[str], force: Optional[bool], user_context: Optional[Dict[str, Any]]) ‑> Union[CreateUserIdMappingOkResultUnknownSupertokensUserIDErrorUserIdMappingAlreadyExistsError] -
-
-
-
- -Expand source code - -
async def create_user_id_mapping(  # pylint: disable=no-self-use
-    self,
-    supertokens_user_id: str,
-    external_user_id: str,
-    external_user_id_info: Optional[str],
-    force: Optional[bool],
-    user_context: Optional[Dict[str, Any]],
-) -> Union[
-    CreateUserIdMappingOkResult,
-    UnknownSupertokensUserIDError,
-    UserIdMappingAlreadyExistsError,
-]:
-    querier = Querier.get_instance(None)
-
-    cdi_version = await querier.get_api_version(user_context)
-
-    if is_version_gte(cdi_version, "2.15"):
-        body: Dict[str, Any] = {
-            "superTokensUserId": supertokens_user_id,
-            "externalUserId": external_user_id,
-            "externalUserIdInfo": external_user_id_info,
-        }
-        if force:
-            body["force"] = force
-
-        res = await querier.send_post_request(
-            NormalisedURLPath("/recipe/userid/map"), body, user_context=user_context
-        )
-        if res["status"] == "OK":
-            return CreateUserIdMappingOkResult()
-        if res["status"] == "UNKNOWN_SUPERTOKENS_USER_ID_ERROR":
-            return UnknownSupertokensUserIDError()
-        if res["status"] == "USER_ID_MAPPING_ALREADY_EXISTS_ERROR":
-            return UserIdMappingAlreadyExistsError(
-                does_super_tokens_user_id_exist=res["doesSuperTokensUserIdExist"],
-                does_external_user_id_exist=res["does_external_user_id_exist"],
-            )
-
-        raise_general_exception("Unknown response")
-
-    raise_general_exception("Please upgrade the SuperTokens core to >= 3.15.0")
-
-
-
-async def delete_user(self, user_id: str, user_context: Optional[Dict[str, Any]]) ‑> None -
-
-
-
- -Expand source code - -
async def delete_user(  # pylint: disable=no-self-use
-    self,
-    user_id: str,
-    user_context: Optional[Dict[str, Any]],
-) -> None:
-    querier = Querier.get_instance(None)
-
-    cdi_version = await querier.get_api_version(user_context)
-
-    if is_version_gte(cdi_version, "2.10"):
-        await querier.send_post_request(
-            NormalisedURLPath(USER_DELETE),
-            {"userId": user_id},
-            user_context=user_context,
-        )
-
-        return None
-    raise_general_exception("Please upgrade the SuperTokens core to >= 3.7.0")
-
-
-
-async def delete_user_id_mapping(self, user_id: str, user_id_type: Optional[UserIDTypes], force: Optional[bool], user_context: Optional[Dict[str, Any]]) ‑> DeleteUserIdMappingOkResult -
-
-
-
- -Expand source code - -
async def delete_user_id_mapping(  # pylint: disable=no-self-use
-    self,
-    user_id: str,
-    user_id_type: Optional[UserIDTypes],
-    force: Optional[bool],
-    user_context: Optional[Dict[str, Any]],
-) -> DeleteUserIdMappingOkResult:
-    querier = Querier.get_instance(None)
-
-    cdi_version = await querier.get_api_version(user_context)
-
-    if is_version_gte(cdi_version, "2.15"):
-        body: Dict[str, Any] = {
-            "userId": user_id,
-            "userIdType": user_id_type,
-        }
-        if force:
-            body["force"] = force
-        res = await querier.send_post_request(
-            NormalisedURLPath("/recipe/userid/map/remove"),
-            body,
-            user_context=user_context,
-        )
-        if res["status"] == "OK":
-            return DeleteUserIdMappingOkResult(
-                did_mapping_exist=res["didMappingExist"]
-            )
-
-        raise_general_exception("Unknown response")
-
-    raise_general_exception("Please upgrade the SuperTokens core to >= 3.15.0")
-
-
-
-def get_all_cors_headers(self) ‑> List[str] -
-
-
-
- -Expand source code - -
def get_all_cors_headers(self) -> List[str]:
-    headers_set: Set[str] = set()
-    headers_set.add(RID_KEY_HEADER)
-    headers_set.add(FDI_KEY_HEADER)
-    for recipe in self.recipe_modules:
-        headers = recipe.get_all_cors_headers()
-        for header in headers:
-            headers_set.add(header)
-
-    return list(headers_set)
-
-
-
-def get_request_from_user_context(self, user_context: Optional[Dict[str, Any]] = None) ‑> Optional[BaseRequest] -
-
-
-
- -Expand source code - -
def get_request_from_user_context(  # pylint: disable=no-self-use
-    self,
-    user_context: Optional[Dict[str, Any]] = None,
-) -> Optional[BaseRequest]:
-    if user_context is None:
-        return None
-
-    if "_default" not in user_context:
-        return None
-
-    if not isinstance(user_context["_default"], dict):
-        return None
-
-    return user_context.get("_default", {}).get("request")
-
-
-
-async def get_user_count(self, include_recipe_ids: Union[None, List[str]], tenant_id: Optional[str] = None, user_context: Optional[Dict[str, Any]] = None) ‑> int -
-
-
-
- -Expand source code - -
async def get_user_count(  # pylint: disable=no-self-use
-    self,
-    include_recipe_ids: Union[None, List[str]],
-    tenant_id: Optional[str] = None,
-    user_context: Optional[Dict[str, Any]] = None,
-) -> int:
-    querier = Querier.get_instance(None)
-    include_recipe_ids_str = None
-    if include_recipe_ids is not None:
-        include_recipe_ids_str = ",".join(include_recipe_ids)
-
-    response = await querier.send_get_request(
-        NormalisedURLPath(f"/{tenant_id or 'public'}{USER_COUNT}"),
-        {
-            "includeRecipeIds": include_recipe_ids_str,
-            "includeAllTenants": tenant_id is None,
-        },
-        user_context=user_context,
-    )
-
-    return int(response["count"])
-
-
-
-async def get_user_id_mapping(self, user_id: str, user_id_type: Optional[UserIDTypes], user_context: Optional[Dict[str, Any]]) ‑> Union[GetUserIdMappingOkResultUnknownMappingError] -
-
-
-
- -Expand source code - -
async def get_user_id_mapping(  # pylint: disable=no-self-use
-    self,
-    user_id: str,
-    user_id_type: Optional[UserIDTypes],
-    user_context: Optional[Dict[str, Any]],
-) -> Union[GetUserIdMappingOkResult, UnknownMappingError]:
-    querier = Querier.get_instance(None)
-
-    cdi_version = await querier.get_api_version(user_context)
-
-    if is_version_gte(cdi_version, "2.15"):
-        body = {
-            "userId": user_id,
-        }
-        if user_id_type:
-            body["userIdType"] = user_id_type
-        res = await querier.send_get_request(
-            NormalisedURLPath("/recipe/userid/map"),
-            body,
-            user_context=user_context,
-        )
-        if res["status"] == "OK":
-            return GetUserIdMappingOkResult(
-                supertokens_user_id=res["superTokensUserId"],
-                external_user_id=res["externalUserId"],
-                external_user_info=res.get("externalUserIdInfo"),
-            )
-        if res["status"] == "UNKNOWN_MAPPING_ERROR":
-            return UnknownMappingError()
-
-        raise_general_exception("Unknown response")
-
-    raise_general_exception("Please upgrade the SuperTokens core to >= 3.15.0")
-
-
-
-async def get_users(self, tenant_id: str, time_joined_order: "Literal['ASC', 'DESC']", limit: Union[int, None], pagination_token: Union[str, None], include_recipe_ids: Union[None, List[str]], query: Union[Dict[str, str], None], user_context: Optional[Dict[str, Any]]) ‑> UsersResponse -
-
-
-
- -Expand source code - -
async def get_users(  # pylint: disable=no-self-use
-    self,
-    tenant_id: str,
-    time_joined_order: Literal["ASC", "DESC"],
-    limit: Union[int, None],
-    pagination_token: Union[str, None],
-    include_recipe_ids: Union[None, List[str]],
-    query: Union[Dict[str, str], None],
-    user_context: Optional[Dict[str, Any]],
-) -> UsersResponse:
-
-    querier = Querier.get_instance(None)
-    params = {"timeJoinedOrder": time_joined_order}
-    if limit is not None:
-        params = {"limit": limit, **params}
-    if pagination_token is not None:
-        params = {"paginationToken": pagination_token, **params}
-
-    include_recipe_ids_str = None
-    if include_recipe_ids is not None:
-        include_recipe_ids_str = ",".join(include_recipe_ids)
-        params = {"includeRecipeIds": include_recipe_ids_str, **params}
-
-    if query is not None:
-        params = {**params, **query}
-
-    response = await querier.send_get_request(
-        NormalisedURLPath(f"/{tenant_id}{USERS}"), params, user_context=user_context
-    )
-    next_pagination_token = None
-    if "nextPaginationToken" in response:
-        next_pagination_token = response["nextPaginationToken"]
-    users_list = response["users"]
-    users: List[User] = []
-    for user in users_list:
-        recipe_id = user["recipeId"]
-        user_obj = user["user"]
-        third_party = None
-        if "thirdParty" in user_obj:
-            third_party = ThirdPartyInfo(
-                user_obj["thirdParty"]["userId"], user_obj["thirdParty"]["id"]
-            )
-        email = None
-        if "email" in user_obj:
-            email = user_obj["email"]
-        phone_number = None
-        if "phoneNumber" in user_obj:
-            phone_number = user_obj["phoneNumber"]
-        users.append(
-            User(
-                recipe_id,
-                user_obj["id"],
-                user_obj["timeJoined"],
-                email,
-                phone_number,
-                third_party,
-                user_obj["tenantIds"],
-            )
-        )
-
-    return UsersResponse(users, next_pagination_token)
-
-
-
-async def handle_supertokens_error(self, request: BaseRequest, err: Exception, response: BaseResponse, user_context: Dict[str, Any]) -
-
-
-
- -Expand source code - -
async def handle_supertokens_error(
-    self,
-    request: BaseRequest,
-    err: Exception,
-    response: BaseResponse,
-    user_context: Dict[str, Any],
-):
-    log_debug_message("errorHandler: Started")
-    log_debug_message(
-        "errorHandler: Error is from SuperTokens recipe. Message: %s", str(err)
-    )
-    if isinstance(err, GeneralError):
-        raise err
-
-    if isinstance(err, BadInputError):
-        log_debug_message("errorHandler: Sending 400 status code response")
-        return send_non_200_response_with_message(str(err), 400, response)
-
-    for recipe in self.recipe_modules:
-        log_debug_message(
-            "errorHandler: Checking recipe for match: %s", recipe.get_recipe_id()
-        )
-        if recipe.is_error_from_this_recipe_based_on_instance(err) and isinstance(
-            err, SuperTokensError
-        ):
-            log_debug_message(
-                "errorHandler: Matched with recipeID: %s", recipe.get_recipe_id()
-            )
-            return await recipe.handle_error(request, err, response, user_context)
-    raise err
-
-
-
-async def middleware(self, request: BaseRequest, response: BaseResponse, user_context: Dict[str, Any]) ‑> Union[BaseResponse, None] -
-
-
-
- -Expand source code - -
async def middleware(  # pylint: disable=no-self-use
-    self, request: BaseRequest, response: BaseResponse, user_context: Dict[str, Any]
-) -> Union[BaseResponse, None]:
-    log_debug_message("middleware: Started")
-    path = Supertokens.get_instance().app_info.api_gateway_path.append(
-        NormalisedURLPath(request.get_path())
-    )
-    method = normalise_http_method(request.method())
-
-    if not path.startswith(Supertokens.get_instance().app_info.api_base_path):
-        log_debug_message(
-            "middleware: Not handling because request path did not start with api base path. Request path: %s",
-            path.get_as_string_dangerous(),
-        )
-        return None
-    request_rid = get_rid_from_header(request)
-    log_debug_message(
-        "middleware: requestRID is: %s", get_maybe_none_as_str(request_rid)
-    )
-    if request_rid is not None and request_rid == "anti-csrf":
-        # see https://github.com/supertokens/supertokens-python/issues/54
-        request_rid = None
-
-    async def handle_without_rid():
-        for recipe in self.recipe_modules:
-            log_debug_message(
-                "middleware: Checking recipe ID for match: %s with path: %s and method: %s",
-                recipe.get_recipe_id(),
-                path.get_as_string_dangerous(),
-                method,
-            )
-            api_and_tenant_id = await recipe.return_api_id_if_can_handle_request(
-                path, method, user_context
-            )
-            if api_and_tenant_id is not None:
-                log_debug_message(
-                    "middleware: Request being handled by recipe. ID is: %s",
-                    api_and_tenant_id.api_id,
-                )
-                api_resp = await recipe.handle_api_request(
-                    api_and_tenant_id.api_id,
-                    api_and_tenant_id.tenant_id,
-                    request,
-                    path,
-                    method,
-                    response,
-                    user_context,
-                )
-                if api_resp is None:
-                    log_debug_message(
-                        "middleware: Not handled because API returned None"
-                    )
-                    return None
-                log_debug_message("middleware: Ended")
-                return api_resp
-        log_debug_message("middleware: Not handling because no recipe matched")
-        return None
-
-    if request_rid is not None:
-        matched_recipes = [
-            recipe
-            for recipe in self.recipe_modules
-            if recipe.get_recipe_id() == request_rid
-            or (
-                request_rid == "thirdpartyemailpassword"
-                and recipe.get_recipe_id() in ["thirdparty", "emailpassword"]
-            )
-            or (
-                request_rid == "thirdpartypasswordless"
-                and recipe.get_recipe_id() in ["thirdparty", "passwordless"]
-            )
-        ]
-
-        if len(matched_recipes) == 0:
-            log_debug_message(
-                "middleware: Not handling based on rid match. Trying without rid."
-            )
-            return await handle_without_rid()
-
-        for recipe in matched_recipes:
-            log_debug_message(
-                "middleware: Matched with recipe Ids: %s", recipe.get_recipe_id()
-            )
-
-        id_result = None
-        final_matched_recipe = None
-        for recipe in matched_recipes:
-            current_id_result = await recipe.return_api_id_if_can_handle_request(
-                path, method, user_context
-            )
-            if current_id_result is not None:
-                if id_result is not None:
-                    raise ValueError(
-                        "Two recipes have matched the same API path and method! This is a bug in the SDK. Please contact support."
-                    )
-                final_matched_recipe = recipe
-                id_result = current_id_result
-
-        if id_result is None or final_matched_recipe is None:
-            return await handle_without_rid()
-
-        log_debug_message(
-            "middleware: Request being handled by recipe. ID is: %s",
-            id_result.api_id,
-        )
-        request_handled = await final_matched_recipe.handle_api_request(
-            id_result.api_id,
-            id_result.tenant_id,
-            request,
-            path,
-            method,
-            response,
-            user_context,
-        )
-        if request_handled is None:
-            log_debug_message(
-                "middleware: Not handled because API returned request_handled as None"
-            )
-            return None
-        log_debug_message("middleware: Ended")
-        return request_handled
-    return await handle_without_rid()
-
-
-
-async def update_or_delete_user_id_mapping_info(self, user_id: str, user_id_type: Optional[UserIDTypes], external_user_id_info: Optional[str], user_context: Optional[Dict[str, Any]]) ‑> Union[UpdateOrDeleteUserIdMappingInfoOkResultUnknownMappingError] -
-
-
-
- -Expand source code - -
async def update_or_delete_user_id_mapping_info(  # pylint: disable=no-self-use
-    self,
-    user_id: str,
-    user_id_type: Optional[UserIDTypes],
-    external_user_id_info: Optional[str],
-    user_context: Optional[Dict[str, Any]],
-) -> Union[UpdateOrDeleteUserIdMappingInfoOkResult, UnknownMappingError]:
-    querier = Querier.get_instance(None)
-
-    cdi_version = await querier.get_api_version(user_context)
-
-    if is_version_gte(cdi_version, "2.15"):
-        res = await querier.send_post_request(
-            NormalisedURLPath("/recipe/userid/external-user-id-info"),
-            {
-                "userId": user_id,
-                "userIdType": user_id_type,
-                "externalUserIdInfo": external_user_id_info,
-            },
-            user_context=user_context,
-        )
-        if res["status"] == "OK":
-            return UpdateOrDeleteUserIdMappingInfoOkResult()
-        if res["status"] == "UNKNOWN_MAPPING_ERROR":
-            return UnknownMappingError()
-
-        raise_general_exception("Unknown response")
-
-    raise_general_exception("Please upgrade the SuperTokens core to >= 3.15.0")
-
-
-
-
-
-class SupertokensConfig -(connection_uri: str, api_key: Union[str, None] = None, network_interceptor: Optional[Callable[[str, str, Dict[str, Any], Optional[Dict[str, Any]], Optional[Dict[str, Any]], Optional[Dict[str, Any]]], Tuple[str, str, Dict[str, Any], Optional[Dict[str, Any]], Optional[Dict[str, Any]]]]] = None, disable_core_call_cache: bool = False) -
-
-
-
- -Expand source code - -
class SupertokensConfig:
-    def __init__(
-        self,
-        connection_uri: str,
-        api_key: Union[str, None] = None,
-        network_interceptor: Optional[
-            Callable[
-                [
-                    str,
-                    str,
-                    Dict[str, Any],
-                    Optional[Dict[str, Any]],
-                    Optional[Dict[str, Any]],
-                    Optional[Dict[str, Any]],
-                ],
-                Tuple[
-                    str,
-                    str,
-                    Dict[str, Any],
-                    Optional[Dict[str, Any]],
-                    Optional[Dict[str, Any]],
-                ],
-            ]
-        ] = None,
-        disable_core_call_cache: bool = False,
-    ):  # We keep this = None here because this is directly used by the user.
-        self.connection_uri = connection_uri
-        self.api_key = api_key
-        self.network_interceptor = network_interceptor
-        self.disable_core_call_cache = disable_core_call_cache
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/syncio/index.html b/html/supertokens_python/syncio/index.html deleted file mode 100644 index 36dd503ce..000000000 --- a/html/supertokens_python/syncio/index.html +++ /dev/null @@ -1,409 +0,0 @@ - - - - - - -supertokens_python.syncio API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.syncio

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from typing import Dict, List, Optional, Union, Any
-
-from supertokens_python import Supertokens
-from supertokens_python.async_to_sync_wrapper import sync
-from supertokens_python.interfaces import (
-    CreateUserIdMappingOkResult,
-    DeleteUserIdMappingOkResult,
-    GetUserIdMappingOkResult,
-    UnknownMappingError,
-    UnknownSupertokensUserIDError,
-    UpdateOrDeleteUserIdMappingInfoOkResult,
-    UserIdMappingAlreadyExistsError,
-    UserIDTypes,
-)
-from supertokens_python.types import UsersResponse
-
-
-def get_users_oldest_first(
-    tenant_id: str,
-    limit: Union[int, None] = None,
-    pagination_token: Union[str, None] = None,
-    include_recipe_ids: Union[None, List[str]] = None,
-    query: Union[None, Dict[str, str]] = None,
-    user_context: Optional[Dict[str, Any]] = None,
-) -> UsersResponse:
-    return sync(
-        Supertokens.get_instance().get_users(
-            tenant_id,
-            "ASC",
-            limit,
-            pagination_token,
-            include_recipe_ids,
-            query,
-            user_context,
-        )
-    )
-
-
-def get_users_newest_first(
-    tenant_id: str,
-    limit: Union[int, None] = None,
-    pagination_token: Union[str, None] = None,
-    include_recipe_ids: Union[None, List[str]] = None,
-    query: Union[None, Dict[str, str]] = None,
-    user_context: Optional[Dict[str, Any]] = None,
-) -> UsersResponse:
-    return sync(
-        Supertokens.get_instance().get_users(
-            tenant_id,
-            "DESC",
-            limit,
-            pagination_token,
-            include_recipe_ids,
-            query,
-            user_context,
-        )
-    )
-
-
-def get_user_count(
-    include_recipe_ids: Union[None, List[str]] = None,
-    tenant_id: Optional[str] = None,
-    user_context: Optional[Dict[str, Any]] = None,
-) -> int:
-    return sync(
-        Supertokens.get_instance().get_user_count(
-            include_recipe_ids, tenant_id, user_context
-        )
-    )
-
-
-def delete_user(user_id: str, user_context: Optional[Dict[str, Any]] = None) -> None:
-    return sync(Supertokens.get_instance().delete_user(user_id, user_context))
-
-
-def create_user_id_mapping(
-    supertokens_user_id: str,
-    external_user_id: str,
-    external_user_id_info: Optional[str] = None,
-    force: Optional[bool] = None,
-    user_context: Optional[Dict[str, Any]] = None,
-) -> Union[
-    CreateUserIdMappingOkResult,
-    UnknownSupertokensUserIDError,
-    UserIdMappingAlreadyExistsError,
-]:
-    return sync(
-        Supertokens.get_instance().create_user_id_mapping(
-            supertokens_user_id,
-            external_user_id,
-            external_user_id_info,
-            force=force,
-            user_context=user_context,
-        )
-    )
-
-
-def get_user_id_mapping(
-    user_id: str,
-    user_id_type: Optional[UserIDTypes] = None,
-    user_context: Optional[Dict[str, Any]] = None,
-) -> Union[GetUserIdMappingOkResult, UnknownMappingError]:
-    return sync(
-        Supertokens.get_instance().get_user_id_mapping(
-            user_id, user_id_type, user_context
-        )
-    )
-
-
-def delete_user_id_mapping(
-    user_id: str,
-    user_id_type: Optional[UserIDTypes] = None,
-    force: Optional[bool] = None,
-    user_context: Optional[Dict[str, Any]] = None,
-) -> DeleteUserIdMappingOkResult:
-    return sync(
-        Supertokens.get_instance().delete_user_id_mapping(
-            user_id, user_id_type, force=force, user_context=user_context
-        )
-    )
-
-
-def update_or_delete_user_id_mapping_info(
-    user_id: str,
-    user_id_type: Optional[UserIDTypes] = None,
-    external_user_id_info: Optional[str] = None,
-    user_context: Optional[Dict[str, Any]] = None,
-) -> Union[UpdateOrDeleteUserIdMappingInfoOkResult, UnknownMappingError]:
-    return sync(
-        Supertokens.get_instance().update_or_delete_user_id_mapping_info(
-            user_id, user_id_type, external_user_id_info, user_context
-        )
-    )
-
-
-
-
-
-
-
-

Functions

-
-
-def create_user_id_mapping(supertokens_user_id: str, external_user_id: str, external_user_id_info: Optional[str] = None, force: Optional[bool] = None, user_context: Optional[Dict[str, Any]] = None) ‑> Union[CreateUserIdMappingOkResultUnknownSupertokensUserIDErrorUserIdMappingAlreadyExistsError] -
-
-
-
- -Expand source code - -
def create_user_id_mapping(
-    supertokens_user_id: str,
-    external_user_id: str,
-    external_user_id_info: Optional[str] = None,
-    force: Optional[bool] = None,
-    user_context: Optional[Dict[str, Any]] = None,
-) -> Union[
-    CreateUserIdMappingOkResult,
-    UnknownSupertokensUserIDError,
-    UserIdMappingAlreadyExistsError,
-]:
-    return sync(
-        Supertokens.get_instance().create_user_id_mapping(
-            supertokens_user_id,
-            external_user_id,
-            external_user_id_info,
-            force=force,
-            user_context=user_context,
-        )
-    )
-
-
-
-def delete_user(user_id: str, user_context: Optional[Dict[str, Any]] = None) ‑> None -
-
-
-
- -Expand source code - -
def delete_user(user_id: str, user_context: Optional[Dict[str, Any]] = None) -> None:
-    return sync(Supertokens.get_instance().delete_user(user_id, user_context))
-
-
-
-def delete_user_id_mapping(user_id: str, user_id_type: Optional[Literal['SUPERTOKENS', 'EXTERNAL', 'ANY']] = None, force: Optional[bool] = None, user_context: Optional[Dict[str, Any]] = None) ‑> DeleteUserIdMappingOkResult -
-
-
-
- -Expand source code - -
def delete_user_id_mapping(
-    user_id: str,
-    user_id_type: Optional[UserIDTypes] = None,
-    force: Optional[bool] = None,
-    user_context: Optional[Dict[str, Any]] = None,
-) -> DeleteUserIdMappingOkResult:
-    return sync(
-        Supertokens.get_instance().delete_user_id_mapping(
-            user_id, user_id_type, force=force, user_context=user_context
-        )
-    )
-
-
-
-def get_user_count(include_recipe_ids: Optional[None] = None, tenant_id: Optional[str] = None, user_context: Optional[Dict[str, Any]] = None) ‑> int -
-
-
-
- -Expand source code - -
def get_user_count(
-    include_recipe_ids: Union[None, List[str]] = None,
-    tenant_id: Optional[str] = None,
-    user_context: Optional[Dict[str, Any]] = None,
-) -> int:
-    return sync(
-        Supertokens.get_instance().get_user_count(
-            include_recipe_ids, tenant_id, user_context
-        )
-    )
-
-
-
-def get_user_id_mapping(user_id: str, user_id_type: Optional[Literal['SUPERTOKENS', 'EXTERNAL', 'ANY']] = None, user_context: Optional[Dict[str, Any]] = None) ‑> Union[GetUserIdMappingOkResultUnknownMappingError] -
-
-
-
- -Expand source code - -
def get_user_id_mapping(
-    user_id: str,
-    user_id_type: Optional[UserIDTypes] = None,
-    user_context: Optional[Dict[str, Any]] = None,
-) -> Union[GetUserIdMappingOkResult, UnknownMappingError]:
-    return sync(
-        Supertokens.get_instance().get_user_id_mapping(
-            user_id, user_id_type, user_context
-        )
-    )
-
-
-
-def get_users_newest_first(tenant_id: str, limit: Optional[int] = None, pagination_token: Optional[str] = None, include_recipe_ids: Optional[None] = None, query: Optional[Dict[str, str]] = None, user_context: Optional[Dict[str, Any]] = None) ‑> UsersResponse -
-
-
-
- -Expand source code - -
def get_users_newest_first(
-    tenant_id: str,
-    limit: Union[int, None] = None,
-    pagination_token: Union[str, None] = None,
-    include_recipe_ids: Union[None, List[str]] = None,
-    query: Union[None, Dict[str, str]] = None,
-    user_context: Optional[Dict[str, Any]] = None,
-) -> UsersResponse:
-    return sync(
-        Supertokens.get_instance().get_users(
-            tenant_id,
-            "DESC",
-            limit,
-            pagination_token,
-            include_recipe_ids,
-            query,
-            user_context,
-        )
-    )
-
-
-
-def get_users_oldest_first(tenant_id: str, limit: Optional[int] = None, pagination_token: Optional[str] = None, include_recipe_ids: Optional[None] = None, query: Optional[Dict[str, str]] = None, user_context: Optional[Dict[str, Any]] = None) ‑> UsersResponse -
-
-
-
- -Expand source code - -
def get_users_oldest_first(
-    tenant_id: str,
-    limit: Union[int, None] = None,
-    pagination_token: Union[str, None] = None,
-    include_recipe_ids: Union[None, List[str]] = None,
-    query: Union[None, Dict[str, str]] = None,
-    user_context: Optional[Dict[str, Any]] = None,
-) -> UsersResponse:
-    return sync(
-        Supertokens.get_instance().get_users(
-            tenant_id,
-            "ASC",
-            limit,
-            pagination_token,
-            include_recipe_ids,
-            query,
-            user_context,
-        )
-    )
-
-
-
-def update_or_delete_user_id_mapping_info(user_id: str, user_id_type: Optional[Literal['SUPERTOKENS', 'EXTERNAL', 'ANY']] = None, external_user_id_info: Optional[str] = None, user_context: Optional[Dict[str, Any]] = None) ‑> Union[UpdateOrDeleteUserIdMappingInfoOkResultUnknownMappingError] -
-
-
-
- -Expand source code - -
def update_or_delete_user_id_mapping_info(
-    user_id: str,
-    user_id_type: Optional[UserIDTypes] = None,
-    external_user_id_info: Optional[str] = None,
-    user_context: Optional[Dict[str, Any]] = None,
-) -> Union[UpdateOrDeleteUserIdMappingInfoOkResult, UnknownMappingError]:
-    return sync(
-        Supertokens.get_instance().update_or_delete_user_id_mapping_info(
-            user_id, user_id_type, external_user_id_info, user_context
-        )
-    )
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/types.html b/html/supertokens_python/types.html deleted file mode 100644 index 7121fad8d..000000000 --- a/html/supertokens_python/types.html +++ /dev/null @@ -1,425 +0,0 @@ - - - - - - -supertokens_python.types API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.types

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-from abc import ABC, abstractmethod
-from typing import Any, Awaitable, Dict, List, TypeVar, Union
-
-_T = TypeVar("_T")
-
-
-class ThirdPartyInfo:
-    def __init__(self, third_party_user_id: str, third_party_id: str):
-        self.user_id = third_party_user_id
-        self.id = third_party_id
-
-
-class User:
-    def __init__(
-        self,
-        recipe_id: str,
-        user_id: str,
-        time_joined: int,
-        email: Union[str, None],
-        phone_number: Union[str, None],
-        third_party_info: Union[ThirdPartyInfo, None],
-        tenant_ids: List[str],
-    ):
-        self.recipe_id = recipe_id
-        self.user_id = user_id
-        self.email = email
-        self.time_joined = time_joined
-        self.third_party_info = third_party_info
-        self.phone_number = phone_number
-        self.tenant_ids = tenant_ids
-
-    def to_json(self) -> Dict[str, Any]:
-        res: Dict[str, Any] = {
-            "recipeId": self.recipe_id,
-            "user": {
-                "id": self.user_id,
-                "timeJoined": self.time_joined,
-                "tenantIds": self.tenant_ids,
-            },
-        }
-
-        if self.email is not None:
-            res["user"]["email"] = self.email
-        if self.phone_number is not None:
-            res["user"]["phoneNumber"] = self.phone_number
-        if self.third_party_info is not None:
-            res["user"]["thirdParty"] = self.third_party_info.__dict__
-
-        return res
-
-
-class UsersResponse:
-    def __init__(self, users: List[User], next_pagination_token: Union[str, None]):
-        self.users: List[User] = users
-        self.next_pagination_token: Union[str, None] = next_pagination_token
-
-
-class APIResponse(ABC):
-    @abstractmethod
-    def to_json(self) -> Dict[str, Any]:
-        pass
-
-
-class GeneralErrorResponse(APIResponse):
-    def __init__(self, message: str):
-        self.status = "GENERAL_ERROR"
-        self.message = message
-
-    def to_json(self) -> Dict[str, Any]:
-        return {"status": self.status, "message": self.message}
-
-
-MaybeAwaitable = Union[Awaitable[_T], _T]
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class APIResponse -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class APIResponse(ABC):
-    @abstractmethod
-    def to_json(self) -> Dict[str, Any]:
-        pass
-
-

Ancestors

-
    -
  • abc.ABC
  • -
-

Subclasses

- -

Methods

-
-
-def to_json(self) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
@abstractmethod
-def to_json(self) -> Dict[str, Any]:
-    pass
-
-
-
-
-
-class GeneralErrorResponse -(message: str) -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class GeneralErrorResponse(APIResponse):
-    def __init__(self, message: str):
-        self.status = "GENERAL_ERROR"
-        self.message = message
-
-    def to_json(self) -> Dict[str, Any]:
-        return {"status": self.status, "message": self.message}
-
-

Ancestors

- -

Methods

-
-
-def to_json(self) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
def to_json(self) -> Dict[str, Any]:
-    return {"status": self.status, "message": self.message}
-
-
-
-
-
-class ThirdPartyInfo -(third_party_user_id: str, third_party_id: str) -
-
-
-
- -Expand source code - -
class ThirdPartyInfo:
-    def __init__(self, third_party_user_id: str, third_party_id: str):
-        self.user_id = third_party_user_id
-        self.id = third_party_id
-
-
-
-class User -(recipe_id: str, user_id: str, time_joined: int, email: Optional[str], phone_number: Optional[str], third_party_info: Optional[ThirdPartyInfo], tenant_ids: List[str]) -
-
-
-
- -Expand source code - -
class User:
-    def __init__(
-        self,
-        recipe_id: str,
-        user_id: str,
-        time_joined: int,
-        email: Union[str, None],
-        phone_number: Union[str, None],
-        third_party_info: Union[ThirdPartyInfo, None],
-        tenant_ids: List[str],
-    ):
-        self.recipe_id = recipe_id
-        self.user_id = user_id
-        self.email = email
-        self.time_joined = time_joined
-        self.third_party_info = third_party_info
-        self.phone_number = phone_number
-        self.tenant_ids = tenant_ids
-
-    def to_json(self) -> Dict[str, Any]:
-        res: Dict[str, Any] = {
-            "recipeId": self.recipe_id,
-            "user": {
-                "id": self.user_id,
-                "timeJoined": self.time_joined,
-                "tenantIds": self.tenant_ids,
-            },
-        }
-
-        if self.email is not None:
-            res["user"]["email"] = self.email
-        if self.phone_number is not None:
-            res["user"]["phoneNumber"] = self.phone_number
-        if self.third_party_info is not None:
-            res["user"]["thirdParty"] = self.third_party_info.__dict__
-
-        return res
-
-

Methods

-
-
-def to_json(self) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
def to_json(self) -> Dict[str, Any]:
-    res: Dict[str, Any] = {
-        "recipeId": self.recipe_id,
-        "user": {
-            "id": self.user_id,
-            "timeJoined": self.time_joined,
-            "tenantIds": self.tenant_ids,
-        },
-    }
-
-    if self.email is not None:
-        res["user"]["email"] = self.email
-    if self.phone_number is not None:
-        res["user"]["phoneNumber"] = self.phone_number
-    if self.third_party_info is not None:
-        res["user"]["thirdParty"] = self.third_party_info.__dict__
-
-    return res
-
-
-
-
-
-class UsersResponse -(users: List[User], next_pagination_token: Optional[str]) -
-
-
-
- -Expand source code - -
class UsersResponse:
-    def __init__(self, users: List[User], next_pagination_token: Union[str, None]):
-        self.users: List[User] = users
-        self.next_pagination_token: Union[str, None] = next_pagination_token
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/html/supertokens_python/utils.html b/html/supertokens_python/utils.html deleted file mode 100644 index 02f4c591e..000000000 --- a/html/supertokens_python/utils.html +++ /dev/null @@ -1,1046 +0,0 @@ - - - - - - -supertokens_python.utils API documentation - - - - - - - - - - - -
-
-
-

Module supertokens_python.utils

-
-
-
- -Expand source code - -
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
-#
-# This software is licensed under the Apache License, Version 2.0 (the
-# "License") as published by the Apache Software Foundation.
-#
-# You may not use this file except in compliance with the License. You may
-# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from __future__ import annotations
-
-import json
-import threading
-import warnings
-from base64 import urlsafe_b64decode, urlsafe_b64encode, b64encode, b64decode
-from math import floor
-from re import fullmatch
-from time import time
-from typing import (
-    TYPE_CHECKING,
-    Any,
-    Awaitable,
-    Callable,
-    Dict,
-    List,
-    TypeVar,
-    Union,
-    Optional,
-)
-from urllib.parse import urlparse
-
-from httpx import HTTPStatusError, Response
-from tldextract import extract  # type: ignore
-
-from supertokens_python.framework.django.framework import DjangoFramework
-from supertokens_python.framework.fastapi.framework import FastapiFramework
-from supertokens_python.framework.flask.framework import FlaskFramework
-from supertokens_python.framework.request import BaseRequest
-from supertokens_python.framework.response import BaseResponse
-from supertokens_python.logger import log_debug_message
-
-from .constants import ERROR_MESSAGE_KEY, RID_KEY_HEADER
-from .exceptions import raise_general_exception
-from .types import MaybeAwaitable
-
-_T = TypeVar("_T")
-
-if TYPE_CHECKING:
-    pass
-
-
-FRAMEWORKS = {
-    "fastapi": FastapiFramework(),
-    "flask": FlaskFramework(),
-    "django": DjangoFramework(),
-}
-
-
-def is_an_ip_address(ip_address: str) -> bool:
-    return (
-        fullmatch(
-            r"^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|["
-            r"01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$",
-            ip_address,
-        )
-        is not None
-    )
-
-
-def normalise_http_method(method: str) -> str:
-    return method.lower()
-
-
-def get_rid_from_header(request: BaseRequest) -> Union[str, None]:
-    return get_header(request, RID_KEY_HEADER)
-
-
-def get_header(request: BaseRequest, key: str) -> Union[str, None]:
-    return request.get_header(key)
-
-
-def find_max_version(versions_1: List[str], versions_2: List[str]) -> Union[str, None]:
-    versions = list(set(versions_1) & set(versions_2))
-    if len(versions) == 0:
-        return None
-
-    max_v = versions[0]
-    for i in range(1, len(versions)):
-        version = versions[i]
-        max_v = _get_max_version(max_v, version)
-
-    return max_v
-
-
-def is_version_gte(version: str, minimum_version: str) -> bool:
-    return _get_max_version(version, minimum_version) == version
-
-
-def _get_max_version(v1: str, v2: str) -> str:
-    v1_split = v1.split(".")
-    v2_split = v2.split(".")
-    max_loop = min(len(v1_split), len(v2_split))
-
-    for i in range(max_loop):
-        if int(v1_split[i]) > int(v2_split[i]):
-            return v1
-        if int(v2_split[i]) > int(v1_split[i]):
-            return v2
-
-    if len(v1_split) > len(v2_split):
-        return v1
-
-    return v2
-
-
-def is_4xx_error(status_code: int) -> bool:
-    return status_code // 100 == 4
-
-
-def is_5xx_error(status_code: int) -> bool:
-    return status_code // 100 == 5
-
-
-def send_non_200_response(
-    body: Dict[str, Any], status_code: int, response: BaseResponse
-) -> BaseResponse:
-    if status_code < 300:
-        raise_general_exception("Calling sendNon200Response with status code < 300")
-    log_debug_message(
-        "Sending response to client with status code: %s", str(status_code)
-    )
-    response.set_status_code(status_code)
-    response.set_json_content(content=body)
-    return response
-
-
-def send_non_200_response_with_message(
-    message: str, status_code: int, response: BaseResponse
-) -> BaseResponse:
-    return send_non_200_response({ERROR_MESSAGE_KEY: message}, status_code, response)
-
-
-def send_unauthorised_access_response(response: BaseResponse) -> BaseResponse:
-    return send_non_200_response_with_message("Unauthorised access", 401, response)
-
-
-def send_200_response(
-    data_json: Dict[str, Any], response: BaseResponse
-) -> BaseResponse:
-    log_debug_message("Sending response to client with status code: 200")
-    response.set_json_content(data_json)
-    response.set_status_code(200)
-    return response
-
-
-def get_timestamp_ms() -> int:
-    return int(time() * 1000)
-
-
-def utf_base64encode(s: str, urlsafe: bool) -> str:
-    if urlsafe:
-        return urlsafe_b64encode(s.encode("utf-8")).decode("utf-8")
-
-    return b64encode(s.encode("utf-8")).decode("utf-8")
-
-
-def utf_base64decode(s: str, urlsafe: bool) -> str:
-    # Adding extra "==" based on
-    # https://stackoverflow.com/questions/2941995/python-ignore-incorrect-padding-error-when-base64-decoding
-    # Otherwise it can raise "incorrect padding" error
-    if urlsafe:
-        return urlsafe_b64decode(s.encode("utf-8") + b"==").decode("utf-8")
-
-    return b64decode(s.encode("utf-8")).decode("utf-8")
-
-
-def get_filtered_list(func: Callable[[_T], bool], given_list: List[_T]) -> List[_T]:
-    return list(filter(func, given_list))
-
-
-def find_first_occurrence_in_list(
-    condition: Callable[[_T], bool], given_list: List[_T]
-) -> Union[_T, None]:
-    for item in given_list:
-        if condition(item):
-            return item
-    return None
-
-
-def frontend_has_interceptor(request: BaseRequest) -> bool:
-    return get_rid_from_header(request) is not None
-
-
-def deprecated_warn(msg: str):
-    warnings.warn(msg, DeprecationWarning, stacklevel=2)
-
-
-def handle_httpx_client_exceptions(
-    e: Exception, input_: Union[Dict[str, Any], None] = None
-):
-    if isinstance(e, HTTPStatusError) and isinstance(e.response, Response):  # type: ignore
-        res = e.response  # type: ignore
-        log_debug_message("Error status: %s", res.status_code)  # type: ignore
-        log_debug_message("Error response: %s", res.json())
-    else:
-        log_debug_message("Error: %s", str(e))
-
-    if input_ is not None:
-        log_debug_message("Logging the input:")
-        log_debug_message("%s", json.dumps(input_))
-
-
-def humanize_time(ms: int) -> str:
-    t = floor(ms / 1000)
-    suffix = ""
-
-    if t < 60:
-        if t > 1:
-            suffix = "s"
-        time_str = f"{t} second{suffix}"
-    elif t < 3600:
-        m = floor(t / 60)
-        if m > 1:
-            suffix = "s"
-        time_str = f"{m} minute{suffix}"
-    else:
-        h = floor(t / 360) / 10
-        if h > 1:
-            suffix = "s"
-        if h % 1 == 0:
-            h = int(h)
-        time_str = f"{h} hour{suffix}"
-
-    return time_str
-
-
-def set_request_in_user_context_if_not_defined(
-    user_context: Optional[Dict[str, Any]], request: BaseRequest
-) -> Dict[str, Any]:
-    if user_context is None:
-        user_context = {}
-
-    if "_default" not in user_context:
-        user_context["_default"] = {}
-
-    if isinstance(user_context["_default"], dict):
-        user_context["_default"]["request"] = request
-        user_context["_default"]["keep_cache_alive"] = True
-
-    return user_context
-
-
-def default_user_context(request: BaseRequest) -> Dict[str, Any]:
-    return set_request_in_user_context_if_not_defined({}, request)
-
-
-async def resolve(obj: MaybeAwaitable[_T]) -> _T:
-    """Returns value or value of awaitable object passed"""
-    if isinstance(obj, Awaitable):
-        return await obj  # type: ignore
-    return obj  # type: ignore
-
-
-def get_top_level_domain_for_same_site_resolution(url: str) -> str:
-    url_obj = urlparse(url)
-    hostname = url_obj.hostname
-
-    if hostname is None:
-        raise Exception("Should not come here")
-
-    if hostname.startswith("localhost") or is_an_ip_address(hostname):
-        return "localhost"
-
-    parsed_url: Any = extract(hostname, include_psl_private_domains=True)
-    if parsed_url.domain == "":  # type: ignore
-        # We need to do this because of https://github.com/supertokens/supertokens-python/issues/394
-        if hostname.endswith(".amazonaws.com") and parsed_url.suffix == hostname:
-            return hostname
-
-        raise Exception(
-            "Please make sure that the apiDomain and websiteDomain have correct values"
-        )
-
-    return parsed_url.domain + "." + parsed_url.suffix  # type: ignore
-
-
-class RWMutex:
-    def __init__(self):
-        self._lock = threading.Lock()
-        self._readers = threading.Condition(self._lock)
-        self._writers = threading.Condition(self._lock)
-        self._reader_count = 0
-        self._writer_count = 0
-
-    def lock(self):
-        with self._lock:
-            while self._writer_count > 0 or self._reader_count > 0:
-                self._writers.wait()
-            self._writer_count += 1
-
-    def unlock(self):
-        with self._lock:
-            self._writer_count -= 1
-            self._readers.notify_all()
-            self._writers.notify_all()
-
-    def r_lock(self):
-        with self._lock:
-            while self._writer_count > 0:
-                self._readers.wait()
-            self._reader_count += 1
-
-    def r_unlock(self):
-        with self._lock:
-            self._reader_count -= 1
-            if self._reader_count == 0:
-                self._writers.notify_all()
-
-
-class RWLockContext:
-    def __init__(self, mutex: RWMutex, read: bool = True):
-        self.mutex = mutex
-        self.read = read
-
-    def __enter__(self):
-        if self.read:
-            self.mutex.r_lock()
-        else:
-            self.mutex.lock()
-
-    def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any):
-        if self.read:
-            self.mutex.r_unlock()
-        else:
-            self.mutex.unlock()
-
-        if exc_type is not None:
-            raise exc_type(exc_value).with_traceback(traceback)
-
-
-def normalise_email(email: str) -> str:
-    return email.strip().lower()
-
-
-
-
-
-
-
-

Functions

-
-
-def default_user_context(request: BaseRequest) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
def default_user_context(request: BaseRequest) -> Dict[str, Any]:
-    return set_request_in_user_context_if_not_defined({}, request)
-
-
-
-def deprecated_warn(msg: str) -
-
-
-
- -Expand source code - -
def deprecated_warn(msg: str):
-    warnings.warn(msg, DeprecationWarning, stacklevel=2)
-
-
-
-def find_first_occurrence_in_list(condition: Callable[[_T], bool], given_list: List[_T]) ‑> Optional[~_T] -
-
-
-
- -Expand source code - -
def find_first_occurrence_in_list(
-    condition: Callable[[_T], bool], given_list: List[_T]
-) -> Union[_T, None]:
-    for item in given_list:
-        if condition(item):
-            return item
-    return None
-
-
-
-def find_max_version(versions_1: List[str], versions_2: List[str]) ‑> Optional[str] -
-
-
-
- -Expand source code - -
def find_max_version(versions_1: List[str], versions_2: List[str]) -> Union[str, None]:
-    versions = list(set(versions_1) & set(versions_2))
-    if len(versions) == 0:
-        return None
-
-    max_v = versions[0]
-    for i in range(1, len(versions)):
-        version = versions[i]
-        max_v = _get_max_version(max_v, version)
-
-    return max_v
-
-
-
-def frontend_has_interceptor(request: BaseRequest) ‑> bool -
-
-
-
- -Expand source code - -
def frontend_has_interceptor(request: BaseRequest) -> bool:
-    return get_rid_from_header(request) is not None
-
-
-
-def get_filtered_list(func: Callable[[_T], bool], given_list: List[_T]) ‑> List[~_T] -
-
-
-
- -Expand source code - -
def get_filtered_list(func: Callable[[_T], bool], given_list: List[_T]) -> List[_T]:
-    return list(filter(func, given_list))
-
-
-
-def get_header(request: BaseRequest, key: str) ‑> Optional[str] -
-
-
-
- -Expand source code - -
def get_header(request: BaseRequest, key: str) -> Union[str, None]:
-    return request.get_header(key)
-
-
-
-def get_rid_from_header(request: BaseRequest) ‑> Optional[str] -
-
-
-
- -Expand source code - -
def get_rid_from_header(request: BaseRequest) -> Union[str, None]:
-    return get_header(request, RID_KEY_HEADER)
-
-
-
-def get_timestamp_ms() ‑> int -
-
-
-
- -Expand source code - -
def get_timestamp_ms() -> int:
-    return int(time() * 1000)
-
-
-
-def get_top_level_domain_for_same_site_resolution(url: str) ‑> str -
-
-
-
- -Expand source code - -
def get_top_level_domain_for_same_site_resolution(url: str) -> str:
-    url_obj = urlparse(url)
-    hostname = url_obj.hostname
-
-    if hostname is None:
-        raise Exception("Should not come here")
-
-    if hostname.startswith("localhost") or is_an_ip_address(hostname):
-        return "localhost"
-
-    parsed_url: Any = extract(hostname, include_psl_private_domains=True)
-    if parsed_url.domain == "":  # type: ignore
-        # We need to do this because of https://github.com/supertokens/supertokens-python/issues/394
-        if hostname.endswith(".amazonaws.com") and parsed_url.suffix == hostname:
-            return hostname
-
-        raise Exception(
-            "Please make sure that the apiDomain and websiteDomain have correct values"
-        )
-
-    return parsed_url.domain + "." + parsed_url.suffix  # type: ignore
-
-
-
-def handle_httpx_client_exceptions(e: Exception, input_: Union[Dict[str, Any], None] = None) -
-
-
-
- -Expand source code - -
def handle_httpx_client_exceptions(
-    e: Exception, input_: Union[Dict[str, Any], None] = None
-):
-    if isinstance(e, HTTPStatusError) and isinstance(e.response, Response):  # type: ignore
-        res = e.response  # type: ignore
-        log_debug_message("Error status: %s", res.status_code)  # type: ignore
-        log_debug_message("Error response: %s", res.json())
-    else:
-        log_debug_message("Error: %s", str(e))
-
-    if input_ is not None:
-        log_debug_message("Logging the input:")
-        log_debug_message("%s", json.dumps(input_))
-
-
-
-def humanize_time(ms: int) ‑> str -
-
-
-
- -Expand source code - -
def humanize_time(ms: int) -> str:
-    t = floor(ms / 1000)
-    suffix = ""
-
-    if t < 60:
-        if t > 1:
-            suffix = "s"
-        time_str = f"{t} second{suffix}"
-    elif t < 3600:
-        m = floor(t / 60)
-        if m > 1:
-            suffix = "s"
-        time_str = f"{m} minute{suffix}"
-    else:
-        h = floor(t / 360) / 10
-        if h > 1:
-            suffix = "s"
-        if h % 1 == 0:
-            h = int(h)
-        time_str = f"{h} hour{suffix}"
-
-    return time_str
-
-
-
-def is_4xx_error(status_code: int) ‑> bool -
-
-
-
- -Expand source code - -
def is_4xx_error(status_code: int) -> bool:
-    return status_code // 100 == 4
-
-
-
-def is_5xx_error(status_code: int) ‑> bool -
-
-
-
- -Expand source code - -
def is_5xx_error(status_code: int) -> bool:
-    return status_code // 100 == 5
-
-
-
-def is_an_ip_address(ip_address: str) ‑> bool -
-
-
-
- -Expand source code - -
def is_an_ip_address(ip_address: str) -> bool:
-    return (
-        fullmatch(
-            r"^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|["
-            r"01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$",
-            ip_address,
-        )
-        is not None
-    )
-
-
-
-def is_version_gte(version: str, minimum_version: str) ‑> bool -
-
-
-
- -Expand source code - -
def is_version_gte(version: str, minimum_version: str) -> bool:
-    return _get_max_version(version, minimum_version) == version
-
-
-
-def normalise_email(email: str) ‑> str -
-
-
-
- -Expand source code - -
def normalise_email(email: str) -> str:
-    return email.strip().lower()
-
-
-
-def normalise_http_method(method: str) ‑> str -
-
-
-
- -Expand source code - -
def normalise_http_method(method: str) -> str:
-    return method.lower()
-
-
-
-async def resolve(obj: MaybeAwaitable[_T]) ‑> ~_T -
-
-

Returns value or value of awaitable object passed

-
- -Expand source code - -
async def resolve(obj: MaybeAwaitable[_T]) -> _T:
-    """Returns value or value of awaitable object passed"""
-    if isinstance(obj, Awaitable):
-        return await obj  # type: ignore
-    return obj  # type: ignore
-
-
-
-def send_200_response(data_json: Dict[str, Any], response: BaseResponse) ‑> BaseResponse -
-
-
-
- -Expand source code - -
def send_200_response(
-    data_json: Dict[str, Any], response: BaseResponse
-) -> BaseResponse:
-    log_debug_message("Sending response to client with status code: 200")
-    response.set_json_content(data_json)
-    response.set_status_code(200)
-    return response
-
-
-
-def send_non_200_response(body: Dict[str, Any], status_code: int, response: BaseResponse) ‑> BaseResponse -
-
-
-
- -Expand source code - -
def send_non_200_response(
-    body: Dict[str, Any], status_code: int, response: BaseResponse
-) -> BaseResponse:
-    if status_code < 300:
-        raise_general_exception("Calling sendNon200Response with status code < 300")
-    log_debug_message(
-        "Sending response to client with status code: %s", str(status_code)
-    )
-    response.set_status_code(status_code)
-    response.set_json_content(content=body)
-    return response
-
-
-
-def send_non_200_response_with_message(message: str, status_code: int, response: BaseResponse) ‑> BaseResponse -
-
-
-
- -Expand source code - -
def send_non_200_response_with_message(
-    message: str, status_code: int, response: BaseResponse
-) -> BaseResponse:
-    return send_non_200_response({ERROR_MESSAGE_KEY: message}, status_code, response)
-
-
-
-def send_unauthorised_access_response(response: BaseResponse) ‑> BaseResponse -
-
-
-
- -Expand source code - -
def send_unauthorised_access_response(response: BaseResponse) -> BaseResponse:
-    return send_non_200_response_with_message("Unauthorised access", 401, response)
-
-
-
-def set_request_in_user_context_if_not_defined(user_context: Optional[Dict[str, Any]], request: BaseRequest) ‑> Dict[str, Any] -
-
-
-
- -Expand source code - -
def set_request_in_user_context_if_not_defined(
-    user_context: Optional[Dict[str, Any]], request: BaseRequest
-) -> Dict[str, Any]:
-    if user_context is None:
-        user_context = {}
-
-    if "_default" not in user_context:
-        user_context["_default"] = {}
-
-    if isinstance(user_context["_default"], dict):
-        user_context["_default"]["request"] = request
-        user_context["_default"]["keep_cache_alive"] = True
-
-    return user_context
-
-
-
-def utf_base64decode(s: str, urlsafe: bool) ‑> str -
-
-
-
- -Expand source code - -
def utf_base64decode(s: str, urlsafe: bool) -> str:
-    # Adding extra "==" based on
-    # https://stackoverflow.com/questions/2941995/python-ignore-incorrect-padding-error-when-base64-decoding
-    # Otherwise it can raise "incorrect padding" error
-    if urlsafe:
-        return urlsafe_b64decode(s.encode("utf-8") + b"==").decode("utf-8")
-
-    return b64decode(s.encode("utf-8")).decode("utf-8")
-
-
-
-def utf_base64encode(s: str, urlsafe: bool) ‑> str -
-
-
-
- -Expand source code - -
def utf_base64encode(s: str, urlsafe: bool) -> str:
-    if urlsafe:
-        return urlsafe_b64encode(s.encode("utf-8")).decode("utf-8")
-
-    return b64encode(s.encode("utf-8")).decode("utf-8")
-
-
-
-
-
-

Classes

-
-
-class RWLockContext -(mutex: RWMutex, read: bool = True) -
-
-
-
- -Expand source code - -
class RWLockContext:
-    def __init__(self, mutex: RWMutex, read: bool = True):
-        self.mutex = mutex
-        self.read = read
-
-    def __enter__(self):
-        if self.read:
-            self.mutex.r_lock()
-        else:
-            self.mutex.lock()
-
-    def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any):
-        if self.read:
-            self.mutex.r_unlock()
-        else:
-            self.mutex.unlock()
-
-        if exc_type is not None:
-            raise exc_type(exc_value).with_traceback(traceback)
-
-
-
-class RWMutex -
-
-
-
- -Expand source code - -
class RWMutex:
-    def __init__(self):
-        self._lock = threading.Lock()
-        self._readers = threading.Condition(self._lock)
-        self._writers = threading.Condition(self._lock)
-        self._reader_count = 0
-        self._writer_count = 0
-
-    def lock(self):
-        with self._lock:
-            while self._writer_count > 0 or self._reader_count > 0:
-                self._writers.wait()
-            self._writer_count += 1
-
-    def unlock(self):
-        with self._lock:
-            self._writer_count -= 1
-            self._readers.notify_all()
-            self._writers.notify_all()
-
-    def r_lock(self):
-        with self._lock:
-            while self._writer_count > 0:
-                self._readers.wait()
-            self._reader_count += 1
-
-    def r_unlock(self):
-        with self._lock:
-            self._reader_count -= 1
-            if self._reader_count == 0:
-                self._writers.notify_all()
-
-

Methods

-
-
-def lock(self) -
-
-
-
- -Expand source code - -
def lock(self):
-    with self._lock:
-        while self._writer_count > 0 or self._reader_count > 0:
-            self._writers.wait()
-        self._writer_count += 1
-
-
-
-def r_lock(self) -
-
-
-
- -Expand source code - -
def r_lock(self):
-    with self._lock:
-        while self._writer_count > 0:
-            self._readers.wait()
-        self._reader_count += 1
-
-
-
-def r_unlock(self) -
-
-
-
- -Expand source code - -
def r_unlock(self):
-    with self._lock:
-        self._reader_count -= 1
-        if self._reader_count == 0:
-            self._writers.notify_all()
-
-
-
-def unlock(self) -
-
-
-
- -Expand source code - -
def unlock(self):
-    with self._lock:
-        self._writer_count -= 1
-        self._readers.notify_all()
-        self._writers.notify_all()
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file From 32ed8b02ee7afb2a8a640f69bc1a5444e9a8f7ac Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Thu, 17 Oct 2024 18:37:46 +0530 Subject: [PATCH 2/5] fix: cicd image (#535) --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 5c79bb1cf..c37c6a10d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -10,7 +10,7 @@ orbs: jobs: create-test-jobs: machine: - image: ubuntu-2204:2022.04.1 + image: ubuntu-2204:2024.04.4 steps: - checkout - run: From 112b871b60b1065a119626d0b2e1717f29239704 Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Thu, 17 Oct 2024 18:38:36 +0530 Subject: [PATCH 3/5] adding dev-v0.24.4 tag to this commit to ensure building From 752e93379375cedbaa8f6b36433736afb1bff21c Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Fri, 18 Oct 2024 11:17:09 +0530 Subject: [PATCH 4/5] fix: pylint (#536) * fix: pylint * fix: pylint * fix: pylint --- .github/workflows/pre-commit-hook-run.yml | 5 ++--- Makefile | 2 +- dev-requirements.txt | 3 ++- test-pre-commit.sh | 7 ------- 4 files changed, 5 insertions(+), 12 deletions(-) delete mode 100644 test-pre-commit.sh diff --git a/.github/workflows/pre-commit-hook-run.yml b/.github/workflows/pre-commit-hook-run.yml index a93db8cad..0c25a22bf 100644 --- a/.github/workflows/pre-commit-hook-run.yml +++ b/.github/workflows/pre-commit-hook-run.yml @@ -14,10 +14,9 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - name: Set up node - uses: actions/setup-node@v1 + - uses: actions/setup-python@v5 with: - node-version: '12' + python-version: '3.7' - name: Create virtual environment and install dependencies run: | python3 -m venv venv diff --git a/Makefile b/Makefile index 100335466..f9afc228e 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,7 @@ format: black . check-lint: - pyright supertokens_python tests examples && pylint --disable=too-many-positional-arguments --load-plugins=pylint.extensions.no_self_use supertokens_python tests examples + pyright supertokens_python tests examples && pylint supertokens_python tests examples set-up-hooks: cp hooks/pre-commit.sh .git/hooks/pre-commit diff --git a/dev-requirements.txt b/dev-requirements.txt index c1cd3098a..f959daf22 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -1,6 +1,7 @@ aiosmtplib>=1.1.6,<4.0.0 anyio==3.5.0 asgiref==3.5.2 +astroid==2.9.3 attrs==21.4.0 black==22.3.0 certifi==2021.10.8 @@ -50,7 +51,7 @@ pycparser==2.21 pycryptodome==3.10.4 pydantic==1.9.0 PyJWT==2.6.0 -pylint==3.3.1 +pylint==2.12.2 pyparsing==3.0.7 pyright==1.1.236 pyrsistent==0.18.1 diff --git a/test-pre-commit.sh b/test-pre-commit.sh deleted file mode 100644 index a4236b052..000000000 --- a/test-pre-commit.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash - -python3 -m venv venv -source venv/bin/activate -pip3 install "cython<3.0.0" wheel -pip3 install "PyYAML==5.4.1" --no-build-isolation -make dev-install && rm -rf src From 78e10728790618b7f63a2110d31364246d71fdfc Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Fri, 18 Oct 2024 11:18:16 +0530 Subject: [PATCH 5/5] adding dev-v0.24.4 tag to this commit to ensure building --- .../async_to_sync_wrapper.html | 166 + html/supertokens_python/asyncio/index.html | 384 +++ html/supertokens_python/constants.html | 90 + html/supertokens_python/exceptions.html | 233 ++ .../framework/django/django_middleware.html | 258 ++ .../framework/django/django_request.html | 429 +++ .../framework/django/django_response.html | 395 +++ .../framework/django/framework.html | 140 + .../framework/django/index.html | 101 + .../framework/fastapi/fastapi_middleware.html | 281 ++ .../framework/fastapi/fastapi_request.html | 391 +++ .../framework/fastapi/fastapi_response.html | 412 +++ .../framework/fastapi/framework.html | 139 + .../framework/fastapi/index.html | 101 + .../framework/flask/flask_middleware.html | 400 +++ .../framework/flask/flask_request.html | 412 +++ .../framework/flask/flask_response.html | 416 +++ .../framework/flask/framework.html | 139 + .../framework/flask/index.html | 101 + html/supertokens_python/framework/index.html | 113 + .../supertokens_python/framework/request.html | 417 +++ .../framework/response.html | 320 ++ html/supertokens_python/framework/types.html | 190 ++ html/supertokens_python/index.html | 267 ++ .../ingredients/emaildelivery/index.html | 166 + .../emaildelivery/services/index.html | 83 + .../emaildelivery/services/smtp.html | 275 ++ .../ingredients/emaildelivery/types.html | 434 +++ .../supertokens_python/ingredients/index.html | 88 + .../ingredients/smsdelivery/index.html | 166 + .../smsdelivery/services/index.html | 88 + .../smsdelivery/services/supertokens.html | 73 + .../smsdelivery/services/twilio.html | 117 + .../ingredients/smsdelivery/types.html | 420 +++ html/supertokens_python/interfaces.html | 254 ++ html/supertokens_python/logger.html | 257 ++ .../normalised_url_domain.html | 253 ++ .../normalised_url_path.html | 369 +++ .../post_init_callbacks.html | 172 + html/supertokens_python/process_state.html | 243 ++ html/supertokens_python/querier.html | 1661 ++++++++++ .../recipe/dashboard/api/analytics.html | 253 ++ .../recipe/dashboard/api/dashboard.html | 127 + .../recipe/dashboard/api/implementation.html | 271 ++ .../recipe/dashboard/api/index.html | 1174 +++++++ .../recipe/dashboard/api/list_tenants.html | 150 + .../recipe/dashboard/api/search/getTags.html | 116 + .../recipe/dashboard/api/search/index.html | 65 + .../recipe/dashboard/api/signin.html | 169 + .../recipe/dashboard/api/signout.html | 144 + .../dashboard/api/userdetails/index.html | 115 + .../api/userdetails/user_delete.html | 112 + .../userdetails/user_email_verify_get.html | 128 + .../userdetails/user_email_verify_put.html | 181 + .../user_email_verify_token_post.html | 143 + .../dashboard/api/userdetails/user_get.html | 188 ++ .../api/userdetails/user_metadata_get.html | 125 + .../api/userdetails/user_metadata_put.html | 182 ++ .../api/userdetails/user_password_put.html | 256 ++ .../dashboard/api/userdetails/user_put.html | 584 ++++ .../api/userdetails/user_sessions_get.html | 164 + .../api/userdetails/user_sessions_post.html | 116 + .../recipe/dashboard/api/users_count_get.html | 126 + .../recipe/dashboard/api/users_get.html | 275 ++ .../recipe/dashboard/api/validate_key.html | 134 + .../recipe/dashboard/constants.html | 73 + .../recipe/dashboard/exceptions.html | 125 + .../recipe/dashboard/index.html | 166 + .../recipe/dashboard/interfaces.html | 2084 ++++++++++++ .../recipe/dashboard/recipe.html | 1187 +++++++ .../dashboard/recipe_implementation.html | 342 ++ .../recipe/dashboard/utils.html | 1060 ++++++ .../emailpassword/api/email_exists.html | 136 + .../api/generate_password_reset_token.html | 150 + .../emailpassword/api/implementation.html | 720 ++++ .../recipe/emailpassword/api/index.html | 120 + .../emailpassword/api/password_reset.html | 164 + .../recipe/emailpassword/api/signin.html | 148 + .../recipe/emailpassword/api/signup.html | 180 + .../recipe/emailpassword/api/utils.html | 270 ++ .../recipe/emailpassword/asyncio/index.html | 490 +++ .../recipe/emailpassword/constants.html | 80 + .../emailpassword/emaildelivery/index.html | 83 + .../backward_compatibility/index.html | 287 ++ .../emaildelivery/services/index.html | 92 + .../emaildelivery/services/smtp/index.html | 217 ++ .../services/smtp/password_reset.html | 146 + .../services/smtp/password_reset_email.html | 969 ++++++ .../smtp/service_implementation/index.html | 170 + .../recipe/emailpassword/exceptions.html | 208 ++ .../recipe/emailpassword/index.html | 198 ++ .../recipe/emailpassword/interfaces.html | 1607 +++++++++ .../recipe/emailpassword/recipe.html | 861 +++++ .../emailpassword/recipe_implementation.html | 675 ++++ .../recipe/emailpassword/syncio/index.html | 428 +++ .../recipe/emailpassword/types.html | 422 +++ .../recipe/emailpassword/utils.html | 839 +++++ .../emailverification/api/email_verify.html | 193 ++ .../api/generate_email_verify_token.html | 137 + .../recipe/emailverification/api/index.html | 92 + .../emailverification/asyncio/index.html | 577 ++++ .../recipe/emailverification/constants.html | 73 + .../emaildelivery/index.html | 83 + .../backward_compatibility/index.html | 245 ++ .../emaildelivery/services/index.html | 92 + .../services/smtp/email_verify.html | 146 + .../services/smtp/email_verify_email.html | 977 ++++++ .../emaildelivery/services/smtp/index.html | 215 ++ .../services/smtp/service_implementation.html | 175 + .../ev_claim_validators.html | 72 + .../recipe/emailverification/exceptions.html | 140 + .../recipe/emailverification/index.html | 210 ++ .../recipe/emailverification/interfaces.html | 1169 +++++++ .../recipe/emailverification/recipe.html | 1679 ++++++++++ .../recipe_implementation.html | 372 +++ .../emailverification/syncio/index.html | 357 ++ .../recipe/emailverification/types.html | 236 ++ .../recipe/emailverification/utils.html | 322 ++ html/supertokens_python/recipe/index.html | 115 + .../recipe/jwt/api/implementation.html | 157 + .../recipe/jwt/api/index.html | 70 + .../recipe/jwt/api/jwks_get.html | 126 + .../recipe/jwt/asyncio/index.html | 150 + .../recipe/jwt/constants.html | 73 + .../recipe/jwt/exceptions.html | 108 + html/supertokens_python/recipe/jwt/index.html | 165 + .../recipe/jwt/interfaces.html | 484 +++ .../supertokens_python/recipe/jwt/recipe.html | 543 +++ .../recipe/jwt/recipe_implementation.html | 332 ++ .../recipe/jwt/syncio/index.html | 141 + html/supertokens_python/recipe/jwt/utils.html | 195 ++ .../multitenancy/api/implementation.html | 316 ++ .../recipe/multitenancy/api/index.html | 89 + .../multitenancy/api/login_methods.html | 132 + .../recipe/multitenancy/asyncio/index.html | 404 +++ .../recipe/multitenancy/constants.html | 73 + .../recipe/multitenancy/exceptions.html | 108 + .../recipe/multitenancy/index.html | 180 + .../recipe/multitenancy/interfaces.html | 1705 ++++++++++ .../recipe/multitenancy/recipe.html | 743 +++++ .../multitenancy/recipe_implementation.html | 960 ++++++ .../recipe/multitenancy/syncio/index.html | 367 +++ .../recipe/multitenancy/utils.html | 360 ++ .../recipe/openid/api/implementation.html | 157 + .../recipe/openid/api/index.html | 70 + .../open_id_discovery_configuration_get.html | 128 + .../recipe/openid/asyncio/index.html | 188 ++ .../recipe/openid/constants.html | 73 + .../recipe/openid/exceptions.html | 108 + .../recipe/openid/index.html | 167 + .../recipe/openid/interfaces.html | 417 +++ .../recipe/openid/recipe.html | 595 ++++ .../recipe/openid/recipe_implementation.html | 295 ++ .../recipe/openid/syncio/index.html | 167 + .../recipe/openid/utils.html | 273 ++ .../recipe/passwordless/api/consume_code.html | 194 ++ .../recipe/passwordless/api/create_code.html | 287 ++ .../recipe/passwordless/api/email_exists.html | 131 + .../passwordless/api/implementation.html | 1056 ++++++ .../recipe/passwordless/api/index.html | 114 + .../passwordless/api/phone_number_exists.html | 130 + .../recipe/passwordless/api/resend_code.html | 148 + .../recipe/passwordless/asyncio/index.html | 848 +++++ .../recipe/passwordless/constants.html | 79 + .../passwordless/emaildelivery/index.html | 83 + .../backward_compatibility/index.html | 267 ++ .../emaildelivery/services/index.html | 92 + .../emaildelivery/services/smtp/index.html | 217 ++ .../services/smtp/pless_login.html | 193 ++ .../services/smtp/pless_login_email.html | 2879 ++++++++++++++++ .../services/smtp/service_implementation.html | 183 ++ .../recipe/passwordless/exceptions.html | 108 + .../recipe/passwordless/index.html | 237 ++ .../recipe/passwordless/interfaces.html | 2105 ++++++++++++ .../recipe/passwordless/recipe.html | 1165 +++++++ .../passwordless/recipe_implementation.html | 1625 +++++++++ .../passwordless/smsdelivery/index.html | 83 + .../backward_compatibility/index.html | 305 ++ .../smsdelivery/services/index.html | 98 + .../services/supertokens/index.html | 237 ++ .../smsdelivery/services/twilio/index.html | 244 ++ .../services/twilio/passwordless_login.html | 212 ++ .../twilio/service_implementation.html | 228 ++ .../recipe/passwordless/syncio/index.html | 770 +++++ .../recipe/passwordless/types.html | 499 +++ .../recipe/passwordless/utils.html | 657 ++++ .../recipe/session/access_token.html | 428 +++ .../recipe/session/api/implementation.html | 344 ++ .../recipe/session/api/index.html | 95 + .../recipe/session/api/refresh.html | 130 + .../recipe/session/api/signout.html | 155 + .../recipe/session/asyncio/index.html | 1330 ++++++++ .../claim_base_classes/boolean_claim.html | 245 ++ .../session/claim_base_classes/index.html | 93 + .../primitive_array_claim.html | 1158 +++++++ .../claim_base_classes/primitive_claim.html | 636 ++++ .../recipe/session/claims.html | 79 + .../recipe/session/constants.html | 104 + .../recipe/session/cookie_and_header.html | 1061 ++++++ .../recipe/session/exceptions.html | 470 +++ .../framework/django/asyncio/index.html | 223 ++ .../session/framework/django/index.html | 88 + .../framework/django/syncio/index.html | 232 ++ .../session/framework/fastapi/index.html | 240 ++ .../recipe/session/framework/flask/index.html | 228 ++ .../recipe/session/framework/index.html | 93 + .../recipe/session/index.html | 285 ++ .../recipe/session/interfaces.html | 2903 +++++++++++++++++ .../recipe/session/jwks.html | 442 +++ .../recipe/session/jwt.html | 265 ++ .../recipe/session/recipe.html | 1237 +++++++ .../recipe/session/recipe_implementation.html | 1687 ++++++++++ .../recipe/session/session_class.html | 1208 +++++++ .../recipe/session/session_functions.html | 1262 +++++++ .../session/session_request_functions.html | 1084 ++++++ .../recipe/session/syncio/index.html | 1033 ++++++ .../recipe/session/utils.html | 1556 +++++++++ .../recipe/thirdparty/api/apple_redirect.html | 130 + .../thirdparty/api/authorisation_url.html | 188 ++ .../recipe/thirdparty/api/implementation.html | 546 ++++ .../recipe/thirdparty/api/index.html | 275 ++ .../recipe/thirdparty/api/signinup.html | 234 ++ .../recipe/thirdparty/asyncio/index.html | 272 ++ .../recipe/thirdparty/constants.html | 77 + .../recipe/thirdparty/exceptions.html | 139 + .../recipe/thirdparty/index.html | 194 ++ .../recipe/thirdparty/interfaces.html | 953 ++++++ .../recipe/thirdparty/provider.html | 1193 +++++++ .../providers/active_directory.html | 245 ++ .../recipe/thirdparty/providers/apple.html | 299 ++ .../thirdparty/providers/bitbucket.html | 378 +++ .../thirdparty/providers/boxy_saml.html | 248 ++ .../thirdparty/providers/config_utils.html | 684 ++++ .../recipe/thirdparty/providers/custom.html | 1253 +++++++ .../recipe/thirdparty/providers/discord.html | 240 ++ .../recipe/thirdparty/providers/facebook.html | 282 ++ .../recipe/thirdparty/providers/github.html | 357 ++ .../recipe/thirdparty/providers/gitlab.html | 261 ++ .../recipe/thirdparty/providers/google.html | 262 ++ .../providers/google_workspaces.html | 241 ++ .../recipe/thirdparty/providers/index.html | 158 + .../recipe/thirdparty/providers/linkedin.html | 340 ++ .../recipe/thirdparty/providers/okta.html | 270 ++ .../recipe/thirdparty/providers/twitter.html | 385 +++ .../recipe/thirdparty/providers/utils.html | 277 ++ .../recipe/thirdparty/recipe.html | 711 ++++ .../thirdparty/recipe_implementation.html | 723 ++++ .../recipe/thirdparty/syncio/index.html | 261 ++ .../recipe/thirdparty/types.html | 408 +++ .../recipe/thirdparty/utils.html | 362 ++ .../recipe/usermetadata/asyncio/index.html | 166 + .../recipe/usermetadata/exceptions.html | 107 + .../recipe/usermetadata/index.html | 153 + .../recipe/usermetadata/interfaces.html | 266 ++ .../recipe/usermetadata/recipe.html | 458 +++ .../usermetadata/recipe_implementation.html | 263 ++ .../recipe/usermetadata/syncio/index.html | 142 + .../recipe/usermetadata/utils.html | 192 ++ .../recipe/userroles/asyncio/index.html | 397 +++ .../recipe/userroles/exceptions.html | 107 + .../recipe/userroles/index.html | 169 + .../recipe/userroles/interfaces.html | 677 ++++ .../recipe/userroles/recipe.html | 704 ++++ .../userroles/recipe_implementation.html | 641 ++++ .../recipe/userroles/syncio/index.html | 375 +++ .../recipe/userroles/utils.html | 229 ++ html/supertokens_python/recipe_module.html | 580 ++++ html/supertokens_python/supertokens.html | 2276 +++++++++++++ html/supertokens_python/syncio/index.html | 409 +++ html/supertokens_python/types.html | 425 +++ html/supertokens_python/utils.html | 1046 ++++++ 271 files changed, 108201 insertions(+) create mode 100644 html/supertokens_python/async_to_sync_wrapper.html create mode 100644 html/supertokens_python/asyncio/index.html create mode 100644 html/supertokens_python/constants.html create mode 100644 html/supertokens_python/exceptions.html create mode 100644 html/supertokens_python/framework/django/django_middleware.html create mode 100644 html/supertokens_python/framework/django/django_request.html create mode 100644 html/supertokens_python/framework/django/django_response.html create mode 100644 html/supertokens_python/framework/django/framework.html create mode 100644 html/supertokens_python/framework/django/index.html create mode 100644 html/supertokens_python/framework/fastapi/fastapi_middleware.html create mode 100644 html/supertokens_python/framework/fastapi/fastapi_request.html create mode 100644 html/supertokens_python/framework/fastapi/fastapi_response.html create mode 100644 html/supertokens_python/framework/fastapi/framework.html create mode 100644 html/supertokens_python/framework/fastapi/index.html create mode 100644 html/supertokens_python/framework/flask/flask_middleware.html create mode 100644 html/supertokens_python/framework/flask/flask_request.html create mode 100644 html/supertokens_python/framework/flask/flask_response.html create mode 100644 html/supertokens_python/framework/flask/framework.html create mode 100644 html/supertokens_python/framework/flask/index.html create mode 100644 html/supertokens_python/framework/index.html create mode 100644 html/supertokens_python/framework/request.html create mode 100644 html/supertokens_python/framework/response.html create mode 100644 html/supertokens_python/framework/types.html create mode 100644 html/supertokens_python/index.html create mode 100644 html/supertokens_python/ingredients/emaildelivery/index.html create mode 100644 html/supertokens_python/ingredients/emaildelivery/services/index.html create mode 100644 html/supertokens_python/ingredients/emaildelivery/services/smtp.html create mode 100644 html/supertokens_python/ingredients/emaildelivery/types.html create mode 100644 html/supertokens_python/ingredients/index.html create mode 100644 html/supertokens_python/ingredients/smsdelivery/index.html create mode 100644 html/supertokens_python/ingredients/smsdelivery/services/index.html create mode 100644 html/supertokens_python/ingredients/smsdelivery/services/supertokens.html create mode 100644 html/supertokens_python/ingredients/smsdelivery/services/twilio.html create mode 100644 html/supertokens_python/ingredients/smsdelivery/types.html create mode 100644 html/supertokens_python/interfaces.html create mode 100644 html/supertokens_python/logger.html create mode 100644 html/supertokens_python/normalised_url_domain.html create mode 100644 html/supertokens_python/normalised_url_path.html create mode 100644 html/supertokens_python/post_init_callbacks.html create mode 100644 html/supertokens_python/process_state.html create mode 100644 html/supertokens_python/querier.html create mode 100644 html/supertokens_python/recipe/dashboard/api/analytics.html create mode 100644 html/supertokens_python/recipe/dashboard/api/dashboard.html create mode 100644 html/supertokens_python/recipe/dashboard/api/implementation.html create mode 100644 html/supertokens_python/recipe/dashboard/api/index.html create mode 100644 html/supertokens_python/recipe/dashboard/api/list_tenants.html create mode 100644 html/supertokens_python/recipe/dashboard/api/search/getTags.html create mode 100644 html/supertokens_python/recipe/dashboard/api/search/index.html create mode 100644 html/supertokens_python/recipe/dashboard/api/signin.html create mode 100644 html/supertokens_python/recipe/dashboard/api/signout.html create mode 100644 html/supertokens_python/recipe/dashboard/api/userdetails/index.html create mode 100644 html/supertokens_python/recipe/dashboard/api/userdetails/user_delete.html create mode 100644 html/supertokens_python/recipe/dashboard/api/userdetails/user_email_verify_get.html create mode 100644 html/supertokens_python/recipe/dashboard/api/userdetails/user_email_verify_put.html create mode 100644 html/supertokens_python/recipe/dashboard/api/userdetails/user_email_verify_token_post.html create mode 100644 html/supertokens_python/recipe/dashboard/api/userdetails/user_get.html create mode 100644 html/supertokens_python/recipe/dashboard/api/userdetails/user_metadata_get.html create mode 100644 html/supertokens_python/recipe/dashboard/api/userdetails/user_metadata_put.html create mode 100644 html/supertokens_python/recipe/dashboard/api/userdetails/user_password_put.html create mode 100644 html/supertokens_python/recipe/dashboard/api/userdetails/user_put.html create mode 100644 html/supertokens_python/recipe/dashboard/api/userdetails/user_sessions_get.html create mode 100644 html/supertokens_python/recipe/dashboard/api/userdetails/user_sessions_post.html create mode 100644 html/supertokens_python/recipe/dashboard/api/users_count_get.html create mode 100644 html/supertokens_python/recipe/dashboard/api/users_get.html create mode 100644 html/supertokens_python/recipe/dashboard/api/validate_key.html create mode 100644 html/supertokens_python/recipe/dashboard/constants.html create mode 100644 html/supertokens_python/recipe/dashboard/exceptions.html create mode 100644 html/supertokens_python/recipe/dashboard/index.html create mode 100644 html/supertokens_python/recipe/dashboard/interfaces.html create mode 100644 html/supertokens_python/recipe/dashboard/recipe.html create mode 100644 html/supertokens_python/recipe/dashboard/recipe_implementation.html create mode 100644 html/supertokens_python/recipe/dashboard/utils.html create mode 100644 html/supertokens_python/recipe/emailpassword/api/email_exists.html create mode 100644 html/supertokens_python/recipe/emailpassword/api/generate_password_reset_token.html create mode 100644 html/supertokens_python/recipe/emailpassword/api/implementation.html create mode 100644 html/supertokens_python/recipe/emailpassword/api/index.html create mode 100644 html/supertokens_python/recipe/emailpassword/api/password_reset.html create mode 100644 html/supertokens_python/recipe/emailpassword/api/signin.html create mode 100644 html/supertokens_python/recipe/emailpassword/api/signup.html create mode 100644 html/supertokens_python/recipe/emailpassword/api/utils.html create mode 100644 html/supertokens_python/recipe/emailpassword/asyncio/index.html create mode 100644 html/supertokens_python/recipe/emailpassword/constants.html create mode 100644 html/supertokens_python/recipe/emailpassword/emaildelivery/index.html create mode 100644 html/supertokens_python/recipe/emailpassword/emaildelivery/services/backward_compatibility/index.html create mode 100644 html/supertokens_python/recipe/emailpassword/emaildelivery/services/index.html create mode 100644 html/supertokens_python/recipe/emailpassword/emaildelivery/services/smtp/index.html create mode 100644 html/supertokens_python/recipe/emailpassword/emaildelivery/services/smtp/password_reset.html create mode 100644 html/supertokens_python/recipe/emailpassword/emaildelivery/services/smtp/password_reset_email.html create mode 100644 html/supertokens_python/recipe/emailpassword/emaildelivery/services/smtp/service_implementation/index.html create mode 100644 html/supertokens_python/recipe/emailpassword/exceptions.html create mode 100644 html/supertokens_python/recipe/emailpassword/index.html create mode 100644 html/supertokens_python/recipe/emailpassword/interfaces.html create mode 100644 html/supertokens_python/recipe/emailpassword/recipe.html create mode 100644 html/supertokens_python/recipe/emailpassword/recipe_implementation.html create mode 100644 html/supertokens_python/recipe/emailpassword/syncio/index.html create mode 100644 html/supertokens_python/recipe/emailpassword/types.html create mode 100644 html/supertokens_python/recipe/emailpassword/utils.html create mode 100644 html/supertokens_python/recipe/emailverification/api/email_verify.html create mode 100644 html/supertokens_python/recipe/emailverification/api/generate_email_verify_token.html create mode 100644 html/supertokens_python/recipe/emailverification/api/index.html create mode 100644 html/supertokens_python/recipe/emailverification/asyncio/index.html create mode 100644 html/supertokens_python/recipe/emailverification/constants.html create mode 100644 html/supertokens_python/recipe/emailverification/emaildelivery/index.html create mode 100644 html/supertokens_python/recipe/emailverification/emaildelivery/services/backward_compatibility/index.html create mode 100644 html/supertokens_python/recipe/emailverification/emaildelivery/services/index.html create mode 100644 html/supertokens_python/recipe/emailverification/emaildelivery/services/smtp/email_verify.html create mode 100644 html/supertokens_python/recipe/emailverification/emaildelivery/services/smtp/email_verify_email.html create mode 100644 html/supertokens_python/recipe/emailverification/emaildelivery/services/smtp/index.html create mode 100644 html/supertokens_python/recipe/emailverification/emaildelivery/services/smtp/service_implementation.html create mode 100644 html/supertokens_python/recipe/emailverification/ev_claim_validators.html create mode 100644 html/supertokens_python/recipe/emailverification/exceptions.html create mode 100644 html/supertokens_python/recipe/emailverification/index.html create mode 100644 html/supertokens_python/recipe/emailverification/interfaces.html create mode 100644 html/supertokens_python/recipe/emailverification/recipe.html create mode 100644 html/supertokens_python/recipe/emailverification/recipe_implementation.html create mode 100644 html/supertokens_python/recipe/emailverification/syncio/index.html create mode 100644 html/supertokens_python/recipe/emailverification/types.html create mode 100644 html/supertokens_python/recipe/emailverification/utils.html create mode 100644 html/supertokens_python/recipe/index.html create mode 100644 html/supertokens_python/recipe/jwt/api/implementation.html create mode 100644 html/supertokens_python/recipe/jwt/api/index.html create mode 100644 html/supertokens_python/recipe/jwt/api/jwks_get.html create mode 100644 html/supertokens_python/recipe/jwt/asyncio/index.html create mode 100644 html/supertokens_python/recipe/jwt/constants.html create mode 100644 html/supertokens_python/recipe/jwt/exceptions.html create mode 100644 html/supertokens_python/recipe/jwt/index.html create mode 100644 html/supertokens_python/recipe/jwt/interfaces.html create mode 100644 html/supertokens_python/recipe/jwt/recipe.html create mode 100644 html/supertokens_python/recipe/jwt/recipe_implementation.html create mode 100644 html/supertokens_python/recipe/jwt/syncio/index.html create mode 100644 html/supertokens_python/recipe/jwt/utils.html create mode 100644 html/supertokens_python/recipe/multitenancy/api/implementation.html create mode 100644 html/supertokens_python/recipe/multitenancy/api/index.html create mode 100644 html/supertokens_python/recipe/multitenancy/api/login_methods.html create mode 100644 html/supertokens_python/recipe/multitenancy/asyncio/index.html create mode 100644 html/supertokens_python/recipe/multitenancy/constants.html create mode 100644 html/supertokens_python/recipe/multitenancy/exceptions.html create mode 100644 html/supertokens_python/recipe/multitenancy/index.html create mode 100644 html/supertokens_python/recipe/multitenancy/interfaces.html create mode 100644 html/supertokens_python/recipe/multitenancy/recipe.html create mode 100644 html/supertokens_python/recipe/multitenancy/recipe_implementation.html create mode 100644 html/supertokens_python/recipe/multitenancy/syncio/index.html create mode 100644 html/supertokens_python/recipe/multitenancy/utils.html create mode 100644 html/supertokens_python/recipe/openid/api/implementation.html create mode 100644 html/supertokens_python/recipe/openid/api/index.html create mode 100644 html/supertokens_python/recipe/openid/api/open_id_discovery_configuration_get.html create mode 100644 html/supertokens_python/recipe/openid/asyncio/index.html create mode 100644 html/supertokens_python/recipe/openid/constants.html create mode 100644 html/supertokens_python/recipe/openid/exceptions.html create mode 100644 html/supertokens_python/recipe/openid/index.html create mode 100644 html/supertokens_python/recipe/openid/interfaces.html create mode 100644 html/supertokens_python/recipe/openid/recipe.html create mode 100644 html/supertokens_python/recipe/openid/recipe_implementation.html create mode 100644 html/supertokens_python/recipe/openid/syncio/index.html create mode 100644 html/supertokens_python/recipe/openid/utils.html create mode 100644 html/supertokens_python/recipe/passwordless/api/consume_code.html create mode 100644 html/supertokens_python/recipe/passwordless/api/create_code.html create mode 100644 html/supertokens_python/recipe/passwordless/api/email_exists.html create mode 100644 html/supertokens_python/recipe/passwordless/api/implementation.html create mode 100644 html/supertokens_python/recipe/passwordless/api/index.html create mode 100644 html/supertokens_python/recipe/passwordless/api/phone_number_exists.html create mode 100644 html/supertokens_python/recipe/passwordless/api/resend_code.html create mode 100644 html/supertokens_python/recipe/passwordless/asyncio/index.html create mode 100644 html/supertokens_python/recipe/passwordless/constants.html create mode 100644 html/supertokens_python/recipe/passwordless/emaildelivery/index.html create mode 100644 html/supertokens_python/recipe/passwordless/emaildelivery/services/backward_compatibility/index.html create mode 100644 html/supertokens_python/recipe/passwordless/emaildelivery/services/index.html create mode 100644 html/supertokens_python/recipe/passwordless/emaildelivery/services/smtp/index.html create mode 100644 html/supertokens_python/recipe/passwordless/emaildelivery/services/smtp/pless_login.html create mode 100644 html/supertokens_python/recipe/passwordless/emaildelivery/services/smtp/pless_login_email.html create mode 100644 html/supertokens_python/recipe/passwordless/emaildelivery/services/smtp/service_implementation.html create mode 100644 html/supertokens_python/recipe/passwordless/exceptions.html create mode 100644 html/supertokens_python/recipe/passwordless/index.html create mode 100644 html/supertokens_python/recipe/passwordless/interfaces.html create mode 100644 html/supertokens_python/recipe/passwordless/recipe.html create mode 100644 html/supertokens_python/recipe/passwordless/recipe_implementation.html create mode 100644 html/supertokens_python/recipe/passwordless/smsdelivery/index.html create mode 100644 html/supertokens_python/recipe/passwordless/smsdelivery/services/backward_compatibility/index.html create mode 100644 html/supertokens_python/recipe/passwordless/smsdelivery/services/index.html create mode 100644 html/supertokens_python/recipe/passwordless/smsdelivery/services/supertokens/index.html create mode 100644 html/supertokens_python/recipe/passwordless/smsdelivery/services/twilio/index.html create mode 100644 html/supertokens_python/recipe/passwordless/smsdelivery/services/twilio/passwordless_login.html create mode 100644 html/supertokens_python/recipe/passwordless/smsdelivery/services/twilio/service_implementation.html create mode 100644 html/supertokens_python/recipe/passwordless/syncio/index.html create mode 100644 html/supertokens_python/recipe/passwordless/types.html create mode 100644 html/supertokens_python/recipe/passwordless/utils.html create mode 100644 html/supertokens_python/recipe/session/access_token.html create mode 100644 html/supertokens_python/recipe/session/api/implementation.html create mode 100644 html/supertokens_python/recipe/session/api/index.html create mode 100644 html/supertokens_python/recipe/session/api/refresh.html create mode 100644 html/supertokens_python/recipe/session/api/signout.html create mode 100644 html/supertokens_python/recipe/session/asyncio/index.html create mode 100644 html/supertokens_python/recipe/session/claim_base_classes/boolean_claim.html create mode 100644 html/supertokens_python/recipe/session/claim_base_classes/index.html create mode 100644 html/supertokens_python/recipe/session/claim_base_classes/primitive_array_claim.html create mode 100644 html/supertokens_python/recipe/session/claim_base_classes/primitive_claim.html create mode 100644 html/supertokens_python/recipe/session/claims.html create mode 100644 html/supertokens_python/recipe/session/constants.html create mode 100644 html/supertokens_python/recipe/session/cookie_and_header.html create mode 100644 html/supertokens_python/recipe/session/exceptions.html create mode 100644 html/supertokens_python/recipe/session/framework/django/asyncio/index.html create mode 100644 html/supertokens_python/recipe/session/framework/django/index.html create mode 100644 html/supertokens_python/recipe/session/framework/django/syncio/index.html create mode 100644 html/supertokens_python/recipe/session/framework/fastapi/index.html create mode 100644 html/supertokens_python/recipe/session/framework/flask/index.html create mode 100644 html/supertokens_python/recipe/session/framework/index.html create mode 100644 html/supertokens_python/recipe/session/index.html create mode 100644 html/supertokens_python/recipe/session/interfaces.html create mode 100644 html/supertokens_python/recipe/session/jwks.html create mode 100644 html/supertokens_python/recipe/session/jwt.html create mode 100644 html/supertokens_python/recipe/session/recipe.html create mode 100644 html/supertokens_python/recipe/session/recipe_implementation.html create mode 100644 html/supertokens_python/recipe/session/session_class.html create mode 100644 html/supertokens_python/recipe/session/session_functions.html create mode 100644 html/supertokens_python/recipe/session/session_request_functions.html create mode 100644 html/supertokens_python/recipe/session/syncio/index.html create mode 100644 html/supertokens_python/recipe/session/utils.html create mode 100644 html/supertokens_python/recipe/thirdparty/api/apple_redirect.html create mode 100644 html/supertokens_python/recipe/thirdparty/api/authorisation_url.html create mode 100644 html/supertokens_python/recipe/thirdparty/api/implementation.html create mode 100644 html/supertokens_python/recipe/thirdparty/api/index.html create mode 100644 html/supertokens_python/recipe/thirdparty/api/signinup.html create mode 100644 html/supertokens_python/recipe/thirdparty/asyncio/index.html create mode 100644 html/supertokens_python/recipe/thirdparty/constants.html create mode 100644 html/supertokens_python/recipe/thirdparty/exceptions.html create mode 100644 html/supertokens_python/recipe/thirdparty/index.html create mode 100644 html/supertokens_python/recipe/thirdparty/interfaces.html create mode 100644 html/supertokens_python/recipe/thirdparty/provider.html create mode 100644 html/supertokens_python/recipe/thirdparty/providers/active_directory.html create mode 100644 html/supertokens_python/recipe/thirdparty/providers/apple.html create mode 100644 html/supertokens_python/recipe/thirdparty/providers/bitbucket.html create mode 100644 html/supertokens_python/recipe/thirdparty/providers/boxy_saml.html create mode 100644 html/supertokens_python/recipe/thirdparty/providers/config_utils.html create mode 100644 html/supertokens_python/recipe/thirdparty/providers/custom.html create mode 100644 html/supertokens_python/recipe/thirdparty/providers/discord.html create mode 100644 html/supertokens_python/recipe/thirdparty/providers/facebook.html create mode 100644 html/supertokens_python/recipe/thirdparty/providers/github.html create mode 100644 html/supertokens_python/recipe/thirdparty/providers/gitlab.html create mode 100644 html/supertokens_python/recipe/thirdparty/providers/google.html create mode 100644 html/supertokens_python/recipe/thirdparty/providers/google_workspaces.html create mode 100644 html/supertokens_python/recipe/thirdparty/providers/index.html create mode 100644 html/supertokens_python/recipe/thirdparty/providers/linkedin.html create mode 100644 html/supertokens_python/recipe/thirdparty/providers/okta.html create mode 100644 html/supertokens_python/recipe/thirdparty/providers/twitter.html create mode 100644 html/supertokens_python/recipe/thirdparty/providers/utils.html create mode 100644 html/supertokens_python/recipe/thirdparty/recipe.html create mode 100644 html/supertokens_python/recipe/thirdparty/recipe_implementation.html create mode 100644 html/supertokens_python/recipe/thirdparty/syncio/index.html create mode 100644 html/supertokens_python/recipe/thirdparty/types.html create mode 100644 html/supertokens_python/recipe/thirdparty/utils.html create mode 100644 html/supertokens_python/recipe/usermetadata/asyncio/index.html create mode 100644 html/supertokens_python/recipe/usermetadata/exceptions.html create mode 100644 html/supertokens_python/recipe/usermetadata/index.html create mode 100644 html/supertokens_python/recipe/usermetadata/interfaces.html create mode 100644 html/supertokens_python/recipe/usermetadata/recipe.html create mode 100644 html/supertokens_python/recipe/usermetadata/recipe_implementation.html create mode 100644 html/supertokens_python/recipe/usermetadata/syncio/index.html create mode 100644 html/supertokens_python/recipe/usermetadata/utils.html create mode 100644 html/supertokens_python/recipe/userroles/asyncio/index.html create mode 100644 html/supertokens_python/recipe/userroles/exceptions.html create mode 100644 html/supertokens_python/recipe/userroles/index.html create mode 100644 html/supertokens_python/recipe/userroles/interfaces.html create mode 100644 html/supertokens_python/recipe/userroles/recipe.html create mode 100644 html/supertokens_python/recipe/userroles/recipe_implementation.html create mode 100644 html/supertokens_python/recipe/userroles/syncio/index.html create mode 100644 html/supertokens_python/recipe/userroles/utils.html create mode 100644 html/supertokens_python/recipe_module.html create mode 100644 html/supertokens_python/supertokens.html create mode 100644 html/supertokens_python/syncio/index.html create mode 100644 html/supertokens_python/types.html create mode 100644 html/supertokens_python/utils.html diff --git a/html/supertokens_python/async_to_sync_wrapper.html b/html/supertokens_python/async_to_sync_wrapper.html new file mode 100644 index 000000000..dd7eb001c --- /dev/null +++ b/html/supertokens_python/async_to_sync_wrapper.html @@ -0,0 +1,166 @@ + + + + + + +supertokens_python.async_to_sync_wrapper API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.async_to_sync_wrapper

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import asyncio
+from typing import Any, Coroutine, TypeVar
+from os import getenv
+
+_T = TypeVar("_T")
+
+
+def nest_asyncio_enabled():
+    return getenv("SUPERTOKENS_NEST_ASYNCIO", "") == "1"
+
+
+def create_or_get_event_loop() -> asyncio.AbstractEventLoop:
+    try:
+        return asyncio.get_event_loop()
+    except Exception as ex:
+        if "There is no current event loop in thread" in str(ex):
+            loop = asyncio.new_event_loop()
+
+            if nest_asyncio_enabled():
+                import nest_asyncio  # type: ignore
+
+                nest_asyncio.apply(loop)  # type: ignore
+
+            asyncio.set_event_loop(loop)
+            return loop
+        raise ex
+
+
+def sync(co: Coroutine[Any, Any, _T]) -> _T:
+    loop = create_or_get_event_loop()
+    return loop.run_until_complete(co)
+
+
+
+
+
+
+
+

Functions

+
+
+def create_or_get_event_loop() ‑> asyncio.events.AbstractEventLoop +
+
+
+
+ +Expand source code + +
def create_or_get_event_loop() -> asyncio.AbstractEventLoop:
+    try:
+        return asyncio.get_event_loop()
+    except Exception as ex:
+        if "There is no current event loop in thread" in str(ex):
+            loop = asyncio.new_event_loop()
+
+            if nest_asyncio_enabled():
+                import nest_asyncio  # type: ignore
+
+                nest_asyncio.apply(loop)  # type: ignore
+
+            asyncio.set_event_loop(loop)
+            return loop
+        raise ex
+
+
+
+def nest_asyncio_enabled() +
+
+
+
+ +Expand source code + +
def nest_asyncio_enabled():
+    return getenv("SUPERTOKENS_NEST_ASYNCIO", "") == "1"
+
+
+
+def sync(co: Coroutine[Any, Any, ~_T]) ‑> ~_T +
+
+
+
+ +Expand source code + +
def sync(co: Coroutine[Any, Any, _T]) -> _T:
+    loop = create_or_get_event_loop()
+    return loop.run_until_complete(co)
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/asyncio/index.html b/html/supertokens_python/asyncio/index.html new file mode 100644 index 000000000..f720f0adb --- /dev/null +++ b/html/supertokens_python/asyncio/index.html @@ -0,0 +1,384 @@ + + + + + + +supertokens_python.asyncio API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.asyncio

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from typing import Dict, List, Optional, Union, Any
+
+from supertokens_python import Supertokens
+from supertokens_python.interfaces import (
+    CreateUserIdMappingOkResult,
+    DeleteUserIdMappingOkResult,
+    GetUserIdMappingOkResult,
+    UnknownMappingError,
+    UnknownSupertokensUserIDError,
+    UpdateOrDeleteUserIdMappingInfoOkResult,
+    UserIdMappingAlreadyExistsError,
+    UserIDTypes,
+)
+from supertokens_python.types import UsersResponse
+
+
+async def get_users_oldest_first(
+    tenant_id: str,
+    limit: Union[int, None] = None,
+    pagination_token: Union[str, None] = None,
+    include_recipe_ids: Union[None, List[str]] = None,
+    query: Union[None, Dict[str, str]] = None,
+    user_context: Optional[Dict[str, Any]] = None,
+) -> UsersResponse:
+    return await Supertokens.get_instance().get_users(
+        tenant_id,
+        "ASC",
+        limit,
+        pagination_token,
+        include_recipe_ids,
+        query,
+        user_context,
+    )
+
+
+async def get_users_newest_first(
+    tenant_id: str,
+    limit: Union[int, None] = None,
+    pagination_token: Union[str, None] = None,
+    include_recipe_ids: Union[None, List[str]] = None,
+    query: Union[None, Dict[str, str]] = None,
+    user_context: Optional[Dict[str, Any]] = None,
+) -> UsersResponse:
+    return await Supertokens.get_instance().get_users(
+        tenant_id,
+        "DESC",
+        limit,
+        pagination_token,
+        include_recipe_ids,
+        query,
+        user_context,
+    )
+
+
+async def get_user_count(
+    include_recipe_ids: Union[None, List[str]] = None,
+    tenant_id: Optional[str] = None,
+    user_context: Optional[Dict[str, Any]] = None,
+) -> int:
+    return await Supertokens.get_instance().get_user_count(
+        include_recipe_ids, tenant_id, user_context
+    )
+
+
+async def delete_user(
+    user_id: str, user_context: Optional[Dict[str, Any]] = None
+) -> None:
+    return await Supertokens.get_instance().delete_user(user_id, user_context)
+
+
+async def create_user_id_mapping(
+    supertokens_user_id: str,
+    external_user_id: str,
+    external_user_id_info: Optional[str] = None,
+    force: Optional[bool] = None,
+    user_context: Optional[Dict[str, Any]] = None,
+) -> Union[
+    CreateUserIdMappingOkResult,
+    UnknownSupertokensUserIDError,
+    UserIdMappingAlreadyExistsError,
+]:
+    return await Supertokens.get_instance().create_user_id_mapping(
+        supertokens_user_id,
+        external_user_id,
+        external_user_id_info,
+        force,
+        user_context,
+    )
+
+
+async def get_user_id_mapping(
+    user_id: str,
+    user_id_type: Optional[UserIDTypes] = None,
+    user_context: Optional[Dict[str, Any]] = None,
+) -> Union[GetUserIdMappingOkResult, UnknownMappingError]:
+    return await Supertokens.get_instance().get_user_id_mapping(
+        user_id, user_id_type, user_context
+    )
+
+
+async def delete_user_id_mapping(
+    user_id: str,
+    user_id_type: Optional[UserIDTypes] = None,
+    force: Optional[bool] = None,
+    user_context: Optional[Dict[str, Any]] = None,
+) -> DeleteUserIdMappingOkResult:
+    return await Supertokens.get_instance().delete_user_id_mapping(
+        user_id, user_id_type, force, user_context
+    )
+
+
+async def update_or_delete_user_id_mapping_info(
+    user_id: str,
+    user_id_type: Optional[UserIDTypes] = None,
+    external_user_id_info: Optional[str] = None,
+    user_context: Optional[Dict[str, Any]] = None,
+) -> Union[UpdateOrDeleteUserIdMappingInfoOkResult, UnknownMappingError]:
+    return await Supertokens.get_instance().update_or_delete_user_id_mapping_info(
+        user_id, user_id_type, external_user_id_info, user_context
+    )
+
+
+
+
+
+
+
+

Functions

+
+
+async def create_user_id_mapping(supertokens_user_id: str, external_user_id: str, external_user_id_info: Optional[str] = None, force: Optional[bool] = None, user_context: Optional[Dict[str, Any]] = None) ‑> Union[CreateUserIdMappingOkResultUnknownSupertokensUserIDErrorUserIdMappingAlreadyExistsError] +
+
+
+
+ +Expand source code + +
async def create_user_id_mapping(
+    supertokens_user_id: str,
+    external_user_id: str,
+    external_user_id_info: Optional[str] = None,
+    force: Optional[bool] = None,
+    user_context: Optional[Dict[str, Any]] = None,
+) -> Union[
+    CreateUserIdMappingOkResult,
+    UnknownSupertokensUserIDError,
+    UserIdMappingAlreadyExistsError,
+]:
+    return await Supertokens.get_instance().create_user_id_mapping(
+        supertokens_user_id,
+        external_user_id,
+        external_user_id_info,
+        force,
+        user_context,
+    )
+
+
+
+async def delete_user(user_id: str, user_context: Optional[Dict[str, Any]] = None) ‑> None +
+
+
+
+ +Expand source code + +
async def delete_user(
+    user_id: str, user_context: Optional[Dict[str, Any]] = None
+) -> None:
+    return await Supertokens.get_instance().delete_user(user_id, user_context)
+
+
+
+async def delete_user_id_mapping(user_id: str, user_id_type: Optional[typing_extensions.Literal['SUPERTOKENS', 'EXTERNAL', 'ANY']] = None, force: Optional[bool] = None, user_context: Optional[Dict[str, Any]] = None) ‑> DeleteUserIdMappingOkResult +
+
+
+
+ +Expand source code + +
async def delete_user_id_mapping(
+    user_id: str,
+    user_id_type: Optional[UserIDTypes] = None,
+    force: Optional[bool] = None,
+    user_context: Optional[Dict[str, Any]] = None,
+) -> DeleteUserIdMappingOkResult:
+    return await Supertokens.get_instance().delete_user_id_mapping(
+        user_id, user_id_type, force, user_context
+    )
+
+
+
+async def get_user_count(include_recipe_ids: Optional[None] = None, tenant_id: Optional[str] = None, user_context: Optional[Dict[str, Any]] = None) ‑> int +
+
+
+
+ +Expand source code + +
async def get_user_count(
+    include_recipe_ids: Union[None, List[str]] = None,
+    tenant_id: Optional[str] = None,
+    user_context: Optional[Dict[str, Any]] = None,
+) -> int:
+    return await Supertokens.get_instance().get_user_count(
+        include_recipe_ids, tenant_id, user_context
+    )
+
+
+
+async def get_user_id_mapping(user_id: str, user_id_type: Optional[typing_extensions.Literal['SUPERTOKENS', 'EXTERNAL', 'ANY']] = None, user_context: Optional[Dict[str, Any]] = None) ‑> Union[GetUserIdMappingOkResultUnknownMappingError] +
+
+
+
+ +Expand source code + +
async def get_user_id_mapping(
+    user_id: str,
+    user_id_type: Optional[UserIDTypes] = None,
+    user_context: Optional[Dict[str, Any]] = None,
+) -> Union[GetUserIdMappingOkResult, UnknownMappingError]:
+    return await Supertokens.get_instance().get_user_id_mapping(
+        user_id, user_id_type, user_context
+    )
+
+
+
+async def get_users_newest_first(tenant_id: str, limit: Optional[int] = None, pagination_token: Optional[str] = None, include_recipe_ids: Optional[None] = None, query: Optional[None] = None, user_context: Optional[Dict[str, Any]] = None) ‑> UsersResponse +
+
+
+
+ +Expand source code + +
async def get_users_newest_first(
+    tenant_id: str,
+    limit: Union[int, None] = None,
+    pagination_token: Union[str, None] = None,
+    include_recipe_ids: Union[None, List[str]] = None,
+    query: Union[None, Dict[str, str]] = None,
+    user_context: Optional[Dict[str, Any]] = None,
+) -> UsersResponse:
+    return await Supertokens.get_instance().get_users(
+        tenant_id,
+        "DESC",
+        limit,
+        pagination_token,
+        include_recipe_ids,
+        query,
+        user_context,
+    )
+
+
+
+async def get_users_oldest_first(tenant_id: str, limit: Optional[int] = None, pagination_token: Optional[str] = None, include_recipe_ids: Optional[None] = None, query: Optional[None] = None, user_context: Optional[Dict[str, Any]] = None) ‑> UsersResponse +
+
+
+
+ +Expand source code + +
async def get_users_oldest_first(
+    tenant_id: str,
+    limit: Union[int, None] = None,
+    pagination_token: Union[str, None] = None,
+    include_recipe_ids: Union[None, List[str]] = None,
+    query: Union[None, Dict[str, str]] = None,
+    user_context: Optional[Dict[str, Any]] = None,
+) -> UsersResponse:
+    return await Supertokens.get_instance().get_users(
+        tenant_id,
+        "ASC",
+        limit,
+        pagination_token,
+        include_recipe_ids,
+        query,
+        user_context,
+    )
+
+
+
+async def update_or_delete_user_id_mapping_info(user_id: str, user_id_type: Optional[typing_extensions.Literal['SUPERTOKENS', 'EXTERNAL', 'ANY']] = None, external_user_id_info: Optional[str] = None, user_context: Optional[Dict[str, Any]] = None) ‑> Union[UpdateOrDeleteUserIdMappingInfoOkResultUnknownMappingError] +
+
+
+
+ +Expand source code + +
async def update_or_delete_user_id_mapping_info(
+    user_id: str,
+    user_id_type: Optional[UserIDTypes] = None,
+    external_user_id_info: Optional[str] = None,
+    user_context: Optional[Dict[str, Any]] = None,
+) -> Union[UpdateOrDeleteUserIdMappingInfoOkResult, UnknownMappingError]:
+    return await Supertokens.get_instance().update_or_delete_user_id_mapping_info(
+        user_id, user_id_type, external_user_id_info, user_context
+    )
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/constants.html b/html/supertokens_python/constants.html new file mode 100644 index 000000000..19bafcb1c --- /dev/null +++ b/html/supertokens_python/constants.html @@ -0,0 +1,90 @@ + + + + + + +supertokens_python.constants API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.constants

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+SUPPORTED_CDI_VERSIONS = ["3.0"]
+VERSION = "0.24.4"
+TELEMETRY = "/telemetry"
+USER_COUNT = "/users/count"
+USER_DELETE = "/user/remove"
+USERS = "/users"
+TELEMETRY_SUPERTOKENS_API_URL = "https://api.supertokens.com/0/st/telemetry"
+TELEMETRY_SUPERTOKENS_API_VERSION = "3"
+ERROR_MESSAGE_KEY = "message"
+API_KEY_HEADER = "api-key"
+RID_KEY_HEADER = "rid"
+FDI_KEY_HEADER = "fdi-version"
+API_VERSION = "/apiversion"
+API_VERSION_HEADER = "cdi-version"
+DASHBOARD_VERSION = "0.7"
+ONE_YEAR_IN_MS = 31536000000
+RATE_LIMIT_STATUS_CODE = 429
+
+
+
+
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/exceptions.html b/html/supertokens_python/exceptions.html new file mode 100644 index 000000000..aa8b22e20 --- /dev/null +++ b/html/supertokens_python/exceptions.html @@ -0,0 +1,233 @@ + + + + + + +supertokens_python.exceptions API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.exceptions

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from typing import Union, NoReturn
+
+
+def raise_general_exception(
+    msg: Union[str, Exception], previous: Union[None, Exception] = None
+) -> NoReturn:
+    if isinstance(msg, SuperTokensError):
+        raise msg
+    if isinstance(msg, Exception):
+        raise GeneralError(msg) from None
+    raise GeneralError(msg) from previous
+
+
+def raise_bad_input_exception(msg: str) -> NoReturn:
+    raise BadInputError(msg)
+
+
+class SuperTokensError(Exception):
+    pass
+
+
+class GeneralError(SuperTokensError):
+    pass
+
+
+class BadInputError(SuperTokensError):
+    pass
+
+
+
+
+
+
+
+

Functions

+
+
+def raise_bad_input_exception(msg: str) ‑> NoReturn +
+
+
+
+ +Expand source code + +
def raise_bad_input_exception(msg: str) -> NoReturn:
+    raise BadInputError(msg)
+
+
+
+def raise_general_exception(msg: Union[str, Exception], previous: Union[None, Exception] = None) ‑> NoReturn +
+
+
+
+ +Expand source code + +
def raise_general_exception(
+    msg: Union[str, Exception], previous: Union[None, Exception] = None
+) -> NoReturn:
+    if isinstance(msg, SuperTokensError):
+        raise msg
+    if isinstance(msg, Exception):
+        raise GeneralError(msg) from None
+    raise GeneralError(msg) from previous
+
+
+
+
+
+

Classes

+
+
+class BadInputError +(*args, **kwargs) +
+
+

Common base class for all non-exit exceptions.

+
+ +Expand source code + +
class BadInputError(SuperTokensError):
+    pass
+
+

Ancestors

+ +
+
+class GeneralError +(*args, **kwargs) +
+
+

Common base class for all non-exit exceptions.

+
+ +Expand source code + +
class GeneralError(SuperTokensError):
+    pass
+
+

Ancestors

+ +
+
+class SuperTokensError +(*args, **kwargs) +
+
+

Common base class for all non-exit exceptions.

+
+ +Expand source code + +
class SuperTokensError(Exception):
+    pass
+
+

Ancestors

+
    +
  • builtins.Exception
  • +
  • builtins.BaseException
  • +
+

Subclasses

+ +
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/framework/django/django_middleware.html b/html/supertokens_python/framework/django/django_middleware.html new file mode 100644 index 000000000..f890290a4 --- /dev/null +++ b/html/supertokens_python/framework/django/django_middleware.html @@ -0,0 +1,258 @@ + + + + + + +supertokens_python.framework.django.django_middleware API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.framework.django.django_middleware

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+import asyncio
+from typing import Any, Union
+
+from asgiref.sync import async_to_sync
+
+
+def middleware(get_response: Any):
+    from supertokens_python import Supertokens
+    from supertokens_python.exceptions import SuperTokensError
+    from supertokens_python.framework.django.django_request import DjangoRequest
+    from supertokens_python.framework.django.django_response import DjangoResponse
+    from supertokens_python.recipe.session import SessionContainer
+    from supertokens_python.supertokens import manage_session_post_response
+
+    from django.http import HttpRequest
+    from supertokens_python.utils import default_user_context
+
+    if asyncio.iscoroutinefunction(get_response):
+
+        async def __asyncMiddleware(request: HttpRequest):
+            st = Supertokens.get_instance()
+            custom_request = DjangoRequest(request)
+            from django.http import HttpResponse
+
+            response = DjangoResponse(HttpResponse())
+            user_context = default_user_context(custom_request)
+
+            try:
+                result = await st.middleware(custom_request, response, user_context)
+                if result is None:
+                    result = await get_response(request)
+                    result = DjangoResponse(result)
+                if hasattr(request, "supertokens") and isinstance(
+                    request.supertokens, SessionContainer  # type: ignore
+                ):
+                    manage_session_post_response(
+                        request.supertokens, result, user_context  # type: ignore
+                    )
+                if isinstance(result, DjangoResponse):
+                    return result.response
+            except SuperTokensError as e:
+                response = DjangoResponse(HttpResponse())
+                result = await st.handle_supertokens_error(
+                    DjangoRequest(request), e, response, user_context
+                )
+                if isinstance(result, DjangoResponse):
+                    return result.response
+
+            raise Exception("Should never come here")
+
+        return __asyncMiddleware
+
+    def __syncMiddleware(request: HttpRequest):
+        st = Supertokens.get_instance()
+        custom_request = DjangoRequest(request)
+        from django.http import HttpResponse
+
+        response = DjangoResponse(HttpResponse())
+        user_context = default_user_context(custom_request)
+
+        try:
+            result: Union[DjangoResponse, None] = async_to_sync(st.middleware)(
+                custom_request, response, user_context
+            )
+
+            if result is None:
+                result = DjangoResponse(get_response(request))
+
+            if hasattr(request, "supertokens") and isinstance(
+                request.supertokens, SessionContainer  # type: ignore
+            ):
+                manage_session_post_response(
+                    request.supertokens, result, user_context  # type: ignore
+                )
+            return result.response
+
+        except SuperTokensError as e:
+            response = DjangoResponse(HttpResponse())
+            result: Union[DjangoResponse, None] = async_to_sync(
+                st.handle_supertokens_error
+            )(DjangoRequest(request), e, response, user_context)
+            if result is not None:
+                return result.response
+        raise Exception("Should never come here")
+
+    return __syncMiddleware
+
+
+
+
+
+
+
+

Functions

+
+
+def middleware(get_response: Any) +
+
+
+
+ +Expand source code + +
def middleware(get_response: Any):
+    from supertokens_python import Supertokens
+    from supertokens_python.exceptions import SuperTokensError
+    from supertokens_python.framework.django.django_request import DjangoRequest
+    from supertokens_python.framework.django.django_response import DjangoResponse
+    from supertokens_python.recipe.session import SessionContainer
+    from supertokens_python.supertokens import manage_session_post_response
+
+    from django.http import HttpRequest
+    from supertokens_python.utils import default_user_context
+
+    if asyncio.iscoroutinefunction(get_response):
+
+        async def __asyncMiddleware(request: HttpRequest):
+            st = Supertokens.get_instance()
+            custom_request = DjangoRequest(request)
+            from django.http import HttpResponse
+
+            response = DjangoResponse(HttpResponse())
+            user_context = default_user_context(custom_request)
+
+            try:
+                result = await st.middleware(custom_request, response, user_context)
+                if result is None:
+                    result = await get_response(request)
+                    result = DjangoResponse(result)
+                if hasattr(request, "supertokens") and isinstance(
+                    request.supertokens, SessionContainer  # type: ignore
+                ):
+                    manage_session_post_response(
+                        request.supertokens, result, user_context  # type: ignore
+                    )
+                if isinstance(result, DjangoResponse):
+                    return result.response
+            except SuperTokensError as e:
+                response = DjangoResponse(HttpResponse())
+                result = await st.handle_supertokens_error(
+                    DjangoRequest(request), e, response, user_context
+                )
+                if isinstance(result, DjangoResponse):
+                    return result.response
+
+            raise Exception("Should never come here")
+
+        return __asyncMiddleware
+
+    def __syncMiddleware(request: HttpRequest):
+        st = Supertokens.get_instance()
+        custom_request = DjangoRequest(request)
+        from django.http import HttpResponse
+
+        response = DjangoResponse(HttpResponse())
+        user_context = default_user_context(custom_request)
+
+        try:
+            result: Union[DjangoResponse, None] = async_to_sync(st.middleware)(
+                custom_request, response, user_context
+            )
+
+            if result is None:
+                result = DjangoResponse(get_response(request))
+
+            if hasattr(request, "supertokens") and isinstance(
+                request.supertokens, SessionContainer  # type: ignore
+            ):
+                manage_session_post_response(
+                    request.supertokens, result, user_context  # type: ignore
+                )
+            return result.response
+
+        except SuperTokensError as e:
+            response = DjangoResponse(HttpResponse())
+            result: Union[DjangoResponse, None] = async_to_sync(
+                st.handle_supertokens_error
+            )(DjangoRequest(request), e, response, user_context)
+            if result is not None:
+                return result.response
+        raise Exception("Should never come here")
+
+    return __syncMiddleware
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/framework/django/django_request.html b/html/supertokens_python/framework/django/django_request.html new file mode 100644 index 000000000..e224e053e --- /dev/null +++ b/html/supertokens_python/framework/django/django_request.html @@ -0,0 +1,429 @@ + + + + + + +supertokens_python.framework.django.django_request API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.framework.django.django_request

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+import json
+from typing import TYPE_CHECKING, Any, Dict, Union
+from urllib.parse import parse_qsl
+
+from supertokens_python.framework.request import BaseRequest
+
+if TYPE_CHECKING:
+    from supertokens_python.recipe.session.interfaces import SessionContainer
+    from django.http import HttpRequest
+
+
+class DjangoRequest(BaseRequest):
+    def __init__(self, request: HttpRequest):
+        super().__init__()
+        self.request = request
+
+    def get_original_url(self) -> str:
+        return self.request.build_absolute_uri()
+
+    def get_query_param(
+        self, key: str, default: Union[str, None] = None
+    ) -> Union[str, None]:
+        return self.request.GET.get(key, default)
+
+    def get_query_params(self) -> Dict[str, Any]:
+        return self.request.GET.dict()
+
+    async def json(self) -> Union[Any, None]:
+        try:
+            body = json.loads(self.request.body)
+            return body
+        except Exception:
+            return {}
+
+    def method(self) -> str:
+        if self.request.method is None:
+            raise Exception("Should never come here")
+        return self.request.method
+
+    def get_cookie(self, key: str) -> Union[str, None]:
+        return self.request.COOKIES.get(key)
+
+    def get_header(self, key: str) -> Union[None, str]:
+        key = key.replace("-", "_")
+        key = "HTTP_" + key
+        return self.request.META.get(key.upper())
+
+    def get_session(self) -> Union[SessionContainer, None]:
+        return self.request.supertokens  # type: ignore
+
+    def set_session(self, session: SessionContainer):
+        self.request.supertokens = session  # type: ignore
+        if hasattr(self.request, "_request"):
+            # this is there because in Django reset framework, the request object
+            # in the API is the one from reset framework, however, the
+            # one from the middleware is the original one. So we need to set
+            # this so that the middleware also gets the token updates which can then be
+            # used to update the response.
+            # pylint: disable=protected-access
+            self.request._request.supertokens = session  # type: ignore
+
+    def set_session_as_none(self):
+        self.request.supertokens = None  # type: ignore
+        if hasattr(self.request, "_request"):
+            # this is there because in Django reset framework, the request object
+            # in the API is the one from reset framework, however, the
+            # one from the middleware is the original one. So we need to set
+            # this so that the middleware also gets the token updates which can then be
+            # used to update the response.
+            # pylint: disable=protected-access
+            self.request._request.supertokens = None  # type: ignore
+
+    def get_path(self) -> str:
+        return self.request.path
+
+    async def form_data(self):
+        return dict(parse_qsl(self.request.body.decode("utf-8")))
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class DjangoRequest +(request: HttpRequest) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class DjangoRequest(BaseRequest):
+    def __init__(self, request: HttpRequest):
+        super().__init__()
+        self.request = request
+
+    def get_original_url(self) -> str:
+        return self.request.build_absolute_uri()
+
+    def get_query_param(
+        self, key: str, default: Union[str, None] = None
+    ) -> Union[str, None]:
+        return self.request.GET.get(key, default)
+
+    def get_query_params(self) -> Dict[str, Any]:
+        return self.request.GET.dict()
+
+    async def json(self) -> Union[Any, None]:
+        try:
+            body = json.loads(self.request.body)
+            return body
+        except Exception:
+            return {}
+
+    def method(self) -> str:
+        if self.request.method is None:
+            raise Exception("Should never come here")
+        return self.request.method
+
+    def get_cookie(self, key: str) -> Union[str, None]:
+        return self.request.COOKIES.get(key)
+
+    def get_header(self, key: str) -> Union[None, str]:
+        key = key.replace("-", "_")
+        key = "HTTP_" + key
+        return self.request.META.get(key.upper())
+
+    def get_session(self) -> Union[SessionContainer, None]:
+        return self.request.supertokens  # type: ignore
+
+    def set_session(self, session: SessionContainer):
+        self.request.supertokens = session  # type: ignore
+        if hasattr(self.request, "_request"):
+            # this is there because in Django reset framework, the request object
+            # in the API is the one from reset framework, however, the
+            # one from the middleware is the original one. So we need to set
+            # this so that the middleware also gets the token updates which can then be
+            # used to update the response.
+            # pylint: disable=protected-access
+            self.request._request.supertokens = session  # type: ignore
+
+    def set_session_as_none(self):
+        self.request.supertokens = None  # type: ignore
+        if hasattr(self.request, "_request"):
+            # this is there because in Django reset framework, the request object
+            # in the API is the one from reset framework, however, the
+            # one from the middleware is the original one. So we need to set
+            # this so that the middleware also gets the token updates which can then be
+            # used to update the response.
+            # pylint: disable=protected-access
+            self.request._request.supertokens = None  # type: ignore
+
+    def get_path(self) -> str:
+        return self.request.path
+
+    async def form_data(self):
+        return dict(parse_qsl(self.request.body.decode("utf-8")))
+
+

Ancestors

+ +

Methods

+
+
+async def form_data(self) +
+
+
+
+ +Expand source code + +
async def form_data(self):
+    return dict(parse_qsl(self.request.body.decode("utf-8")))
+
+
+ +
+
+
+ +Expand source code + +
def get_cookie(self, key: str) -> Union[str, None]:
+    return self.request.COOKIES.get(key)
+
+
+
+def get_header(self, key: str) ‑> Optional[str] +
+
+
+
+ +Expand source code + +
def get_header(self, key: str) -> Union[None, str]:
+    key = key.replace("-", "_")
+    key = "HTTP_" + key
+    return self.request.META.get(key.upper())
+
+
+
+def get_original_url(self) ‑> str +
+
+
+
+ +Expand source code + +
def get_original_url(self) -> str:
+    return self.request.build_absolute_uri()
+
+
+
+def get_path(self) ‑> str +
+
+
+
+ +Expand source code + +
def get_path(self) -> str:
+    return self.request.path
+
+
+
+def get_query_param(self, key: str, default: Union[str, None] = None) ‑> Optional[str] +
+
+
+
+ +Expand source code + +
def get_query_param(
+    self, key: str, default: Union[str, None] = None
+) -> Union[str, None]:
+    return self.request.GET.get(key, default)
+
+
+
+def get_query_params(self) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
def get_query_params(self) -> Dict[str, Any]:
+    return self.request.GET.dict()
+
+
+
+def get_session(self) ‑> Union[SessionContainer, None] +
+
+
+
+ +Expand source code + +
def get_session(self) -> Union[SessionContainer, None]:
+    return self.request.supertokens  # type: ignore
+
+
+
+async def json(self) ‑> Optional[Any] +
+
+
+
+ +Expand source code + +
async def json(self) -> Union[Any, None]:
+    try:
+        body = json.loads(self.request.body)
+        return body
+    except Exception:
+        return {}
+
+
+
+def method(self) ‑> str +
+
+
+
+ +Expand source code + +
def method(self) -> str:
+    if self.request.method is None:
+        raise Exception("Should never come here")
+    return self.request.method
+
+
+
+def set_session(self, session: SessionContainer) +
+
+
+
+ +Expand source code + +
def set_session(self, session: SessionContainer):
+    self.request.supertokens = session  # type: ignore
+    if hasattr(self.request, "_request"):
+        # this is there because in Django reset framework, the request object
+        # in the API is the one from reset framework, however, the
+        # one from the middleware is the original one. So we need to set
+        # this so that the middleware also gets the token updates which can then be
+        # used to update the response.
+        # pylint: disable=protected-access
+        self.request._request.supertokens = session  # type: ignore
+
+
+
+

Inherited members

+ +
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/framework/django/django_response.html b/html/supertokens_python/framework/django/django_response.html new file mode 100644 index 000000000..c8b888d71 --- /dev/null +++ b/html/supertokens_python/framework/django/django_response.html @@ -0,0 +1,395 @@ + + + + + + +supertokens_python.framework.django.django_response API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.framework.django.django_response

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+import json
+from datetime import datetime
+from math import ceil
+from typing import Any, Dict, Optional
+
+from supertokens_python.framework.response import BaseResponse
+
+
+class DjangoResponse(BaseResponse):
+    from django.http import HttpResponse
+
+    def __init__(self, response: HttpResponse):
+        super().__init__({})
+        self.response = response
+        self.original = response
+        self.parser_checked = False
+        self.response_sent = False
+        self.status_set = False
+
+    def set_html_content(self, content: str):
+        if not self.response_sent:
+            self.response.content = content
+            self.set_header("Content-Type", "text/html")
+            self.response_sent = True
+
+    def set_cookie(
+        self,
+        key: str,
+        value: str,
+        expires: int,
+        path: str = "/",
+        domain: Optional[str] = None,
+        secure: bool = False,
+        httponly: bool = False,
+        samesite: str = "lax",
+    ):
+        self.response.set_cookie(
+            key=key,
+            value=value,
+            expires=datetime.fromtimestamp(ceil(expires / 1000)).strftime(
+                "%a, %d %b %Y %H:%M:%S GMT"
+            ),
+            path=path,
+            domain=domain,
+            secure=secure,
+            httponly=httponly,
+        )
+        self.response.cookies[key]["samesite"] = samesite
+
+    def set_status_code(self, status_code: int):
+        if not self.status_set:
+            self.response.status_code = status_code
+            self.status_code = status_code
+            self.status_set = True
+
+    def set_header(self, key: str, value: str):
+        self.response[key] = value
+
+    def get_header(self, key: str) -> Optional[str]:
+        if self.response.has_header(key):
+            return self.response[key]
+        return None
+
+    def remove_header(self, key: str):
+        del self.response[key]
+
+    def set_json_content(self, content: Dict[str, Any]):
+        if not self.response_sent:
+            self.set_header("Content-Type", "application/json; charset=utf-8")
+            self.response.content = json.dumps(
+                content,
+                ensure_ascii=False,
+                allow_nan=False,
+                indent=None,
+                separators=(",", ":"),
+            ).encode("utf-8")
+            self.response_sent = True
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class DjangoResponse +(response: django.http.response.HttpResponse) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class DjangoResponse(BaseResponse):
+    from django.http import HttpResponse
+
+    def __init__(self, response: HttpResponse):
+        super().__init__({})
+        self.response = response
+        self.original = response
+        self.parser_checked = False
+        self.response_sent = False
+        self.status_set = False
+
+    def set_html_content(self, content: str):
+        if not self.response_sent:
+            self.response.content = content
+            self.set_header("Content-Type", "text/html")
+            self.response_sent = True
+
+    def set_cookie(
+        self,
+        key: str,
+        value: str,
+        expires: int,
+        path: str = "/",
+        domain: Optional[str] = None,
+        secure: bool = False,
+        httponly: bool = False,
+        samesite: str = "lax",
+    ):
+        self.response.set_cookie(
+            key=key,
+            value=value,
+            expires=datetime.fromtimestamp(ceil(expires / 1000)).strftime(
+                "%a, %d %b %Y %H:%M:%S GMT"
+            ),
+            path=path,
+            domain=domain,
+            secure=secure,
+            httponly=httponly,
+        )
+        self.response.cookies[key]["samesite"] = samesite
+
+    def set_status_code(self, status_code: int):
+        if not self.status_set:
+            self.response.status_code = status_code
+            self.status_code = status_code
+            self.status_set = True
+
+    def set_header(self, key: str, value: str):
+        self.response[key] = value
+
+    def get_header(self, key: str) -> Optional[str]:
+        if self.response.has_header(key):
+            return self.response[key]
+        return None
+
+    def remove_header(self, key: str):
+        del self.response[key]
+
+    def set_json_content(self, content: Dict[str, Any]):
+        if not self.response_sent:
+            self.set_header("Content-Type", "application/json; charset=utf-8")
+            self.response.content = json.dumps(
+                content,
+                ensure_ascii=False,
+                allow_nan=False,
+                indent=None,
+                separators=(",", ":"),
+            ).encode("utf-8")
+            self.response_sent = True
+
+

Ancestors

+ +

Class variables

+
+
var HttpResponse
+
+

An HTTP response class with a string as content.

+

This content can be read, appended to, or replaced.

+
+
+

Methods

+
+
+def get_header(self, key: str) ‑> Optional[str] +
+
+
+
+ +Expand source code + +
def get_header(self, key: str) -> Optional[str]:
+    if self.response.has_header(key):
+        return self.response[key]
+    return None
+
+
+
+def remove_header(self, key: str) +
+
+
+
+ +Expand source code + +
def remove_header(self, key: str):
+    del self.response[key]
+
+
+ +
+
+
+ +Expand source code + +
def set_cookie(
+    self,
+    key: str,
+    value: str,
+    expires: int,
+    path: str = "/",
+    domain: Optional[str] = None,
+    secure: bool = False,
+    httponly: bool = False,
+    samesite: str = "lax",
+):
+    self.response.set_cookie(
+        key=key,
+        value=value,
+        expires=datetime.fromtimestamp(ceil(expires / 1000)).strftime(
+            "%a, %d %b %Y %H:%M:%S GMT"
+        ),
+        path=path,
+        domain=domain,
+        secure=secure,
+        httponly=httponly,
+    )
+    self.response.cookies[key]["samesite"] = samesite
+
+
+
+def set_header(self, key: str, value: str) +
+
+
+
+ +Expand source code + +
def set_header(self, key: str, value: str):
+    self.response[key] = value
+
+
+
+def set_html_content(self, content: str) +
+
+
+
+ +Expand source code + +
def set_html_content(self, content: str):
+    if not self.response_sent:
+        self.response.content = content
+        self.set_header("Content-Type", "text/html")
+        self.response_sent = True
+
+
+
+def set_json_content(self, content: Dict[str, Any]) +
+
+
+
+ +Expand source code + +
def set_json_content(self, content: Dict[str, Any]):
+    if not self.response_sent:
+        self.set_header("Content-Type", "application/json; charset=utf-8")
+        self.response.content = json.dumps(
+            content,
+            ensure_ascii=False,
+            allow_nan=False,
+            indent=None,
+            separators=(",", ":"),
+        ).encode("utf-8")
+        self.response_sent = True
+
+
+
+def set_status_code(self, status_code: int) +
+
+
+
+ +Expand source code + +
def set_status_code(self, status_code: int):
+    if not self.status_set:
+        self.response.status_code = status_code
+        self.status_code = status_code
+        self.status_set = True
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/framework/django/framework.html b/html/supertokens_python/framework/django/framework.html new file mode 100644 index 000000000..deedf85d1 --- /dev/null +++ b/html/supertokens_python/framework/django/framework.html @@ -0,0 +1,140 @@ + + + + + + +supertokens_python.framework.django.framework API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.framework.django.framework

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from __future__ import annotations
+
+from typing import TYPE_CHECKING
+
+from supertokens_python.framework.types import Framework
+
+if TYPE_CHECKING:
+    from django.http import HttpRequest
+
+
+class DjangoFramework(Framework):
+    def wrap_request(self, unwrapped: HttpRequest):
+        from supertokens_python.framework.django.django_request import DjangoRequest
+
+        return DjangoRequest(unwrapped)
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class DjangoFramework +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class DjangoFramework(Framework):
+    def wrap_request(self, unwrapped: HttpRequest):
+        from supertokens_python.framework.django.django_request import DjangoRequest
+
+        return DjangoRequest(unwrapped)
+
+

Ancestors

+ +

Methods

+
+
+def wrap_request(self, unwrapped: HttpRequest) +
+
+
+
+ +Expand source code + +
def wrap_request(self, unwrapped: HttpRequest):
+    from supertokens_python.framework.django.django_request import DjangoRequest
+
+    return DjangoRequest(unwrapped)
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/framework/django/index.html b/html/supertokens_python/framework/django/index.html new file mode 100644 index 000000000..9eddf5134 --- /dev/null +++ b/html/supertokens_python/framework/django/index.html @@ -0,0 +1,101 @@ + + + + + + +supertokens_python.framework.django API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.framework.django

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from supertokens_python.framework.django.django_middleware import (
+    middleware,  # type: ignore
+)
+
+
+
+

Sub-modules

+
+
supertokens_python.framework.django.django_middleware
+
+
+
+
supertokens_python.framework.django.django_request
+
+
+
+
supertokens_python.framework.django.django_response
+
+
+
+
supertokens_python.framework.django.framework
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/framework/fastapi/fastapi_middleware.html b/html/supertokens_python/framework/fastapi/fastapi_middleware.html new file mode 100644 index 000000000..a61f9c5c6 --- /dev/null +++ b/html/supertokens_python/framework/fastapi/fastapi_middleware.html @@ -0,0 +1,281 @@ + + + + + + +supertokens_python.framework.fastapi.fastapi_middleware API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.framework.fastapi.fastapi_middleware

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from typing import Union
+
+
+def get_middleware():
+    from supertokens_python import Supertokens
+    from supertokens_python.utils import default_user_context
+    from supertokens_python.exceptions import SuperTokensError
+    from supertokens_python.framework import BaseResponse
+    from supertokens_python.recipe.session import SessionContainer
+    from supertokens_python.supertokens import manage_session_post_response
+
+    from starlette.requests import Request
+    from starlette.responses import Response
+    from starlette.types import ASGIApp, Message, Receive, Scope, Send
+
+    from supertokens_python.framework.fastapi.fastapi_request import (
+        FastApiRequest,
+    )
+    from supertokens_python.framework.fastapi.fastapi_response import (
+        FastApiResponse,
+    )
+
+    class ASGIMiddleware:
+        def __init__(self, app: ASGIApp) -> None:
+            self.app = app
+
+        async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
+            if scope["type"] != "http":  # we pass through the non-http requests, if any
+                await self.app(scope, receive, send)
+                return
+
+            st = Supertokens.get_instance()
+
+            request = Request(scope, receive=receive)
+            custom_request = FastApiRequest(request)
+            user_context = default_user_context(custom_request)
+
+            try:
+                response = FastApiResponse(Response())
+                result: Union[BaseResponse, None] = await st.middleware(
+                    custom_request, response, user_context
+                )
+                if result is None:
+                    # This means that the supertokens middleware did not handle the request,
+                    # however, we may need to handle the header changes in the response,
+                    # based on response mutators used by the session.
+                    async def send_wrapper(message: Message):
+                        if message["type"] == "http.response.start":
+                            # Start message has the headers, so we update the headers here
+                            # by using `manage_session_post_response` function, which will
+                            # apply all the Response Mutators. In the end, we just replace
+                            # the updated headers in the message.
+                            if hasattr(request.state, "supertokens") and isinstance(
+                                request.state.supertokens, SessionContainer
+                            ):
+                                fapi_response = Response()
+                                fapi_response.raw_headers = message["headers"]
+                                response = FastApiResponse(fapi_response)
+                                manage_session_post_response(
+                                    request.state.supertokens, response, user_context
+                                )
+                                message["headers"] = fapi_response.raw_headers
+
+                        # For `http.response.start` message, we might have the headers updated,
+                        # otherwise, we just send all the messages as is
+                        await send(message)
+
+                    await self.app(scope, receive, send_wrapper)
+                    return
+
+                # This means that the request was handled by the supertokens middleware
+                # and hence we respond using the response object returned by the middleware.
+                if hasattr(request.state, "supertokens") and isinstance(
+                    request.state.supertokens, SessionContainer
+                ):
+                    manage_session_post_response(
+                        request.state.supertokens, result, user_context
+                    )
+
+                if isinstance(result, FastApiResponse):
+                    await result.response(scope, receive, send)
+                    return
+
+                return
+
+            except SuperTokensError as e:
+                response = FastApiResponse(Response())
+                result: Union[BaseResponse, None] = await st.handle_supertokens_error(
+                    FastApiRequest(request), e, response, user_context
+                )
+                if isinstance(result, FastApiResponse):
+                    await result.response(scope, receive, send)
+                    return
+
+            raise Exception("Should never come here")
+
+    return ASGIMiddleware
+
+
+
+
+
+
+
+

Functions

+
+
+def get_middleware() +
+
+
+
+ +Expand source code + +
def get_middleware():
+    from supertokens_python import Supertokens
+    from supertokens_python.utils import default_user_context
+    from supertokens_python.exceptions import SuperTokensError
+    from supertokens_python.framework import BaseResponse
+    from supertokens_python.recipe.session import SessionContainer
+    from supertokens_python.supertokens import manage_session_post_response
+
+    from starlette.requests import Request
+    from starlette.responses import Response
+    from starlette.types import ASGIApp, Message, Receive, Scope, Send
+
+    from supertokens_python.framework.fastapi.fastapi_request import (
+        FastApiRequest,
+    )
+    from supertokens_python.framework.fastapi.fastapi_response import (
+        FastApiResponse,
+    )
+
+    class ASGIMiddleware:
+        def __init__(self, app: ASGIApp) -> None:
+            self.app = app
+
+        async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
+            if scope["type"] != "http":  # we pass through the non-http requests, if any
+                await self.app(scope, receive, send)
+                return
+
+            st = Supertokens.get_instance()
+
+            request = Request(scope, receive=receive)
+            custom_request = FastApiRequest(request)
+            user_context = default_user_context(custom_request)
+
+            try:
+                response = FastApiResponse(Response())
+                result: Union[BaseResponse, None] = await st.middleware(
+                    custom_request, response, user_context
+                )
+                if result is None:
+                    # This means that the supertokens middleware did not handle the request,
+                    # however, we may need to handle the header changes in the response,
+                    # based on response mutators used by the session.
+                    async def send_wrapper(message: Message):
+                        if message["type"] == "http.response.start":
+                            # Start message has the headers, so we update the headers here
+                            # by using `manage_session_post_response` function, which will
+                            # apply all the Response Mutators. In the end, we just replace
+                            # the updated headers in the message.
+                            if hasattr(request.state, "supertokens") and isinstance(
+                                request.state.supertokens, SessionContainer
+                            ):
+                                fapi_response = Response()
+                                fapi_response.raw_headers = message["headers"]
+                                response = FastApiResponse(fapi_response)
+                                manage_session_post_response(
+                                    request.state.supertokens, response, user_context
+                                )
+                                message["headers"] = fapi_response.raw_headers
+
+                        # For `http.response.start` message, we might have the headers updated,
+                        # otherwise, we just send all the messages as is
+                        await send(message)
+
+                    await self.app(scope, receive, send_wrapper)
+                    return
+
+                # This means that the request was handled by the supertokens middleware
+                # and hence we respond using the response object returned by the middleware.
+                if hasattr(request.state, "supertokens") and isinstance(
+                    request.state.supertokens, SessionContainer
+                ):
+                    manage_session_post_response(
+                        request.state.supertokens, result, user_context
+                    )
+
+                if isinstance(result, FastApiResponse):
+                    await result.response(scope, receive, send)
+                    return
+
+                return
+
+            except SuperTokensError as e:
+                response = FastApiResponse(Response())
+                result: Union[BaseResponse, None] = await st.handle_supertokens_error(
+                    FastApiRequest(request), e, response, user_context
+                )
+                if isinstance(result, FastApiResponse):
+                    await result.response(scope, receive, send)
+                    return
+
+            raise Exception("Should never come here")
+
+    return ASGIMiddleware
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/framework/fastapi/fastapi_request.html b/html/supertokens_python/framework/fastapi/fastapi_request.html new file mode 100644 index 000000000..efa01ad1a --- /dev/null +++ b/html/supertokens_python/framework/fastapi/fastapi_request.html @@ -0,0 +1,391 @@ + + + + + + +supertokens_python.framework.fastapi.fastapi_request API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.framework.fastapi.fastapi_request

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Any, Dict, Union
+from urllib.parse import parse_qsl
+
+from supertokens_python.framework.request import BaseRequest
+
+if TYPE_CHECKING:
+    from supertokens_python.recipe.session.interfaces import SessionContainer
+    from fastapi import Request
+
+
+class FastApiRequest(BaseRequest):
+    def __init__(self, request: Request):
+        super().__init__()
+        self.request = request
+
+    def get_original_url(self) -> str:
+        return self.request.url.components.geturl()
+
+    def get_query_param(
+        self, key: str, default: Union[str, None] = None
+    ) -> Union[str, None]:
+        return self.request.query_params.get(key, default)
+
+    def get_query_params(self) -> Dict[str, Any]:
+        return dict(self.request.query_params.items())  # type: ignore
+
+    async def json(self) -> Union[Any, None]:
+        try:
+            return await self.request.json()
+        except Exception:
+            return {}
+
+    def method(self) -> str:
+        return self.request.method
+
+    def get_cookie(self, key: str) -> Union[str, None]:
+        # Note: Unlike other frameworks, FastAPI wraps the value in quotes in Set-Cookie header
+        # It also takes care of escaping the quotes while fetching the value
+        return self.request.cookies.get(key)
+
+    def get_header(self, key: str) -> Union[str, None]:
+        return self.request.headers.get(key, None)
+
+    def get_session(self) -> Union[SessionContainer, None]:
+        return self.request.state.supertokens
+
+    def set_session(self, session: SessionContainer):
+        self.request.state.supertokens = session
+
+    def set_session_as_none(self):
+        self.request.state.supertokens = None
+
+    def get_path(self) -> str:
+        root_path = self.request.scope.get("root_path")
+        url = self.request.url.path
+        # FastAPI seems buggy and it adds an extra root_path (if it matches):
+        # So we trim the extra root_path (from the left) from the url
+        return url[url.startswith(root_path) and len(root_path) :]
+
+    async def form_data(self):
+        return dict(parse_qsl((await self.request.body()).decode("utf-8")))
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class FastApiRequest +(request: Request) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class FastApiRequest(BaseRequest):
+    def __init__(self, request: Request):
+        super().__init__()
+        self.request = request
+
+    def get_original_url(self) -> str:
+        return self.request.url.components.geturl()
+
+    def get_query_param(
+        self, key: str, default: Union[str, None] = None
+    ) -> Union[str, None]:
+        return self.request.query_params.get(key, default)
+
+    def get_query_params(self) -> Dict[str, Any]:
+        return dict(self.request.query_params.items())  # type: ignore
+
+    async def json(self) -> Union[Any, None]:
+        try:
+            return await self.request.json()
+        except Exception:
+            return {}
+
+    def method(self) -> str:
+        return self.request.method
+
+    def get_cookie(self, key: str) -> Union[str, None]:
+        # Note: Unlike other frameworks, FastAPI wraps the value in quotes in Set-Cookie header
+        # It also takes care of escaping the quotes while fetching the value
+        return self.request.cookies.get(key)
+
+    def get_header(self, key: str) -> Union[str, None]:
+        return self.request.headers.get(key, None)
+
+    def get_session(self) -> Union[SessionContainer, None]:
+        return self.request.state.supertokens
+
+    def set_session(self, session: SessionContainer):
+        self.request.state.supertokens = session
+
+    def set_session_as_none(self):
+        self.request.state.supertokens = None
+
+    def get_path(self) -> str:
+        root_path = self.request.scope.get("root_path")
+        url = self.request.url.path
+        # FastAPI seems buggy and it adds an extra root_path (if it matches):
+        # So we trim the extra root_path (from the left) from the url
+        return url[url.startswith(root_path) and len(root_path) :]
+
+    async def form_data(self):
+        return dict(parse_qsl((await self.request.body()).decode("utf-8")))
+
+

Ancestors

+ +

Methods

+
+
+async def form_data(self) +
+
+
+
+ +Expand source code + +
async def form_data(self):
+    return dict(parse_qsl((await self.request.body()).decode("utf-8")))
+
+
+ +
+
+
+ +Expand source code + +
def get_cookie(self, key: str) -> Union[str, None]:
+    # Note: Unlike other frameworks, FastAPI wraps the value in quotes in Set-Cookie header
+    # It also takes care of escaping the quotes while fetching the value
+    return self.request.cookies.get(key)
+
+
+
+def get_header(self, key: str) ‑> Optional[str] +
+
+
+
+ +Expand source code + +
def get_header(self, key: str) -> Union[str, None]:
+    return self.request.headers.get(key, None)
+
+
+
+def get_original_url(self) ‑> str +
+
+
+
+ +Expand source code + +
def get_original_url(self) -> str:
+    return self.request.url.components.geturl()
+
+
+
+def get_path(self) ‑> str +
+
+
+
+ +Expand source code + +
def get_path(self) -> str:
+    root_path = self.request.scope.get("root_path")
+    url = self.request.url.path
+    # FastAPI seems buggy and it adds an extra root_path (if it matches):
+    # So we trim the extra root_path (from the left) from the url
+    return url[url.startswith(root_path) and len(root_path) :]
+
+
+
+def get_query_param(self, key: str, default: Union[str, None] = None) ‑> Optional[str] +
+
+
+
+ +Expand source code + +
def get_query_param(
+    self, key: str, default: Union[str, None] = None
+) -> Union[str, None]:
+    return self.request.query_params.get(key, default)
+
+
+
+def get_query_params(self) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
def get_query_params(self) -> Dict[str, Any]:
+    return dict(self.request.query_params.items())  # type: ignore
+
+
+
+def get_session(self) ‑> Union[SessionContainer, None] +
+
+
+
+ +Expand source code + +
def get_session(self) -> Union[SessionContainer, None]:
+    return self.request.state.supertokens
+
+
+
+async def json(self) ‑> Optional[Any] +
+
+
+
+ +Expand source code + +
async def json(self) -> Union[Any, None]:
+    try:
+        return await self.request.json()
+    except Exception:
+        return {}
+
+
+
+def method(self) ‑> str +
+
+
+
+ +Expand source code + +
def method(self) -> str:
+    return self.request.method
+
+
+
+def set_session(self, session: SessionContainer) +
+
+
+
+ +Expand source code + +
def set_session(self, session: SessionContainer):
+    self.request.state.supertokens = session
+
+
+
+

Inherited members

+ +
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/framework/fastapi/fastapi_response.html b/html/supertokens_python/framework/fastapi/fastapi_response.html new file mode 100644 index 000000000..4a581ceb3 --- /dev/null +++ b/html/supertokens_python/framework/fastapi/fastapi_response.html @@ -0,0 +1,412 @@ + + + + + + +supertokens_python.framework.fastapi.fastapi_response API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.framework.fastapi.fastapi_response

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+import json
+from math import ceil
+from typing import Any, Dict, Optional
+
+from supertokens_python.framework.response import BaseResponse
+from supertokens_python.utils import get_timestamp_ms
+
+
+class FastApiResponse(BaseResponse):
+    from fastapi import Response
+
+    def __init__(self, response: Response):
+        super().__init__({})
+        self.response = response
+        self.original = response
+        self.parser_checked = False
+        self.response_sent = False
+        self.status_set = False
+
+    def set_html_content(self, content: str):
+        if not self.response_sent:
+            body = bytes(content, "utf-8")
+            self.set_header("Content-Length", str(len(body)))
+            self.set_header("Content-Type", "text/html")
+            self.response.body = body
+            self.response_sent = True
+
+    def set_cookie(
+        self,
+        key: str,
+        value: str,
+        expires: int,
+        path: str = "/",
+        domain: Optional[str] = None,
+        secure: bool = False,
+        httponly: bool = False,
+        samesite: str = "lax",
+    ):
+        # Note: For FastAPI response object, the expires value
+        # doesn't mean the absolute time in ms, but the duration in seconds
+        # So we need to convert our absolute expiry time (ms) to a duration (seconds)
+
+        # we do ceil because if we do floor, we tests may fail where the access
+        # token lifetime is set to 1 second
+        self.response.set_cookie(
+            key=key,
+            value=value,  # Note: Unlike other frameworks, FastAPI wraps the value in quotes in Set-Cookie header
+            expires=ceil((expires - get_timestamp_ms()) / 1000),
+            path=path,
+            domain=domain,  # type: ignore # starlette didn't set domain as optional type but their default value is None anyways
+            secure=secure,
+            httponly=httponly,
+            samesite=samesite,
+        )
+
+    def set_header(self, key: str, value: str):
+        self.response.headers[key] = value
+
+    def get_header(self, key: str) -> Optional[str]:
+        return self.response.headers.get(key, None)
+
+    def remove_header(self, key: str):
+        del self.response.headers[key]
+
+    def set_status_code(self, status_code: int):
+        if not self.status_set:
+            self.response.status_code = status_code
+            self.status_code = status_code
+            self.status_set = True
+
+    def set_json_content(self, content: Dict[str, Any]):
+        if not self.response_sent:
+            body = json.dumps(
+                content,
+                ensure_ascii=False,
+                allow_nan=False,
+                indent=None,
+                separators=(",", ":"),
+            ).encode("utf-8")
+            self.set_header("Content-Type", "application/json; charset=utf-8")
+            self.set_header("Content-Length", str(len(body)))
+            self.response.body = body
+            self.response_sent = True
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class FastApiResponse +(response: starlette.responses.Response) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class FastApiResponse(BaseResponse):
+    from fastapi import Response
+
+    def __init__(self, response: Response):
+        super().__init__({})
+        self.response = response
+        self.original = response
+        self.parser_checked = False
+        self.response_sent = False
+        self.status_set = False
+
+    def set_html_content(self, content: str):
+        if not self.response_sent:
+            body = bytes(content, "utf-8")
+            self.set_header("Content-Length", str(len(body)))
+            self.set_header("Content-Type", "text/html")
+            self.response.body = body
+            self.response_sent = True
+
+    def set_cookie(
+        self,
+        key: str,
+        value: str,
+        expires: int,
+        path: str = "/",
+        domain: Optional[str] = None,
+        secure: bool = False,
+        httponly: bool = False,
+        samesite: str = "lax",
+    ):
+        # Note: For FastAPI response object, the expires value
+        # doesn't mean the absolute time in ms, but the duration in seconds
+        # So we need to convert our absolute expiry time (ms) to a duration (seconds)
+
+        # we do ceil because if we do floor, we tests may fail where the access
+        # token lifetime is set to 1 second
+        self.response.set_cookie(
+            key=key,
+            value=value,  # Note: Unlike other frameworks, FastAPI wraps the value in quotes in Set-Cookie header
+            expires=ceil((expires - get_timestamp_ms()) / 1000),
+            path=path,
+            domain=domain,  # type: ignore # starlette didn't set domain as optional type but their default value is None anyways
+            secure=secure,
+            httponly=httponly,
+            samesite=samesite,
+        )
+
+    def set_header(self, key: str, value: str):
+        self.response.headers[key] = value
+
+    def get_header(self, key: str) -> Optional[str]:
+        return self.response.headers.get(key, None)
+
+    def remove_header(self, key: str):
+        del self.response.headers[key]
+
+    def set_status_code(self, status_code: int):
+        if not self.status_set:
+            self.response.status_code = status_code
+            self.status_code = status_code
+            self.status_set = True
+
+    def set_json_content(self, content: Dict[str, Any]):
+        if not self.response_sent:
+            body = json.dumps(
+                content,
+                ensure_ascii=False,
+                allow_nan=False,
+                indent=None,
+                separators=(",", ":"),
+            ).encode("utf-8")
+            self.set_header("Content-Type", "application/json; charset=utf-8")
+            self.set_header("Content-Length", str(len(body)))
+            self.response.body = body
+            self.response_sent = True
+
+

Ancestors

+ +

Class variables

+
+
var Response
+
+
+
+
+

Methods

+
+
+def get_header(self, key: str) ‑> Optional[str] +
+
+
+
+ +Expand source code + +
def get_header(self, key: str) -> Optional[str]:
+    return self.response.headers.get(key, None)
+
+
+
+def remove_header(self, key: str) +
+
+
+
+ +Expand source code + +
def remove_header(self, key: str):
+    del self.response.headers[key]
+
+
+ +
+
+
+ +Expand source code + +
def set_cookie(
+    self,
+    key: str,
+    value: str,
+    expires: int,
+    path: str = "/",
+    domain: Optional[str] = None,
+    secure: bool = False,
+    httponly: bool = False,
+    samesite: str = "lax",
+):
+    # Note: For FastAPI response object, the expires value
+    # doesn't mean the absolute time in ms, but the duration in seconds
+    # So we need to convert our absolute expiry time (ms) to a duration (seconds)
+
+    # we do ceil because if we do floor, we tests may fail where the access
+    # token lifetime is set to 1 second
+    self.response.set_cookie(
+        key=key,
+        value=value,  # Note: Unlike other frameworks, FastAPI wraps the value in quotes in Set-Cookie header
+        expires=ceil((expires - get_timestamp_ms()) / 1000),
+        path=path,
+        domain=domain,  # type: ignore # starlette didn't set domain as optional type but their default value is None anyways
+        secure=secure,
+        httponly=httponly,
+        samesite=samesite,
+    )
+
+
+
+def set_header(self, key: str, value: str) +
+
+
+
+ +Expand source code + +
def set_header(self, key: str, value: str):
+    self.response.headers[key] = value
+
+
+
+def set_html_content(self, content: str) +
+
+
+
+ +Expand source code + +
def set_html_content(self, content: str):
+    if not self.response_sent:
+        body = bytes(content, "utf-8")
+        self.set_header("Content-Length", str(len(body)))
+        self.set_header("Content-Type", "text/html")
+        self.response.body = body
+        self.response_sent = True
+
+
+
+def set_json_content(self, content: Dict[str, Any]) +
+
+
+
+ +Expand source code + +
def set_json_content(self, content: Dict[str, Any]):
+    if not self.response_sent:
+        body = json.dumps(
+            content,
+            ensure_ascii=False,
+            allow_nan=False,
+            indent=None,
+            separators=(",", ":"),
+        ).encode("utf-8")
+        self.set_header("Content-Type", "application/json; charset=utf-8")
+        self.set_header("Content-Length", str(len(body)))
+        self.response.body = body
+        self.response_sent = True
+
+
+
+def set_status_code(self, status_code: int) +
+
+
+
+ +Expand source code + +
def set_status_code(self, status_code: int):
+    if not self.status_set:
+        self.response.status_code = status_code
+        self.status_code = status_code
+        self.status_set = True
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/framework/fastapi/framework.html b/html/supertokens_python/framework/fastapi/framework.html new file mode 100644 index 000000000..8de3ca8d0 --- /dev/null +++ b/html/supertokens_python/framework/fastapi/framework.html @@ -0,0 +1,139 @@ + + + + + + +supertokens_python.framework.fastapi.framework API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.framework.fastapi.framework

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from typing import TYPE_CHECKING
+
+from supertokens_python.framework.types import Framework
+
+if TYPE_CHECKING:
+    from fastapi import Request
+
+
+class FastapiFramework(Framework):
+    def wrap_request(self, unwrapped: Request):
+        from supertokens_python.framework.fastapi.fastapi_request import FastApiRequest
+
+        return FastApiRequest(unwrapped)
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class FastapiFramework +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class FastapiFramework(Framework):
+    def wrap_request(self, unwrapped: Request):
+        from supertokens_python.framework.fastapi.fastapi_request import FastApiRequest
+
+        return FastApiRequest(unwrapped)
+
+

Ancestors

+ +

Methods

+
+
+def wrap_request(self, unwrapped: Request) +
+
+
+
+ +Expand source code + +
def wrap_request(self, unwrapped: Request):
+    from supertokens_python.framework.fastapi.fastapi_request import FastApiRequest
+
+    return FastApiRequest(unwrapped)
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/framework/fastapi/index.html b/html/supertokens_python/framework/fastapi/index.html new file mode 100644 index 000000000..e3fd29b30 --- /dev/null +++ b/html/supertokens_python/framework/fastapi/index.html @@ -0,0 +1,101 @@ + + + + + + +supertokens_python.framework.fastapi API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.framework.fastapi

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from supertokens_python.framework.fastapi import fastapi_middleware
+
+get_middleware = fastapi_middleware.get_middleware
+
+
+
+

Sub-modules

+
+
supertokens_python.framework.fastapi.fastapi_middleware
+
+
+
+
supertokens_python.framework.fastapi.fastapi_request
+
+
+
+
supertokens_python.framework.fastapi.fastapi_response
+
+
+
+
supertokens_python.framework.fastapi.framework
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/framework/flask/flask_middleware.html b/html/supertokens_python/framework/flask/flask_middleware.html new file mode 100644 index 000000000..dc520919a --- /dev/null +++ b/html/supertokens_python/framework/flask/flask_middleware.html @@ -0,0 +1,400 @@ + + + + + + +supertokens_python.framework.flask.flask_middleware API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.framework.flask.flask_middleware

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+import json
+from typing import TYPE_CHECKING, Union
+
+from supertokens_python.async_to_sync_wrapper import sync
+from supertokens_python.framework import BaseResponse
+
+if TYPE_CHECKING:
+    from flask import Flask
+
+
+class Middleware:
+    def __init__(self, app: Flask):
+        self.app = app
+        self.set_before_after_request()
+        self.set_error_handler()
+
+    def set_before_after_request(self):
+        app = self.app
+        from supertokens_python.framework.flask.flask_request import FlaskRequest
+        from supertokens_python.framework.flask.flask_response import FlaskResponse
+        from supertokens_python.supertokens import manage_session_post_response
+        from supertokens_python.utils import default_user_context
+
+        from flask.wrappers import Response
+
+        # There is an error in the typing provided by flask, so we ignore it
+        # for now.
+        @app.before_request  # type: ignore
+        def _():
+            from supertokens_python import Supertokens
+
+            from flask import request
+            from flask.wrappers import Response
+
+            st = Supertokens.get_instance()
+
+            request_ = FlaskRequest(request)
+            response_ = FlaskResponse(Response())
+            user_context = default_user_context(request_)
+
+            result: Union[BaseResponse, None] = sync(
+                st.middleware(request_, response_, user_context)
+            )
+
+            if result is not None:
+                if isinstance(result, FlaskResponse):
+                    return result.response
+                raise Exception("Should never come here")
+            return None
+
+        @app.after_request
+        def _(response: Response):
+            from flask import g
+
+            response_ = FlaskResponse(response)
+            if hasattr(g, "supertokens") and g.supertokens is not None:
+                manage_session_post_response(g.supertokens, response_, {})
+
+            return response_.response
+
+        @app.teardown_request
+        def _(_):
+            from flask import g
+
+            if hasattr(g, "supertokens"):
+                # this is to ensure there are no shared objects between requests.
+                # calling any other API with a shared request causes a security issue, resulting in unintentional
+                # sign-ins. More on this here - https://github.com/supertokens/supertokens-python/issues/463
+                g.pop("supertokens")
+
+    def set_error_handler(self):
+        app = self.app
+        from supertokens_python.exceptions import SuperTokensError
+
+        from flask import request
+
+        @app.errorhandler(SuperTokensError)
+        def _(error: Exception):
+            from supertokens_python import Supertokens
+            from supertokens_python.framework.flask.flask_request import FlaskRequest
+            from supertokens_python.framework.flask.flask_response import FlaskResponse
+            from supertokens_python.utils import default_user_context
+
+            from flask.wrappers import Response
+
+            st = Supertokens.get_instance()
+            response = Response(json.dumps({}), mimetype="application/json", status=200)
+            base_request = FlaskRequest(request)
+            user_context = default_user_context(base_request)
+
+            result: BaseResponse = sync(
+                st.handle_supertokens_error(
+                    base_request,
+                    error,
+                    FlaskResponse(response),
+                    user_context,
+                )
+            )
+            if isinstance(result, FlaskResponse):
+                return result.response
+            raise Exception("Should never come here")
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class Middleware +(app: Flask) +
+
+
+
+ +Expand source code + +
class Middleware:
+    def __init__(self, app: Flask):
+        self.app = app
+        self.set_before_after_request()
+        self.set_error_handler()
+
+    def set_before_after_request(self):
+        app = self.app
+        from supertokens_python.framework.flask.flask_request import FlaskRequest
+        from supertokens_python.framework.flask.flask_response import FlaskResponse
+        from supertokens_python.supertokens import manage_session_post_response
+        from supertokens_python.utils import default_user_context
+
+        from flask.wrappers import Response
+
+        # There is an error in the typing provided by flask, so we ignore it
+        # for now.
+        @app.before_request  # type: ignore
+        def _():
+            from supertokens_python import Supertokens
+
+            from flask import request
+            from flask.wrappers import Response
+
+            st = Supertokens.get_instance()
+
+            request_ = FlaskRequest(request)
+            response_ = FlaskResponse(Response())
+            user_context = default_user_context(request_)
+
+            result: Union[BaseResponse, None] = sync(
+                st.middleware(request_, response_, user_context)
+            )
+
+            if result is not None:
+                if isinstance(result, FlaskResponse):
+                    return result.response
+                raise Exception("Should never come here")
+            return None
+
+        @app.after_request
+        def _(response: Response):
+            from flask import g
+
+            response_ = FlaskResponse(response)
+            if hasattr(g, "supertokens") and g.supertokens is not None:
+                manage_session_post_response(g.supertokens, response_, {})
+
+            return response_.response
+
+        @app.teardown_request
+        def _(_):
+            from flask import g
+
+            if hasattr(g, "supertokens"):
+                # this is to ensure there are no shared objects between requests.
+                # calling any other API with a shared request causes a security issue, resulting in unintentional
+                # sign-ins. More on this here - https://github.com/supertokens/supertokens-python/issues/463
+                g.pop("supertokens")
+
+    def set_error_handler(self):
+        app = self.app
+        from supertokens_python.exceptions import SuperTokensError
+
+        from flask import request
+
+        @app.errorhandler(SuperTokensError)
+        def _(error: Exception):
+            from supertokens_python import Supertokens
+            from supertokens_python.framework.flask.flask_request import FlaskRequest
+            from supertokens_python.framework.flask.flask_response import FlaskResponse
+            from supertokens_python.utils import default_user_context
+
+            from flask.wrappers import Response
+
+            st = Supertokens.get_instance()
+            response = Response(json.dumps({}), mimetype="application/json", status=200)
+            base_request = FlaskRequest(request)
+            user_context = default_user_context(base_request)
+
+            result: BaseResponse = sync(
+                st.handle_supertokens_error(
+                    base_request,
+                    error,
+                    FlaskResponse(response),
+                    user_context,
+                )
+            )
+            if isinstance(result, FlaskResponse):
+                return result.response
+            raise Exception("Should never come here")
+
+

Methods

+
+
+def set_before_after_request(self) +
+
+
+
+ +Expand source code + +
def set_before_after_request(self):
+    app = self.app
+    from supertokens_python.framework.flask.flask_request import FlaskRequest
+    from supertokens_python.framework.flask.flask_response import FlaskResponse
+    from supertokens_python.supertokens import manage_session_post_response
+    from supertokens_python.utils import default_user_context
+
+    from flask.wrappers import Response
+
+    # There is an error in the typing provided by flask, so we ignore it
+    # for now.
+    @app.before_request  # type: ignore
+    def _():
+        from supertokens_python import Supertokens
+
+        from flask import request
+        from flask.wrappers import Response
+
+        st = Supertokens.get_instance()
+
+        request_ = FlaskRequest(request)
+        response_ = FlaskResponse(Response())
+        user_context = default_user_context(request_)
+
+        result: Union[BaseResponse, None] = sync(
+            st.middleware(request_, response_, user_context)
+        )
+
+        if result is not None:
+            if isinstance(result, FlaskResponse):
+                return result.response
+            raise Exception("Should never come here")
+        return None
+
+    @app.after_request
+    def _(response: Response):
+        from flask import g
+
+        response_ = FlaskResponse(response)
+        if hasattr(g, "supertokens") and g.supertokens is not None:
+            manage_session_post_response(g.supertokens, response_, {})
+
+        return response_.response
+
+    @app.teardown_request
+    def _(_):
+        from flask import g
+
+        if hasattr(g, "supertokens"):
+            # this is to ensure there are no shared objects between requests.
+            # calling any other API with a shared request causes a security issue, resulting in unintentional
+            # sign-ins. More on this here - https://github.com/supertokens/supertokens-python/issues/463
+            g.pop("supertokens")
+
+
+
+def set_error_handler(self) +
+
+
+
+ +Expand source code + +
def set_error_handler(self):
+    app = self.app
+    from supertokens_python.exceptions import SuperTokensError
+
+    from flask import request
+
+    @app.errorhandler(SuperTokensError)
+    def _(error: Exception):
+        from supertokens_python import Supertokens
+        from supertokens_python.framework.flask.flask_request import FlaskRequest
+        from supertokens_python.framework.flask.flask_response import FlaskResponse
+        from supertokens_python.utils import default_user_context
+
+        from flask.wrappers import Response
+
+        st = Supertokens.get_instance()
+        response = Response(json.dumps({}), mimetype="application/json", status=200)
+        base_request = FlaskRequest(request)
+        user_context = default_user_context(base_request)
+
+        result: BaseResponse = sync(
+            st.handle_supertokens_error(
+                base_request,
+                error,
+                FlaskResponse(response),
+                user_context,
+            )
+        )
+        if isinstance(result, FlaskResponse):
+            return result.response
+        raise Exception("Should never come here")
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/framework/flask/flask_request.html b/html/supertokens_python/framework/flask/flask_request.html new file mode 100644 index 000000000..09d256795 --- /dev/null +++ b/html/supertokens_python/framework/flask/flask_request.html @@ -0,0 +1,412 @@ + + + + + + +supertokens_python.framework.flask.flask_request API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.framework.flask.flask_request

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Any, Dict, Union
+
+from supertokens_python.framework.request import BaseRequest
+
+if TYPE_CHECKING:
+    from supertokens_python.recipe.session.interfaces import SessionContainer
+    from flask.wrappers import Request
+
+
+class FlaskRequest(BaseRequest):
+    def __init__(self, req: Request):
+        super().__init__()
+        self.request = req
+
+    def get_original_url(self) -> str:
+        return self.request.url
+
+    def get_query_param(self, key: str, default: Union[str, None] = None):
+        return self.request.args.get(key, default)
+
+    def get_query_params(self) -> Dict[str, Any]:
+        return self.request.args.to_dict()
+
+    async def json(self) -> Union[Any, None]:
+        try:
+            return self.request.get_json()
+        except Exception:
+            return {}
+
+    def method(self) -> str:
+        if isinstance(self.request, dict):
+            temp: str = self.request["REQUEST_METHOD"]
+            return temp
+        return self.request.method  # type: ignore
+
+    def get_cookie(self, key: str) -> Union[str, None]:
+        return self.request.cookies.get(key, None)
+
+    def get_header(self, key: str) -> Union[None, str]:
+        if isinstance(self.request, dict):
+            return self.request.get(key, None)  # type: ignore
+        return self.request.headers.get(key)  # type: ignore
+
+    def get_session(self) -> Union[SessionContainer, None]:
+        from flask import g
+
+        if hasattr(g, "supertokens"):
+            return g.supertokens
+        return None
+
+    def set_session(self, session: SessionContainer):
+        from flask import g
+
+        g.supertokens = session
+
+    def set_session_as_none(self):
+        from flask import g
+
+        g.supertokens = None
+
+    def get_path(self) -> str:
+        if isinstance(self.request, dict):
+            temp: str = self.request["PATH_INFO"]
+            return temp
+        return self.request.base_url
+
+    async def form_data(self) -> Dict[str, Any]:
+        return self.request.form.to_dict()
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class FlaskRequest +(req: Request) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class FlaskRequest(BaseRequest):
+    def __init__(self, req: Request):
+        super().__init__()
+        self.request = req
+
+    def get_original_url(self) -> str:
+        return self.request.url
+
+    def get_query_param(self, key: str, default: Union[str, None] = None):
+        return self.request.args.get(key, default)
+
+    def get_query_params(self) -> Dict[str, Any]:
+        return self.request.args.to_dict()
+
+    async def json(self) -> Union[Any, None]:
+        try:
+            return self.request.get_json()
+        except Exception:
+            return {}
+
+    def method(self) -> str:
+        if isinstance(self.request, dict):
+            temp: str = self.request["REQUEST_METHOD"]
+            return temp
+        return self.request.method  # type: ignore
+
+    def get_cookie(self, key: str) -> Union[str, None]:
+        return self.request.cookies.get(key, None)
+
+    def get_header(self, key: str) -> Union[None, str]:
+        if isinstance(self.request, dict):
+            return self.request.get(key, None)  # type: ignore
+        return self.request.headers.get(key)  # type: ignore
+
+    def get_session(self) -> Union[SessionContainer, None]:
+        from flask import g
+
+        if hasattr(g, "supertokens"):
+            return g.supertokens
+        return None
+
+    def set_session(self, session: SessionContainer):
+        from flask import g
+
+        g.supertokens = session
+
+    def set_session_as_none(self):
+        from flask import g
+
+        g.supertokens = None
+
+    def get_path(self) -> str:
+        if isinstance(self.request, dict):
+            temp: str = self.request["PATH_INFO"]
+            return temp
+        return self.request.base_url
+
+    async def form_data(self) -> Dict[str, Any]:
+        return self.request.form.to_dict()
+
+

Ancestors

+ +

Methods

+
+
+async def form_data(self) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
async def form_data(self) -> Dict[str, Any]:
+    return self.request.form.to_dict()
+
+
+ +
+
+
+ +Expand source code + +
def get_cookie(self, key: str) -> Union[str, None]:
+    return self.request.cookies.get(key, None)
+
+
+
+def get_header(self, key: str) ‑> Optional[str] +
+
+
+
+ +Expand source code + +
def get_header(self, key: str) -> Union[None, str]:
+    if isinstance(self.request, dict):
+        return self.request.get(key, None)  # type: ignore
+    return self.request.headers.get(key)  # type: ignore
+
+
+
+def get_original_url(self) ‑> str +
+
+
+
+ +Expand source code + +
def get_original_url(self) -> str:
+    return self.request.url
+
+
+
+def get_path(self) ‑> str +
+
+
+
+ +Expand source code + +
def get_path(self) -> str:
+    if isinstance(self.request, dict):
+        temp: str = self.request["PATH_INFO"]
+        return temp
+    return self.request.base_url
+
+
+
+def get_query_param(self, key: str, default: Union[str, None] = None) +
+
+
+
+ +Expand source code + +
def get_query_param(self, key: str, default: Union[str, None] = None):
+    return self.request.args.get(key, default)
+
+
+
+def get_query_params(self) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
def get_query_params(self) -> Dict[str, Any]:
+    return self.request.args.to_dict()
+
+
+
+def get_session(self) ‑> Union[SessionContainer, None] +
+
+
+
+ +Expand source code + +
def get_session(self) -> Union[SessionContainer, None]:
+    from flask import g
+
+    if hasattr(g, "supertokens"):
+        return g.supertokens
+    return None
+
+
+
+async def json(self) ‑> Optional[Any] +
+
+
+
+ +Expand source code + +
async def json(self) -> Union[Any, None]:
+    try:
+        return self.request.get_json()
+    except Exception:
+        return {}
+
+
+
+def method(self) ‑> str +
+
+
+
+ +Expand source code + +
def method(self) -> str:
+    if isinstance(self.request, dict):
+        temp: str = self.request["REQUEST_METHOD"]
+        return temp
+    return self.request.method  # type: ignore
+
+
+
+def set_session(self, session: SessionContainer) +
+
+
+
+ +Expand source code + +
def set_session(self, session: SessionContainer):
+    from flask import g
+
+    g.supertokens = session
+
+
+
+

Inherited members

+ +
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/framework/flask/flask_response.html b/html/supertokens_python/framework/flask/flask_response.html new file mode 100644 index 000000000..ef62d579f --- /dev/null +++ b/html/supertokens_python/framework/flask/flask_response.html @@ -0,0 +1,416 @@ + + + + + + +supertokens_python.framework.flask.flask_response API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.framework.flask.flask_response

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+import json
+from typing import Any, Dict, List, Optional
+
+from supertokens_python.framework.response import BaseResponse
+
+
+class FlaskResponse(BaseResponse):
+    from flask.wrappers import Response
+
+    def __init__(self, response: Response):
+        super().__init__({})
+        self.response = response
+        self.original = response
+        self.headers: List[Any] = []
+        self.response_sent = False
+        self.status_set = False
+
+    def set_html_content(self, content: str):
+        if not self.response_sent:
+            self.response.data = content
+            self.set_header("Content-Type", "text/html")
+            self.response_sent = True
+
+    def set_cookie(
+        self,
+        key: str,
+        value: str,
+        expires: int,
+        path: str = "/",
+        domain: Optional[str] = None,
+        secure: bool = False,
+        httponly: bool = False,
+        samesite: str = "lax",
+    ):
+        self.response.set_cookie(
+            key,
+            value=value,
+            expires=expires / 1000,
+            path=path,
+            domain=domain,
+            secure=secure,
+            httponly=httponly,
+            samesite=samesite,
+        )
+
+    def set_header(self, key: str, value: str):
+        self.response.headers.set(key, value)
+
+    def get_header(self, key: str) -> Optional[str]:
+        return self.response.headers.get(key)
+
+    def remove_header(self, key: str):
+        del self.response.headers[key]
+
+    def set_status_code(self, status_code: int):
+        if not self.status_set:
+            self.response.status_code = status_code
+            self.status_code = status_code
+            self.status_set = True
+
+    def get_headers(self):
+        return self.response.headers
+
+    def set_json_content(self, content: Dict[str, Any]):
+        if not self.response_sent:
+            self.set_header("Content-Type", "application/json; charset=utf-8")
+            self.response.data = json.dumps(
+                content,
+                ensure_ascii=False,
+                allow_nan=False,
+                indent=None,
+                separators=(",", ":"),
+            ).encode("utf-8")
+            self.response_sent = True
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class FlaskResponse +(response: flask.wrappers.Response) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class FlaskResponse(BaseResponse):
+    from flask.wrappers import Response
+
+    def __init__(self, response: Response):
+        super().__init__({})
+        self.response = response
+        self.original = response
+        self.headers: List[Any] = []
+        self.response_sent = False
+        self.status_set = False
+
+    def set_html_content(self, content: str):
+        if not self.response_sent:
+            self.response.data = content
+            self.set_header("Content-Type", "text/html")
+            self.response_sent = True
+
+    def set_cookie(
+        self,
+        key: str,
+        value: str,
+        expires: int,
+        path: str = "/",
+        domain: Optional[str] = None,
+        secure: bool = False,
+        httponly: bool = False,
+        samesite: str = "lax",
+    ):
+        self.response.set_cookie(
+            key,
+            value=value,
+            expires=expires / 1000,
+            path=path,
+            domain=domain,
+            secure=secure,
+            httponly=httponly,
+            samesite=samesite,
+        )
+
+    def set_header(self, key: str, value: str):
+        self.response.headers.set(key, value)
+
+    def get_header(self, key: str) -> Optional[str]:
+        return self.response.headers.get(key)
+
+    def remove_header(self, key: str):
+        del self.response.headers[key]
+
+    def set_status_code(self, status_code: int):
+        if not self.status_set:
+            self.response.status_code = status_code
+            self.status_code = status_code
+            self.status_set = True
+
+    def get_headers(self):
+        return self.response.headers
+
+    def set_json_content(self, content: Dict[str, Any]):
+        if not self.response_sent:
+            self.set_header("Content-Type", "application/json; charset=utf-8")
+            self.response.data = json.dumps(
+                content,
+                ensure_ascii=False,
+                allow_nan=False,
+                indent=None,
+                separators=(",", ":"),
+            ).encode("utf-8")
+            self.response_sent = True
+
+

Ancestors

+ +

Class variables

+
+
var Response
+
+

The response object that is used by default in Flask. +Works like the +response object from Werkzeug but is set to have an HTML mimetype by +default. +Quite often you don't have to create this object yourself because +:meth:~flask.Flask.make_response will take care of that for you.

+

If you want to replace the response object used you can subclass this and +set :attr:~flask.Flask.response_class to your subclass.

+
+

Changed in version: 1.0

+

JSON support is added to the response, like the request. This is useful +when testing to get the test client response data as JSON.

+
+
+

Changed in version: 1.0

+

Added :attr:max_cookie_size.

+
+
+
+

Methods

+
+
+def get_header(self, key: str) ‑> Optional[str] +
+
+
+
+ +Expand source code + +
def get_header(self, key: str) -> Optional[str]:
+    return self.response.headers.get(key)
+
+
+
+def get_headers(self) +
+
+
+
+ +Expand source code + +
def get_headers(self):
+    return self.response.headers
+
+
+
+def remove_header(self, key: str) +
+
+
+
+ +Expand source code + +
def remove_header(self, key: str):
+    del self.response.headers[key]
+
+
+ +
+
+
+ +Expand source code + +
def set_cookie(
+    self,
+    key: str,
+    value: str,
+    expires: int,
+    path: str = "/",
+    domain: Optional[str] = None,
+    secure: bool = False,
+    httponly: bool = False,
+    samesite: str = "lax",
+):
+    self.response.set_cookie(
+        key,
+        value=value,
+        expires=expires / 1000,
+        path=path,
+        domain=domain,
+        secure=secure,
+        httponly=httponly,
+        samesite=samesite,
+    )
+
+
+
+def set_header(self, key: str, value: str) +
+
+
+
+ +Expand source code + +
def set_header(self, key: str, value: str):
+    self.response.headers.set(key, value)
+
+
+
+def set_html_content(self, content: str) +
+
+
+
+ +Expand source code + +
def set_html_content(self, content: str):
+    if not self.response_sent:
+        self.response.data = content
+        self.set_header("Content-Type", "text/html")
+        self.response_sent = True
+
+
+
+def set_json_content(self, content: Dict[str, Any]) +
+
+
+
+ +Expand source code + +
def set_json_content(self, content: Dict[str, Any]):
+    if not self.response_sent:
+        self.set_header("Content-Type", "application/json; charset=utf-8")
+        self.response.data = json.dumps(
+            content,
+            ensure_ascii=False,
+            allow_nan=False,
+            indent=None,
+            separators=(",", ":"),
+        ).encode("utf-8")
+        self.response_sent = True
+
+
+
+def set_status_code(self, status_code: int) +
+
+
+
+ +Expand source code + +
def set_status_code(self, status_code: int):
+    if not self.status_set:
+        self.response.status_code = status_code
+        self.status_code = status_code
+        self.status_set = True
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/framework/flask/framework.html b/html/supertokens_python/framework/flask/framework.html new file mode 100644 index 000000000..1e7439b99 --- /dev/null +++ b/html/supertokens_python/framework/flask/framework.html @@ -0,0 +1,139 @@ + + + + + + +supertokens_python.framework.flask.framework API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.framework.flask.framework

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from typing import TYPE_CHECKING
+
+from supertokens_python.framework.types import Framework
+
+if TYPE_CHECKING:
+    from flask.wrappers import Request
+
+
+class FlaskFramework(Framework):
+    def wrap_request(self, unwrapped: Request):
+        from supertokens_python.framework.flask.flask_request import FlaskRequest
+
+        return FlaskRequest(unwrapped)
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class FlaskFramework +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class FlaskFramework(Framework):
+    def wrap_request(self, unwrapped: Request):
+        from supertokens_python.framework.flask.flask_request import FlaskRequest
+
+        return FlaskRequest(unwrapped)
+
+

Ancestors

+ +

Methods

+
+
+def wrap_request(self, unwrapped: Request) +
+
+
+
+ +Expand source code + +
def wrap_request(self, unwrapped: Request):
+    from supertokens_python.framework.flask.flask_request import FlaskRequest
+
+    return FlaskRequest(unwrapped)
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/framework/flask/index.html b/html/supertokens_python/framework/flask/index.html new file mode 100644 index 000000000..72dabf143 --- /dev/null +++ b/html/supertokens_python/framework/flask/index.html @@ -0,0 +1,101 @@ + + + + + + +supertokens_python.framework.flask API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.framework.flask

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from supertokens_python.framework.flask import flask_middleware
+
+Middleware = flask_middleware.Middleware
+
+
+
+

Sub-modules

+
+
supertokens_python.framework.flask.flask_middleware
+
+
+
+
supertokens_python.framework.flask.flask_request
+
+
+
+
supertokens_python.framework.flask.flask_response
+
+
+
+
supertokens_python.framework.flask.framework
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/framework/index.html b/html/supertokens_python/framework/index.html new file mode 100644 index 000000000..6cd358791 --- /dev/null +++ b/html/supertokens_python/framework/index.html @@ -0,0 +1,113 @@ + + + + + + +supertokens_python.framework API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.framework

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from . import request
+from . import response
+
+BaseRequest = request.BaseRequest
+BaseResponse = response.BaseResponse
+
+
+
+

Sub-modules

+
+
supertokens_python.framework.django
+
+
+
+
supertokens_python.framework.fastapi
+
+
+
+
supertokens_python.framework.flask
+
+
+
+
supertokens_python.framework.request
+
+
+
+
supertokens_python.framework.response
+
+
+
+
supertokens_python.framework.types
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/framework/request.html b/html/supertokens_python/framework/request.html new file mode 100644 index 000000000..594cf723a --- /dev/null +++ b/html/supertokens_python/framework/request.html @@ -0,0 +1,417 @@ + + + + + + +supertokens_python.framework.request API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.framework.request

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from abc import ABC, abstractmethod
+from typing import TYPE_CHECKING, Any, Dict, Union
+
+if TYPE_CHECKING:
+    from supertokens_python.recipe.session.interfaces import SessionContainer
+
+
+class BaseRequest(ABC):
+    def __init__(self):
+        self.wrapper_used = True
+        self.request = None
+
+    @abstractmethod
+    def get_original_url(self) -> str:
+        pass
+
+    @abstractmethod
+    def get_query_param(
+        self, key: str, default: Union[str, None] = None
+    ) -> Union[str, None]:
+        pass
+
+    @abstractmethod
+    def get_query_params(self) -> Dict[str, Any]:
+        pass
+
+    @abstractmethod
+    async def json(self) -> Union[Any, None]:
+        pass
+
+    @abstractmethod
+    async def form_data(self) -> Dict[str, Any]:
+        pass
+
+    @abstractmethod
+    def method(self) -> str:
+        pass
+
+    @abstractmethod
+    def get_cookie(self, key: str) -> Union[str, None]:
+        pass
+
+    @abstractmethod
+    def get_header(self, key: str) -> Union[None, str]:
+        pass
+
+    @abstractmethod
+    def get_session(self) -> Union[SessionContainer, None]:
+        pass
+
+    @abstractmethod
+    def set_session(self, session: SessionContainer):
+        pass
+
+    @abstractmethod
+    def set_session_as_none(self):
+        """
+        This function is used to set the request's session variable to None.
+        See https://github.com/supertokens/supertokens-python/issues/90
+        """
+
+    @abstractmethod
+    def get_path(self) -> str:
+        pass
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class BaseRequest +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class BaseRequest(ABC):
+    def __init__(self):
+        self.wrapper_used = True
+        self.request = None
+
+    @abstractmethod
+    def get_original_url(self) -> str:
+        pass
+
+    @abstractmethod
+    def get_query_param(
+        self, key: str, default: Union[str, None] = None
+    ) -> Union[str, None]:
+        pass
+
+    @abstractmethod
+    def get_query_params(self) -> Dict[str, Any]:
+        pass
+
+    @abstractmethod
+    async def json(self) -> Union[Any, None]:
+        pass
+
+    @abstractmethod
+    async def form_data(self) -> Dict[str, Any]:
+        pass
+
+    @abstractmethod
+    def method(self) -> str:
+        pass
+
+    @abstractmethod
+    def get_cookie(self, key: str) -> Union[str, None]:
+        pass
+
+    @abstractmethod
+    def get_header(self, key: str) -> Union[None, str]:
+        pass
+
+    @abstractmethod
+    def get_session(self) -> Union[SessionContainer, None]:
+        pass
+
+    @abstractmethod
+    def set_session(self, session: SessionContainer):
+        pass
+
+    @abstractmethod
+    def set_session_as_none(self):
+        """
+        This function is used to set the request's session variable to None.
+        See https://github.com/supertokens/supertokens-python/issues/90
+        """
+
+    @abstractmethod
+    def get_path(self) -> str:
+        pass
+
+

Ancestors

+
    +
  • abc.ABC
  • +
+

Subclasses

+ +

Methods

+
+
+async def form_data(self) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def form_data(self) -> Dict[str, Any]:
+    pass
+
+
+ +
+
+
+ +Expand source code + +
@abstractmethod
+def get_cookie(self, key: str) -> Union[str, None]:
+    pass
+
+
+
+def get_header(self, key: str) ‑> Optional[str] +
+
+
+
+ +Expand source code + +
@abstractmethod
+def get_header(self, key: str) -> Union[None, str]:
+    pass
+
+
+
+def get_original_url(self) ‑> str +
+
+
+
+ +Expand source code + +
@abstractmethod
+def get_original_url(self) -> str:
+    pass
+
+
+
+def get_path(self) ‑> str +
+
+
+
+ +Expand source code + +
@abstractmethod
+def get_path(self) -> str:
+    pass
+
+
+
+def get_query_param(self, key: str, default: Union[str, None] = None) ‑> Optional[str] +
+
+
+
+ +Expand source code + +
@abstractmethod
+def get_query_param(
+    self, key: str, default: Union[str, None] = None
+) -> Union[str, None]:
+    pass
+
+
+
+def get_query_params(self) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
@abstractmethod
+def get_query_params(self) -> Dict[str, Any]:
+    pass
+
+
+
+def get_session(self) ‑> Union[SessionContainer, None] +
+
+
+
+ +Expand source code + +
@abstractmethod
+def get_session(self) -> Union[SessionContainer, None]:
+    pass
+
+
+
+async def json(self) ‑> Optional[Any] +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def json(self) -> Union[Any, None]:
+    pass
+
+
+
+def method(self) ‑> str +
+
+
+
+ +Expand source code + +
@abstractmethod
+def method(self) -> str:
+    pass
+
+
+
+def set_session(self, session: SessionContainer) +
+
+
+
+ +Expand source code + +
@abstractmethod
+def set_session(self, session: SessionContainer):
+    pass
+
+
+
+def set_session_as_none(self) +
+
+

This function is used to set the request's session variable to None. +See https://github.com/supertokens/supertokens-python/issues/90

+
+ +Expand source code + +
@abstractmethod
+def set_session_as_none(self):
+    """
+    This function is used to set the request's session variable to None.
+    See https://github.com/supertokens/supertokens-python/issues/90
+    """
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/framework/response.html b/html/supertokens_python/framework/response.html new file mode 100644 index 000000000..56134d1b5 --- /dev/null +++ b/html/supertokens_python/framework/response.html @@ -0,0 +1,320 @@ + + + + + + +supertokens_python.framework.response API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.framework.response

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from abc import ABC, abstractmethod
+from typing import Any, Dict, Optional
+
+
+class BaseResponse(ABC):
+    @abstractmethod
+    def __init__(self, content: Dict[str, Any], status_code: int = 200):
+        self.content = content
+        self.status_code = status_code
+        self.wrapper_used = True
+
+    @abstractmethod
+    def set_cookie(
+        self,
+        key: str,
+        value: str,
+        #    max_age: Union[int, None] = None,
+        expires: int,
+        path: str = "/",
+        domain: Optional[str] = None,
+        secure: bool = False,
+        httponly: bool = False,
+        samesite: str = "lax",
+    ):
+        pass
+
+    @abstractmethod
+    def set_header(self, key: str, value: str) -> None:
+        pass
+
+    @abstractmethod
+    def get_header(self, key: str) -> Optional[str]:
+        pass
+
+    @abstractmethod
+    def remove_header(self, key: str) -> None:
+        pass
+
+    @abstractmethod
+    def set_status_code(self, status_code: int):
+        pass
+
+    @abstractmethod
+    def set_json_content(self, content: Dict[str, Any]):
+        pass
+
+    @abstractmethod
+    def set_html_content(self, content: str):
+        pass
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class BaseResponse +(content: Dict[str, Any], status_code: int = 200) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class BaseResponse(ABC):
+    @abstractmethod
+    def __init__(self, content: Dict[str, Any], status_code: int = 200):
+        self.content = content
+        self.status_code = status_code
+        self.wrapper_used = True
+
+    @abstractmethod
+    def set_cookie(
+        self,
+        key: str,
+        value: str,
+        #    max_age: Union[int, None] = None,
+        expires: int,
+        path: str = "/",
+        domain: Optional[str] = None,
+        secure: bool = False,
+        httponly: bool = False,
+        samesite: str = "lax",
+    ):
+        pass
+
+    @abstractmethod
+    def set_header(self, key: str, value: str) -> None:
+        pass
+
+    @abstractmethod
+    def get_header(self, key: str) -> Optional[str]:
+        pass
+
+    @abstractmethod
+    def remove_header(self, key: str) -> None:
+        pass
+
+    @abstractmethod
+    def set_status_code(self, status_code: int):
+        pass
+
+    @abstractmethod
+    def set_json_content(self, content: Dict[str, Any]):
+        pass
+
+    @abstractmethod
+    def set_html_content(self, content: str):
+        pass
+
+

Ancestors

+
    +
  • abc.ABC
  • +
+

Subclasses

+ +

Methods

+
+
+def get_header(self, key: str) ‑> Optional[str] +
+
+
+
+ +Expand source code + +
@abstractmethod
+def get_header(self, key: str) -> Optional[str]:
+    pass
+
+
+
+def remove_header(self, key: str) ‑> None +
+
+
+
+ +Expand source code + +
@abstractmethod
+def remove_header(self, key: str) -> None:
+    pass
+
+
+ +
+
+
+ +Expand source code + +
@abstractmethod
+def set_cookie(
+    self,
+    key: str,
+    value: str,
+    #    max_age: Union[int, None] = None,
+    expires: int,
+    path: str = "/",
+    domain: Optional[str] = None,
+    secure: bool = False,
+    httponly: bool = False,
+    samesite: str = "lax",
+):
+    pass
+
+
+
+def set_header(self, key: str, value: str) ‑> None +
+
+
+
+ +Expand source code + +
@abstractmethod
+def set_header(self, key: str, value: str) -> None:
+    pass
+
+
+
+def set_html_content(self, content: str) +
+
+
+
+ +Expand source code + +
@abstractmethod
+def set_html_content(self, content: str):
+    pass
+
+
+
+def set_json_content(self, content: Dict[str, Any]) +
+
+
+
+ +Expand source code + +
@abstractmethod
+def set_json_content(self, content: Dict[str, Any]):
+    pass
+
+
+
+def set_status_code(self, status_code: int) +
+
+
+
+ +Expand source code + +
@abstractmethod
+def set_status_code(self, status_code: int):
+    pass
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/framework/types.html b/html/supertokens_python/framework/types.html new file mode 100644 index 000000000..d2a6aff05 --- /dev/null +++ b/html/supertokens_python/framework/types.html @@ -0,0 +1,190 @@ + + + + + + +supertokens_python.framework.types API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.framework.types

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from abc import ABC, abstractmethod
+from enum import Enum
+from typing import Any, Union
+
+from supertokens_python.framework.request import BaseRequest
+
+frameworks = ["fastapi", "flask", "django"]
+
+
+class FrameworkEnum(Enum):
+    FASTAPI = 1
+    FLASK = 2
+    DJANGO = 3
+
+
+class Framework(ABC):
+    @abstractmethod
+    def wrap_request(self, unwrapped: Any) -> Union[BaseRequest, None]:
+        pass
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class Framework +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class Framework(ABC):
+    @abstractmethod
+    def wrap_request(self, unwrapped: Any) -> Union[BaseRequest, None]:
+        pass
+
+

Ancestors

+
    +
  • abc.ABC
  • +
+

Subclasses

+ +

Methods

+
+
+def wrap_request(self, unwrapped: Any) ‑> Optional[BaseRequest] +
+
+
+
+ +Expand source code + +
@abstractmethod
+def wrap_request(self, unwrapped: Any) -> Union[BaseRequest, None]:
+    pass
+
+
+
+
+
+class FrameworkEnum +(value, names=None, *, module=None, qualname=None, type=None, start=1) +
+
+

An enumeration.

+
+ +Expand source code + +
class FrameworkEnum(Enum):
+    FASTAPI = 1
+    FLASK = 2
+    DJANGO = 3
+
+

Ancestors

+
    +
  • enum.Enum
  • +
+

Class variables

+
+
var DJANGO
+
+
+
+
var FASTAPI
+
+
+
+
var FLASK
+
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/index.html b/html/supertokens_python/index.html new file mode 100644 index 000000000..613c7fdea --- /dev/null +++ b/html/supertokens_python/index.html @@ -0,0 +1,267 @@ + + + + + + +supertokens_python API documentation + + + + + + + + + + + +
+
+
+

Package supertokens_python

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from typing import Any, Callable, Dict, List, Optional
+
+from typing_extensions import Literal
+
+from supertokens_python.framework.request import BaseRequest
+
+from . import supertokens
+from .recipe_module import RecipeModule
+
+InputAppInfo = supertokens.InputAppInfo
+Supertokens = supertokens.Supertokens
+SupertokensConfig = supertokens.SupertokensConfig
+AppInfo = supertokens.AppInfo
+
+
+def init(
+    app_info: InputAppInfo,
+    framework: Literal["fastapi", "flask", "django"],
+    supertokens_config: SupertokensConfig,
+    recipe_list: List[Callable[[supertokens.AppInfo], RecipeModule]],
+    mode: Optional[Literal["asgi", "wsgi"]] = None,
+    telemetry: Optional[bool] = None,
+    debug: Optional[bool] = None,
+):
+    return Supertokens.init(
+        app_info, framework, supertokens_config, recipe_list, mode, telemetry, debug
+    )
+
+
+def get_all_cors_headers() -> List[str]:
+    return supertokens.Supertokens.get_instance().get_all_cors_headers()
+
+
+def get_request_from_user_context(
+    user_context: Optional[Dict[str, Any]],
+) -> Optional[BaseRequest]:
+    return Supertokens.get_instance().get_request_from_user_context(user_context)
+
+
+
+

Sub-modules

+
+
supertokens_python.async_to_sync_wrapper
+
+
+
+
supertokens_python.asyncio
+
+
+
+
supertokens_python.constants
+
+
+
+
supertokens_python.exceptions
+
+
+
+
supertokens_python.framework
+
+
+
+
supertokens_python.ingredients
+
+
+
+
supertokens_python.interfaces
+
+
+
+
supertokens_python.logger
+
+
+
+
supertokens_python.normalised_url_domain
+
+
+
+
supertokens_python.normalised_url_path
+
+
+
+
supertokens_python.post_init_callbacks
+
+
+
+
supertokens_python.process_state
+
+
+
+
supertokens_python.querier
+
+
+
+
supertokens_python.recipe
+
+
+
+
supertokens_python.recipe_module
+
+
+
+
supertokens_python.supertokens
+
+
+
+
supertokens_python.syncio
+
+
+
+
supertokens_python.types
+
+
+
+
supertokens_python.utils
+
+
+
+
+
+
+
+
+

Functions

+
+
+def get_all_cors_headers() ‑> List[str] +
+
+
+
+ +Expand source code + +
def get_all_cors_headers() -> List[str]:
+    return supertokens.Supertokens.get_instance().get_all_cors_headers()
+
+
+
+def get_request_from_user_context(user_context: Optional[Dict[str, Any]]) ‑> Optional[BaseRequest] +
+
+
+
+ +Expand source code + +
def get_request_from_user_context(
+    user_context: Optional[Dict[str, Any]],
+) -> Optional[BaseRequest]:
+    return Supertokens.get_instance().get_request_from_user_context(user_context)
+
+
+
+def init(app_info: InputAppInfo, framework: typing_extensions.Literal['fastapi', 'flask', 'django'], supertokens_config: SupertokensConfig, recipe_list: List[Callable[[AppInfo], RecipeModule]], mode: Optional[typing_extensions.Literal['asgi', 'wsgi']] = None, telemetry: Optional[bool] = None, debug: Optional[bool] = None) +
+
+
+
+ +Expand source code + +
def init(
+    app_info: InputAppInfo,
+    framework: Literal["fastapi", "flask", "django"],
+    supertokens_config: SupertokensConfig,
+    recipe_list: List[Callable[[supertokens.AppInfo], RecipeModule]],
+    mode: Optional[Literal["asgi", "wsgi"]] = None,
+    telemetry: Optional[bool] = None,
+    debug: Optional[bool] = None,
+):
+    return Supertokens.init(
+        app_info, framework, supertokens_config, recipe_list, mode, telemetry, debug
+    )
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/ingredients/emaildelivery/index.html b/html/supertokens_python/ingredients/emaildelivery/index.html new file mode 100644 index 000000000..fe280bdd2 --- /dev/null +++ b/html/supertokens_python/ingredients/emaildelivery/index.html @@ -0,0 +1,166 @@ + + + + + + +supertokens_python.ingredients.emaildelivery API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.ingredients.emaildelivery

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from typing import Generic, TypeVar
+
+from supertokens_python.ingredients.emaildelivery.types import (
+    EmailDeliveryConfigWithService,
+    EmailDeliveryInterface,
+)
+
+_T = TypeVar("_T")
+
+
+class EmailDeliveryIngredient(Generic[_T]):
+    ingredient_interface_impl: EmailDeliveryInterface[_T]
+
+    def __init__(self, config: EmailDeliveryConfigWithService[_T]) -> None:
+        self.ingredient_interface_impl = (
+            config.service
+            if config.override is None
+            else config.override(config.service)
+        )
+
+
+
+

Sub-modules

+
+
supertokens_python.ingredients.emaildelivery.services
+
+
+
+
supertokens_python.ingredients.emaildelivery.types
+
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class EmailDeliveryIngredient +(config: EmailDeliveryConfigWithService[~_T]) +
+
+

Abstract base class for generic types.

+

A generic type is typically declared by inheriting from +this class parameterized with one or more type variables. +For example, a generic mapping type might be defined as::

+

class Mapping(Generic[KT, VT]): +def getitem(self, key: KT) -> VT: +… +# Etc.

+

This class can then be used as follows::

+

def lookup_name(mapping: Mapping[KT, VT], key: KT, default: VT) -> VT: +try: +return mapping[key] +except KeyError: +return default

+
+ +Expand source code + +
class EmailDeliveryIngredient(Generic[_T]):
+    ingredient_interface_impl: EmailDeliveryInterface[_T]
+
+    def __init__(self, config: EmailDeliveryConfigWithService[_T]) -> None:
+        self.ingredient_interface_impl = (
+            config.service
+            if config.override is None
+            else config.override(config.service)
+        )
+
+

Ancestors

+
    +
  • typing.Generic
  • +
+

Class variables

+
+
var ingredient_interface_implEmailDeliveryInterface[~_T]
+
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/ingredients/emaildelivery/services/index.html b/html/supertokens_python/ingredients/emaildelivery/services/index.html new file mode 100644 index 000000000..634f38f2d --- /dev/null +++ b/html/supertokens_python/ingredients/emaildelivery/services/index.html @@ -0,0 +1,83 @@ + + + + + + +supertokens_python.ingredients.emaildelivery.services API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.ingredients.emaildelivery.services

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+
+
+

Sub-modules

+
+
supertokens_python.ingredients.emaildelivery.services.smtp
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/ingredients/emaildelivery/services/smtp.html b/html/supertokens_python/ingredients/emaildelivery/services/smtp.html new file mode 100644 index 000000000..0541b36c6 --- /dev/null +++ b/html/supertokens_python/ingredients/emaildelivery/services/smtp.html @@ -0,0 +1,275 @@ + + + + + + +supertokens_python.ingredients.emaildelivery.services.smtp API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.ingredients.emaildelivery.services.smtp

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+
+import ssl
+from email.mime.text import MIMEText
+from typing import Any, Dict, TypeVar
+
+import aiosmtplib
+from supertokens_python.ingredients.emaildelivery.types import (
+    EmailContent,
+    SMTPSettings,
+)
+from supertokens_python.logger import log_debug_message
+
+_T = TypeVar("_T")
+
+
+class Transporter:
+    def __init__(self, smtp_settings: SMTPSettings) -> None:
+        self.smtp_settings = smtp_settings
+
+    async def _connect(self):
+        try:
+            tls_context = ssl.create_default_context()
+            if self.smtp_settings.secure:
+                # Use TLS from the beginning
+                mail = aiosmtplib.SMTP(
+                    hostname=self.smtp_settings.host,
+                    port=self.smtp_settings.port,
+                    use_tls=True,
+                    tls_context=tls_context,
+                )
+            else:
+                # Start without TLS (but later try upgrading)
+                mail = aiosmtplib.SMTP(
+                    hostname=self.smtp_settings.host,
+                    port=self.smtp_settings.port,
+                    use_tls=False,
+                )
+
+            await mail.connect()  # type: ignore
+
+            if not self.smtp_settings.secure:
+                # Try upgrading to TLS (even if the user opted for secure=False)
+                try:
+                    await mail.starttls(tls_context=tls_context)
+                except aiosmtplib.SMTPException:  # TLS wasn't supported by the server, so ignore.
+                    pass
+
+            if self.smtp_settings.password:
+                await mail.login(
+                    self.smtp_settings.username or self.smtp_settings.from_.email,
+                    self.smtp_settings.password,
+                )
+
+            return mail
+        except Exception as e:
+            log_debug_message("Couldn't connect to the SMTP server: %s", e)
+            raise e
+
+    async def send_email(self, input_: EmailContent, _: Dict[str, Any]) -> None:
+        connection = await self._connect()
+
+        from_ = self.smtp_settings.from_
+        try:
+            from_addr = f"{from_.name} <{from_.email}>"
+            if input_.is_html:
+                email_content = MIMEText(input_.body, "html")
+                email_content["From"] = from_addr
+                email_content["To"] = input_.to_email
+                email_content["Subject"] = input_.subject
+                await connection.sendmail(
+                    from_.email, input_.to_email, email_content.as_string()
+                )
+            else:
+                await connection.sendmail(from_addr, input_.to_email, input_.body)
+        except Exception as e:
+            log_debug_message("Error in sending email: %s", e)
+            raise e
+        finally:
+            await connection.quit()
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class Transporter +(smtp_settings: SMTPSettings) +
+
+
+
+ +Expand source code + +
class Transporter:
+    def __init__(self, smtp_settings: SMTPSettings) -> None:
+        self.smtp_settings = smtp_settings
+
+    async def _connect(self):
+        try:
+            tls_context = ssl.create_default_context()
+            if self.smtp_settings.secure:
+                # Use TLS from the beginning
+                mail = aiosmtplib.SMTP(
+                    hostname=self.smtp_settings.host,
+                    port=self.smtp_settings.port,
+                    use_tls=True,
+                    tls_context=tls_context,
+                )
+            else:
+                # Start without TLS (but later try upgrading)
+                mail = aiosmtplib.SMTP(
+                    hostname=self.smtp_settings.host,
+                    port=self.smtp_settings.port,
+                    use_tls=False,
+                )
+
+            await mail.connect()  # type: ignore
+
+            if not self.smtp_settings.secure:
+                # Try upgrading to TLS (even if the user opted for secure=False)
+                try:
+                    await mail.starttls(tls_context=tls_context)
+                except aiosmtplib.SMTPException:  # TLS wasn't supported by the server, so ignore.
+                    pass
+
+            if self.smtp_settings.password:
+                await mail.login(
+                    self.smtp_settings.username or self.smtp_settings.from_.email,
+                    self.smtp_settings.password,
+                )
+
+            return mail
+        except Exception as e:
+            log_debug_message("Couldn't connect to the SMTP server: %s", e)
+            raise e
+
+    async def send_email(self, input_: EmailContent, _: Dict[str, Any]) -> None:
+        connection = await self._connect()
+
+        from_ = self.smtp_settings.from_
+        try:
+            from_addr = f"{from_.name} <{from_.email}>"
+            if input_.is_html:
+                email_content = MIMEText(input_.body, "html")
+                email_content["From"] = from_addr
+                email_content["To"] = input_.to_email
+                email_content["Subject"] = input_.subject
+                await connection.sendmail(
+                    from_.email, input_.to_email, email_content.as_string()
+                )
+            else:
+                await connection.sendmail(from_addr, input_.to_email, input_.body)
+        except Exception as e:
+            log_debug_message("Error in sending email: %s", e)
+            raise e
+        finally:
+            await connection.quit()
+
+

Methods

+
+
+async def send_email(self, input_: EmailContent, _: Dict[str, Any]) ‑> None +
+
+
+
+ +Expand source code + +
async def send_email(self, input_: EmailContent, _: Dict[str, Any]) -> None:
+    connection = await self._connect()
+
+    from_ = self.smtp_settings.from_
+    try:
+        from_addr = f"{from_.name} <{from_.email}>"
+        if input_.is_html:
+            email_content = MIMEText(input_.body, "html")
+            email_content["From"] = from_addr
+            email_content["To"] = input_.to_email
+            email_content["Subject"] = input_.subject
+            await connection.sendmail(
+                from_.email, input_.to_email, email_content.as_string()
+            )
+        else:
+            await connection.sendmail(from_addr, input_.to_email, input_.body)
+    except Exception as e:
+        log_debug_message("Error in sending email: %s", e)
+        raise e
+    finally:
+        await connection.quit()
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/ingredients/emaildelivery/types.html b/html/supertokens_python/ingredients/emaildelivery/types.html new file mode 100644 index 000000000..62070f722 --- /dev/null +++ b/html/supertokens_python/ingredients/emaildelivery/types.html @@ -0,0 +1,434 @@ + + + + + + +supertokens_python.ingredients.emaildelivery.types API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.ingredients.emaildelivery.types

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from __future__ import annotations
+from abc import ABC, abstractmethod
+from typing import Any, Callable, Dict, Generic, TypeVar, Union, TYPE_CHECKING
+
+if TYPE_CHECKING:
+    from supertokens_python.ingredients.emaildelivery.services.smtp import Transporter
+
+_T = TypeVar("_T")
+
+
+class EmailDeliveryInterface(ABC, Generic[_T]):
+    @abstractmethod
+    async def send_email(self, template_vars: _T, user_context: Dict[str, Any]) -> None:
+        pass
+
+
+class EmailDeliveryConfig(ABC, Generic[_T]):
+    def __init__(
+        self,
+        service: Union[EmailDeliveryInterface[_T], None] = None,
+        override: Union[
+            Callable[[EmailDeliveryInterface[_T]], EmailDeliveryInterface[_T]], None
+        ] = None,
+    ) -> None:
+        self.service = service
+        self.override = override
+
+
+class EmailDeliveryConfigWithService(ABC, Generic[_T]):
+    def __init__(
+        self,
+        service: EmailDeliveryInterface[_T],
+        override: Union[
+            Callable[[EmailDeliveryInterface[_T]], EmailDeliveryInterface[_T]], None
+        ] = None,
+    ) -> None:
+        self.service = service
+        self.override = override
+
+
+class SMTPSettingsFrom:
+    def __init__(self, name: str, email: str) -> None:
+        self.name = name
+        self.email = email
+
+
+class SMTPSettings:
+    def __init__(
+        self,
+        host: str,
+        port: int,
+        from_: SMTPSettingsFrom,
+        password: Union[str, None] = None,
+        secure: Union[bool, None] = None,
+        username: Union[str, None] = None,
+    ) -> None:
+        self.host = host
+        self.from_ = from_
+        self.password = password
+        self.port = port
+        self.secure = secure
+        self.username = username
+
+
+class EmailContent:
+    def __init__(self, body: str, subject: str, to_email: str, is_html: bool) -> None:
+        self.body = body
+        self.subject = subject
+        self.to_email = to_email
+        self.is_html = is_html
+
+
+class SMTPServiceInterface(ABC, Generic[_T]):
+    def __init__(self, transporter: Transporter) -> None:
+        self.transporter = transporter
+
+    @abstractmethod
+    async def send_raw_email(
+        self, content: EmailContent, user_context: Dict[str, Any]
+    ) -> None:
+        pass
+
+    @abstractmethod
+    async def get_content(
+        self, template_vars: _T, user_context: Dict[str, Any]
+    ) -> EmailContent:
+        pass
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class EmailContent +(body: str, subject: str, to_email: str, is_html: bool) +
+
+
+
+ +Expand source code + +
class EmailContent:
+    def __init__(self, body: str, subject: str, to_email: str, is_html: bool) -> None:
+        self.body = body
+        self.subject = subject
+        self.to_email = to_email
+        self.is_html = is_html
+
+
+
+class EmailDeliveryConfig +(service: Union[EmailDeliveryInterface[_T], None] = None, override: Union[Callable[[EmailDeliveryInterface[_T]], EmailDeliveryInterface[_T]], None] = None) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class EmailDeliveryConfig(ABC, Generic[_T]):
+    def __init__(
+        self,
+        service: Union[EmailDeliveryInterface[_T], None] = None,
+        override: Union[
+            Callable[[EmailDeliveryInterface[_T]], EmailDeliveryInterface[_T]], None
+        ] = None,
+    ) -> None:
+        self.service = service
+        self.override = override
+
+

Ancestors

+
    +
  • abc.ABC
  • +
  • typing.Generic
  • +
+
+
+class EmailDeliveryConfigWithService +(service: EmailDeliveryInterface[_T], override: Union[Callable[[EmailDeliveryInterface[_T]], EmailDeliveryInterface[_T]], None] = None) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class EmailDeliveryConfigWithService(ABC, Generic[_T]):
+    def __init__(
+        self,
+        service: EmailDeliveryInterface[_T],
+        override: Union[
+            Callable[[EmailDeliveryInterface[_T]], EmailDeliveryInterface[_T]], None
+        ] = None,
+    ) -> None:
+        self.service = service
+        self.override = override
+
+

Ancestors

+
    +
  • abc.ABC
  • +
  • typing.Generic
  • +
+
+
+class EmailDeliveryInterface +(*args, **kwds) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class EmailDeliveryInterface(ABC, Generic[_T]):
+    @abstractmethod
+    async def send_email(self, template_vars: _T, user_context: Dict[str, Any]) -> None:
+        pass
+
+

Ancestors

+
    +
  • abc.ABC
  • +
  • typing.Generic
  • +
+

Subclasses

+ +

Methods

+
+
+async def send_email(self, template_vars: _T, user_context: Dict[str, Any]) ‑> None +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def send_email(self, template_vars: _T, user_context: Dict[str, Any]) -> None:
+    pass
+
+
+
+
+
+class SMTPServiceInterface +(transporter: Transporter) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class SMTPServiceInterface(ABC, Generic[_T]):
+    def __init__(self, transporter: Transporter) -> None:
+        self.transporter = transporter
+
+    @abstractmethod
+    async def send_raw_email(
+        self, content: EmailContent, user_context: Dict[str, Any]
+    ) -> None:
+        pass
+
+    @abstractmethod
+    async def get_content(
+        self, template_vars: _T, user_context: Dict[str, Any]
+    ) -> EmailContent:
+        pass
+
+

Ancestors

+
    +
  • abc.ABC
  • +
  • typing.Generic
  • +
+

Subclasses

+ +

Methods

+
+
+async def get_content(self, template_vars: _T, user_context: Dict[str, Any]) ‑> EmailContent +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def get_content(
+    self, template_vars: _T, user_context: Dict[str, Any]
+) -> EmailContent:
+    pass
+
+
+
+async def send_raw_email(self, content: EmailContent, user_context: Dict[str, Any]) ‑> None +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def send_raw_email(
+    self, content: EmailContent, user_context: Dict[str, Any]
+) -> None:
+    pass
+
+
+
+
+
+class SMTPSettings +(host: str, port: int, from_: SMTPSettingsFrom, password: Union[str, None] = None, secure: Union[bool, None] = None, username: Union[str, None] = None) +
+
+
+
+ +Expand source code + +
class SMTPSettings:
+    def __init__(
+        self,
+        host: str,
+        port: int,
+        from_: SMTPSettingsFrom,
+        password: Union[str, None] = None,
+        secure: Union[bool, None] = None,
+        username: Union[str, None] = None,
+    ) -> None:
+        self.host = host
+        self.from_ = from_
+        self.password = password
+        self.port = port
+        self.secure = secure
+        self.username = username
+
+
+
+class SMTPSettingsFrom +(name: str, email: str) +
+
+
+
+ +Expand source code + +
class SMTPSettingsFrom:
+    def __init__(self, name: str, email: str) -> None:
+        self.name = name
+        self.email = email
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/ingredients/index.html b/html/supertokens_python/ingredients/index.html new file mode 100644 index 000000000..04cc63a0f --- /dev/null +++ b/html/supertokens_python/ingredients/index.html @@ -0,0 +1,88 @@ + + + + + + +supertokens_python.ingredients API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.ingredients

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+
+
+

Sub-modules

+
+
supertokens_python.ingredients.emaildelivery
+
+
+
+
supertokens_python.ingredients.smsdelivery
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/ingredients/smsdelivery/index.html b/html/supertokens_python/ingredients/smsdelivery/index.html new file mode 100644 index 000000000..3187249a3 --- /dev/null +++ b/html/supertokens_python/ingredients/smsdelivery/index.html @@ -0,0 +1,166 @@ + + + + + + +supertokens_python.ingredients.smsdelivery API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.ingredients.smsdelivery

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from typing import Generic, TypeVar
+
+from supertokens_python.ingredients.smsdelivery.types import (
+    SMSDeliveryConfigWithService,
+    SMSDeliveryInterface,
+)
+
+_T = TypeVar("_T")
+
+
+class SMSDeliveryIngredient(Generic[_T]):
+    ingredient_interface_impl: SMSDeliveryInterface[_T]
+
+    def __init__(self, config: SMSDeliveryConfigWithService[_T]) -> None:
+        self.ingredient_interface_impl = (
+            config.service
+            if config.override is None
+            else config.override(config.service)
+        )
+
+
+
+

Sub-modules

+
+
supertokens_python.ingredients.smsdelivery.services
+
+
+
+
supertokens_python.ingredients.smsdelivery.types
+
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class SMSDeliveryIngredient +(config: SMSDeliveryConfigWithService[~_T]) +
+
+

Abstract base class for generic types.

+

A generic type is typically declared by inheriting from +this class parameterized with one or more type variables. +For example, a generic mapping type might be defined as::

+

class Mapping(Generic[KT, VT]): +def getitem(self, key: KT) -> VT: +… +# Etc.

+

This class can then be used as follows::

+

def lookup_name(mapping: Mapping[KT, VT], key: KT, default: VT) -> VT: +try: +return mapping[key] +except KeyError: +return default

+
+ +Expand source code + +
class SMSDeliveryIngredient(Generic[_T]):
+    ingredient_interface_impl: SMSDeliveryInterface[_T]
+
+    def __init__(self, config: SMSDeliveryConfigWithService[_T]) -> None:
+        self.ingredient_interface_impl = (
+            config.service
+            if config.override is None
+            else config.override(config.service)
+        )
+
+

Ancestors

+
    +
  • typing.Generic
  • +
+

Class variables

+
+
var ingredient_interface_implSMSDeliveryInterface[~_T]
+
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/ingredients/smsdelivery/services/index.html b/html/supertokens_python/ingredients/smsdelivery/services/index.html new file mode 100644 index 000000000..06233dd71 --- /dev/null +++ b/html/supertokens_python/ingredients/smsdelivery/services/index.html @@ -0,0 +1,88 @@ + + + + + + +supertokens_python.ingredients.smsdelivery.services API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.ingredients.smsdelivery.services

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+
+
+

Sub-modules

+
+
supertokens_python.ingredients.smsdelivery.services.supertokens
+
+
+
+
supertokens_python.ingredients.smsdelivery.services.twilio
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/ingredients/smsdelivery/services/supertokens.html b/html/supertokens_python/ingredients/smsdelivery/services/supertokens.html new file mode 100644 index 000000000..bc4c7afd0 --- /dev/null +++ b/html/supertokens_python/ingredients/smsdelivery/services/supertokens.html @@ -0,0 +1,73 @@ + + + + + + +supertokens_python.ingredients.smsdelivery.services.supertokens API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.ingredients.smsdelivery.services.supertokens

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+SUPERTOKENS_SMS_SERVICE_URL = "https://api.supertokens.com/0/services/sms"
+
+
+
+
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/ingredients/smsdelivery/services/twilio.html b/html/supertokens_python/ingredients/smsdelivery/services/twilio.html new file mode 100644 index 000000000..17007753d --- /dev/null +++ b/html/supertokens_python/ingredients/smsdelivery/services/twilio.html @@ -0,0 +1,117 @@ + + + + + + +supertokens_python.ingredients.smsdelivery.services.twilio API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.ingredients.smsdelivery.services.twilio

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from typing import TypeVar
+
+from supertokens_python.ingredients.smsdelivery.types import TwilioSettings
+
+_T = TypeVar("_T")
+
+
+def normalize_twilio_settings(twilio_settings: TwilioSettings) -> TwilioSettings:
+    from_ = twilio_settings.from_
+    messaging_service_sid = twilio_settings.messaging_service_sid
+
+    if (from_ and messaging_service_sid) or (not from_ and not messaging_service_sid):
+        raise Exception(
+            'Please pass exactly one of "from" and "messaging_service_sid" config for twilio_settings.'
+        )
+
+    return twilio_settings
+
+
+
+
+
+
+
+

Functions

+
+
+def normalize_twilio_settings(twilio_settings: TwilioSettings) ‑> TwilioSettings +
+
+
+
+ +Expand source code + +
def normalize_twilio_settings(twilio_settings: TwilioSettings) -> TwilioSettings:
+    from_ = twilio_settings.from_
+    messaging_service_sid = twilio_settings.messaging_service_sid
+
+    if (from_ and messaging_service_sid) or (not from_ and not messaging_service_sid):
+        raise Exception(
+            'Please pass exactly one of "from" and "messaging_service_sid" config for twilio_settings.'
+        )
+
+    return twilio_settings
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/ingredients/smsdelivery/types.html b/html/supertokens_python/ingredients/smsdelivery/types.html new file mode 100644 index 000000000..f6f21d7aa --- /dev/null +++ b/html/supertokens_python/ingredients/smsdelivery/types.html @@ -0,0 +1,420 @@ + + + + + + +supertokens_python.ingredients.smsdelivery.types API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.ingredients.smsdelivery.types

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from abc import ABC, abstractmethod
+from typing import Any, Callable, Dict, Generic, TypeVar, Union
+
+from twilio.rest import Client  # type: ignore
+
+_T = TypeVar("_T")
+
+
+class SMSDeliveryInterface(ABC, Generic[_T]):
+    @abstractmethod
+    async def send_sms(self, template_vars: _T, user_context: Dict[str, Any]) -> None:
+        pass
+
+
+class SMSDeliveryConfig(ABC, Generic[_T]):
+    def __init__(
+        self,
+        service: Union[SMSDeliveryInterface[_T], None] = None,
+        override: Union[
+            Callable[[SMSDeliveryInterface[_T]], SMSDeliveryInterface[_T]], None
+        ] = None,
+    ) -> None:
+        self.service = service
+        self.override = override
+
+
+class SMSDeliveryConfigWithService(ABC, Generic[_T]):
+    def __init__(
+        self,
+        service: SMSDeliveryInterface[_T],
+        override: Union[
+            Callable[[SMSDeliveryInterface[_T]], SMSDeliveryInterface[_T]], None
+        ] = None,
+    ) -> None:
+        self.service = service
+        self.override = override
+
+
+class TwilioSettings:
+    def __init__(
+        self,
+        account_sid: str,
+        auth_token: str,
+        from_: Union[str, None] = None,
+        messaging_service_sid: Union[str, None] = None,
+        opts: Union[Dict[str, Any], None] = None,
+    ) -> None:
+        """
+        Note: `self.otps` can be used to override values passed to the Twilio Client.
+        Read docs from `twilio.rest.Client.__init__` to discover possible args.
+
+        For example, `opts = {"region": "...", "user_agent_extensions": ["..."], }`
+        """
+        self.account_sid = account_sid
+        self.auth_token = auth_token
+        self.from_ = from_
+        self.messaging_service_sid = messaging_service_sid
+        self.opts = opts
+
+
+class SMSContent:
+    def __init__(self, body: str, to_phone: str) -> None:
+        self.body = body
+        self.to_phone = to_phone
+
+
+class TwilioServiceInterface(ABC, Generic[_T]):
+    def __init__(self, twilio_client: Client) -> None:  # type: ignore
+        self.twilio_client = twilio_client  # type: ignore
+
+    @abstractmethod
+    async def send_raw_sms(
+        self,
+        content: SMSContent,
+        user_context: Dict[str, Any],
+        from_: Union[str, None] = None,
+        messaging_service_sid: Union[str, None] = None,
+    ) -> None:
+        pass
+
+    @abstractmethod
+    async def get_content(
+        self, template_vars: _T, user_context: Dict[str, Any]
+    ) -> SMSContent:
+        pass
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class SMSContent +(body: str, to_phone: str) +
+
+
+
+ +Expand source code + +
class SMSContent:
+    def __init__(self, body: str, to_phone: str) -> None:
+        self.body = body
+        self.to_phone = to_phone
+
+
+
+class SMSDeliveryConfig +(service: Optional[SMSDeliveryInterface[~_T]] = None, override: Optional[Callable[[SMSDeliveryInterface[~_T]], SMSDeliveryInterface[~_T]]] = None) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class SMSDeliveryConfig(ABC, Generic[_T]):
+    def __init__(
+        self,
+        service: Union[SMSDeliveryInterface[_T], None] = None,
+        override: Union[
+            Callable[[SMSDeliveryInterface[_T]], SMSDeliveryInterface[_T]], None
+        ] = None,
+    ) -> None:
+        self.service = service
+        self.override = override
+
+

Ancestors

+
    +
  • abc.ABC
  • +
  • typing.Generic
  • +
+
+
+class SMSDeliveryConfigWithService +(service: SMSDeliveryInterface[~_T], override: Optional[Callable[[SMSDeliveryInterface[~_T]], SMSDeliveryInterface[~_T]]] = None) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class SMSDeliveryConfigWithService(ABC, Generic[_T]):
+    def __init__(
+        self,
+        service: SMSDeliveryInterface[_T],
+        override: Union[
+            Callable[[SMSDeliveryInterface[_T]], SMSDeliveryInterface[_T]], None
+        ] = None,
+    ) -> None:
+        self.service = service
+        self.override = override
+
+

Ancestors

+
    +
  • abc.ABC
  • +
  • typing.Generic
  • +
+
+
+class SMSDeliveryInterface +(*args, **kwds) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class SMSDeliveryInterface(ABC, Generic[_T]):
+    @abstractmethod
+    async def send_sms(self, template_vars: _T, user_context: Dict[str, Any]) -> None:
+        pass
+
+

Ancestors

+
    +
  • abc.ABC
  • +
  • typing.Generic
  • +
+

Subclasses

+ +

Methods

+
+
+async def send_sms(self, template_vars: ~_T, user_context: Dict[str, Any]) ‑> None +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def send_sms(self, template_vars: _T, user_context: Dict[str, Any]) -> None:
+    pass
+
+
+
+
+
+class TwilioServiceInterface +(twilio_client: twilio.rest.Client) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class TwilioServiceInterface(ABC, Generic[_T]):
+    def __init__(self, twilio_client: Client) -> None:  # type: ignore
+        self.twilio_client = twilio_client  # type: ignore
+
+    @abstractmethod
+    async def send_raw_sms(
+        self,
+        content: SMSContent,
+        user_context: Dict[str, Any],
+        from_: Union[str, None] = None,
+        messaging_service_sid: Union[str, None] = None,
+    ) -> None:
+        pass
+
+    @abstractmethod
+    async def get_content(
+        self, template_vars: _T, user_context: Dict[str, Any]
+    ) -> SMSContent:
+        pass
+
+

Ancestors

+
    +
  • abc.ABC
  • +
  • typing.Generic
  • +
+

Subclasses

+ +

Methods

+
+
+async def get_content(self, template_vars: ~_T, user_context: Dict[str, Any]) ‑> SMSContent +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def get_content(
+    self, template_vars: _T, user_context: Dict[str, Any]
+) -> SMSContent:
+    pass
+
+
+
+async def send_raw_sms(self, content: SMSContent, user_context: Dict[str, Any], from_: Optional[str] = None, messaging_service_sid: Optional[str] = None) ‑> None +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def send_raw_sms(
+    self,
+    content: SMSContent,
+    user_context: Dict[str, Any],
+    from_: Union[str, None] = None,
+    messaging_service_sid: Union[str, None] = None,
+) -> None:
+    pass
+
+
+
+
+
+class TwilioSettings +(account_sid: str, auth_token: str, from_: Optional[str] = None, messaging_service_sid: Optional[str] = None, opts: Optional[Dict[str, Any]] = None) +
+
+

Note: self.otps can be used to override values passed to the Twilio Client. +Read docs from twilio.rest.Client.__init__ to discover possible args.

+

For example, opts = {"region": "...", "user_agent_extensions": ["..."], }

+
+ +Expand source code + +
class TwilioSettings:
+    def __init__(
+        self,
+        account_sid: str,
+        auth_token: str,
+        from_: Union[str, None] = None,
+        messaging_service_sid: Union[str, None] = None,
+        opts: Union[Dict[str, Any], None] = None,
+    ) -> None:
+        """
+        Note: `self.otps` can be used to override values passed to the Twilio Client.
+        Read docs from `twilio.rest.Client.__init__` to discover possible args.
+
+        For example, `opts = {"region": "...", "user_agent_extensions": ["..."], }`
+        """
+        self.account_sid = account_sid
+        self.auth_token = auth_token
+        self.from_ = from_
+        self.messaging_service_sid = messaging_service_sid
+        self.opts = opts
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/interfaces.html b/html/supertokens_python/interfaces.html new file mode 100644 index 000000000..422cbb3e0 --- /dev/null +++ b/html/supertokens_python/interfaces.html @@ -0,0 +1,254 @@ + + + + + + +supertokens_python.interfaces API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.interfaces

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from typing import Optional
+
+from typing_extensions import Literal
+
+
+class UnknownSupertokensUserIDError:
+    pass
+
+
+class CreateUserIdMappingOkResult:
+    pass
+
+
+class UserIdMappingAlreadyExistsError:
+    def __init__(
+        self, does_super_tokens_user_id_exist: bool, does_external_user_id_exist: str
+    ):
+        self.does_super_tokens_user_id_exist = does_super_tokens_user_id_exist
+        self.does_external_user_id_exist = does_external_user_id_exist
+
+
+UserIDTypes = Literal["SUPERTOKENS", "EXTERNAL", "ANY"]
+
+
+class GetUserIdMappingOkResult:
+    def __init__(
+        self,
+        supertokens_user_id: str,
+        external_user_id: str,
+        external_user_info: Optional[str] = None,
+    ):
+        self.supertokens_user_id = supertokens_user_id
+        self.external_user_id = external_user_id
+        self.external_user_info = external_user_info
+
+
+class UnknownMappingError:
+    pass
+
+
+class DeleteUserIdMappingOkResult:
+    def __init__(self, did_mapping_exist: bool):
+        self.did_mapping_exist = did_mapping_exist
+
+
+class UpdateOrDeleteUserIdMappingInfoOkResult:
+    pass
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class CreateUserIdMappingOkResult +
+
+
+
+ +Expand source code + +
class CreateUserIdMappingOkResult:
+    pass
+
+
+
+class DeleteUserIdMappingOkResult +(did_mapping_exist: bool) +
+
+
+
+ +Expand source code + +
class DeleteUserIdMappingOkResult:
+    def __init__(self, did_mapping_exist: bool):
+        self.did_mapping_exist = did_mapping_exist
+
+
+
+class GetUserIdMappingOkResult +(supertokens_user_id: str, external_user_id: str, external_user_info: Optional[str] = None) +
+
+
+
+ +Expand source code + +
class GetUserIdMappingOkResult:
+    def __init__(
+        self,
+        supertokens_user_id: str,
+        external_user_id: str,
+        external_user_info: Optional[str] = None,
+    ):
+        self.supertokens_user_id = supertokens_user_id
+        self.external_user_id = external_user_id
+        self.external_user_info = external_user_info
+
+
+
+class UnknownMappingError +
+
+
+
+ +Expand source code + +
class UnknownMappingError:
+    pass
+
+
+
+class UnknownSupertokensUserIDError +
+
+
+
+ +Expand source code + +
class UnknownSupertokensUserIDError:
+    pass
+
+
+
+class UpdateOrDeleteUserIdMappingInfoOkResult +
+
+
+
+ +Expand source code + +
class UpdateOrDeleteUserIdMappingInfoOkResult:
+    pass
+
+
+
+class UserIdMappingAlreadyExistsError +(does_super_tokens_user_id_exist: bool, does_external_user_id_exist: str) +
+
+
+
+ +Expand source code + +
class UserIdMappingAlreadyExistsError:
+    def __init__(
+        self, does_super_tokens_user_id_exist: bool, does_external_user_id_exist: str
+    ):
+        self.does_super_tokens_user_id_exist = does_super_tokens_user_id_exist
+        self.does_external_user_id_exist = does_external_user_id_exist
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/logger.html b/html/supertokens_python/logger.html new file mode 100644 index 000000000..cb653e598 --- /dev/null +++ b/html/supertokens_python/logger.html @@ -0,0 +1,257 @@ + + + + + + +supertokens_python.logger API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.logger

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+import json
+import logging
+from datetime import datetime
+from os import getenv, path
+from typing import Union
+
+from .constants import VERSION
+
+NAMESPACE = "com.supertokens"
+DEBUG_ENV_VAR = "SUPERTOKENS_DEBUG"
+
+supertokens_dir = path.dirname(__file__)
+
+
+def enable_debug_logging():
+    _logger.setLevel(logging.DEBUG)
+
+
+# Configure logger
+_logger = logging.getLogger(NAMESPACE)
+debug_env = getenv(DEBUG_ENV_VAR, "").lower()
+if debug_env == "1":
+    enable_debug_logging()
+
+
+def _get_log_timestamp() -> str:
+    return datetime.utcnow().isoformat()[:-3] + "Z"
+
+
+class CustomStreamHandler(logging.StreamHandler):  # type: ignore
+    def emit(self, record: logging.LogRecord):
+        relative_path = path.relpath(record.pathname, supertokens_dir)
+
+        record.msg = json.dumps(
+            {
+                "t": _get_log_timestamp(),
+                "sdkVer": VERSION,
+                "message": record.msg,
+                "file": f"{relative_path}:{record.lineno}",
+            }
+        )
+
+        return super().emit(record)
+
+
+# Add stream handler and format
+streamHandler = CustomStreamHandler()
+streamFormatter = logging.Formatter("{name} {message}\n", style="{")
+streamHandler.setFormatter(streamFormatter)
+_logger.addHandler(streamHandler)
+
+
+# The debug logger can be used like this:
+# log_debug_message("Hello")
+# Output log format:
+# com.supertokens {"t": "2022-03-24T06:28:33.659Z", "sdkVer": "0.5.1", "message": "Hello", "file": "logger.py:73"}
+
+# Export logger.debug as log_debug_message function
+log_debug_message = _logger.debug
+
+
+def get_maybe_none_as_str(o: Union[str, None]) -> str:
+    if o is None:
+        return "None"
+    return o
+
+
+
+
+
+
+
+

Functions

+
+
+def enable_debug_logging() +
+
+
+
+ +Expand source code + +
def enable_debug_logging():
+    _logger.setLevel(logging.DEBUG)
+
+
+
+def get_maybe_none_as_str(o: Optional[str]) ‑> str +
+
+
+
+ +Expand source code + +
def get_maybe_none_as_str(o: Union[str, None]) -> str:
+    if o is None:
+        return "None"
+    return o
+
+
+
+
+
+

Classes

+
+
+class CustomStreamHandler +(stream=None) +
+
+

A handler class which writes logging records, appropriately formatted, +to a stream. Note that this class does not close the stream, as +sys.stdout or sys.stderr may be used.

+

Initialize the handler.

+

If stream is not specified, sys.stderr is used.

+
+ +Expand source code + +
class CustomStreamHandler(logging.StreamHandler):  # type: ignore
+    def emit(self, record: logging.LogRecord):
+        relative_path = path.relpath(record.pathname, supertokens_dir)
+
+        record.msg = json.dumps(
+            {
+                "t": _get_log_timestamp(),
+                "sdkVer": VERSION,
+                "message": record.msg,
+                "file": f"{relative_path}:{record.lineno}",
+            }
+        )
+
+        return super().emit(record)
+
+

Ancestors

+
    +
  • logging.StreamHandler
  • +
  • logging.Handler
  • +
  • logging.Filterer
  • +
+

Methods

+
+
+def emit(self, record: logging.LogRecord) +
+
+

Emit a record.

+

If a formatter is specified, it is used to format the record. +The record is then written to the stream with a trailing newline. +If +exception information is present, it is formatted using +traceback.print_exception and appended to the stream. +If the stream +has an 'encoding' attribute, it is used to determine how to do the +output to the stream.

+
+ +Expand source code + +
def emit(self, record: logging.LogRecord):
+    relative_path = path.relpath(record.pathname, supertokens_dir)
+
+    record.msg = json.dumps(
+        {
+            "t": _get_log_timestamp(),
+            "sdkVer": VERSION,
+            "message": record.msg,
+            "file": f"{relative_path}:{record.lineno}",
+        }
+    )
+
+    return super().emit(record)
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/normalised_url_domain.html b/html/supertokens_python/normalised_url_domain.html new file mode 100644 index 000000000..392a985df --- /dev/null +++ b/html/supertokens_python/normalised_url_domain.html @@ -0,0 +1,253 @@ + + + + + + +supertokens_python.normalised_url_domain API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.normalised_url_domain

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from typing import TYPE_CHECKING
+from urllib.parse import urlparse
+
+from .utils import is_an_ip_address
+
+if TYPE_CHECKING:
+    pass
+from .exceptions import raise_general_exception
+
+
+class NormalisedURLDomain:
+    def __init__(self, url: str):
+        self.__value = normalise_domain_path_or_throw_error(url)
+
+    def get_as_string_dangerous(self):
+        return self.__value
+
+
+def normalise_domain_path_or_throw_error(
+    input_str: str, ignore_protocol: bool = False
+) -> str:
+    input_str = input_str.strip().lower()
+
+    try:
+        if (
+            (not input_str.startswith("http://"))
+            and (not input_str.startswith("https://"))
+            and (not input_str.startswith("supertokens://"))
+        ):
+            raise Exception("converting to proper URL")
+        url_obj = urlparse(input_str)
+
+        if ignore_protocol:
+            if url_obj.hostname is None:
+                raise Exception("Should never come here")
+            if url_obj.hostname.startswith("localhost") or is_an_ip_address(
+                url_obj.hostname
+            ):
+                input_str = "http://" + url_obj.netloc
+            else:
+                input_str = "https://" + url_obj.netloc
+        else:
+            input_str = url_obj.scheme + "://" + url_obj.netloc
+
+        return input_str
+    except Exception:
+        pass
+
+    if input_str.startswith("/"):
+        raise_general_exception("Please provide a valid domain name")
+
+    if input_str.startswith("."):
+        input_str = input_str[1:]
+
+    if (
+        ("." in input_str or input_str.startswith("localhost"))
+        and (not input_str.startswith("http://"))
+        and (not input_str.startswith("https://"))
+    ):
+        input_str = "https://" + input_str
+        try:
+            urlparse(input_str)
+            return normalise_domain_path_or_throw_error(input_str, True)
+        except Exception:
+            pass
+    raise_general_exception("Please provide a valid domain name")
+
+
+
+
+
+
+
+

Functions

+
+
+def normalise_domain_path_or_throw_error(input_str: str, ignore_protocol: bool = False) ‑> str +
+
+
+
+ +Expand source code + +
def normalise_domain_path_or_throw_error(
+    input_str: str, ignore_protocol: bool = False
+) -> str:
+    input_str = input_str.strip().lower()
+
+    try:
+        if (
+            (not input_str.startswith("http://"))
+            and (not input_str.startswith("https://"))
+            and (not input_str.startswith("supertokens://"))
+        ):
+            raise Exception("converting to proper URL")
+        url_obj = urlparse(input_str)
+
+        if ignore_protocol:
+            if url_obj.hostname is None:
+                raise Exception("Should never come here")
+            if url_obj.hostname.startswith("localhost") or is_an_ip_address(
+                url_obj.hostname
+            ):
+                input_str = "http://" + url_obj.netloc
+            else:
+                input_str = "https://" + url_obj.netloc
+        else:
+            input_str = url_obj.scheme + "://" + url_obj.netloc
+
+        return input_str
+    except Exception:
+        pass
+
+    if input_str.startswith("/"):
+        raise_general_exception("Please provide a valid domain name")
+
+    if input_str.startswith("."):
+        input_str = input_str[1:]
+
+    if (
+        ("." in input_str or input_str.startswith("localhost"))
+        and (not input_str.startswith("http://"))
+        and (not input_str.startswith("https://"))
+    ):
+        input_str = "https://" + input_str
+        try:
+            urlparse(input_str)
+            return normalise_domain_path_or_throw_error(input_str, True)
+        except Exception:
+            pass
+    raise_general_exception("Please provide a valid domain name")
+
+
+
+
+
+

Classes

+
+
+class NormalisedURLDomain +(url: str) +
+
+
+
+ +Expand source code + +
class NormalisedURLDomain:
+    def __init__(self, url: str):
+        self.__value = normalise_domain_path_or_throw_error(url)
+
+    def get_as_string_dangerous(self):
+        return self.__value
+
+

Methods

+
+
+def get_as_string_dangerous(self) +
+
+
+
+ +Expand source code + +
def get_as_string_dangerous(self):
+    return self.__value
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/normalised_url_path.html b/html/supertokens_python/normalised_url_path.html new file mode 100644 index 000000000..d36078bb8 --- /dev/null +++ b/html/supertokens_python/normalised_url_path.html @@ -0,0 +1,369 @@ + + + + + + +supertokens_python.normalised_url_path API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.normalised_url_path

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from __future__ import annotations
+
+from typing import TYPE_CHECKING
+from urllib.parse import urlparse
+
+if TYPE_CHECKING:
+    pass
+from .exceptions import raise_general_exception
+
+
+class NormalisedURLPath:
+    def __init__(self, url: str):
+        self.__value = normalise_url_path_or_throw_error(url)
+
+    def startswith(self, other: NormalisedURLPath) -> bool:
+        return self.__value.startswith(other.get_as_string_dangerous())
+
+    def append(self, other: NormalisedURLPath) -> NormalisedURLPath:
+        return NormalisedURLPath(self.__value + other.get_as_string_dangerous())
+
+    def get_as_string_dangerous(self) -> str:
+        return self.__value
+
+    def equals(self, other: NormalisedURLPath) -> bool:
+        return self.__value == other.get_as_string_dangerous()
+
+    def is_a_recipe_path(self) -> bool:
+        parts = self.__value.split("/")
+        return (len(parts) > 1 and parts[1] == "recipe") or (
+            len(parts) > 2 and parts[2] == "recipe"
+        )
+
+
+def normalise_url_path_or_throw_error(input_str: str) -> str:
+    input_str = input_str.strip().lower()
+
+    try:
+        if (not input_str.startswith("http://")) and (
+            not input_str.startswith("https://")
+        ):
+            raise Exception("converting to proper URL")
+        url_obj = urlparse(input_str)
+        input_str = url_obj.path
+
+        if input_str.endswith("/"):
+            return input_str[:-1]
+
+        return input_str
+    except Exception:
+        pass
+
+    if (
+        (domain_given(input_str) or input_str.startswith("localhost"))
+        and (not input_str.startswith("http://"))
+        and (not input_str.startswith("https://"))
+    ):
+        input_str = "http://" + input_str
+        return normalise_url_path_or_throw_error(input_str)
+
+    if not input_str.startswith("/"):
+        input_str = "/" + input_str
+
+    try:
+        urlparse("http://example.com" + input_str)
+        return normalise_url_path_or_throw_error("http://example.com" + input_str)
+    except Exception:
+        raise_general_exception("Please provide a valid URL path")
+
+
+def domain_given(input_str: str) -> bool:
+    if ("." not in input_str) or (input_str.startswith("/")):
+        return False
+
+    try:
+        url = urlparse(input_str)
+        if url.hostname is None:
+            raise Exception("Should never come here")
+        return url.hostname.find(".") != -1
+    except Exception:
+        pass
+
+    try:
+        url = urlparse("http://" + input_str)
+        if url.hostname is None:
+            raise Exception("Should never come here")
+        return url.hostname.find(".") != -1
+    except Exception:
+        pass
+
+    return False
+
+
+
+
+
+
+
+

Functions

+
+
+def domain_given(input_str: str) ‑> bool +
+
+
+
+ +Expand source code + +
def domain_given(input_str: str) -> bool:
+    if ("." not in input_str) or (input_str.startswith("/")):
+        return False
+
+    try:
+        url = urlparse(input_str)
+        if url.hostname is None:
+            raise Exception("Should never come here")
+        return url.hostname.find(".") != -1
+    except Exception:
+        pass
+
+    try:
+        url = urlparse("http://" + input_str)
+        if url.hostname is None:
+            raise Exception("Should never come here")
+        return url.hostname.find(".") != -1
+    except Exception:
+        pass
+
+    return False
+
+
+
+def normalise_url_path_or_throw_error(input_str: str) ‑> str +
+
+
+
+ +Expand source code + +
def normalise_url_path_or_throw_error(input_str: str) -> str:
+    input_str = input_str.strip().lower()
+
+    try:
+        if (not input_str.startswith("http://")) and (
+            not input_str.startswith("https://")
+        ):
+            raise Exception("converting to proper URL")
+        url_obj = urlparse(input_str)
+        input_str = url_obj.path
+
+        if input_str.endswith("/"):
+            return input_str[:-1]
+
+        return input_str
+    except Exception:
+        pass
+
+    if (
+        (domain_given(input_str) or input_str.startswith("localhost"))
+        and (not input_str.startswith("http://"))
+        and (not input_str.startswith("https://"))
+    ):
+        input_str = "http://" + input_str
+        return normalise_url_path_or_throw_error(input_str)
+
+    if not input_str.startswith("/"):
+        input_str = "/" + input_str
+
+    try:
+        urlparse("http://example.com" + input_str)
+        return normalise_url_path_or_throw_error("http://example.com" + input_str)
+    except Exception:
+        raise_general_exception("Please provide a valid URL path")
+
+
+
+
+
+

Classes

+
+
+class NormalisedURLPath +(url: str) +
+
+
+
+ +Expand source code + +
class NormalisedURLPath:
+    def __init__(self, url: str):
+        self.__value = normalise_url_path_or_throw_error(url)
+
+    def startswith(self, other: NormalisedURLPath) -> bool:
+        return self.__value.startswith(other.get_as_string_dangerous())
+
+    def append(self, other: NormalisedURLPath) -> NormalisedURLPath:
+        return NormalisedURLPath(self.__value + other.get_as_string_dangerous())
+
+    def get_as_string_dangerous(self) -> str:
+        return self.__value
+
+    def equals(self, other: NormalisedURLPath) -> bool:
+        return self.__value == other.get_as_string_dangerous()
+
+    def is_a_recipe_path(self) -> bool:
+        parts = self.__value.split("/")
+        return (len(parts) > 1 and parts[1] == "recipe") or (
+            len(parts) > 2 and parts[2] == "recipe"
+        )
+
+

Methods

+
+
+def append(self, other: NormalisedURLPath) ‑> NormalisedURLPath +
+
+
+
+ +Expand source code + +
def append(self, other: NormalisedURLPath) -> NormalisedURLPath:
+    return NormalisedURLPath(self.__value + other.get_as_string_dangerous())
+
+
+
+def equals(self, other: NormalisedURLPath) ‑> bool +
+
+
+
+ +Expand source code + +
def equals(self, other: NormalisedURLPath) -> bool:
+    return self.__value == other.get_as_string_dangerous()
+
+
+
+def get_as_string_dangerous(self) ‑> str +
+
+
+
+ +Expand source code + +
def get_as_string_dangerous(self) -> str:
+    return self.__value
+
+
+
+def is_a_recipe_path(self) ‑> bool +
+
+
+
+ +Expand source code + +
def is_a_recipe_path(self) -> bool:
+    parts = self.__value.split("/")
+    return (len(parts) > 1 and parts[1] == "recipe") or (
+        len(parts) > 2 and parts[2] == "recipe"
+    )
+
+
+
+def startswith(self, other: NormalisedURLPath) ‑> bool +
+
+
+
+ +Expand source code + +
def startswith(self, other: NormalisedURLPath) -> bool:
+    return self.__value.startswith(other.get_as_string_dangerous())
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/post_init_callbacks.html b/html/supertokens_python/post_init_callbacks.html new file mode 100644 index 000000000..d474456f6 --- /dev/null +++ b/html/supertokens_python/post_init_callbacks.html @@ -0,0 +1,172 @@ + + + + + + +supertokens_python.post_init_callbacks API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.post_init_callbacks

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from typing import Callable, List
+
+
+class PostSTInitCallbacks:
+    """Callbacks that are called after the SuperTokens instance is initialized."""
+
+    callbacks: List[Callable[[], None]] = []
+
+    @staticmethod
+    def add_post_init_callback(cb: Callable[[], None]) -> None:
+        PostSTInitCallbacks.callbacks.append(cb)
+
+    @staticmethod
+    def run_post_init_callbacks() -> None:
+        for cb in PostSTInitCallbacks.callbacks:
+            cb()
+
+        PostSTInitCallbacks.callbacks = []
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class PostSTInitCallbacks +
+
+

Callbacks that are called after the SuperTokens instance is initialized.

+
+ +Expand source code + +
class PostSTInitCallbacks:
+    """Callbacks that are called after the SuperTokens instance is initialized."""
+
+    callbacks: List[Callable[[], None]] = []
+
+    @staticmethod
+    def add_post_init_callback(cb: Callable[[], None]) -> None:
+        PostSTInitCallbacks.callbacks.append(cb)
+
+    @staticmethod
+    def run_post_init_callbacks() -> None:
+        for cb in PostSTInitCallbacks.callbacks:
+            cb()
+
+        PostSTInitCallbacks.callbacks = []
+
+

Class variables

+
+
var callbacks : List[Callable[[], None]]
+
+
+
+
+

Static methods

+
+
+def add_post_init_callback(cb: Callable[[], None]) ‑> None +
+
+
+
+ +Expand source code + +
@staticmethod
+def add_post_init_callback(cb: Callable[[], None]) -> None:
+    PostSTInitCallbacks.callbacks.append(cb)
+
+
+
+def run_post_init_callbacks() ‑> None +
+
+
+
+ +Expand source code + +
@staticmethod
+def run_post_init_callbacks() -> None:
+    for cb in PostSTInitCallbacks.callbacks:
+        cb()
+
+    PostSTInitCallbacks.callbacks = []
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/process_state.html b/html/supertokens_python/process_state.html new file mode 100644 index 000000000..fbde96fd3 --- /dev/null +++ b/html/supertokens_python/process_state.html @@ -0,0 +1,243 @@ + + + + + + +supertokens_python.process_state API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.process_state

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from os import environ
+from typing import List
+from enum import Enum
+
+
+class AllowedProcessStates(Enum):
+    CALLING_SERVICE_IN_VERIFY = 1
+    CALLING_SERVICE_IN_GET_HANDSHAKE_INFO = 2
+    CALLING_SERVICE_IN_GET_API_VERSION = 3
+    CALLING_SERVICE_IN_REQUEST_HELPER = 4
+
+
+class ProcessState:
+    __instance = None
+
+    def __init__(self):
+        self.history: List[AllowedProcessStates] = []
+
+    @staticmethod
+    def get_instance():
+        if ProcessState.__instance is None:
+            ProcessState.__instance = ProcessState()
+        return ProcessState.__instance
+
+    def add_state(self, state: AllowedProcessStates):
+        if ("SUPERTOKENS_ENV" in environ) and (environ["SUPERTOKENS_ENV"] == "testing"):
+            self.history.append(state)
+
+    def reset(self):
+        self.history = []
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class AllowedProcessStates +(value, names=None, *, module=None, qualname=None, type=None, start=1) +
+
+

An enumeration.

+
+ +Expand source code + +
class AllowedProcessStates(Enum):
+    CALLING_SERVICE_IN_VERIFY = 1
+    CALLING_SERVICE_IN_GET_HANDSHAKE_INFO = 2
+    CALLING_SERVICE_IN_GET_API_VERSION = 3
+    CALLING_SERVICE_IN_REQUEST_HELPER = 4
+
+

Ancestors

+
    +
  • enum.Enum
  • +
+

Class variables

+
+
var CALLING_SERVICE_IN_GET_API_VERSION
+
+
+
+
var CALLING_SERVICE_IN_GET_HANDSHAKE_INFO
+
+
+
+
var CALLING_SERVICE_IN_REQUEST_HELPER
+
+
+
+
var CALLING_SERVICE_IN_VERIFY
+
+
+
+
+
+
+class ProcessState +
+
+
+
+ +Expand source code + +
class ProcessState:
+    __instance = None
+
+    def __init__(self):
+        self.history: List[AllowedProcessStates] = []
+
+    @staticmethod
+    def get_instance():
+        if ProcessState.__instance is None:
+            ProcessState.__instance = ProcessState()
+        return ProcessState.__instance
+
+    def add_state(self, state: AllowedProcessStates):
+        if ("SUPERTOKENS_ENV" in environ) and (environ["SUPERTOKENS_ENV"] == "testing"):
+            self.history.append(state)
+
+    def reset(self):
+        self.history = []
+
+

Static methods

+
+
+def get_instance() +
+
+
+
+ +Expand source code + +
@staticmethod
+def get_instance():
+    if ProcessState.__instance is None:
+        ProcessState.__instance = ProcessState()
+    return ProcessState.__instance
+
+
+
+

Methods

+
+
+def add_state(self, state: AllowedProcessStates) +
+
+
+
+ +Expand source code + +
def add_state(self, state: AllowedProcessStates):
+    if ("SUPERTOKENS_ENV" in environ) and (environ["SUPERTOKENS_ENV"] == "testing"):
+        self.history.append(state)
+
+
+
+def reset(self) +
+
+
+
+ +Expand source code + +
def reset(self):
+    self.history = []
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/querier.html b/html/supertokens_python/querier.html new file mode 100644 index 000000000..6b3b7a147 --- /dev/null +++ b/html/supertokens_python/querier.html @@ -0,0 +1,1661 @@ + + + + + + +supertokens_python.querier API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.querier

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+import asyncio
+from json import JSONDecodeError
+from os import environ
+from typing import TYPE_CHECKING, Any, Awaitable, Callable, Dict, Optional, Tuple
+
+from httpx import AsyncClient, ConnectTimeout, NetworkError, Response
+
+from .constants import (
+    API_KEY_HEADER,
+    API_VERSION,
+    API_VERSION_HEADER,
+    RID_KEY_HEADER,
+    SUPPORTED_CDI_VERSIONS,
+    RATE_LIMIT_STATUS_CODE,
+)
+from .normalised_url_path import NormalisedURLPath
+
+if TYPE_CHECKING:
+    from .supertokens import Host
+
+from typing import List, Set, Union
+
+from .process_state import AllowedProcessStates, ProcessState
+from .utils import find_max_version, is_4xx_error, is_5xx_error
+from sniffio import AsyncLibraryNotFoundError
+from supertokens_python.async_to_sync_wrapper import create_or_get_event_loop
+from supertokens_python.utils import get_timestamp_ms
+
+
+class Querier:
+    __init_called = False
+    __hosts: List[Host] = []
+    __api_key: Union[None, str] = None
+    api_version = None
+    __last_tried_index: int = 0
+    __hosts_alive_for_testing: Set[str] = set()
+    network_interceptor: Optional[
+        Callable[
+            [
+                str,
+                str,
+                Dict[str, Any],
+                Optional[Dict[str, Any]],
+                Optional[Dict[str, Any]],
+                Optional[Dict[str, Any]],
+            ],
+            Tuple[
+                str,
+                str,
+                Dict[str, Any],
+                Optional[Dict[str, Any]],
+                Optional[Dict[str, Any]],
+            ],
+        ]
+    ] = None
+    __global_cache_tag = get_timestamp_ms()
+    __disable_cache = False
+
+    def __init__(self, hosts: List[Host], rid_to_core: Union[None, str] = None):
+        self.__hosts = hosts
+        self.__rid_to_core = None
+        self.__global_cache_tag = get_timestamp_ms()
+        if rid_to_core is not None:
+            self.__rid_to_core = rid_to_core
+
+    @staticmethod
+    def reset():
+        if ("SUPERTOKENS_ENV" not in environ) or (
+            environ["SUPERTOKENS_ENV"] != "testing"
+        ):
+            raise Exception("calling testing function in non testing env")
+        Querier.__init_called = False
+
+    @staticmethod
+    def get_hosts_alive_for_testing():
+        if ("SUPERTOKENS_ENV" not in environ) or (
+            environ["SUPERTOKENS_ENV"] != "testing"
+        ):
+            raise Exception("calling testing function in non testing env")
+        return Querier.__hosts_alive_for_testing
+
+    async def api_request(
+        self,
+        url: str,
+        method: str,
+        attempts_remaining: int,
+        *args: Any,
+        **kwargs: Any,
+    ) -> Response:
+        if attempts_remaining == 0:
+            raise Exception("Retry request failed")
+
+        try:
+            async with AsyncClient(timeout=30.0) as client:
+                if method == "GET":
+                    return await client.get(url, *args, **kwargs)  # type: ignore
+                if method == "POST":
+                    return await client.post(url, *args, **kwargs)  # type: ignore
+                if method == "PUT":
+                    return await client.put(url, *args, **kwargs)  # type: ignore
+                if method == "DELETE":
+                    return await client.delete(url, *args, **kwargs)  # type: ignore
+                raise Exception("Shouldn't come here")
+        except AsyncLibraryNotFoundError:
+            # Retry
+            loop = create_or_get_event_loop()
+            return loop.run_until_complete(
+                self.api_request(url, method, attempts_remaining - 1, *args, **kwargs)
+            )
+
+    async def get_api_version(self, user_context: Union[Dict[str, Any], None] = None):
+        if user_context is None:
+            user_context = {}
+
+        if Querier.api_version is not None:
+            return Querier.api_version
+
+        ProcessState.get_instance().add_state(
+            AllowedProcessStates.CALLING_SERVICE_IN_GET_API_VERSION
+        )
+
+        async def f(url: str, method: str) -> Response:
+            from supertokens_python.supertokens import (
+                Supertokens,
+                get_request_from_user_context,
+            )
+
+            headers = {}
+            if Querier.__api_key is not None:
+                headers = {API_KEY_HEADER: Querier.__api_key}
+
+            # Get app info
+            app_info = Supertokens.get_instance().app_info
+
+            req = get_request_from_user_context(user_context)
+            website_domain = app_info.get_origin(req, user_context)
+            # Prepare query parameters
+            query_params = {
+                "apiDomain": app_info.api_domain.get_as_string_dangerous(),
+                "websiteDomain": website_domain.get_as_string_dangerous(),
+            }
+
+            if Querier.network_interceptor is not None:
+                (
+                    url,
+                    method,
+                    headers,
+                    query_params,
+                    _,
+                ) = Querier.network_interceptor(  # pylint:disable=not-callable
+                    url, method, headers, query_params, {}, user_context
+                )
+
+            return await self.api_request(
+                url, method, 2, headers=headers, params=query_params
+            )
+
+        response = await self.__send_request_helper(
+            NormalisedURLPath(API_VERSION), "GET", f, len(self.__hosts)
+        )
+        cdi_supported_by_server = response["versions"]
+        api_version = find_max_version(cdi_supported_by_server, SUPPORTED_CDI_VERSIONS)
+
+        if api_version is None:
+            raise Exception(
+                "The running SuperTokens core version is not compatible with this python "
+                "SDK. Please visit https://supertokens.io/docs/community/compatibility-table "
+                "to find the right versions"
+            )
+
+        Querier.api_version = api_version
+        return Querier.api_version
+
+    @staticmethod
+    def get_instance(rid_to_core: Union[str, None] = None):
+        if not Querier.__init_called:
+            raise Exception(
+                "Please call the supertokens.init function before using SuperTokens"
+            )
+        return Querier(Querier.__hosts, rid_to_core)
+
+    @staticmethod
+    def init(
+        hosts: List[Host],
+        api_key: Union[str, None] = None,
+        network_interceptor: Optional[
+            Callable[
+                [
+                    str,
+                    str,
+                    Dict[str, Any],
+                    Optional[Dict[str, Any]],
+                    Optional[Dict[str, Any]],
+                    Optional[Dict[str, Any]],
+                ],
+                Tuple[
+                    str,
+                    str,
+                    Dict[str, Any],
+                    Optional[Dict[str, Any]],
+                    Optional[Dict[str, Any]],
+                ],
+            ]
+        ] = None,
+        disable_cache: bool = False,
+    ):
+        if not Querier.__init_called:
+            Querier.__init_called = True
+            Querier.__hosts = hosts
+            Querier.__api_key = api_key
+            Querier.api_version = None
+            Querier.__last_tried_index = 0
+            Querier.__hosts_alive_for_testing = set()
+            Querier.network_interceptor = network_interceptor
+            Querier.__disable_cache = disable_cache
+
+    async def __get_headers_with_api_version(
+        self, path: NormalisedURLPath, user_context: Union[Dict[str, Any], None]
+    ):
+        headers = {API_VERSION_HEADER: await self.get_api_version(user_context)}
+        if Querier.__api_key is not None:
+            headers = {**headers, API_KEY_HEADER: Querier.__api_key}
+        if path.is_a_recipe_path() and self.__rid_to_core is not None:
+            headers = {**headers, RID_KEY_HEADER: self.__rid_to_core}
+        return headers
+
+    async def send_get_request(
+        self,
+        path: NormalisedURLPath,
+        params: Union[Dict[str, Any], None],
+        user_context: Union[Dict[str, Any], None],
+    ) -> Dict[str, Any]:
+        if params is None:
+            params = {}
+
+        async def f(url: str, method: str) -> Response:
+            headers = await self.__get_headers_with_api_version(path, user_context)
+            nonlocal params
+
+            assert params is not None
+
+            # Sort the keys for deterministic order
+            sorted_keys = sorted(params.keys())
+            sorted_header_keys = sorted(headers.keys())
+
+            # Start with the path as the unique key
+            unique_key = path.get_as_string_dangerous()
+
+            # Append sorted params to the unique key
+            for key in sorted_keys:
+                value = params[key]
+                unique_key += f";{key}={value}"
+
+            # Append a separator for headers
+            unique_key += ";hdrs"
+
+            # Append sorted headers to the unique key
+            for key in sorted_header_keys:
+                value = headers[key]
+                unique_key += f";{key}={value}"
+
+            if user_context is not None:
+                if (
+                    user_context.get("_default", {}).get("global_cache_tag", -1)
+                    != self.__global_cache_tag
+                ):
+                    self.invalidate_core_call_cache(user_context, False)
+
+                if not Querier.__disable_cache and unique_key in user_context.get(
+                    "_default", {}
+                ).get("core_call_cache", {}):
+                    return user_context["_default"]["core_call_cache"][unique_key]
+
+            if Querier.network_interceptor is not None:
+                (
+                    url,
+                    method,
+                    headers,
+                    params,
+                    _,
+                ) = Querier.network_interceptor(  # pylint:disable=not-callable
+                    url, method, headers, params, {}, user_context
+                )
+
+            response = await self.api_request(
+                url,
+                method,
+                2,
+                headers=headers,
+                params=params,
+            )
+
+            if (
+                response.status_code == 200
+                and not Querier.__disable_cache
+                and user_context is not None
+            ):
+                user_context["_default"] = {
+                    **user_context.get("_default", {}),
+                    "core_call_cache": {
+                        **user_context.get("_default", {}).get("core_call_cache", {}),
+                        unique_key: response,
+                    },
+                    "global_cache_tag": self.__global_cache_tag,
+                }
+
+            return response
+
+        return await self.__send_request_helper(path, "GET", f, len(self.__hosts))
+
+    async def send_post_request(
+        self,
+        path: NormalisedURLPath,
+        data: Union[Dict[str, Any], None],
+        user_context: Union[Dict[str, Any], None],
+        test: bool = False,
+    ) -> Dict[str, Any]:
+        self.invalidate_core_call_cache(user_context)
+        if data is None:
+            data = {}
+
+        if (
+            ("SUPERTOKENS_ENV" in environ)
+            and (environ["SUPERTOKENS_ENV"] == "testing")
+            and test
+        ):
+            return data
+
+        headers = await self.__get_headers_with_api_version(path, user_context)
+        headers["content-type"] = "application/json; charset=utf-8"
+
+        async def f(url: str, method: str) -> Response:
+            nonlocal headers, data
+            if Querier.network_interceptor is not None:
+                (
+                    url,
+                    method,
+                    headers,
+                    _,
+                    data,
+                ) = Querier.network_interceptor(  # pylint:disable=not-callable
+                    url, method, headers, {}, data, user_context
+                )
+            return await self.api_request(
+                url,
+                method,
+                2,
+                headers=headers,
+                json=data,
+            )
+
+        return await self.__send_request_helper(path, "POST", f, len(self.__hosts))
+
+    async def send_delete_request(
+        self,
+        path: NormalisedURLPath,
+        params: Union[Dict[str, Any], None],
+        user_context: Union[Dict[str, Any], None],
+    ) -> Dict[str, Any]:
+        self.invalidate_core_call_cache(user_context)
+        if params is None:
+            params = {}
+
+        async def f(url: str, method: str) -> Response:
+            headers = await self.__get_headers_with_api_version(path, user_context)
+            nonlocal params
+            if Querier.network_interceptor is not None:
+                (
+                    url,
+                    method,
+                    headers,
+                    params,
+                    _,
+                ) = Querier.network_interceptor(  # pylint:disable=not-callable
+                    url, method, headers, params, {}, user_context
+                )
+            return await self.api_request(
+                url,
+                method,
+                2,
+                headers=headers,
+                params=params,
+            )
+
+        return await self.__send_request_helper(path, "DELETE", f, len(self.__hosts))
+
+    async def send_put_request(
+        self,
+        path: NormalisedURLPath,
+        data: Union[Dict[str, Any], None],
+        user_context: Union[Dict[str, Any], None],
+    ) -> Dict[str, Any]:
+        self.invalidate_core_call_cache(user_context)
+        if data is None:
+            data = {}
+
+        headers = await self.__get_headers_with_api_version(path, user_context)
+        headers["content-type"] = "application/json; charset=utf-8"
+
+        async def f(url: str, method: str) -> Response:
+            nonlocal headers, data
+            if Querier.network_interceptor is not None:
+                (
+                    url,
+                    method,
+                    headers,
+                    _,
+                    data,
+                ) = Querier.network_interceptor(  # pylint:disable=not-callable
+                    url, method, headers, {}, data, user_context
+                )
+            return await self.api_request(url, method, 2, headers=headers, json=data)
+
+        return await self.__send_request_helper(path, "PUT", f, len(self.__hosts))
+
+    def invalidate_core_call_cache(
+        self,
+        user_context: Union[Dict[str, Any], None],
+        upd_global_cache_tag_if_necessary: bool = True,
+    ):
+        if user_context is None:
+            # this is done so that the code below runs as expected.
+            # It will reset the __global_cache_tag if needed, and the
+            # stuff we assign to the user_context will just be ignored (as expected)
+            user_context = {}
+
+        if upd_global_cache_tag_if_necessary and (
+            user_context.get("_default", {}).get("keep_cache_alive", False) is not True
+        ):
+            # there can be race conditions here, but i think we can ignore them.
+            self.__global_cache_tag = get_timestamp_ms()
+
+        user_context["_default"] = {
+            **user_context.get("_default", {}),
+            "core_call_cache": {},
+        }
+
+    def get_all_core_urls_for_path(self, path: str) -> List[str]:
+        normalized_path = NormalisedURLPath(path)
+
+        result: List[str] = []
+
+        for h in self.__hosts:
+            current_domain = h.domain.get_as_string_dangerous()
+            current_base_path = h.base_path.get_as_string_dangerous()
+
+            result.append(
+                current_domain
+                + current_base_path
+                + normalized_path.get_as_string_dangerous()
+            )
+        return result
+
+    async def __send_request_helper(
+        self,
+        path: NormalisedURLPath,
+        method: str,
+        http_function: Callable[[str, str], Awaitable[Response]],
+        no_of_tries: int,
+        retry_info_map: Optional[Dict[str, int]] = None,
+    ) -> Dict[str, Any]:
+        if no_of_tries == 0:
+            raise Exception("No SuperTokens core available to query")
+
+        try:
+            current_host_domain = self.__hosts[
+                Querier.__last_tried_index
+            ].domain.get_as_string_dangerous()
+            current_host_base_path = self.__hosts[
+                Querier.__last_tried_index
+            ].base_path.get_as_string_dangerous()
+            current_host: str = current_host_domain + current_host_base_path
+            Querier.__last_tried_index += 1
+            Querier.__last_tried_index %= len(self.__hosts)
+            url = current_host + path.get_as_string_dangerous()
+
+            max_retries = 5
+
+            if retry_info_map is None:
+                retry_info_map = {}
+
+            if retry_info_map.get(url) is None:
+                retry_info_map[url] = max_retries
+
+            ProcessState.get_instance().add_state(
+                AllowedProcessStates.CALLING_SERVICE_IN_REQUEST_HELPER
+            )
+            response = await http_function(url, method)
+            if ("SUPERTOKENS_ENV" in environ) and (
+                environ["SUPERTOKENS_ENV"] == "testing"
+            ):
+                Querier.__hosts_alive_for_testing.add(current_host)
+
+            if response.status_code == RATE_LIMIT_STATUS_CODE:
+                retries_left = retry_info_map[url]
+
+                if retries_left > 0:
+                    retry_info_map[url] = retries_left - 1
+
+                    attempts_made = max_retries - retries_left
+                    delay = (10 + attempts_made * 250) / 1000
+
+                    await asyncio.sleep(delay)
+                    return await self.__send_request_helper(
+                        path, method, http_function, no_of_tries, retry_info_map
+                    )
+
+            if is_4xx_error(response.status_code) or is_5xx_error(response.status_code):  # type: ignore
+                raise Exception(
+                    "SuperTokens core threw an error for a "
+                    + method
+                    + " request to path: "
+                    + path.get_as_string_dangerous()
+                    + " with status code: "
+                    + str(response.status_code)
+                    + " and message: "
+                    + response.text  # type: ignore
+                )
+
+            res: Dict[str, Any] = {"_headers": dict(response.headers)}
+
+            try:
+                res.update(response.json())
+            except JSONDecodeError:
+                res["_text"] = response.text
+
+            return res
+
+        except (ConnectionError, NetworkError, ConnectTimeout) as _:
+            return await self.__send_request_helper(
+                path, method, http_function, no_of_tries - 1, retry_info_map
+            )
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class Querier +(hosts: List[Host], rid_to_core: Union[None, str] = None) +
+
+
+
+ +Expand source code + +
class Querier:
+    __init_called = False
+    __hosts: List[Host] = []
+    __api_key: Union[None, str] = None
+    api_version = None
+    __last_tried_index: int = 0
+    __hosts_alive_for_testing: Set[str] = set()
+    network_interceptor: Optional[
+        Callable[
+            [
+                str,
+                str,
+                Dict[str, Any],
+                Optional[Dict[str, Any]],
+                Optional[Dict[str, Any]],
+                Optional[Dict[str, Any]],
+            ],
+            Tuple[
+                str,
+                str,
+                Dict[str, Any],
+                Optional[Dict[str, Any]],
+                Optional[Dict[str, Any]],
+            ],
+        ]
+    ] = None
+    __global_cache_tag = get_timestamp_ms()
+    __disable_cache = False
+
+    def __init__(self, hosts: List[Host], rid_to_core: Union[None, str] = None):
+        self.__hosts = hosts
+        self.__rid_to_core = None
+        self.__global_cache_tag = get_timestamp_ms()
+        if rid_to_core is not None:
+            self.__rid_to_core = rid_to_core
+
+    @staticmethod
+    def reset():
+        if ("SUPERTOKENS_ENV" not in environ) or (
+            environ["SUPERTOKENS_ENV"] != "testing"
+        ):
+            raise Exception("calling testing function in non testing env")
+        Querier.__init_called = False
+
+    @staticmethod
+    def get_hosts_alive_for_testing():
+        if ("SUPERTOKENS_ENV" not in environ) or (
+            environ["SUPERTOKENS_ENV"] != "testing"
+        ):
+            raise Exception("calling testing function in non testing env")
+        return Querier.__hosts_alive_for_testing
+
+    async def api_request(
+        self,
+        url: str,
+        method: str,
+        attempts_remaining: int,
+        *args: Any,
+        **kwargs: Any,
+    ) -> Response:
+        if attempts_remaining == 0:
+            raise Exception("Retry request failed")
+
+        try:
+            async with AsyncClient(timeout=30.0) as client:
+                if method == "GET":
+                    return await client.get(url, *args, **kwargs)  # type: ignore
+                if method == "POST":
+                    return await client.post(url, *args, **kwargs)  # type: ignore
+                if method == "PUT":
+                    return await client.put(url, *args, **kwargs)  # type: ignore
+                if method == "DELETE":
+                    return await client.delete(url, *args, **kwargs)  # type: ignore
+                raise Exception("Shouldn't come here")
+        except AsyncLibraryNotFoundError:
+            # Retry
+            loop = create_or_get_event_loop()
+            return loop.run_until_complete(
+                self.api_request(url, method, attempts_remaining - 1, *args, **kwargs)
+            )
+
+    async def get_api_version(self, user_context: Union[Dict[str, Any], None] = None):
+        if user_context is None:
+            user_context = {}
+
+        if Querier.api_version is not None:
+            return Querier.api_version
+
+        ProcessState.get_instance().add_state(
+            AllowedProcessStates.CALLING_SERVICE_IN_GET_API_VERSION
+        )
+
+        async def f(url: str, method: str) -> Response:
+            from supertokens_python.supertokens import (
+                Supertokens,
+                get_request_from_user_context,
+            )
+
+            headers = {}
+            if Querier.__api_key is not None:
+                headers = {API_KEY_HEADER: Querier.__api_key}
+
+            # Get app info
+            app_info = Supertokens.get_instance().app_info
+
+            req = get_request_from_user_context(user_context)
+            website_domain = app_info.get_origin(req, user_context)
+            # Prepare query parameters
+            query_params = {
+                "apiDomain": app_info.api_domain.get_as_string_dangerous(),
+                "websiteDomain": website_domain.get_as_string_dangerous(),
+            }
+
+            if Querier.network_interceptor is not None:
+                (
+                    url,
+                    method,
+                    headers,
+                    query_params,
+                    _,
+                ) = Querier.network_interceptor(  # pylint:disable=not-callable
+                    url, method, headers, query_params, {}, user_context
+                )
+
+            return await self.api_request(
+                url, method, 2, headers=headers, params=query_params
+            )
+
+        response = await self.__send_request_helper(
+            NormalisedURLPath(API_VERSION), "GET", f, len(self.__hosts)
+        )
+        cdi_supported_by_server = response["versions"]
+        api_version = find_max_version(cdi_supported_by_server, SUPPORTED_CDI_VERSIONS)
+
+        if api_version is None:
+            raise Exception(
+                "The running SuperTokens core version is not compatible with this python "
+                "SDK. Please visit https://supertokens.io/docs/community/compatibility-table "
+                "to find the right versions"
+            )
+
+        Querier.api_version = api_version
+        return Querier.api_version
+
+    @staticmethod
+    def get_instance(rid_to_core: Union[str, None] = None):
+        if not Querier.__init_called:
+            raise Exception(
+                "Please call the supertokens.init function before using SuperTokens"
+            )
+        return Querier(Querier.__hosts, rid_to_core)
+
+    @staticmethod
+    def init(
+        hosts: List[Host],
+        api_key: Union[str, None] = None,
+        network_interceptor: Optional[
+            Callable[
+                [
+                    str,
+                    str,
+                    Dict[str, Any],
+                    Optional[Dict[str, Any]],
+                    Optional[Dict[str, Any]],
+                    Optional[Dict[str, Any]],
+                ],
+                Tuple[
+                    str,
+                    str,
+                    Dict[str, Any],
+                    Optional[Dict[str, Any]],
+                    Optional[Dict[str, Any]],
+                ],
+            ]
+        ] = None,
+        disable_cache: bool = False,
+    ):
+        if not Querier.__init_called:
+            Querier.__init_called = True
+            Querier.__hosts = hosts
+            Querier.__api_key = api_key
+            Querier.api_version = None
+            Querier.__last_tried_index = 0
+            Querier.__hosts_alive_for_testing = set()
+            Querier.network_interceptor = network_interceptor
+            Querier.__disable_cache = disable_cache
+
+    async def __get_headers_with_api_version(
+        self, path: NormalisedURLPath, user_context: Union[Dict[str, Any], None]
+    ):
+        headers = {API_VERSION_HEADER: await self.get_api_version(user_context)}
+        if Querier.__api_key is not None:
+            headers = {**headers, API_KEY_HEADER: Querier.__api_key}
+        if path.is_a_recipe_path() and self.__rid_to_core is not None:
+            headers = {**headers, RID_KEY_HEADER: self.__rid_to_core}
+        return headers
+
+    async def send_get_request(
+        self,
+        path: NormalisedURLPath,
+        params: Union[Dict[str, Any], None],
+        user_context: Union[Dict[str, Any], None],
+    ) -> Dict[str, Any]:
+        if params is None:
+            params = {}
+
+        async def f(url: str, method: str) -> Response:
+            headers = await self.__get_headers_with_api_version(path, user_context)
+            nonlocal params
+
+            assert params is not None
+
+            # Sort the keys for deterministic order
+            sorted_keys = sorted(params.keys())
+            sorted_header_keys = sorted(headers.keys())
+
+            # Start with the path as the unique key
+            unique_key = path.get_as_string_dangerous()
+
+            # Append sorted params to the unique key
+            for key in sorted_keys:
+                value = params[key]
+                unique_key += f";{key}={value}"
+
+            # Append a separator for headers
+            unique_key += ";hdrs"
+
+            # Append sorted headers to the unique key
+            for key in sorted_header_keys:
+                value = headers[key]
+                unique_key += f";{key}={value}"
+
+            if user_context is not None:
+                if (
+                    user_context.get("_default", {}).get("global_cache_tag", -1)
+                    != self.__global_cache_tag
+                ):
+                    self.invalidate_core_call_cache(user_context, False)
+
+                if not Querier.__disable_cache and unique_key in user_context.get(
+                    "_default", {}
+                ).get("core_call_cache", {}):
+                    return user_context["_default"]["core_call_cache"][unique_key]
+
+            if Querier.network_interceptor is not None:
+                (
+                    url,
+                    method,
+                    headers,
+                    params,
+                    _,
+                ) = Querier.network_interceptor(  # pylint:disable=not-callable
+                    url, method, headers, params, {}, user_context
+                )
+
+            response = await self.api_request(
+                url,
+                method,
+                2,
+                headers=headers,
+                params=params,
+            )
+
+            if (
+                response.status_code == 200
+                and not Querier.__disable_cache
+                and user_context is not None
+            ):
+                user_context["_default"] = {
+                    **user_context.get("_default", {}),
+                    "core_call_cache": {
+                        **user_context.get("_default", {}).get("core_call_cache", {}),
+                        unique_key: response,
+                    },
+                    "global_cache_tag": self.__global_cache_tag,
+                }
+
+            return response
+
+        return await self.__send_request_helper(path, "GET", f, len(self.__hosts))
+
+    async def send_post_request(
+        self,
+        path: NormalisedURLPath,
+        data: Union[Dict[str, Any], None],
+        user_context: Union[Dict[str, Any], None],
+        test: bool = False,
+    ) -> Dict[str, Any]:
+        self.invalidate_core_call_cache(user_context)
+        if data is None:
+            data = {}
+
+        if (
+            ("SUPERTOKENS_ENV" in environ)
+            and (environ["SUPERTOKENS_ENV"] == "testing")
+            and test
+        ):
+            return data
+
+        headers = await self.__get_headers_with_api_version(path, user_context)
+        headers["content-type"] = "application/json; charset=utf-8"
+
+        async def f(url: str, method: str) -> Response:
+            nonlocal headers, data
+            if Querier.network_interceptor is not None:
+                (
+                    url,
+                    method,
+                    headers,
+                    _,
+                    data,
+                ) = Querier.network_interceptor(  # pylint:disable=not-callable
+                    url, method, headers, {}, data, user_context
+                )
+            return await self.api_request(
+                url,
+                method,
+                2,
+                headers=headers,
+                json=data,
+            )
+
+        return await self.__send_request_helper(path, "POST", f, len(self.__hosts))
+
+    async def send_delete_request(
+        self,
+        path: NormalisedURLPath,
+        params: Union[Dict[str, Any], None],
+        user_context: Union[Dict[str, Any], None],
+    ) -> Dict[str, Any]:
+        self.invalidate_core_call_cache(user_context)
+        if params is None:
+            params = {}
+
+        async def f(url: str, method: str) -> Response:
+            headers = await self.__get_headers_with_api_version(path, user_context)
+            nonlocal params
+            if Querier.network_interceptor is not None:
+                (
+                    url,
+                    method,
+                    headers,
+                    params,
+                    _,
+                ) = Querier.network_interceptor(  # pylint:disable=not-callable
+                    url, method, headers, params, {}, user_context
+                )
+            return await self.api_request(
+                url,
+                method,
+                2,
+                headers=headers,
+                params=params,
+            )
+
+        return await self.__send_request_helper(path, "DELETE", f, len(self.__hosts))
+
+    async def send_put_request(
+        self,
+        path: NormalisedURLPath,
+        data: Union[Dict[str, Any], None],
+        user_context: Union[Dict[str, Any], None],
+    ) -> Dict[str, Any]:
+        self.invalidate_core_call_cache(user_context)
+        if data is None:
+            data = {}
+
+        headers = await self.__get_headers_with_api_version(path, user_context)
+        headers["content-type"] = "application/json; charset=utf-8"
+
+        async def f(url: str, method: str) -> Response:
+            nonlocal headers, data
+            if Querier.network_interceptor is not None:
+                (
+                    url,
+                    method,
+                    headers,
+                    _,
+                    data,
+                ) = Querier.network_interceptor(  # pylint:disable=not-callable
+                    url, method, headers, {}, data, user_context
+                )
+            return await self.api_request(url, method, 2, headers=headers, json=data)
+
+        return await self.__send_request_helper(path, "PUT", f, len(self.__hosts))
+
+    def invalidate_core_call_cache(
+        self,
+        user_context: Union[Dict[str, Any], None],
+        upd_global_cache_tag_if_necessary: bool = True,
+    ):
+        if user_context is None:
+            # this is done so that the code below runs as expected.
+            # It will reset the __global_cache_tag if needed, and the
+            # stuff we assign to the user_context will just be ignored (as expected)
+            user_context = {}
+
+        if upd_global_cache_tag_if_necessary and (
+            user_context.get("_default", {}).get("keep_cache_alive", False) is not True
+        ):
+            # there can be race conditions here, but i think we can ignore them.
+            self.__global_cache_tag = get_timestamp_ms()
+
+        user_context["_default"] = {
+            **user_context.get("_default", {}),
+            "core_call_cache": {},
+        }
+
+    def get_all_core_urls_for_path(self, path: str) -> List[str]:
+        normalized_path = NormalisedURLPath(path)
+
+        result: List[str] = []
+
+        for h in self.__hosts:
+            current_domain = h.domain.get_as_string_dangerous()
+            current_base_path = h.base_path.get_as_string_dangerous()
+
+            result.append(
+                current_domain
+                + current_base_path
+                + normalized_path.get_as_string_dangerous()
+            )
+        return result
+
+    async def __send_request_helper(
+        self,
+        path: NormalisedURLPath,
+        method: str,
+        http_function: Callable[[str, str], Awaitable[Response]],
+        no_of_tries: int,
+        retry_info_map: Optional[Dict[str, int]] = None,
+    ) -> Dict[str, Any]:
+        if no_of_tries == 0:
+            raise Exception("No SuperTokens core available to query")
+
+        try:
+            current_host_domain = self.__hosts[
+                Querier.__last_tried_index
+            ].domain.get_as_string_dangerous()
+            current_host_base_path = self.__hosts[
+                Querier.__last_tried_index
+            ].base_path.get_as_string_dangerous()
+            current_host: str = current_host_domain + current_host_base_path
+            Querier.__last_tried_index += 1
+            Querier.__last_tried_index %= len(self.__hosts)
+            url = current_host + path.get_as_string_dangerous()
+
+            max_retries = 5
+
+            if retry_info_map is None:
+                retry_info_map = {}
+
+            if retry_info_map.get(url) is None:
+                retry_info_map[url] = max_retries
+
+            ProcessState.get_instance().add_state(
+                AllowedProcessStates.CALLING_SERVICE_IN_REQUEST_HELPER
+            )
+            response = await http_function(url, method)
+            if ("SUPERTOKENS_ENV" in environ) and (
+                environ["SUPERTOKENS_ENV"] == "testing"
+            ):
+                Querier.__hosts_alive_for_testing.add(current_host)
+
+            if response.status_code == RATE_LIMIT_STATUS_CODE:
+                retries_left = retry_info_map[url]
+
+                if retries_left > 0:
+                    retry_info_map[url] = retries_left - 1
+
+                    attempts_made = max_retries - retries_left
+                    delay = (10 + attempts_made * 250) / 1000
+
+                    await asyncio.sleep(delay)
+                    return await self.__send_request_helper(
+                        path, method, http_function, no_of_tries, retry_info_map
+                    )
+
+            if is_4xx_error(response.status_code) or is_5xx_error(response.status_code):  # type: ignore
+                raise Exception(
+                    "SuperTokens core threw an error for a "
+                    + method
+                    + " request to path: "
+                    + path.get_as_string_dangerous()
+                    + " with status code: "
+                    + str(response.status_code)
+                    + " and message: "
+                    + response.text  # type: ignore
+                )
+
+            res: Dict[str, Any] = {"_headers": dict(response.headers)}
+
+            try:
+                res.update(response.json())
+            except JSONDecodeError:
+                res["_text"] = response.text
+
+            return res
+
+        except (ConnectionError, NetworkError, ConnectTimeout) as _:
+            return await self.__send_request_helper(
+                path, method, http_function, no_of_tries - 1, retry_info_map
+            )
+
+

Class variables

+
+
var api_version
+
+
+
+
var network_interceptor : Optional[Callable[[str, str, Dict[str, Any], Optional[Dict[str, Any]], Optional[Dict[str, Any]], Optional[Dict[str, Any]]], Tuple[str, str, Dict[str, Any], Optional[Dict[str, Any]], Optional[Dict[str, Any]]]]]
+
+
+
+
+

Static methods

+
+
+def get_hosts_alive_for_testing() +
+
+
+
+ +Expand source code + +
@staticmethod
+def get_hosts_alive_for_testing():
+    if ("SUPERTOKENS_ENV" not in environ) or (
+        environ["SUPERTOKENS_ENV"] != "testing"
+    ):
+        raise Exception("calling testing function in non testing env")
+    return Querier.__hosts_alive_for_testing
+
+
+
+def get_instance(rid_to_core: Union[str, None] = None) +
+
+
+
+ +Expand source code + +
@staticmethod
+def get_instance(rid_to_core: Union[str, None] = None):
+    if not Querier.__init_called:
+        raise Exception(
+            "Please call the supertokens.init function before using SuperTokens"
+        )
+    return Querier(Querier.__hosts, rid_to_core)
+
+
+
+def init(hosts: List[Host], api_key: Union[str, None] = None, network_interceptor: Optional[Callable[[str, str, Dict[str, Any], Optional[Dict[str, Any]], Optional[Dict[str, Any]], Optional[Dict[str, Any]]], Tuple[str, str, Dict[str, Any], Optional[Dict[str, Any]], Optional[Dict[str, Any]]]]] = None, disable_cache: bool = False) +
+
+
+
+ +Expand source code + +
@staticmethod
+def init(
+    hosts: List[Host],
+    api_key: Union[str, None] = None,
+    network_interceptor: Optional[
+        Callable[
+            [
+                str,
+                str,
+                Dict[str, Any],
+                Optional[Dict[str, Any]],
+                Optional[Dict[str, Any]],
+                Optional[Dict[str, Any]],
+            ],
+            Tuple[
+                str,
+                str,
+                Dict[str, Any],
+                Optional[Dict[str, Any]],
+                Optional[Dict[str, Any]],
+            ],
+        ]
+    ] = None,
+    disable_cache: bool = False,
+):
+    if not Querier.__init_called:
+        Querier.__init_called = True
+        Querier.__hosts = hosts
+        Querier.__api_key = api_key
+        Querier.api_version = None
+        Querier.__last_tried_index = 0
+        Querier.__hosts_alive_for_testing = set()
+        Querier.network_interceptor = network_interceptor
+        Querier.__disable_cache = disable_cache
+
+
+
+def reset() +
+
+
+
+ +Expand source code + +
@staticmethod
+def reset():
+    if ("SUPERTOKENS_ENV" not in environ) or (
+        environ["SUPERTOKENS_ENV"] != "testing"
+    ):
+        raise Exception("calling testing function in non testing env")
+    Querier.__init_called = False
+
+
+
+

Methods

+
+
+async def api_request(self, url: str, method: str, attempts_remaining: int, *args: Any, **kwargs: Any) ‑> httpx.Response +
+
+
+
+ +Expand source code + +
async def api_request(
+    self,
+    url: str,
+    method: str,
+    attempts_remaining: int,
+    *args: Any,
+    **kwargs: Any,
+) -> Response:
+    if attempts_remaining == 0:
+        raise Exception("Retry request failed")
+
+    try:
+        async with AsyncClient(timeout=30.0) as client:
+            if method == "GET":
+                return await client.get(url, *args, **kwargs)  # type: ignore
+            if method == "POST":
+                return await client.post(url, *args, **kwargs)  # type: ignore
+            if method == "PUT":
+                return await client.put(url, *args, **kwargs)  # type: ignore
+            if method == "DELETE":
+                return await client.delete(url, *args, **kwargs)  # type: ignore
+            raise Exception("Shouldn't come here")
+    except AsyncLibraryNotFoundError:
+        # Retry
+        loop = create_or_get_event_loop()
+        return loop.run_until_complete(
+            self.api_request(url, method, attempts_remaining - 1, *args, **kwargs)
+        )
+
+
+
+def get_all_core_urls_for_path(self, path: str) ‑> List[str] +
+
+
+
+ +Expand source code + +
def get_all_core_urls_for_path(self, path: str) -> List[str]:
+    normalized_path = NormalisedURLPath(path)
+
+    result: List[str] = []
+
+    for h in self.__hosts:
+        current_domain = h.domain.get_as_string_dangerous()
+        current_base_path = h.base_path.get_as_string_dangerous()
+
+        result.append(
+            current_domain
+            + current_base_path
+            + normalized_path.get_as_string_dangerous()
+        )
+    return result
+
+
+
+async def get_api_version(self, user_context: Union[Dict[str, Any], None] = None) +
+
+
+
+ +Expand source code + +
async def get_api_version(self, user_context: Union[Dict[str, Any], None] = None):
+    if user_context is None:
+        user_context = {}
+
+    if Querier.api_version is not None:
+        return Querier.api_version
+
+    ProcessState.get_instance().add_state(
+        AllowedProcessStates.CALLING_SERVICE_IN_GET_API_VERSION
+    )
+
+    async def f(url: str, method: str) -> Response:
+        from supertokens_python.supertokens import (
+            Supertokens,
+            get_request_from_user_context,
+        )
+
+        headers = {}
+        if Querier.__api_key is not None:
+            headers = {API_KEY_HEADER: Querier.__api_key}
+
+        # Get app info
+        app_info = Supertokens.get_instance().app_info
+
+        req = get_request_from_user_context(user_context)
+        website_domain = app_info.get_origin(req, user_context)
+        # Prepare query parameters
+        query_params = {
+            "apiDomain": app_info.api_domain.get_as_string_dangerous(),
+            "websiteDomain": website_domain.get_as_string_dangerous(),
+        }
+
+        if Querier.network_interceptor is not None:
+            (
+                url,
+                method,
+                headers,
+                query_params,
+                _,
+            ) = Querier.network_interceptor(  # pylint:disable=not-callable
+                url, method, headers, query_params, {}, user_context
+            )
+
+        return await self.api_request(
+            url, method, 2, headers=headers, params=query_params
+        )
+
+    response = await self.__send_request_helper(
+        NormalisedURLPath(API_VERSION), "GET", f, len(self.__hosts)
+    )
+    cdi_supported_by_server = response["versions"]
+    api_version = find_max_version(cdi_supported_by_server, SUPPORTED_CDI_VERSIONS)
+
+    if api_version is None:
+        raise Exception(
+            "The running SuperTokens core version is not compatible with this python "
+            "SDK. Please visit https://supertokens.io/docs/community/compatibility-table "
+            "to find the right versions"
+        )
+
+    Querier.api_version = api_version
+    return Querier.api_version
+
+
+
+def invalidate_core_call_cache(self, user_context: Union[Dict[str, Any], None], upd_global_cache_tag_if_necessary: bool = True) +
+
+
+
+ +Expand source code + +
def invalidate_core_call_cache(
+    self,
+    user_context: Union[Dict[str, Any], None],
+    upd_global_cache_tag_if_necessary: bool = True,
+):
+    if user_context is None:
+        # this is done so that the code below runs as expected.
+        # It will reset the __global_cache_tag if needed, and the
+        # stuff we assign to the user_context will just be ignored (as expected)
+        user_context = {}
+
+    if upd_global_cache_tag_if_necessary and (
+        user_context.get("_default", {}).get("keep_cache_alive", False) is not True
+    ):
+        # there can be race conditions here, but i think we can ignore them.
+        self.__global_cache_tag = get_timestamp_ms()
+
+    user_context["_default"] = {
+        **user_context.get("_default", {}),
+        "core_call_cache": {},
+    }
+
+
+
+async def send_delete_request(self, path: NormalisedURLPath, params: Union[Dict[str, Any], None], user_context: Union[Dict[str, Any], None]) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
async def send_delete_request(
+    self,
+    path: NormalisedURLPath,
+    params: Union[Dict[str, Any], None],
+    user_context: Union[Dict[str, Any], None],
+) -> Dict[str, Any]:
+    self.invalidate_core_call_cache(user_context)
+    if params is None:
+        params = {}
+
+    async def f(url: str, method: str) -> Response:
+        headers = await self.__get_headers_with_api_version(path, user_context)
+        nonlocal params
+        if Querier.network_interceptor is not None:
+            (
+                url,
+                method,
+                headers,
+                params,
+                _,
+            ) = Querier.network_interceptor(  # pylint:disable=not-callable
+                url, method, headers, params, {}, user_context
+            )
+        return await self.api_request(
+            url,
+            method,
+            2,
+            headers=headers,
+            params=params,
+        )
+
+    return await self.__send_request_helper(path, "DELETE", f, len(self.__hosts))
+
+
+
+async def send_get_request(self, path: NormalisedURLPath, params: Union[Dict[str, Any], None], user_context: Union[Dict[str, Any], None]) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
async def send_get_request(
+    self,
+    path: NormalisedURLPath,
+    params: Union[Dict[str, Any], None],
+    user_context: Union[Dict[str, Any], None],
+) -> Dict[str, Any]:
+    if params is None:
+        params = {}
+
+    async def f(url: str, method: str) -> Response:
+        headers = await self.__get_headers_with_api_version(path, user_context)
+        nonlocal params
+
+        assert params is not None
+
+        # Sort the keys for deterministic order
+        sorted_keys = sorted(params.keys())
+        sorted_header_keys = sorted(headers.keys())
+
+        # Start with the path as the unique key
+        unique_key = path.get_as_string_dangerous()
+
+        # Append sorted params to the unique key
+        for key in sorted_keys:
+            value = params[key]
+            unique_key += f";{key}={value}"
+
+        # Append a separator for headers
+        unique_key += ";hdrs"
+
+        # Append sorted headers to the unique key
+        for key in sorted_header_keys:
+            value = headers[key]
+            unique_key += f";{key}={value}"
+
+        if user_context is not None:
+            if (
+                user_context.get("_default", {}).get("global_cache_tag", -1)
+                != self.__global_cache_tag
+            ):
+                self.invalidate_core_call_cache(user_context, False)
+
+            if not Querier.__disable_cache and unique_key in user_context.get(
+                "_default", {}
+            ).get("core_call_cache", {}):
+                return user_context["_default"]["core_call_cache"][unique_key]
+
+        if Querier.network_interceptor is not None:
+            (
+                url,
+                method,
+                headers,
+                params,
+                _,
+            ) = Querier.network_interceptor(  # pylint:disable=not-callable
+                url, method, headers, params, {}, user_context
+            )
+
+        response = await self.api_request(
+            url,
+            method,
+            2,
+            headers=headers,
+            params=params,
+        )
+
+        if (
+            response.status_code == 200
+            and not Querier.__disable_cache
+            and user_context is not None
+        ):
+            user_context["_default"] = {
+                **user_context.get("_default", {}),
+                "core_call_cache": {
+                    **user_context.get("_default", {}).get("core_call_cache", {}),
+                    unique_key: response,
+                },
+                "global_cache_tag": self.__global_cache_tag,
+            }
+
+        return response
+
+    return await self.__send_request_helper(path, "GET", f, len(self.__hosts))
+
+
+
+async def send_post_request(self, path: NormalisedURLPath, data: Union[Dict[str, Any], None], user_context: Union[Dict[str, Any], None], test: bool = False) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
async def send_post_request(
+    self,
+    path: NormalisedURLPath,
+    data: Union[Dict[str, Any], None],
+    user_context: Union[Dict[str, Any], None],
+    test: bool = False,
+) -> Dict[str, Any]:
+    self.invalidate_core_call_cache(user_context)
+    if data is None:
+        data = {}
+
+    if (
+        ("SUPERTOKENS_ENV" in environ)
+        and (environ["SUPERTOKENS_ENV"] == "testing")
+        and test
+    ):
+        return data
+
+    headers = await self.__get_headers_with_api_version(path, user_context)
+    headers["content-type"] = "application/json; charset=utf-8"
+
+    async def f(url: str, method: str) -> Response:
+        nonlocal headers, data
+        if Querier.network_interceptor is not None:
+            (
+                url,
+                method,
+                headers,
+                _,
+                data,
+            ) = Querier.network_interceptor(  # pylint:disable=not-callable
+                url, method, headers, {}, data, user_context
+            )
+        return await self.api_request(
+            url,
+            method,
+            2,
+            headers=headers,
+            json=data,
+        )
+
+    return await self.__send_request_helper(path, "POST", f, len(self.__hosts))
+
+
+
+async def send_put_request(self, path: NormalisedURLPath, data: Union[Dict[str, Any], None], user_context: Union[Dict[str, Any], None]) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
async def send_put_request(
+    self,
+    path: NormalisedURLPath,
+    data: Union[Dict[str, Any], None],
+    user_context: Union[Dict[str, Any], None],
+) -> Dict[str, Any]:
+    self.invalidate_core_call_cache(user_context)
+    if data is None:
+        data = {}
+
+    headers = await self.__get_headers_with_api_version(path, user_context)
+    headers["content-type"] = "application/json; charset=utf-8"
+
+    async def f(url: str, method: str) -> Response:
+        nonlocal headers, data
+        if Querier.network_interceptor is not None:
+            (
+                url,
+                method,
+                headers,
+                _,
+                data,
+            ) = Querier.network_interceptor(  # pylint:disable=not-callable
+                url, method, headers, {}, data, user_context
+            )
+        return await self.api_request(url, method, 2, headers=headers, json=data)
+
+    return await self.__send_request_helper(path, "PUT", f, len(self.__hosts))
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/dashboard/api/analytics.html b/html/supertokens_python/recipe/dashboard/api/analytics.html new file mode 100644 index 000000000..61801cbf1 --- /dev/null +++ b/html/supertokens_python/recipe/dashboard/api/analytics.html @@ -0,0 +1,253 @@ + + + + + + +supertokens_python.recipe.dashboard.api.analytics API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.dashboard.api.analytics

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Dict, Any
+
+from httpx import AsyncClient
+
+from supertokens_python import Supertokens
+from supertokens_python.constants import (
+    TELEMETRY_SUPERTOKENS_API_URL,
+    TELEMETRY_SUPERTOKENS_API_VERSION,
+)
+from supertokens_python.constants import VERSION as SDKVersion
+from supertokens_python.exceptions import raise_bad_input_exception
+from supertokens_python.normalised_url_path import NormalisedURLPath
+from supertokens_python.querier import Querier
+
+from ..interfaces import AnalyticsResponse
+
+if TYPE_CHECKING:
+    from supertokens_python.recipe.dashboard.interfaces import APIInterface, APIOptions
+
+
+async def handle_analytics_post(
+    _: APIInterface,
+    _tenant_id: str,
+    api_options: APIOptions,
+    _user_context: Dict[str, Any],
+) -> AnalyticsResponse:
+    if not Supertokens.get_instance().telemetry:
+        return AnalyticsResponse()
+    body = await api_options.request.json()
+    if body is None:
+        raise_bad_input_exception("Please send body")
+    email = body.get("email")
+    dashboard_version = body.get("dashboardVersion")
+
+    if email is None:
+        raise_bad_input_exception("Missing required property 'email'")
+    if dashboard_version is None:
+        raise_bad_input_exception("Missing required property 'dashboardVersion'")
+
+    telemetry_id = None
+
+    try:
+        response = await Querier.get_instance().send_get_request(
+            NormalisedURLPath("/telemetry"),
+            None,
+            _user_context,
+        )
+        if "exists" in response and response["exists"] and "telemetryId" in response:
+            telemetry_id = response["telemetryId"]
+
+        number_of_users = await Supertokens.get_instance().get_user_count(
+            include_recipe_ids=None
+        )
+
+    except Exception as __:
+        # If either telemetry id API or user count fetch fails, no event should be sent
+        return AnalyticsResponse()
+
+    apiDomain, websiteDomain, appName = (
+        api_options.app_info.api_domain,
+        api_options.app_info.get_origin(api_options.request, {}),
+        api_options.app_info.app_name,
+    )
+
+    data = {
+        "websiteDomain": websiteDomain.get_as_string_dangerous(),
+        "apiDomain": apiDomain.get_as_string_dangerous(),
+        "appName": appName,
+        "sdk": "python",
+        "sdkVersion": SDKVersion,
+        "numberOfUsers": number_of_users,
+        "email": email,
+        "dashboardVersion": dashboard_version,
+    }
+
+    if telemetry_id is not None:
+        data["telemetryId"] = telemetry_id
+
+    try:
+        async with AsyncClient(timeout=30.0) as client:
+            await client.post(  # type: ignore
+                url=TELEMETRY_SUPERTOKENS_API_URL,
+                json=data,
+                headers={"api-version": TELEMETRY_SUPERTOKENS_API_VERSION},
+            )
+    except Exception as __:
+        # If telemetry event fails, no error should be thrown
+        pass
+
+    return AnalyticsResponse()
+
+
+
+
+
+
+
+

Functions

+
+
+async def handle_analytics_post(_: APIInterface, _tenant_id: str, api_options: APIOptions, _user_context: Dict[str, Any]) ‑> AnalyticsResponse +
+
+
+
+ +Expand source code + +
async def handle_analytics_post(
+    _: APIInterface,
+    _tenant_id: str,
+    api_options: APIOptions,
+    _user_context: Dict[str, Any],
+) -> AnalyticsResponse:
+    if not Supertokens.get_instance().telemetry:
+        return AnalyticsResponse()
+    body = await api_options.request.json()
+    if body is None:
+        raise_bad_input_exception("Please send body")
+    email = body.get("email")
+    dashboard_version = body.get("dashboardVersion")
+
+    if email is None:
+        raise_bad_input_exception("Missing required property 'email'")
+    if dashboard_version is None:
+        raise_bad_input_exception("Missing required property 'dashboardVersion'")
+
+    telemetry_id = None
+
+    try:
+        response = await Querier.get_instance().send_get_request(
+            NormalisedURLPath("/telemetry"),
+            None,
+            _user_context,
+        )
+        if "exists" in response and response["exists"] and "telemetryId" in response:
+            telemetry_id = response["telemetryId"]
+
+        number_of_users = await Supertokens.get_instance().get_user_count(
+            include_recipe_ids=None
+        )
+
+    except Exception as __:
+        # If either telemetry id API or user count fetch fails, no event should be sent
+        return AnalyticsResponse()
+
+    apiDomain, websiteDomain, appName = (
+        api_options.app_info.api_domain,
+        api_options.app_info.get_origin(api_options.request, {}),
+        api_options.app_info.app_name,
+    )
+
+    data = {
+        "websiteDomain": websiteDomain.get_as_string_dangerous(),
+        "apiDomain": apiDomain.get_as_string_dangerous(),
+        "appName": appName,
+        "sdk": "python",
+        "sdkVersion": SDKVersion,
+        "numberOfUsers": number_of_users,
+        "email": email,
+        "dashboardVersion": dashboard_version,
+    }
+
+    if telemetry_id is not None:
+        data["telemetryId"] = telemetry_id
+
+    try:
+        async with AsyncClient(timeout=30.0) as client:
+            await client.post(  # type: ignore
+                url=TELEMETRY_SUPERTOKENS_API_URL,
+                json=data,
+                headers={"api-version": TELEMETRY_SUPERTOKENS_API_VERSION},
+            )
+    except Exception as __:
+        # If telemetry event fails, no error should be thrown
+        pass
+
+    return AnalyticsResponse()
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/dashboard/api/dashboard.html b/html/supertokens_python/recipe/dashboard/api/dashboard.html new file mode 100644 index 000000000..95bbe7535 --- /dev/null +++ b/html/supertokens_python/recipe/dashboard/api/dashboard.html @@ -0,0 +1,127 @@ + + + + + + +supertokens_python.recipe.dashboard.api.dashboard API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.dashboard.api.dashboard

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Optional, Dict, Any
+
+from supertokens_python.framework import BaseResponse
+
+if TYPE_CHECKING:
+    from supertokens_python.recipe.dashboard.interfaces import (
+        APIOptions,
+        APIInterface,
+    )
+
+
+async def handle_dashboard_api(
+    api_implementation: APIInterface,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+) -> Optional[BaseResponse]:
+    if api_implementation.dashboard_get is None:
+        return None
+
+    html_str = await api_implementation.dashboard_get(api_options, user_context)
+
+    api_options.response.set_html_content(html_str)
+    return api_options.response
+
+
+
+
+
+
+
+

Functions

+
+
+async def handle_dashboard_api(api_implementation: APIInterface, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Optional[BaseResponse] +
+
+
+
+ +Expand source code + +
async def handle_dashboard_api(
+    api_implementation: APIInterface,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+) -> Optional[BaseResponse]:
+    if api_implementation.dashboard_get is None:
+        return None
+
+    html_str = await api_implementation.dashboard_get(api_options, user_context)
+
+    api_options.response.set_html_content(html_str)
+    return api_options.response
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/dashboard/api/implementation.html b/html/supertokens_python/recipe/dashboard/api/implementation.html new file mode 100644 index 000000000..3f76cdfa5 --- /dev/null +++ b/html/supertokens_python/recipe/dashboard/api/implementation.html @@ -0,0 +1,271 @@ + + + + + + +supertokens_python.recipe.dashboard.api.implementation API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.dashboard.api.implementation

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from string import Template
+from textwrap import dedent
+from typing import TYPE_CHECKING, Any, Dict
+
+from supertokens_python import Supertokens
+from supertokens_python.normalised_url_domain import NormalisedURLDomain
+from supertokens_python.normalised_url_path import NormalisedURLPath
+from supertokens_python.querier import Querier
+from supertokens_python.utils import is_version_gte
+
+from ..constants import DASHBOARD_API
+from ..interfaces import APIInterface
+
+if TYPE_CHECKING:
+    from ..interfaces import APIOptions
+
+
+class APIImplementation(APIInterface):
+    def __init__(self):
+        super().__init__()
+
+        async def dashboard_get(
+            options: APIOptions, user_context: Dict[str, Any]
+        ) -> str:
+            bundle_base_path_string = (
+                await options.recipe_implementation.get_dashboard_bundle_location(
+                    user_context
+                )
+            )
+            bundle_domain = (
+                NormalisedURLDomain(bundle_base_path_string).get_as_string_dangerous()
+                + NormalisedURLPath(bundle_base_path_string).get_as_string_dangerous()
+            )
+
+            connection_uri = ""
+            super_tokens_instance = Supertokens.get_instance()
+            auth_mode = options.config.auth_mode
+            connection_uri = (
+                NormalisedURLDomain(
+                    super_tokens_instance.supertokens_config.connection_uri.split(";")[
+                        0
+                    ]
+                ).get_as_string_dangerous()
+                + NormalisedURLPath(
+                    super_tokens_instance.supertokens_config.connection_uri.split(";")[
+                        0
+                    ]
+                ).get_as_string_dangerous()
+            )
+
+            dashboard_path = options.app_info.api_base_path.append(
+                NormalisedURLPath(DASHBOARD_API)
+            ).get_as_string_dangerous()
+
+            is_search_enabled: bool = False
+            querier = Querier.get_instance(options.recipe_id)
+            cdiVersion = await querier.get_api_version(user_context)
+            if not cdiVersion:
+                is_search_enabled = True
+            elif is_version_gte(cdiVersion, "2.20"):
+                is_search_enabled = True
+
+            return Template(
+                dedent(
+                    """
+                <html>
+                    <head>
+                        <meta name="viewport" content="width=device-width, initial-scale=1.0">
+                        <script>
+                            window.staticBasePath = "${bundleDomain}/static"
+                            window.dashboardAppPath = "${dashboardPath}"
+                            window.connectionURI = "${connectionURI}"
+                            window.authMode = "${authMode}"
+                            window.isSearchEnabled = "${isSearchEnabled}"
+                        </script>
+                        <script defer src="${bundleDomain}/static/js/bundle.js"></script></head>
+                        <link href="${bundleDomain}/static/css/main.css" rel="stylesheet" type="text/css">
+                        <link rel="icon" type="image/x-icon" href="${bundleDomain}/static/media/favicon.ico">
+                    </head>
+                    <body>
+                        <noscript>You need to enable JavaScript to run this app.</noscript>
+                        <div id="root"></div>
+                    </body>
+                </html>
+                """
+                )
+            ).substitute(
+                bundleDomain=bundle_domain,
+                dashboardPath=dashboard_path,
+                connectionURI=connection_uri,
+                authMode=auth_mode,
+                isSearchEnabled=str(is_search_enabled).lower(),
+            )
+
+        self.dashboard_get = dashboard_get
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class APIImplementation +
+
+
+
+ +Expand source code + +
class APIImplementation(APIInterface):
+    def __init__(self):
+        super().__init__()
+
+        async def dashboard_get(
+            options: APIOptions, user_context: Dict[str, Any]
+        ) -> str:
+            bundle_base_path_string = (
+                await options.recipe_implementation.get_dashboard_bundle_location(
+                    user_context
+                )
+            )
+            bundle_domain = (
+                NormalisedURLDomain(bundle_base_path_string).get_as_string_dangerous()
+                + NormalisedURLPath(bundle_base_path_string).get_as_string_dangerous()
+            )
+
+            connection_uri = ""
+            super_tokens_instance = Supertokens.get_instance()
+            auth_mode = options.config.auth_mode
+            connection_uri = (
+                NormalisedURLDomain(
+                    super_tokens_instance.supertokens_config.connection_uri.split(";")[
+                        0
+                    ]
+                ).get_as_string_dangerous()
+                + NormalisedURLPath(
+                    super_tokens_instance.supertokens_config.connection_uri.split(";")[
+                        0
+                    ]
+                ).get_as_string_dangerous()
+            )
+
+            dashboard_path = options.app_info.api_base_path.append(
+                NormalisedURLPath(DASHBOARD_API)
+            ).get_as_string_dangerous()
+
+            is_search_enabled: bool = False
+            querier = Querier.get_instance(options.recipe_id)
+            cdiVersion = await querier.get_api_version(user_context)
+            if not cdiVersion:
+                is_search_enabled = True
+            elif is_version_gte(cdiVersion, "2.20"):
+                is_search_enabled = True
+
+            return Template(
+                dedent(
+                    """
+                <html>
+                    <head>
+                        <meta name="viewport" content="width=device-width, initial-scale=1.0">
+                        <script>
+                            window.staticBasePath = "${bundleDomain}/static"
+                            window.dashboardAppPath = "${dashboardPath}"
+                            window.connectionURI = "${connectionURI}"
+                            window.authMode = "${authMode}"
+                            window.isSearchEnabled = "${isSearchEnabled}"
+                        </script>
+                        <script defer src="${bundleDomain}/static/js/bundle.js"></script></head>
+                        <link href="${bundleDomain}/static/css/main.css" rel="stylesheet" type="text/css">
+                        <link rel="icon" type="image/x-icon" href="${bundleDomain}/static/media/favicon.ico">
+                    </head>
+                    <body>
+                        <noscript>You need to enable JavaScript to run this app.</noscript>
+                        <div id="root"></div>
+                    </body>
+                </html>
+                """
+                )
+            ).substitute(
+                bundleDomain=bundle_domain,
+                dashboardPath=dashboard_path,
+                connectionURI=connection_uri,
+                authMode=auth_mode,
+                isSearchEnabled=str(is_search_enabled).lower(),
+            )
+
+        self.dashboard_get = dashboard_get
+
+

Ancestors

+ +
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/dashboard/api/index.html b/html/supertokens_python/recipe/dashboard/api/index.html new file mode 100644 index 000000000..b64b53d69 --- /dev/null +++ b/html/supertokens_python/recipe/dashboard/api/index.html @@ -0,0 +1,1174 @@ + + + + + + +supertokens_python.recipe.dashboard.api API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.dashboard.api

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from .analytics import handle_analytics_post
+from .api_key_protector import api_key_protector
+from .dashboard import handle_dashboard_api
+from .search.getTags import handle_get_tags
+from .signin import handle_emailpassword_signin_api
+from .signout import handle_emailpassword_signout_api
+from .userdetails.user_delete import handle_user_delete
+from .userdetails.user_email_verify_get import handle_user_email_verify_get
+from .userdetails.user_email_verify_put import handle_user_email_verify_put
+from .userdetails.user_email_verify_token_post import handle_email_verify_token_post
+from .userdetails.user_get import handle_user_get
+from .userdetails.user_metadata_get import handle_metadata_get
+from .userdetails.user_metadata_put import handle_metadata_put
+from .userdetails.user_password_put import handle_user_password_put
+from .userdetails.user_put import handle_user_put
+from .userdetails.user_sessions_get import handle_sessions_get
+from .userdetails.user_sessions_post import handle_user_sessions_post
+from .users_count_get import handle_users_count_get_api
+from .users_get import handle_users_get_api
+from .validate_key import handle_validate_key_api
+from .list_tenants import handle_list_tenants_api
+
+__all__ = [
+    "handle_dashboard_api",
+    "api_key_protector",
+    "handle_users_count_get_api",
+    "handle_users_get_api",
+    "handle_validate_key_api",
+    "handle_user_email_verify_get",
+    "handle_user_get",
+    "handle_metadata_get",
+    "handle_sessions_get",
+    "handle_user_delete",
+    "handle_user_put",
+    "handle_user_email_verify_put",
+    "handle_metadata_put",
+    "handle_user_sessions_post",
+    "handle_user_password_put",
+    "handle_email_verify_token_post",
+    "handle_emailpassword_signin_api",
+    "handle_emailpassword_signout_api",
+    "handle_get_tags",
+    "handle_analytics_post",
+    "handle_list_tenants_api",
+]
+
+
+
+

Sub-modules

+
+
supertokens_python.recipe.dashboard.api.analytics
+
+
+
+
supertokens_python.recipe.dashboard.api.dashboard
+
+
+
+
supertokens_python.recipe.dashboard.api.implementation
+
+
+
+
supertokens_python.recipe.dashboard.api.list_tenants
+
+
+
+
supertokens_python.recipe.dashboard.api.search
+
+
+
+
supertokens_python.recipe.dashboard.api.signin
+
+
+
+
supertokens_python.recipe.dashboard.api.signout
+
+
+
+
supertokens_python.recipe.dashboard.api.userdetails
+
+
+
+
supertokens_python.recipe.dashboard.api.users_count_get
+
+
+
+
supertokens_python.recipe.dashboard.api.users_get
+
+
+
+
supertokens_python.recipe.dashboard.api.validate_key
+
+
+
+
+
+
+
+
+

Functions

+
+
+async def api_key_protector(api_implementation: APIInterface, tenant_id: str, api_options: APIOptions, api_function: Callable[[APIInterface, str, APIOptions, Dict[str, Any]], Awaitable[APIResponse]], user_context: Dict[str, Any]) ‑> Optional[BaseResponse] +
+
+
+
+ +Expand source code + +
async def api_key_protector(
+    api_implementation: APIInterface,
+    tenant_id: str,
+    api_options: APIOptions,
+    api_function: Callable[
+        [APIInterface, str, APIOptions, Dict[str, Any]], Awaitable[APIResponse]
+    ],
+    user_context: Dict[str, Any],
+) -> Optional[BaseResponse]:
+    should_allow_access = False
+
+    try:
+        should_allow_access = (
+            await api_options.recipe_implementation.should_allow_access(
+                api_options.request, api_options.config, user_context
+            )
+        )
+    except DashboardOperationNotAllowedError as _:
+        return send_non_200_response_with_message(
+            "You are not permitted to perform this operation",
+            403,
+            api_options.response,
+        )
+
+    if should_allow_access is False:
+        return send_non_200_response_with_message(
+            "Unauthorised access", 401, api_options.response
+        )
+
+    response = await api_function(
+        api_implementation, tenant_id, api_options, user_context
+    )
+    return send_200_response(response.to_json(), api_options.response)
+
+
+
+async def handle_analytics_post(_: APIInterface, _tenant_id: str, api_options: APIOptions, _user_context: Dict[str, Any]) ‑> AnalyticsResponse +
+
+
+
+ +Expand source code + +
async def handle_analytics_post(
+    _: APIInterface,
+    _tenant_id: str,
+    api_options: APIOptions,
+    _user_context: Dict[str, Any],
+) -> AnalyticsResponse:
+    if not Supertokens.get_instance().telemetry:
+        return AnalyticsResponse()
+    body = await api_options.request.json()
+    if body is None:
+        raise_bad_input_exception("Please send body")
+    email = body.get("email")
+    dashboard_version = body.get("dashboardVersion")
+
+    if email is None:
+        raise_bad_input_exception("Missing required property 'email'")
+    if dashboard_version is None:
+        raise_bad_input_exception("Missing required property 'dashboardVersion'")
+
+    telemetry_id = None
+
+    try:
+        response = await Querier.get_instance().send_get_request(
+            NormalisedURLPath("/telemetry"),
+            None,
+            _user_context,
+        )
+        if "exists" in response and response["exists"] and "telemetryId" in response:
+            telemetry_id = response["telemetryId"]
+
+        number_of_users = await Supertokens.get_instance().get_user_count(
+            include_recipe_ids=None
+        )
+
+    except Exception as __:
+        # If either telemetry id API or user count fetch fails, no event should be sent
+        return AnalyticsResponse()
+
+    apiDomain, websiteDomain, appName = (
+        api_options.app_info.api_domain,
+        api_options.app_info.get_origin(api_options.request, {}),
+        api_options.app_info.app_name,
+    )
+
+    data = {
+        "websiteDomain": websiteDomain.get_as_string_dangerous(),
+        "apiDomain": apiDomain.get_as_string_dangerous(),
+        "appName": appName,
+        "sdk": "python",
+        "sdkVersion": SDKVersion,
+        "numberOfUsers": number_of_users,
+        "email": email,
+        "dashboardVersion": dashboard_version,
+    }
+
+    if telemetry_id is not None:
+        data["telemetryId"] = telemetry_id
+
+    try:
+        async with AsyncClient(timeout=30.0) as client:
+            await client.post(  # type: ignore
+                url=TELEMETRY_SUPERTOKENS_API_URL,
+                json=data,
+                headers={"api-version": TELEMETRY_SUPERTOKENS_API_VERSION},
+            )
+    except Exception as __:
+        # If telemetry event fails, no error should be thrown
+        pass
+
+    return AnalyticsResponse()
+
+
+
+async def handle_dashboard_api(api_implementation: APIInterface, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Optional[BaseResponse] +
+
+
+
+ +Expand source code + +
async def handle_dashboard_api(
+    api_implementation: APIInterface,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+) -> Optional[BaseResponse]:
+    if api_implementation.dashboard_get is None:
+        return None
+
+    html_str = await api_implementation.dashboard_get(api_options, user_context)
+
+    api_options.response.set_html_content(html_str)
+    return api_options.response
+
+
+
+async def handle_email_verify_token_post(_api_interface: APIInterface, tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[UserEmailVerifyTokenPostAPIOkResponseUserEmailVerifyTokenPostAPIEmailAlreadyVerifiedErrorResponse] +
+
+
+
+ +Expand source code + +
async def handle_email_verify_token_post(
+    _api_interface: APIInterface,
+    tenant_id: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+) -> Union[
+    UserEmailVerifyTokenPostAPIOkResponse,
+    UserEmailVerifyTokenPostAPIEmailAlreadyVerifiedErrorResponse,
+]:
+    request_body: Dict[str, Any] = await api_options.request.json()  # type: ignore
+    user_id = request_body.get("userId")
+
+    if user_id is None or not isinstance(user_id, str):
+        raise_bad_input_exception(
+            "Required parameter 'userId' is missing or has an invalid type"
+        )
+
+    res = await send_email_verification_email(
+        tenant_id=tenant_id, user_id=user_id, email=None, user_context=user_context
+    )
+
+    if isinstance(res, SendEmailVerificationEmailAlreadyVerifiedError):
+        return UserEmailVerifyTokenPostAPIEmailAlreadyVerifiedErrorResponse()
+
+    return UserEmailVerifyTokenPostAPIOkResponse()
+
+
+
+async def handle_emailpassword_signin_api(_: APIInterface, api_options: APIOptions, _user_context: Dict[str, Any]) +
+
+
+
+ +Expand source code + +
async def handle_emailpassword_signin_api(
+    _: APIInterface, api_options: APIOptions, _user_context: Dict[str, Any]
+):
+    body = await api_options.request.json()
+    if body is None:
+        raise_bad_input_exception("Please send body")
+    email = body.get("email")
+    password = body.get("password")
+
+    if email is None or not isinstance(email, str):
+        raise_bad_input_exception("Missing required parameter 'email'")
+    if password is None or not isinstance(password, str):
+        raise_bad_input_exception("Missing required parameter 'password'")
+    response = await Querier.get_instance().send_post_request(
+        NormalisedURLPath("/recipe/dashboard/signin"),
+        {"email": email, "password": password},
+        user_context=_user_context,
+    )
+
+    if "status" in response and response["status"] == "OK":
+        return send_200_response(
+            {"status": "OK", "sessionId": response["sessionId"]}, api_options.response
+        )
+    if "status" in response and response["status"] == "INVALID_CREDENTIALS_ERROR":
+        return send_200_response(
+            {"status": "INVALID_CREDENTIALS_ERROR"},
+            api_options.response,
+        )
+    if "status" in response and response["status"] == "USER_SUSPENDED_ERROR":
+        return send_200_response(
+            {"status": "USER_SUSPENDED_ERROR", "message": response["message"]},
+            api_options.response,
+        )
+
+
+
+async def handle_emailpassword_signout_api(_: APIInterface, _tenant_id: str, api_options: APIOptions, _user_context: Dict[str, Any]) ‑> SignOutOK +
+
+
+
+ +Expand source code + +
async def handle_emailpassword_signout_api(
+    _: APIInterface,
+    _tenant_id: str,
+    api_options: APIOptions,
+    _user_context: Dict[str, Any],
+) -> SignOutOK:
+    if api_options.config.auth_mode == "api-key":
+        return SignOutOK()
+    session_id_form_auth_header = api_options.request.get_header("authorization")
+    if not session_id_form_auth_header:
+        return raise_bad_input_exception(
+            "Neither 'API Key' nor 'Authorization' header was found"
+        )
+    session_id_form_auth_header = session_id_form_auth_header.split()[1]
+    await Querier.get_instance().send_delete_request(
+        NormalisedURLPath("/recipe/dashboard/session"),
+        {"sessionId": session_id_form_auth_header},
+        user_context=_user_context,
+    )
+    return SignOutOK()
+
+
+
+async def handle_get_tags(_: APIInterface, _tenant_id: str, __: APIOptions, _user_context: Dict[str, Any]) ‑> SearchTagsOK +
+
+
+
+ +Expand source code + +
async def handle_get_tags(
+    _: APIInterface, _tenant_id: str, __: APIOptions, _user_context: Dict[str, Any]
+) -> SearchTagsOK:
+    response = await Querier.get_instance().send_get_request(
+        NormalisedURLPath("/user/search/tags"), None, _user_context
+    )
+    return SearchTagsOK(tags=response["tags"])
+
+
+
+async def handle_list_tenants_api(_api_implementation: APIInterface, _tenant_id: str, _api_options: APIOptions, user_context: Dict[str, Any]) ‑> APIResponse +
+
+
+
+ +Expand source code + +
async def handle_list_tenants_api(
+    _api_implementation: APIInterface,
+    _tenant_id: str,
+    _api_options: APIOptions,
+    user_context: Dict[str, Any],
+) -> APIResponse:
+    tenants = await list_all_tenants(user_context)
+
+    final_tenants: List[DashboardListTenantItem] = []
+
+    for current_tenant in tenants.tenants:
+        dashboard_tenant = DashboardListTenantItem(
+            tenant_id=current_tenant.tenant_id,
+            emailpassword=current_tenant.emailpassword,
+            passwordless=current_tenant.passwordless,
+            third_party=current_tenant.third_party,
+        )
+        final_tenants.append(dashboard_tenant)
+
+    return DashboardListTenantsGetResponse(final_tenants)
+
+
+
+async def handle_metadata_get(_api_interface: APIInterface, _tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[UserMetadataGetAPIOkResponseFeatureNotEnabledError] +
+
+
+
+ +Expand source code + +
async def handle_metadata_get(
+    _api_interface: APIInterface,
+    _tenant_id: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+) -> Union[UserMetadataGetAPIOkResponse, FeatureNotEnabledError]:
+    user_id = api_options.request.get_query_param("userId")
+
+    if user_id is None:
+        raise_bad_input_exception("Missing required parameter 'userId'")
+
+    try:
+        UserMetadataRecipe.get_instance()
+    except Exception:
+        return FeatureNotEnabledError()
+
+    metadata_response = await get_user_metadata(user_id, user_context=user_context)
+    return UserMetadataGetAPIOkResponse(metadata_response.metadata)
+
+
+
+async def handle_metadata_put(_api_interface: APIInterface, _tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> UserMetadataPutAPIResponse +
+
+
+
+ +Expand source code + +
async def handle_metadata_put(
+    _api_interface: APIInterface,
+    _tenant_id: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+) -> UserMetadataPutAPIResponse:
+    request_body: Dict[str, Any] = await api_options.request.json()  # type: ignore
+    user_id = request_body.get("userId")
+    data = request_body.get("data")
+
+    # This is to throw an error early in case the recipe has not been initialised
+    UserMetadataRecipe.get_instance()
+
+    if user_id is None or isinstance(user_id, str) is False:
+        raise_bad_input_exception(
+            "Required parameter 'userId' is missing or has an invalid type"
+        )
+
+    if data is None or isinstance(data, str) is False:
+        raise_bad_input_exception(
+            "Required parameter 'data' is missing or has an invalid type"
+        )
+
+    parsed_data: Dict[str, Any] = {}
+    try:
+        parsed_data = json.loads(data)
+        if not isinstance(parsed_data, dict):  # type: ignore
+            raise Exception()
+
+    except Exception:
+        raise_bad_input_exception("'data' must be a valid JSON body")
+
+    # This API is meant to set the user metadata of a user. We delete the existing data
+    # before updating it because we want to make sure that shallow merging does not result
+    # in the data being incorrect
+    #
+    # For example if the old data is {test: "test", test2: "test2"} and the user wants to delete
+    # test2 from the data simply calling updateUserMetadata with {test: "test"} would not remove
+    # test2 because of shallow merging.
+    #
+    # Removing first ensures that the final data is exactly what the user wanted it to be
+
+    await clear_user_metadata(user_id, user_context)
+    await update_user_metadata(user_id, parsed_data, user_context)
+
+    return UserMetadataPutAPIResponse()
+
+
+
+async def handle_sessions_get(_api_interface: APIInterface, _tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> UserSessionsGetAPIResponse +
+
+
+
+ +Expand source code + +
async def handle_sessions_get(
+    _api_interface: APIInterface,
+    _tenant_id: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+) -> UserSessionsGetAPIResponse:
+    user_id = api_options.request.get_query_param("userId")
+
+    if user_id is None:
+        raise_bad_input_exception("Missing required parameter 'userId'")
+
+    # Passing tenant id as None sets fetch_across_all_tenants to True
+    # which is what we want here.
+    session_handles = await get_all_session_handles_for_user(
+        user_id, None, user_context
+    )
+    sessions: List[Optional[SessionInfo]] = [None for _ in session_handles]
+
+    async def call_(i: int, session_handle: str):
+        try:
+            session_response = await get_session_information(
+                session_handle, user_context
+            )
+            if session_response is not None:
+                sessions[i] = SessionInfo(session_response)
+        except Exception:
+            sessions[i] = None
+
+    session_info_promises = [
+        call_(i, handle) for i, handle in enumerate(session_handles)
+    ]
+
+    await asyncio.gather(*session_info_promises)
+
+    return UserSessionsGetAPIResponse([s for s in sessions if s is not None])
+
+
+
+async def handle_user_delete(_api_interface: APIInterface, _tenant_id: str, api_options: APIOptions, _user_context: Dict[str, Any]) ‑> UserDeleteAPIResponse +
+
+
+
+ +Expand source code + +
async def handle_user_delete(
+    _api_interface: APIInterface,
+    _tenant_id: str,
+    api_options: APIOptions,
+    _user_context: Dict[str, Any],
+) -> UserDeleteAPIResponse:
+    user_id = api_options.request.get_query_param("userId")
+
+    if user_id is None:
+        raise_bad_input_exception("Missing required parameter 'userId'")
+
+    await Supertokens.get_instance().delete_user(user_id, _user_context)
+
+    return UserDeleteAPIResponse()
+
+
+
+async def handle_user_email_verify_get(_api_interface: APIInterface, _tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[UserEmailVerifyGetAPIResponseFeatureNotEnabledError] +
+
+
+
+ +Expand source code + +
async def handle_user_email_verify_get(
+    _api_interface: APIInterface,
+    _tenant_id: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+) -> Union[UserEmailVerifyGetAPIResponse, FeatureNotEnabledError]:
+    req = api_options.request
+    user_id = req.get_query_param("userId")
+
+    if user_id is None:
+        raise_bad_input_exception("Missing required parameter 'userId'")
+
+    try:
+        EmailVerificationRecipe.get_instance()
+    except Exception:
+        return FeatureNotEnabledError()
+
+    is_verified = await is_email_verified(user_id, user_context=user_context)
+    return UserEmailVerifyGetAPIResponse(is_verified)
+
+
+
+async def handle_user_email_verify_put(_api_interface: APIInterface, tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> UserEmailVerifyPutAPIResponse +
+
+
+
+ +Expand source code + +
async def handle_user_email_verify_put(
+    _api_interface: APIInterface,
+    tenant_id: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+) -> UserEmailVerifyPutAPIResponse:
+    request_body: Dict[str, Any] = await api_options.request.json()  # type: ignore
+    user_id = request_body.get("userId")
+    verified = request_body.get("verified")
+
+    if user_id is None or not isinstance(user_id, str):
+        raise_bad_input_exception(
+            "Required parameter 'userId' is missing or has an invalid type"
+        )
+
+    if verified is None or not isinstance(verified, bool):
+        raise_bad_input_exception(
+            "Required parameter 'verified' is missing or has an invalid type"
+        )
+
+    if verified:
+        token_response = await create_email_verification_token(
+            tenant_id=tenant_id, user_id=user_id, email=None, user_context=user_context
+        )
+
+        if isinstance(
+            token_response, CreateEmailVerificationTokenEmailAlreadyVerifiedError
+        ):
+            return UserEmailVerifyPutAPIResponse()
+
+        verify_response = await verify_email_using_token(
+            tenant_id=tenant_id, token=token_response.token, user_context=user_context
+        )
+
+        if isinstance(verify_response, VerifyEmailUsingTokenInvalidTokenError):
+            # This should never happen because we consume the token immediately after creating it
+            raise Exception("Should not come here")
+
+    else:
+        await unverify_email(user_id, user_context=user_context)
+
+    return UserEmailVerifyPutAPIResponse()
+
+
+
+async def handle_user_get(_api_interface: APIInterface, _tenant_id: str, api_options: APIOptions, _user_context: Dict[str, Any]) ‑> Union[UserGetAPINoUserFoundErrorUserGetAPIOkResponseUserGetAPIRecipeNotInitialisedError] +
+
+
+
+ +Expand source code + +
async def handle_user_get(
+    _api_interface: APIInterface,
+    _tenant_id: str,
+    api_options: APIOptions,
+    _user_context: Dict[str, Any],
+) -> Union[
+    UserGetAPINoUserFoundError,
+    UserGetAPIOkResponse,
+    UserGetAPIRecipeNotInitialisedError,
+]:
+    user_id = api_options.request.get_query_param("userId")
+    recipe_id = api_options.request.get_query_param("recipeId")
+
+    if user_id is None:
+        raise_bad_input_exception("Missing required parameter 'userId'")
+
+    if recipe_id is None:
+        raise_bad_input_exception("Missing required parameter 'recipeId'")
+
+    if not is_valid_recipe_id(recipe_id):
+        raise_bad_input_exception("Invalid recipe id")
+
+    if not is_recipe_initialised(recipe_id):
+        return UserGetAPIRecipeNotInitialisedError()
+
+    user_response = await get_user_for_recipe_id(user_id, recipe_id)
+    if user_response is None:
+        return UserGetAPINoUserFoundError()
+
+    user = user_response.user
+
+    try:
+        UserMetadataRecipe.get_instance()
+    except Exception:
+        user.first_name = "FEATURE_NOT_ENABLED"
+        user.last_name = "FEATURE_NOT_ENABLED"
+
+        return UserGetAPIOkResponse(recipe_id, user)
+
+    user_metadata = await get_user_metadata(user_id, user_context=_user_context)
+    first_name = user_metadata.metadata.get("first_name", "")
+    last_name = user_metadata.metadata.get("last_name", "")
+
+    user.first_name = first_name
+    user.last_name = last_name
+
+    return UserGetAPIOkResponse(recipe_id, user)
+
+
+
+async def handle_user_password_put(_api_interface: APIInterface, tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[UserPasswordPutAPIResponseUserPasswordPutAPIInvalidPasswordErrorResponse] +
+
+
+
+ +Expand source code + +
async def handle_user_password_put(
+    _api_interface: APIInterface,
+    tenant_id: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+) -> Union[UserPasswordPutAPIResponse, UserPasswordPutAPIInvalidPasswordErrorResponse]:
+    request_body: Dict[str, Any] = await api_options.request.json()  # type: ignore
+    user_id = request_body.get("userId")
+    new_password = request_body.get("newPassword")
+
+    if user_id is None or not isinstance(user_id, str):
+        raise_bad_input_exception("Missing required parameter 'userId'")
+
+    if new_password is None or not isinstance(new_password, str):
+        raise_bad_input_exception("Missing required parameter 'newPassword'")
+
+    async def reset_password(
+        form_fields: List[NormalisedFormField],
+        create_reset_password_token: Callable[
+            [str, str, Dict[str, Any]],
+            Awaitable[
+                Union[CreateResetPasswordOkResult, CreateResetPasswordWrongUserIdError]
+            ],
+        ],
+        reset_password_using_token: Callable[
+            [str, str, str, Dict[str, Any]],
+            Awaitable[
+                Union[
+                    ResetPasswordUsingTokenOkResult,
+                    ResetPasswordUsingTokenInvalidTokenError,
+                ]
+            ],
+        ],
+    ) -> Union[
+        UserPasswordPutAPIResponse, UserPasswordPutAPIInvalidPasswordErrorResponse
+    ]:
+        password_form_field = [
+            field for field in form_fields if field.id == FORM_FIELD_PASSWORD_ID
+        ][0]
+
+        password_validation_error = await password_form_field.validate(
+            new_password, tenant_id
+        )
+
+        if password_validation_error is not None:
+            return UserPasswordPutAPIInvalidPasswordErrorResponse(
+                password_validation_error
+            )
+
+        password_reset_token = await create_reset_password_token(
+            tenant_id, user_id, user_context
+        )
+
+        if isinstance(password_reset_token, CreateResetPasswordWrongUserIdError):
+            # Techincally it can but its an edge case so we assume that it wont
+            # UNKNOWN_USER_ID_ERROR
+            raise Exception("Should never come here")
+
+        password_reset_response = await reset_password_using_token(
+            tenant_id, password_reset_token.token, new_password, user_context
+        )
+
+        if isinstance(
+            password_reset_response, ResetPasswordUsingTokenInvalidTokenError
+        ):
+            # RESET_PASSWORD_INVALID_TOKEN_ERROR
+            raise Exception("Should not come here")
+
+        return UserPasswordPutAPIResponse()
+
+    return await reset_password(
+        EmailPasswordRecipe.get_instance().config.sign_up_feature.form_fields,
+        ep_create_reset_password_token,
+        ep_reset_password_using_token,
+    )
+
+
+
+async def handle_user_put(_api_interface: APIInterface, tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[UserPutAPIOkResponseUserPutAPIInvalidEmailErrorResponseUserPutAPIEmailAlreadyExistsErrorResponseUserPutAPIInvalidPhoneErrorResponseUserPutPhoneAlreadyExistsAPIResponse] +
+
+
+
+ +Expand source code + +
async def handle_user_put(
+    _api_interface: APIInterface,
+    tenant_id: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+) -> Union[
+    UserPutAPIOkResponse,
+    UserPutAPIInvalidEmailErrorResponse,
+    UserPutAPIEmailAlreadyExistsErrorResponse,
+    UserPutAPIInvalidPhoneErrorResponse,
+    UserPutPhoneAlreadyExistsAPIResponse,
+]:
+    request_body: Dict[str, Any] = await api_options.request.json()  # type: ignore
+    user_id: Optional[str] = request_body.get("userId")
+    recipe_id: Optional[str] = request_body.get("recipeId")
+    first_name: Optional[str] = request_body.get("firstName")
+    last_name: Optional[str] = request_body.get("lastName")
+    email: Optional[str] = request_body.get("email")
+    phone: Optional[str] = request_body.get("phone")
+
+    if not isinstance(user_id, str):
+        return raise_bad_input_exception(
+            "Required parameter 'userId' is missing or has an invalid type"
+        )
+
+    if not isinstance(recipe_id, str):
+        return raise_bad_input_exception(
+            "Required parameter 'recipeId' is missing or has an invalid type"
+        )
+
+    if not is_valid_recipe_id(recipe_id):
+        raise_bad_input_exception("Invalid recipe id")
+
+    if first_name is None and not isinstance(first_name, str):
+        raise_bad_input_exception(
+            "Required parameter 'firstName' is missing or has an invalid type"
+        )
+
+    if last_name is None and not isinstance(last_name, str):
+        raise_bad_input_exception(
+            "Required parameter 'lastName' is missing or has an invalid type"
+        )
+
+    if email is None and not isinstance(email, str):
+        raise_bad_input_exception(
+            "Required parameter 'email' is missing or has an invalid type"
+        )
+
+    if phone is None and not isinstance(phone, str):
+        raise_bad_input_exception(
+            "Required parameter 'phone' is missing or has an invalid type"
+        )
+
+    user_response = await get_user_for_recipe_id(user_id, recipe_id)
+
+    if user_response is None:
+        raise Exception("Should never come here")
+
+    first_name = first_name.strip()
+    last_name = last_name.strip()
+    email = email.strip()
+    phone = phone.strip()
+
+    if first_name != "" or last_name != "":
+        is_recipe_initialized = False
+
+        try:
+            UserMetadataRecipe.get_instance()
+            is_recipe_initialized = True
+        except Exception:
+            pass
+
+        if is_recipe_initialized:
+            metadata_update: Dict[str, Any] = {}
+
+            if first_name != "":
+                metadata_update["first_name"] = first_name
+
+            if last_name != "":
+                metadata_update["last_name"] = last_name
+
+            await update_user_metadata(user_id, metadata_update, user_context)
+
+    if email != "":
+        email_update_response = await update_email_for_recipe_id(
+            user_response.recipe, user_id, email, tenant_id, user_context
+        )
+
+        if not isinstance(email_update_response, UserPutAPIOkResponse):
+            return email_update_response
+
+    if phone != "":
+        phone_update_response = await update_phone_for_recipe_id(
+            user_response.recipe, user_id, phone, tenant_id, user_context
+        )
+
+        if not isinstance(phone_update_response, UserPutAPIOkResponse):
+            return phone_update_response
+
+    return UserPutAPIOkResponse()
+
+
+
+async def handle_user_sessions_post(_api_interface: APIInterface, _tenant_id: str, api_options: APIOptions, _user_context: Dict[str, Any]) ‑> UserSessionsPostAPIResponse +
+
+
+
+ +Expand source code + +
async def handle_user_sessions_post(
+    _api_interface: APIInterface,
+    _tenant_id: str,
+    api_options: APIOptions,
+    _user_context: Dict[str, Any],
+) -> UserSessionsPostAPIResponse:
+    request_body = await api_options.request.json()  # type: ignore
+    session_handles: Optional[List[str]] = request_body.get("sessionHandles")  # type: ignore
+
+    if not isinstance(session_handles, list):
+        raise_bad_input_exception(
+            "Required parameter 'sessionHandles' is missing or has an invalid type"
+        )
+
+    await revoke_multiple_sessions(session_handles, _user_context)
+    return UserSessionsPostAPIResponse()
+
+
+
+async def handle_users_count_get_api(_: APIInterface, tenant_id: str, _api_options: APIOptions, _user_context: Dict[str, Any]) ‑> UserCountGetAPIResponse +
+
+
+
+ +Expand source code + +
async def handle_users_count_get_api(
+    _: APIInterface,
+    tenant_id: str,
+    _api_options: APIOptions,
+    _user_context: Dict[str, Any],
+) -> UserCountGetAPIResponse:
+    count = await Supertokens.get_instance().get_user_count(
+        None,
+        tenant_id,
+    )
+    return UserCountGetAPIResponse(count=count)
+
+
+
+async def handle_users_get_api(api_implementation: APIInterface, tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> APIResponse +
+
+
+
+ +Expand source code + +
async def handle_users_get_api(
+    api_implementation: APIInterface,
+    tenant_id: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+) -> APIResponse:
+    _ = api_implementation
+
+    limit = api_options.request.get_query_param("limit")
+    if limit is None:
+        raise_bad_input_exception("Missing required parameter 'limit'")
+
+    time_joined_order: Literal["ASC", "DESC"] = api_options.request.get_query_param(  # type: ignore
+        "timeJoinedOrder", "DESC"
+    )
+    if time_joined_order not in ["ASC", "DESC"]:
+        raise_bad_input_exception("Invalid value recieved for 'timeJoinedOrder'")
+
+    pagination_token = api_options.request.get_query_param("paginationToken")
+
+    users_response = await Supertokens.get_instance().get_users(
+        tenant_id,
+        time_joined_order=time_joined_order,
+        limit=int(limit),
+        pagination_token=pagination_token,
+        include_recipe_ids=None,
+        query=api_options.request.get_query_params(),
+        user_context=user_context,
+    )
+
+    # user metadata bulk fetch with batches:
+
+    try:
+        UserMetadataRecipe.get_instance()
+    except GeneralError:
+        return DashboardUsersGetResponse(
+            users_response.users, users_response.next_pagination_token
+        )
+
+    users_with_metadata: List[UserWithMetadata] = [
+        UserWithMetadata().from_user(user) for user in users_response.users
+    ]
+    metadata_fetch_awaitables: List[Awaitable[Any]] = []
+
+    async def get_user_metadata_and_update_user(user_idx: int) -> None:
+        user = users_response.users[user_idx]
+        user_metadata = await get_user_metadata(user.user_id, user_context)
+        first_name = user_metadata.metadata.get("first_name")
+        last_name = user_metadata.metadata.get("last_name")
+
+        # None becomes null which is acceptable for the dashboard.
+        users_with_metadata[user_idx].first_name = first_name
+        users_with_metadata[user_idx].last_name = last_name
+
+    # Batch calls to get user metadata:
+    for i, _ in enumerate(users_response.users):
+        metadata_fetch_awaitables.append(get_user_metadata_and_update_user(i))
+
+    promise_arr_start_position = 0
+    batch_size = 5
+
+    while promise_arr_start_position < len(metadata_fetch_awaitables):
+        # We want to query only 5 in parallel at a time
+        promises_to_call = [
+            metadata_fetch_awaitables[i]
+            for i in range(
+                promise_arr_start_position,
+                min(
+                    promise_arr_start_position + batch_size,
+                    len(metadata_fetch_awaitables),
+                ),
+            )
+        ]
+        await asyncio.gather(*promises_to_call)
+
+        promise_arr_start_position += batch_size
+
+    return DashboardUsersGetResponse(
+        users_with_metadata,
+        users_response.next_pagination_token,
+    )
+
+
+
+async def handle_validate_key_api(_api_implementation: APIInterface, api_options: APIOptions, user_context: Dict[str, Any]) +
+
+
+
+ +Expand source code + +
async def handle_validate_key_api(
+    _api_implementation: APIInterface,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+):
+
+    is_valid_key = await validate_api_key(
+        api_options.request, api_options.config, user_context
+    )
+
+    if is_valid_key:
+        return send_200_response({"status": "OK"}, api_options.response)
+    return send_non_200_response_with_message("Unauthorised", 401, api_options.response)
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/dashboard/api/list_tenants.html b/html/supertokens_python/recipe/dashboard/api/list_tenants.html new file mode 100644 index 000000000..aff46e79b --- /dev/null +++ b/html/supertokens_python/recipe/dashboard/api/list_tenants.html @@ -0,0 +1,150 @@ + + + + + + +supertokens_python.recipe.dashboard.api.list_tenants API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.dashboard.api.list_tenants

+
+
+
+ +Expand source code + +
# Copyright (c) 2023, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Any, Dict, List
+
+from supertokens_python.recipe.dashboard.interfaces import DashboardListTenantItem
+
+if TYPE_CHECKING:
+    from supertokens_python.recipe.dashboard.interfaces import (
+        APIOptions,
+        APIInterface,
+    )
+    from supertokens_python.types import APIResponse
+
+from supertokens_python.recipe.multitenancy.asyncio import list_all_tenants
+from supertokens_python.recipe.dashboard.interfaces import (
+    DashboardListTenantsGetResponse,
+)
+
+
+async def handle_list_tenants_api(
+    _api_implementation: APIInterface,
+    _tenant_id: str,
+    _api_options: APIOptions,
+    user_context: Dict[str, Any],
+) -> APIResponse:
+    tenants = await list_all_tenants(user_context)
+
+    final_tenants: List[DashboardListTenantItem] = []
+
+    for current_tenant in tenants.tenants:
+        dashboard_tenant = DashboardListTenantItem(
+            tenant_id=current_tenant.tenant_id,
+            emailpassword=current_tenant.emailpassword,
+            passwordless=current_tenant.passwordless,
+            third_party=current_tenant.third_party,
+        )
+        final_tenants.append(dashboard_tenant)
+
+    return DashboardListTenantsGetResponse(final_tenants)
+
+
+
+
+
+
+
+

Functions

+
+
+async def handle_list_tenants_api(_api_implementation: APIInterface, _tenant_id: str, _api_options: APIOptions, user_context: Dict[str, Any]) ‑> APIResponse +
+
+
+
+ +Expand source code + +
async def handle_list_tenants_api(
+    _api_implementation: APIInterface,
+    _tenant_id: str,
+    _api_options: APIOptions,
+    user_context: Dict[str, Any],
+) -> APIResponse:
+    tenants = await list_all_tenants(user_context)
+
+    final_tenants: List[DashboardListTenantItem] = []
+
+    for current_tenant in tenants.tenants:
+        dashboard_tenant = DashboardListTenantItem(
+            tenant_id=current_tenant.tenant_id,
+            emailpassword=current_tenant.emailpassword,
+            passwordless=current_tenant.passwordless,
+            third_party=current_tenant.third_party,
+        )
+        final_tenants.append(dashboard_tenant)
+
+    return DashboardListTenantsGetResponse(final_tenants)
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/dashboard/api/search/getTags.html b/html/supertokens_python/recipe/dashboard/api/search/getTags.html new file mode 100644 index 000000000..cefc743c7 --- /dev/null +++ b/html/supertokens_python/recipe/dashboard/api/search/getTags.html @@ -0,0 +1,116 @@ + + + + + + +supertokens_python.recipe.dashboard.api.search.getTags API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.dashboard.api.search.getTags

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Dict, Any
+
+if TYPE_CHECKING:
+    from supertokens_python.recipe.dashboard.interfaces import APIInterface, APIOptions
+
+from supertokens_python.normalised_url_path import NormalisedURLPath
+from supertokens_python.querier import Querier
+from supertokens_python.recipe.dashboard.interfaces import SearchTagsOK
+
+
+async def handle_get_tags(
+    _: APIInterface, _tenant_id: str, __: APIOptions, _user_context: Dict[str, Any]
+) -> SearchTagsOK:
+    response = await Querier.get_instance().send_get_request(
+        NormalisedURLPath("/user/search/tags"), None, _user_context
+    )
+    return SearchTagsOK(tags=response["tags"])
+
+
+
+
+
+
+
+

Functions

+
+
+async def handle_get_tags(_: APIInterface, _tenant_id: str, __: APIOptions, _user_context: Dict[str, Any]) ‑> SearchTagsOK +
+
+
+
+ +Expand source code + +
async def handle_get_tags(
+    _: APIInterface, _tenant_id: str, __: APIOptions, _user_context: Dict[str, Any]
+) -> SearchTagsOK:
+    response = await Querier.get_instance().send_get_request(
+        NormalisedURLPath("/user/search/tags"), None, _user_context
+    )
+    return SearchTagsOK(tags=response["tags"])
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/dashboard/api/search/index.html b/html/supertokens_python/recipe/dashboard/api/search/index.html new file mode 100644 index 000000000..1ef92da40 --- /dev/null +++ b/html/supertokens_python/recipe/dashboard/api/search/index.html @@ -0,0 +1,65 @@ + + + + + + +supertokens_python.recipe.dashboard.api.search API documentation + + + + + + + + + + + +
+ + +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/dashboard/api/signin.html b/html/supertokens_python/recipe/dashboard/api/signin.html new file mode 100644 index 000000000..508a1f47f --- /dev/null +++ b/html/supertokens_python/recipe/dashboard/api/signin.html @@ -0,0 +1,169 @@ + + + + + + +supertokens_python.recipe.dashboard.api.signin API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.dashboard.api.signin

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Dict, Any
+
+if TYPE_CHECKING:
+    from supertokens_python.recipe.dashboard.interfaces import APIInterface, APIOptions
+
+from supertokens_python.exceptions import raise_bad_input_exception
+from supertokens_python.normalised_url_path import NormalisedURLPath
+from supertokens_python.querier import Querier
+from supertokens_python.utils import send_200_response
+
+
+async def handle_emailpassword_signin_api(
+    _: APIInterface, api_options: APIOptions, _user_context: Dict[str, Any]
+):
+    body = await api_options.request.json()
+    if body is None:
+        raise_bad_input_exception("Please send body")
+    email = body.get("email")
+    password = body.get("password")
+
+    if email is None or not isinstance(email, str):
+        raise_bad_input_exception("Missing required parameter 'email'")
+    if password is None or not isinstance(password, str):
+        raise_bad_input_exception("Missing required parameter 'password'")
+    response = await Querier.get_instance().send_post_request(
+        NormalisedURLPath("/recipe/dashboard/signin"),
+        {"email": email, "password": password},
+        user_context=_user_context,
+    )
+
+    if "status" in response and response["status"] == "OK":
+        return send_200_response(
+            {"status": "OK", "sessionId": response["sessionId"]}, api_options.response
+        )
+    if "status" in response and response["status"] == "INVALID_CREDENTIALS_ERROR":
+        return send_200_response(
+            {"status": "INVALID_CREDENTIALS_ERROR"},
+            api_options.response,
+        )
+    if "status" in response and response["status"] == "USER_SUSPENDED_ERROR":
+        return send_200_response(
+            {"status": "USER_SUSPENDED_ERROR", "message": response["message"]},
+            api_options.response,
+        )
+
+
+
+
+
+
+
+

Functions

+
+
+async def handle_emailpassword_signin_api(_: APIInterface, api_options: APIOptions, _user_context: Dict[str, Any]) +
+
+
+
+ +Expand source code + +
async def handle_emailpassword_signin_api(
+    _: APIInterface, api_options: APIOptions, _user_context: Dict[str, Any]
+):
+    body = await api_options.request.json()
+    if body is None:
+        raise_bad_input_exception("Please send body")
+    email = body.get("email")
+    password = body.get("password")
+
+    if email is None or not isinstance(email, str):
+        raise_bad_input_exception("Missing required parameter 'email'")
+    if password is None or not isinstance(password, str):
+        raise_bad_input_exception("Missing required parameter 'password'")
+    response = await Querier.get_instance().send_post_request(
+        NormalisedURLPath("/recipe/dashboard/signin"),
+        {"email": email, "password": password},
+        user_context=_user_context,
+    )
+
+    if "status" in response and response["status"] == "OK":
+        return send_200_response(
+            {"status": "OK", "sessionId": response["sessionId"]}, api_options.response
+        )
+    if "status" in response and response["status"] == "INVALID_CREDENTIALS_ERROR":
+        return send_200_response(
+            {"status": "INVALID_CREDENTIALS_ERROR"},
+            api_options.response,
+        )
+    if "status" in response and response["status"] == "USER_SUSPENDED_ERROR":
+        return send_200_response(
+            {"status": "USER_SUSPENDED_ERROR", "message": response["message"]},
+            api_options.response,
+        )
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/dashboard/api/signout.html b/html/supertokens_python/recipe/dashboard/api/signout.html new file mode 100644 index 000000000..ed886c577 --- /dev/null +++ b/html/supertokens_python/recipe/dashboard/api/signout.html @@ -0,0 +1,144 @@ + + + + + + +supertokens_python.recipe.dashboard.api.signout API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.dashboard.api.signout

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Dict, Any
+
+if TYPE_CHECKING:
+    from supertokens_python.recipe.dashboard.interfaces import APIInterface, APIOptions
+
+from supertokens_python.exceptions import raise_bad_input_exception
+from supertokens_python.normalised_url_path import NormalisedURLPath
+from supertokens_python.querier import Querier
+
+from ..interfaces import SignOutOK
+
+
+async def handle_emailpassword_signout_api(
+    _: APIInterface,
+    _tenant_id: str,
+    api_options: APIOptions,
+    _user_context: Dict[str, Any],
+) -> SignOutOK:
+    if api_options.config.auth_mode == "api-key":
+        return SignOutOK()
+    session_id_form_auth_header = api_options.request.get_header("authorization")
+    if not session_id_form_auth_header:
+        return raise_bad_input_exception(
+            "Neither 'API Key' nor 'Authorization' header was found"
+        )
+    session_id_form_auth_header = session_id_form_auth_header.split()[1]
+    await Querier.get_instance().send_delete_request(
+        NormalisedURLPath("/recipe/dashboard/session"),
+        {"sessionId": session_id_form_auth_header},
+        user_context=_user_context,
+    )
+    return SignOutOK()
+
+
+
+
+
+
+
+

Functions

+
+
+async def handle_emailpassword_signout_api(_: APIInterface, _tenant_id: str, api_options: APIOptions, _user_context: Dict[str, Any]) ‑> SignOutOK +
+
+
+
+ +Expand source code + +
async def handle_emailpassword_signout_api(
+    _: APIInterface,
+    _tenant_id: str,
+    api_options: APIOptions,
+    _user_context: Dict[str, Any],
+) -> SignOutOK:
+    if api_options.config.auth_mode == "api-key":
+        return SignOutOK()
+    session_id_form_auth_header = api_options.request.get_header("authorization")
+    if not session_id_form_auth_header:
+        return raise_bad_input_exception(
+            "Neither 'API Key' nor 'Authorization' header was found"
+        )
+    session_id_form_auth_header = session_id_form_auth_header.split()[1]
+    await Querier.get_instance().send_delete_request(
+        NormalisedURLPath("/recipe/dashboard/session"),
+        {"sessionId": session_id_form_auth_header},
+        user_context=_user_context,
+    )
+    return SignOutOK()
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/dashboard/api/userdetails/index.html b/html/supertokens_python/recipe/dashboard/api/userdetails/index.html new file mode 100644 index 000000000..dc3547f9f --- /dev/null +++ b/html/supertokens_python/recipe/dashboard/api/userdetails/index.html @@ -0,0 +1,115 @@ + + + + + + +supertokens_python.recipe.dashboard.api.userdetails API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.dashboard.api.userdetails

+
+
+
+
+

Sub-modules

+
+
supertokens_python.recipe.dashboard.api.userdetails.user_delete
+
+
+
+
supertokens_python.recipe.dashboard.api.userdetails.user_email_verify_get
+
+
+
+
supertokens_python.recipe.dashboard.api.userdetails.user_email_verify_put
+
+
+
+
supertokens_python.recipe.dashboard.api.userdetails.user_email_verify_token_post
+
+
+
+
supertokens_python.recipe.dashboard.api.userdetails.user_get
+
+
+
+
supertokens_python.recipe.dashboard.api.userdetails.user_metadata_get
+
+
+
+
supertokens_python.recipe.dashboard.api.userdetails.user_metadata_put
+
+
+
+
supertokens_python.recipe.dashboard.api.userdetails.user_password_put
+
+
+
+
supertokens_python.recipe.dashboard.api.userdetails.user_put
+
+
+
+
supertokens_python.recipe.dashboard.api.userdetails.user_sessions_get
+
+
+
+
supertokens_python.recipe.dashboard.api.userdetails.user_sessions_post
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/dashboard/api/userdetails/user_delete.html b/html/supertokens_python/recipe/dashboard/api/userdetails/user_delete.html new file mode 100644 index 000000000..0bb3f956f --- /dev/null +++ b/html/supertokens_python/recipe/dashboard/api/userdetails/user_delete.html @@ -0,0 +1,112 @@ + + + + + + +supertokens_python.recipe.dashboard.api.userdetails.user_delete API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.dashboard.api.userdetails.user_delete

+
+
+
+ +Expand source code + +
from typing import Any, Dict
+
+from ...interfaces import APIInterface, APIOptions, UserDeleteAPIResponse
+from supertokens_python.exceptions import raise_bad_input_exception
+from supertokens_python import Supertokens
+
+
+async def handle_user_delete(
+    _api_interface: APIInterface,
+    _tenant_id: str,
+    api_options: APIOptions,
+    _user_context: Dict[str, Any],
+) -> UserDeleteAPIResponse:
+    user_id = api_options.request.get_query_param("userId")
+
+    if user_id is None:
+        raise_bad_input_exception("Missing required parameter 'userId'")
+
+    await Supertokens.get_instance().delete_user(user_id, _user_context)
+
+    return UserDeleteAPIResponse()
+
+
+
+
+
+
+
+

Functions

+
+
+async def handle_user_delete(_api_interface: APIInterface, _tenant_id: str, api_options: APIOptions, _user_context: Dict[str, Any]) ‑> UserDeleteAPIResponse +
+
+
+
+ +Expand source code + +
async def handle_user_delete(
+    _api_interface: APIInterface,
+    _tenant_id: str,
+    api_options: APIOptions,
+    _user_context: Dict[str, Any],
+) -> UserDeleteAPIResponse:
+    user_id = api_options.request.get_query_param("userId")
+
+    if user_id is None:
+        raise_bad_input_exception("Missing required parameter 'userId'")
+
+    await Supertokens.get_instance().delete_user(user_id, _user_context)
+
+    return UserDeleteAPIResponse()
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/dashboard/api/userdetails/user_email_verify_get.html b/html/supertokens_python/recipe/dashboard/api/userdetails/user_email_verify_get.html new file mode 100644 index 000000000..bb41398b2 --- /dev/null +++ b/html/supertokens_python/recipe/dashboard/api/userdetails/user_email_verify_get.html @@ -0,0 +1,128 @@ + + + + + + +supertokens_python.recipe.dashboard.api.userdetails.user_email_verify_get API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.dashboard.api.userdetails.user_email_verify_get

+
+
+
+ +Expand source code + +
from supertokens_python.exceptions import raise_bad_input_exception
+from supertokens_python.recipe.emailverification import EmailVerificationRecipe
+from supertokens_python.recipe.emailverification.asyncio import is_email_verified
+from ...interfaces import (
+    APIInterface,
+    APIOptions,
+    UserEmailVerifyGetAPIResponse,
+    FeatureNotEnabledError,
+)
+
+from typing import Union, Dict, Any
+
+
+async def handle_user_email_verify_get(
+    _api_interface: APIInterface,
+    _tenant_id: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+) -> Union[UserEmailVerifyGetAPIResponse, FeatureNotEnabledError]:
+    req = api_options.request
+    user_id = req.get_query_param("userId")
+
+    if user_id is None:
+        raise_bad_input_exception("Missing required parameter 'userId'")
+
+    try:
+        EmailVerificationRecipe.get_instance()
+    except Exception:
+        return FeatureNotEnabledError()
+
+    is_verified = await is_email_verified(user_id, user_context=user_context)
+    return UserEmailVerifyGetAPIResponse(is_verified)
+
+
+
+
+
+
+
+

Functions

+
+
+async def handle_user_email_verify_get(_api_interface: APIInterface, _tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[UserEmailVerifyGetAPIResponseFeatureNotEnabledError] +
+
+
+
+ +Expand source code + +
async def handle_user_email_verify_get(
+    _api_interface: APIInterface,
+    _tenant_id: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+) -> Union[UserEmailVerifyGetAPIResponse, FeatureNotEnabledError]:
+    req = api_options.request
+    user_id = req.get_query_param("userId")
+
+    if user_id is None:
+        raise_bad_input_exception("Missing required parameter 'userId'")
+
+    try:
+        EmailVerificationRecipe.get_instance()
+    except Exception:
+        return FeatureNotEnabledError()
+
+    is_verified = await is_email_verified(user_id, user_context=user_context)
+    return UserEmailVerifyGetAPIResponse(is_verified)
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/dashboard/api/userdetails/user_email_verify_put.html b/html/supertokens_python/recipe/dashboard/api/userdetails/user_email_verify_put.html new file mode 100644 index 000000000..a3dfba940 --- /dev/null +++ b/html/supertokens_python/recipe/dashboard/api/userdetails/user_email_verify_put.html @@ -0,0 +1,181 @@ + + + + + + +supertokens_python.recipe.dashboard.api.userdetails.user_email_verify_put API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.dashboard.api.userdetails.user_email_verify_put

+
+
+
+ +Expand source code + +
from typing import Any, Dict
+
+from supertokens_python.exceptions import raise_bad_input_exception
+from supertokens_python.recipe.emailverification.asyncio import (
+    create_email_verification_token,
+    unverify_email,
+    verify_email_using_token,
+)
+from supertokens_python.recipe.emailverification.interfaces import (
+    CreateEmailVerificationTokenEmailAlreadyVerifiedError,
+    VerifyEmailUsingTokenInvalidTokenError,
+)
+
+from ...interfaces import (
+    APIInterface,
+    APIOptions,
+    UserEmailVerifyPutAPIResponse,
+)
+
+
+async def handle_user_email_verify_put(
+    _api_interface: APIInterface,
+    tenant_id: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+) -> UserEmailVerifyPutAPIResponse:
+    request_body: Dict[str, Any] = await api_options.request.json()  # type: ignore
+    user_id = request_body.get("userId")
+    verified = request_body.get("verified")
+
+    if user_id is None or not isinstance(user_id, str):
+        raise_bad_input_exception(
+            "Required parameter 'userId' is missing or has an invalid type"
+        )
+
+    if verified is None or not isinstance(verified, bool):
+        raise_bad_input_exception(
+            "Required parameter 'verified' is missing or has an invalid type"
+        )
+
+    if verified:
+        token_response = await create_email_verification_token(
+            tenant_id=tenant_id, user_id=user_id, email=None, user_context=user_context
+        )
+
+        if isinstance(
+            token_response, CreateEmailVerificationTokenEmailAlreadyVerifiedError
+        ):
+            return UserEmailVerifyPutAPIResponse()
+
+        verify_response = await verify_email_using_token(
+            tenant_id=tenant_id, token=token_response.token, user_context=user_context
+        )
+
+        if isinstance(verify_response, VerifyEmailUsingTokenInvalidTokenError):
+            # This should never happen because we consume the token immediately after creating it
+            raise Exception("Should not come here")
+
+    else:
+        await unverify_email(user_id, user_context=user_context)
+
+    return UserEmailVerifyPutAPIResponse()
+
+
+
+
+
+
+
+

Functions

+
+
+async def handle_user_email_verify_put(_api_interface: APIInterface, tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> UserEmailVerifyPutAPIResponse +
+
+
+
+ +Expand source code + +
async def handle_user_email_verify_put(
+    _api_interface: APIInterface,
+    tenant_id: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+) -> UserEmailVerifyPutAPIResponse:
+    request_body: Dict[str, Any] = await api_options.request.json()  # type: ignore
+    user_id = request_body.get("userId")
+    verified = request_body.get("verified")
+
+    if user_id is None or not isinstance(user_id, str):
+        raise_bad_input_exception(
+            "Required parameter 'userId' is missing or has an invalid type"
+        )
+
+    if verified is None or not isinstance(verified, bool):
+        raise_bad_input_exception(
+            "Required parameter 'verified' is missing or has an invalid type"
+        )
+
+    if verified:
+        token_response = await create_email_verification_token(
+            tenant_id=tenant_id, user_id=user_id, email=None, user_context=user_context
+        )
+
+        if isinstance(
+            token_response, CreateEmailVerificationTokenEmailAlreadyVerifiedError
+        ):
+            return UserEmailVerifyPutAPIResponse()
+
+        verify_response = await verify_email_using_token(
+            tenant_id=tenant_id, token=token_response.token, user_context=user_context
+        )
+
+        if isinstance(verify_response, VerifyEmailUsingTokenInvalidTokenError):
+            # This should never happen because we consume the token immediately after creating it
+            raise Exception("Should not come here")
+
+    else:
+        await unverify_email(user_id, user_context=user_context)
+
+    return UserEmailVerifyPutAPIResponse()
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/dashboard/api/userdetails/user_email_verify_token_post.html b/html/supertokens_python/recipe/dashboard/api/userdetails/user_email_verify_token_post.html new file mode 100644 index 000000000..985e177e7 --- /dev/null +++ b/html/supertokens_python/recipe/dashboard/api/userdetails/user_email_verify_token_post.html @@ -0,0 +1,143 @@ + + + + + + +supertokens_python.recipe.dashboard.api.userdetails.user_email_verify_token_post API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.dashboard.api.userdetails.user_email_verify_token_post

+
+
+
+ +Expand source code + +
from typing import Any, Dict, Union
+
+from supertokens_python.exceptions import raise_bad_input_exception
+from supertokens_python.recipe.emailverification.asyncio import (
+    send_email_verification_email,
+    SendEmailVerificationEmailAlreadyVerifiedError,
+)
+
+from ...interfaces import (
+    APIInterface,
+    APIOptions,
+    UserEmailVerifyTokenPostAPIOkResponse,
+    UserEmailVerifyTokenPostAPIEmailAlreadyVerifiedErrorResponse,
+)
+
+
+async def handle_email_verify_token_post(
+    _api_interface: APIInterface,
+    tenant_id: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+) -> Union[
+    UserEmailVerifyTokenPostAPIOkResponse,
+    UserEmailVerifyTokenPostAPIEmailAlreadyVerifiedErrorResponse,
+]:
+    request_body: Dict[str, Any] = await api_options.request.json()  # type: ignore
+    user_id = request_body.get("userId")
+
+    if user_id is None or not isinstance(user_id, str):
+        raise_bad_input_exception(
+            "Required parameter 'userId' is missing or has an invalid type"
+        )
+
+    res = await send_email_verification_email(
+        tenant_id=tenant_id, user_id=user_id, email=None, user_context=user_context
+    )
+
+    if isinstance(res, SendEmailVerificationEmailAlreadyVerifiedError):
+        return UserEmailVerifyTokenPostAPIEmailAlreadyVerifiedErrorResponse()
+
+    return UserEmailVerifyTokenPostAPIOkResponse()
+
+
+
+
+
+
+
+

Functions

+
+
+async def handle_email_verify_token_post(_api_interface: APIInterface, tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[UserEmailVerifyTokenPostAPIOkResponseUserEmailVerifyTokenPostAPIEmailAlreadyVerifiedErrorResponse] +
+
+
+
+ +Expand source code + +
async def handle_email_verify_token_post(
+    _api_interface: APIInterface,
+    tenant_id: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+) -> Union[
+    UserEmailVerifyTokenPostAPIOkResponse,
+    UserEmailVerifyTokenPostAPIEmailAlreadyVerifiedErrorResponse,
+]:
+    request_body: Dict[str, Any] = await api_options.request.json()  # type: ignore
+    user_id = request_body.get("userId")
+
+    if user_id is None or not isinstance(user_id, str):
+        raise_bad_input_exception(
+            "Required parameter 'userId' is missing or has an invalid type"
+        )
+
+    res = await send_email_verification_email(
+        tenant_id=tenant_id, user_id=user_id, email=None, user_context=user_context
+    )
+
+    if isinstance(res, SendEmailVerificationEmailAlreadyVerifiedError):
+        return UserEmailVerifyTokenPostAPIEmailAlreadyVerifiedErrorResponse()
+
+    return UserEmailVerifyTokenPostAPIOkResponse()
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/dashboard/api/userdetails/user_get.html b/html/supertokens_python/recipe/dashboard/api/userdetails/user_get.html new file mode 100644 index 000000000..fb168d95a --- /dev/null +++ b/html/supertokens_python/recipe/dashboard/api/userdetails/user_get.html @@ -0,0 +1,188 @@ + + + + + + +supertokens_python.recipe.dashboard.api.userdetails.user_get API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.dashboard.api.userdetails.user_get

+
+
+
+ +Expand source code + +
from typing import Union, Dict, Any
+
+from supertokens_python.exceptions import raise_bad_input_exception
+from supertokens_python.recipe.dashboard.utils import get_user_for_recipe_id
+from supertokens_python.recipe.usermetadata import UserMetadataRecipe
+from supertokens_python.recipe.usermetadata.asyncio import get_user_metadata
+
+from ...interfaces import (
+    APIInterface,
+    APIOptions,
+    UserGetAPINoUserFoundError,
+    UserGetAPIOkResponse,
+    UserGetAPIRecipeNotInitialisedError,
+)
+from ...utils import is_recipe_initialised, is_valid_recipe_id
+
+
+async def handle_user_get(
+    _api_interface: APIInterface,
+    _tenant_id: str,
+    api_options: APIOptions,
+    _user_context: Dict[str, Any],
+) -> Union[
+    UserGetAPINoUserFoundError,
+    UserGetAPIOkResponse,
+    UserGetAPIRecipeNotInitialisedError,
+]:
+    user_id = api_options.request.get_query_param("userId")
+    recipe_id = api_options.request.get_query_param("recipeId")
+
+    if user_id is None:
+        raise_bad_input_exception("Missing required parameter 'userId'")
+
+    if recipe_id is None:
+        raise_bad_input_exception("Missing required parameter 'recipeId'")
+
+    if not is_valid_recipe_id(recipe_id):
+        raise_bad_input_exception("Invalid recipe id")
+
+    if not is_recipe_initialised(recipe_id):
+        return UserGetAPIRecipeNotInitialisedError()
+
+    user_response = await get_user_for_recipe_id(user_id, recipe_id)
+    if user_response is None:
+        return UserGetAPINoUserFoundError()
+
+    user = user_response.user
+
+    try:
+        UserMetadataRecipe.get_instance()
+    except Exception:
+        user.first_name = "FEATURE_NOT_ENABLED"
+        user.last_name = "FEATURE_NOT_ENABLED"
+
+        return UserGetAPIOkResponse(recipe_id, user)
+
+    user_metadata = await get_user_metadata(user_id, user_context=_user_context)
+    first_name = user_metadata.metadata.get("first_name", "")
+    last_name = user_metadata.metadata.get("last_name", "")
+
+    user.first_name = first_name
+    user.last_name = last_name
+
+    return UserGetAPIOkResponse(recipe_id, user)
+
+
+
+
+
+
+
+

Functions

+
+
+async def handle_user_get(_api_interface: APIInterface, _tenant_id: str, api_options: APIOptions, _user_context: Dict[str, Any]) ‑> Union[UserGetAPINoUserFoundErrorUserGetAPIOkResponseUserGetAPIRecipeNotInitialisedError] +
+
+
+
+ +Expand source code + +
async def handle_user_get(
+    _api_interface: APIInterface,
+    _tenant_id: str,
+    api_options: APIOptions,
+    _user_context: Dict[str, Any],
+) -> Union[
+    UserGetAPINoUserFoundError,
+    UserGetAPIOkResponse,
+    UserGetAPIRecipeNotInitialisedError,
+]:
+    user_id = api_options.request.get_query_param("userId")
+    recipe_id = api_options.request.get_query_param("recipeId")
+
+    if user_id is None:
+        raise_bad_input_exception("Missing required parameter 'userId'")
+
+    if recipe_id is None:
+        raise_bad_input_exception("Missing required parameter 'recipeId'")
+
+    if not is_valid_recipe_id(recipe_id):
+        raise_bad_input_exception("Invalid recipe id")
+
+    if not is_recipe_initialised(recipe_id):
+        return UserGetAPIRecipeNotInitialisedError()
+
+    user_response = await get_user_for_recipe_id(user_id, recipe_id)
+    if user_response is None:
+        return UserGetAPINoUserFoundError()
+
+    user = user_response.user
+
+    try:
+        UserMetadataRecipe.get_instance()
+    except Exception:
+        user.first_name = "FEATURE_NOT_ENABLED"
+        user.last_name = "FEATURE_NOT_ENABLED"
+
+        return UserGetAPIOkResponse(recipe_id, user)
+
+    user_metadata = await get_user_metadata(user_id, user_context=_user_context)
+    first_name = user_metadata.metadata.get("first_name", "")
+    last_name = user_metadata.metadata.get("last_name", "")
+
+    user.first_name = first_name
+    user.last_name = last_name
+
+    return UserGetAPIOkResponse(recipe_id, user)
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/dashboard/api/userdetails/user_metadata_get.html b/html/supertokens_python/recipe/dashboard/api/userdetails/user_metadata_get.html new file mode 100644 index 000000000..e7cd69a44 --- /dev/null +++ b/html/supertokens_python/recipe/dashboard/api/userdetails/user_metadata_get.html @@ -0,0 +1,125 @@ + + + + + + +supertokens_python.recipe.dashboard.api.userdetails.user_metadata_get API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.dashboard.api.userdetails.user_metadata_get

+
+
+
+ +Expand source code + +
from ...interfaces import (
+    APIInterface,
+    APIOptions,
+    FeatureNotEnabledError,
+    UserMetadataGetAPIOkResponse,
+)
+from supertokens_python.exceptions import raise_bad_input_exception
+from supertokens_python.recipe.usermetadata import UserMetadataRecipe
+from supertokens_python.recipe.usermetadata.asyncio import get_user_metadata
+from typing import Union, Dict, Any
+
+
+async def handle_metadata_get(
+    _api_interface: APIInterface,
+    _tenant_id: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+) -> Union[UserMetadataGetAPIOkResponse, FeatureNotEnabledError]:
+    user_id = api_options.request.get_query_param("userId")
+
+    if user_id is None:
+        raise_bad_input_exception("Missing required parameter 'userId'")
+
+    try:
+        UserMetadataRecipe.get_instance()
+    except Exception:
+        return FeatureNotEnabledError()
+
+    metadata_response = await get_user_metadata(user_id, user_context=user_context)
+    return UserMetadataGetAPIOkResponse(metadata_response.metadata)
+
+
+
+
+
+
+
+

Functions

+
+
+async def handle_metadata_get(_api_interface: APIInterface, _tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[UserMetadataGetAPIOkResponseFeatureNotEnabledError] +
+
+
+
+ +Expand source code + +
async def handle_metadata_get(
+    _api_interface: APIInterface,
+    _tenant_id: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+) -> Union[UserMetadataGetAPIOkResponse, FeatureNotEnabledError]:
+    user_id = api_options.request.get_query_param("userId")
+
+    if user_id is None:
+        raise_bad_input_exception("Missing required parameter 'userId'")
+
+    try:
+        UserMetadataRecipe.get_instance()
+    except Exception:
+        return FeatureNotEnabledError()
+
+    metadata_response = await get_user_metadata(user_id, user_context=user_context)
+    return UserMetadataGetAPIOkResponse(metadata_response.metadata)
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/dashboard/api/userdetails/user_metadata_put.html b/html/supertokens_python/recipe/dashboard/api/userdetails/user_metadata_put.html new file mode 100644 index 000000000..985ea2cfe --- /dev/null +++ b/html/supertokens_python/recipe/dashboard/api/userdetails/user_metadata_put.html @@ -0,0 +1,182 @@ + + + + + + +supertokens_python.recipe.dashboard.api.userdetails.user_metadata_put API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.dashboard.api.userdetails.user_metadata_put

+
+
+
+ +Expand source code + +
import json
+from typing import Any, Dict
+
+from supertokens_python.exceptions import raise_bad_input_exception
+from supertokens_python.recipe.usermetadata import UserMetadataRecipe
+from supertokens_python.recipe.usermetadata.asyncio import (
+    clear_user_metadata,
+    update_user_metadata,
+)
+
+from ...interfaces import APIInterface, APIOptions, UserMetadataPutAPIResponse
+
+
+async def handle_metadata_put(
+    _api_interface: APIInterface,
+    _tenant_id: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+) -> UserMetadataPutAPIResponse:
+    request_body: Dict[str, Any] = await api_options.request.json()  # type: ignore
+    user_id = request_body.get("userId")
+    data = request_body.get("data")
+
+    # This is to throw an error early in case the recipe has not been initialised
+    UserMetadataRecipe.get_instance()
+
+    if user_id is None or isinstance(user_id, str) is False:
+        raise_bad_input_exception(
+            "Required parameter 'userId' is missing or has an invalid type"
+        )
+
+    if data is None or isinstance(data, str) is False:
+        raise_bad_input_exception(
+            "Required parameter 'data' is missing or has an invalid type"
+        )
+
+    parsed_data: Dict[str, Any] = {}
+    try:
+        parsed_data = json.loads(data)
+        if not isinstance(parsed_data, dict):  # type: ignore
+            raise Exception()
+
+    except Exception:
+        raise_bad_input_exception("'data' must be a valid JSON body")
+
+    # This API is meant to set the user metadata of a user. We delete the existing data
+    # before updating it because we want to make sure that shallow merging does not result
+    # in the data being incorrect
+    #
+    # For example if the old data is {test: "test", test2: "test2"} and the user wants to delete
+    # test2 from the data simply calling updateUserMetadata with {test: "test"} would not remove
+    # test2 because of shallow merging.
+    #
+    # Removing first ensures that the final data is exactly what the user wanted it to be
+
+    await clear_user_metadata(user_id, user_context)
+    await update_user_metadata(user_id, parsed_data, user_context)
+
+    return UserMetadataPutAPIResponse()
+
+
+
+
+
+
+
+

Functions

+
+
+async def handle_metadata_put(_api_interface: APIInterface, _tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> UserMetadataPutAPIResponse +
+
+
+
+ +Expand source code + +
async def handle_metadata_put(
+    _api_interface: APIInterface,
+    _tenant_id: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+) -> UserMetadataPutAPIResponse:
+    request_body: Dict[str, Any] = await api_options.request.json()  # type: ignore
+    user_id = request_body.get("userId")
+    data = request_body.get("data")
+
+    # This is to throw an error early in case the recipe has not been initialised
+    UserMetadataRecipe.get_instance()
+
+    if user_id is None or isinstance(user_id, str) is False:
+        raise_bad_input_exception(
+            "Required parameter 'userId' is missing or has an invalid type"
+        )
+
+    if data is None or isinstance(data, str) is False:
+        raise_bad_input_exception(
+            "Required parameter 'data' is missing or has an invalid type"
+        )
+
+    parsed_data: Dict[str, Any] = {}
+    try:
+        parsed_data = json.loads(data)
+        if not isinstance(parsed_data, dict):  # type: ignore
+            raise Exception()
+
+    except Exception:
+        raise_bad_input_exception("'data' must be a valid JSON body")
+
+    # This API is meant to set the user metadata of a user. We delete the existing data
+    # before updating it because we want to make sure that shallow merging does not result
+    # in the data being incorrect
+    #
+    # For example if the old data is {test: "test", test2: "test2"} and the user wants to delete
+    # test2 from the data simply calling updateUserMetadata with {test: "test"} would not remove
+    # test2 because of shallow merging.
+    #
+    # Removing first ensures that the final data is exactly what the user wanted it to be
+
+    await clear_user_metadata(user_id, user_context)
+    await update_user_metadata(user_id, parsed_data, user_context)
+
+    return UserMetadataPutAPIResponse()
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/dashboard/api/userdetails/user_password_put.html b/html/supertokens_python/recipe/dashboard/api/userdetails/user_password_put.html new file mode 100644 index 000000000..28da6cca0 --- /dev/null +++ b/html/supertokens_python/recipe/dashboard/api/userdetails/user_password_put.html @@ -0,0 +1,256 @@ + + + + + + +supertokens_python.recipe.dashboard.api.userdetails.user_password_put API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.dashboard.api.userdetails.user_password_put

+
+
+
+ +Expand source code + +
from typing import Any, Callable, Dict, List, Union
+
+from supertokens_python.exceptions import raise_bad_input_exception
+from supertokens_python.recipe.emailpassword import EmailPasswordRecipe
+from supertokens_python.recipe.emailpassword.asyncio import (
+    create_reset_password_token as ep_create_reset_password_token,
+)
+from supertokens_python.recipe.emailpassword.asyncio import (
+    reset_password_using_token as ep_reset_password_using_token,
+)
+from supertokens_python.recipe.emailpassword.constants import FORM_FIELD_PASSWORD_ID
+from supertokens_python.recipe.emailpassword.interfaces import (
+    CreateResetPasswordOkResult,
+    CreateResetPasswordWrongUserIdError,
+    ResetPasswordUsingTokenInvalidTokenError,
+    ResetPasswordUsingTokenOkResult,
+)
+from supertokens_python.recipe.emailpassword.types import NormalisedFormField
+
+from supertokens_python.utils import Awaitable
+
+from ...interfaces import (
+    APIInterface,
+    APIOptions,
+    UserPasswordPutAPIInvalidPasswordErrorResponse,
+    UserPasswordPutAPIResponse,
+)
+
+
+async def handle_user_password_put(
+    _api_interface: APIInterface,
+    tenant_id: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+) -> Union[UserPasswordPutAPIResponse, UserPasswordPutAPIInvalidPasswordErrorResponse]:
+    request_body: Dict[str, Any] = await api_options.request.json()  # type: ignore
+    user_id = request_body.get("userId")
+    new_password = request_body.get("newPassword")
+
+    if user_id is None or not isinstance(user_id, str):
+        raise_bad_input_exception("Missing required parameter 'userId'")
+
+    if new_password is None or not isinstance(new_password, str):
+        raise_bad_input_exception("Missing required parameter 'newPassword'")
+
+    async def reset_password(
+        form_fields: List[NormalisedFormField],
+        create_reset_password_token: Callable[
+            [str, str, Dict[str, Any]],
+            Awaitable[
+                Union[CreateResetPasswordOkResult, CreateResetPasswordWrongUserIdError]
+            ],
+        ],
+        reset_password_using_token: Callable[
+            [str, str, str, Dict[str, Any]],
+            Awaitable[
+                Union[
+                    ResetPasswordUsingTokenOkResult,
+                    ResetPasswordUsingTokenInvalidTokenError,
+                ]
+            ],
+        ],
+    ) -> Union[
+        UserPasswordPutAPIResponse, UserPasswordPutAPIInvalidPasswordErrorResponse
+    ]:
+        password_form_field = [
+            field for field in form_fields if field.id == FORM_FIELD_PASSWORD_ID
+        ][0]
+
+        password_validation_error = await password_form_field.validate(
+            new_password, tenant_id
+        )
+
+        if password_validation_error is not None:
+            return UserPasswordPutAPIInvalidPasswordErrorResponse(
+                password_validation_error
+            )
+
+        password_reset_token = await create_reset_password_token(
+            tenant_id, user_id, user_context
+        )
+
+        if isinstance(password_reset_token, CreateResetPasswordWrongUserIdError):
+            # Techincally it can but its an edge case so we assume that it wont
+            # UNKNOWN_USER_ID_ERROR
+            raise Exception("Should never come here")
+
+        password_reset_response = await reset_password_using_token(
+            tenant_id, password_reset_token.token, new_password, user_context
+        )
+
+        if isinstance(
+            password_reset_response, ResetPasswordUsingTokenInvalidTokenError
+        ):
+            # RESET_PASSWORD_INVALID_TOKEN_ERROR
+            raise Exception("Should not come here")
+
+        return UserPasswordPutAPIResponse()
+
+    return await reset_password(
+        EmailPasswordRecipe.get_instance().config.sign_up_feature.form_fields,
+        ep_create_reset_password_token,
+        ep_reset_password_using_token,
+    )
+
+
+
+
+
+
+
+

Functions

+
+
+async def handle_user_password_put(_api_interface: APIInterface, tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[UserPasswordPutAPIResponseUserPasswordPutAPIInvalidPasswordErrorResponse] +
+
+
+
+ +Expand source code + +
async def handle_user_password_put(
+    _api_interface: APIInterface,
+    tenant_id: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+) -> Union[UserPasswordPutAPIResponse, UserPasswordPutAPIInvalidPasswordErrorResponse]:
+    request_body: Dict[str, Any] = await api_options.request.json()  # type: ignore
+    user_id = request_body.get("userId")
+    new_password = request_body.get("newPassword")
+
+    if user_id is None or not isinstance(user_id, str):
+        raise_bad_input_exception("Missing required parameter 'userId'")
+
+    if new_password is None or not isinstance(new_password, str):
+        raise_bad_input_exception("Missing required parameter 'newPassword'")
+
+    async def reset_password(
+        form_fields: List[NormalisedFormField],
+        create_reset_password_token: Callable[
+            [str, str, Dict[str, Any]],
+            Awaitable[
+                Union[CreateResetPasswordOkResult, CreateResetPasswordWrongUserIdError]
+            ],
+        ],
+        reset_password_using_token: Callable[
+            [str, str, str, Dict[str, Any]],
+            Awaitable[
+                Union[
+                    ResetPasswordUsingTokenOkResult,
+                    ResetPasswordUsingTokenInvalidTokenError,
+                ]
+            ],
+        ],
+    ) -> Union[
+        UserPasswordPutAPIResponse, UserPasswordPutAPIInvalidPasswordErrorResponse
+    ]:
+        password_form_field = [
+            field for field in form_fields if field.id == FORM_FIELD_PASSWORD_ID
+        ][0]
+
+        password_validation_error = await password_form_field.validate(
+            new_password, tenant_id
+        )
+
+        if password_validation_error is not None:
+            return UserPasswordPutAPIInvalidPasswordErrorResponse(
+                password_validation_error
+            )
+
+        password_reset_token = await create_reset_password_token(
+            tenant_id, user_id, user_context
+        )
+
+        if isinstance(password_reset_token, CreateResetPasswordWrongUserIdError):
+            # Techincally it can but its an edge case so we assume that it wont
+            # UNKNOWN_USER_ID_ERROR
+            raise Exception("Should never come here")
+
+        password_reset_response = await reset_password_using_token(
+            tenant_id, password_reset_token.token, new_password, user_context
+        )
+
+        if isinstance(
+            password_reset_response, ResetPasswordUsingTokenInvalidTokenError
+        ):
+            # RESET_PASSWORD_INVALID_TOKEN_ERROR
+            raise Exception("Should not come here")
+
+        return UserPasswordPutAPIResponse()
+
+    return await reset_password(
+        EmailPasswordRecipe.get_instance().config.sign_up_feature.form_fields,
+        ep_create_reset_password_token,
+        ep_reset_password_using_token,
+    )
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/dashboard/api/userdetails/user_put.html b/html/supertokens_python/recipe/dashboard/api/userdetails/user_put.html new file mode 100644 index 000000000..e79cadae7 --- /dev/null +++ b/html/supertokens_python/recipe/dashboard/api/userdetails/user_put.html @@ -0,0 +1,584 @@ + + + + + + +supertokens_python.recipe.dashboard.api.userdetails.user_put API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.dashboard.api.userdetails.user_put

+
+
+
+ +Expand source code + +
from typing import Any, Dict, Optional, Union
+
+from supertokens_python.exceptions import raise_bad_input_exception
+from supertokens_python.recipe.dashboard.utils import (
+    get_user_for_recipe_id,
+    is_valid_recipe_id,
+)
+from supertokens_python.recipe.emailpassword import EmailPasswordRecipe
+from supertokens_python.recipe.emailpassword.asyncio import (
+    update_email_or_password as ep_update_email_or_password,
+)
+from supertokens_python.recipe.emailpassword.constants import FORM_FIELD_EMAIL_ID
+from supertokens_python.recipe.emailpassword.interfaces import (
+    UpdateEmailOrPasswordEmailAlreadyExistsError,
+)
+from supertokens_python.recipe.passwordless import PasswordlessRecipe
+from supertokens_python.recipe.passwordless.asyncio import (
+    update_user as pless_update_user,
+)
+from supertokens_python.recipe.passwordless.interfaces import (
+    UpdateUserEmailAlreadyExistsError as EmailAlreadyExistsErrorResponse,
+)
+from supertokens_python.recipe.passwordless.interfaces import (
+    UpdateUserPhoneNumberAlreadyExistsError as PhoneNumberAlreadyExistsError,
+)
+from supertokens_python.recipe.passwordless.interfaces import (
+    UpdateUserUnknownUserIdError as PlessUpdateUserUnknownUserIdError,
+)
+from supertokens_python.recipe.passwordless.utils import (
+    ContactEmailOnlyConfig,
+    ContactEmailOrPhoneConfig,
+    ContactPhoneOnlyConfig,
+    default_validate_email,
+    default_validate_phone_number,
+)
+from supertokens_python.recipe.usermetadata import UserMetadataRecipe
+from supertokens_python.recipe.usermetadata.asyncio import update_user_metadata
+
+from ...interfaces import (
+    APIInterface,
+    APIOptions,
+    UserPutAPIEmailAlreadyExistsErrorResponse,
+    UserPutAPIInvalidEmailErrorResponse,
+    UserPutAPIInvalidPhoneErrorResponse,
+    UserPutAPIOkResponse,
+    UserPutPhoneAlreadyExistsAPIResponse,
+)
+
+
+async def update_email_for_recipe_id(
+    recipe_id: str,
+    user_id: str,
+    email: str,
+    tenant_id: str,
+    user_context: Dict[str, Any],
+) -> Union[
+    UserPutAPIOkResponse,
+    UserPutAPIInvalidEmailErrorResponse,
+    UserPutAPIEmailAlreadyExistsErrorResponse,
+]:
+    validation_error: Optional[str] = None
+
+    if recipe_id == "emailpassword":
+        form_fields = (
+            EmailPasswordRecipe.get_instance().config.sign_up_feature.form_fields
+        )
+        email_form_fields = [
+            form_field
+            for form_field in form_fields
+            if form_field.id == FORM_FIELD_EMAIL_ID
+        ]
+
+        validation_error = await email_form_fields[0].validate(email, tenant_id)
+
+        if validation_error is not None:
+            return UserPutAPIInvalidEmailErrorResponse(validation_error)
+
+        email_update_response = await ep_update_email_or_password(
+            user_id, email, user_context=user_context
+        )
+
+        if isinstance(
+            email_update_response, UpdateEmailOrPasswordEmailAlreadyExistsError
+        ):
+            return UserPutAPIEmailAlreadyExistsErrorResponse()
+
+        return UserPutAPIOkResponse()
+
+    if recipe_id == "passwordless":
+        validation_error = None
+
+        passwordless_config = PasswordlessRecipe.get_instance().config.contact_config
+
+        if isinstance(passwordless_config.contact_method, ContactPhoneOnlyConfig):
+            validation_error = await default_validate_email(email, tenant_id)
+
+        elif isinstance(
+            passwordless_config, (ContactEmailOnlyConfig, ContactEmailOrPhoneConfig)
+        ):
+            validation_error = await passwordless_config.validate_email_address(
+                email, tenant_id
+            )
+
+        if validation_error is not None:
+            return UserPutAPIInvalidEmailErrorResponse(validation_error)
+
+        update_result = await pless_update_user(
+            user_id, email, user_context=user_context
+        )
+
+        if isinstance(update_result, PlessUpdateUserUnknownUserIdError):
+            raise Exception("Should never come here")
+
+        if isinstance(update_result, EmailAlreadyExistsErrorResponse):
+            return UserPutAPIEmailAlreadyExistsErrorResponse()
+
+        return UserPutAPIOkResponse()
+
+    # If it comes here then the user is a third party user in which case the UI should not have allowed this
+    raise Exception("Should never come here")
+
+
+async def update_phone_for_recipe_id(
+    recipe_id: str,
+    user_id: str,
+    phone: str,
+    tenant_id: str,
+    user_context: Dict[str, Any],
+) -> Union[
+    UserPutAPIOkResponse,
+    UserPutAPIInvalidPhoneErrorResponse,
+    UserPutPhoneAlreadyExistsAPIResponse,
+]:
+    validation_error: Optional[str] = None
+
+    if recipe_id == "passwordless":
+        validation_error = None
+
+        passwordless_config = PasswordlessRecipe.get_instance().config.contact_config
+
+        if isinstance(passwordless_config, ContactEmailOnlyConfig):
+            validation_error = await default_validate_phone_number(phone, tenant_id)
+        elif isinstance(
+            passwordless_config, (ContactPhoneOnlyConfig, ContactEmailOrPhoneConfig)
+        ):
+            validation_error = await passwordless_config.validate_phone_number(
+                phone, tenant_id
+            )
+
+        if validation_error is not None:
+            return UserPutAPIInvalidPhoneErrorResponse(validation_error)
+
+        update_result = await pless_update_user(
+            user_id, phone_number=phone, user_context=user_context
+        )
+
+        if isinstance(update_result, PlessUpdateUserUnknownUserIdError):
+            raise Exception("Should never come here")
+
+        if isinstance(update_result, PhoneNumberAlreadyExistsError):
+            return UserPutPhoneAlreadyExistsAPIResponse()
+
+        return UserPutAPIOkResponse()
+
+    # If it comes here then the user is a third party user in which case the UI should not have allowed this
+    raise Exception("Should never come here")
+
+
+async def handle_user_put(
+    _api_interface: APIInterface,
+    tenant_id: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+) -> Union[
+    UserPutAPIOkResponse,
+    UserPutAPIInvalidEmailErrorResponse,
+    UserPutAPIEmailAlreadyExistsErrorResponse,
+    UserPutAPIInvalidPhoneErrorResponse,
+    UserPutPhoneAlreadyExistsAPIResponse,
+]:
+    request_body: Dict[str, Any] = await api_options.request.json()  # type: ignore
+    user_id: Optional[str] = request_body.get("userId")
+    recipe_id: Optional[str] = request_body.get("recipeId")
+    first_name: Optional[str] = request_body.get("firstName")
+    last_name: Optional[str] = request_body.get("lastName")
+    email: Optional[str] = request_body.get("email")
+    phone: Optional[str] = request_body.get("phone")
+
+    if not isinstance(user_id, str):
+        return raise_bad_input_exception(
+            "Required parameter 'userId' is missing or has an invalid type"
+        )
+
+    if not isinstance(recipe_id, str):
+        return raise_bad_input_exception(
+            "Required parameter 'recipeId' is missing or has an invalid type"
+        )
+
+    if not is_valid_recipe_id(recipe_id):
+        raise_bad_input_exception("Invalid recipe id")
+
+    if first_name is None and not isinstance(first_name, str):
+        raise_bad_input_exception(
+            "Required parameter 'firstName' is missing or has an invalid type"
+        )
+
+    if last_name is None and not isinstance(last_name, str):
+        raise_bad_input_exception(
+            "Required parameter 'lastName' is missing or has an invalid type"
+        )
+
+    if email is None and not isinstance(email, str):
+        raise_bad_input_exception(
+            "Required parameter 'email' is missing or has an invalid type"
+        )
+
+    if phone is None and not isinstance(phone, str):
+        raise_bad_input_exception(
+            "Required parameter 'phone' is missing or has an invalid type"
+        )
+
+    user_response = await get_user_for_recipe_id(user_id, recipe_id)
+
+    if user_response is None:
+        raise Exception("Should never come here")
+
+    first_name = first_name.strip()
+    last_name = last_name.strip()
+    email = email.strip()
+    phone = phone.strip()
+
+    if first_name != "" or last_name != "":
+        is_recipe_initialized = False
+
+        try:
+            UserMetadataRecipe.get_instance()
+            is_recipe_initialized = True
+        except Exception:
+            pass
+
+        if is_recipe_initialized:
+            metadata_update: Dict[str, Any] = {}
+
+            if first_name != "":
+                metadata_update["first_name"] = first_name
+
+            if last_name != "":
+                metadata_update["last_name"] = last_name
+
+            await update_user_metadata(user_id, metadata_update, user_context)
+
+    if email != "":
+        email_update_response = await update_email_for_recipe_id(
+            user_response.recipe, user_id, email, tenant_id, user_context
+        )
+
+        if not isinstance(email_update_response, UserPutAPIOkResponse):
+            return email_update_response
+
+    if phone != "":
+        phone_update_response = await update_phone_for_recipe_id(
+            user_response.recipe, user_id, phone, tenant_id, user_context
+        )
+
+        if not isinstance(phone_update_response, UserPutAPIOkResponse):
+            return phone_update_response
+
+    return UserPutAPIOkResponse()
+
+
+
+
+
+
+
+

Functions

+
+
+async def handle_user_put(_api_interface: APIInterface, tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[UserPutAPIOkResponseUserPutAPIInvalidEmailErrorResponseUserPutAPIEmailAlreadyExistsErrorResponseUserPutAPIInvalidPhoneErrorResponseUserPutPhoneAlreadyExistsAPIResponse] +
+
+
+
+ +Expand source code + +
async def handle_user_put(
+    _api_interface: APIInterface,
+    tenant_id: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+) -> Union[
+    UserPutAPIOkResponse,
+    UserPutAPIInvalidEmailErrorResponse,
+    UserPutAPIEmailAlreadyExistsErrorResponse,
+    UserPutAPIInvalidPhoneErrorResponse,
+    UserPutPhoneAlreadyExistsAPIResponse,
+]:
+    request_body: Dict[str, Any] = await api_options.request.json()  # type: ignore
+    user_id: Optional[str] = request_body.get("userId")
+    recipe_id: Optional[str] = request_body.get("recipeId")
+    first_name: Optional[str] = request_body.get("firstName")
+    last_name: Optional[str] = request_body.get("lastName")
+    email: Optional[str] = request_body.get("email")
+    phone: Optional[str] = request_body.get("phone")
+
+    if not isinstance(user_id, str):
+        return raise_bad_input_exception(
+            "Required parameter 'userId' is missing or has an invalid type"
+        )
+
+    if not isinstance(recipe_id, str):
+        return raise_bad_input_exception(
+            "Required parameter 'recipeId' is missing or has an invalid type"
+        )
+
+    if not is_valid_recipe_id(recipe_id):
+        raise_bad_input_exception("Invalid recipe id")
+
+    if first_name is None and not isinstance(first_name, str):
+        raise_bad_input_exception(
+            "Required parameter 'firstName' is missing or has an invalid type"
+        )
+
+    if last_name is None and not isinstance(last_name, str):
+        raise_bad_input_exception(
+            "Required parameter 'lastName' is missing or has an invalid type"
+        )
+
+    if email is None and not isinstance(email, str):
+        raise_bad_input_exception(
+            "Required parameter 'email' is missing or has an invalid type"
+        )
+
+    if phone is None and not isinstance(phone, str):
+        raise_bad_input_exception(
+            "Required parameter 'phone' is missing or has an invalid type"
+        )
+
+    user_response = await get_user_for_recipe_id(user_id, recipe_id)
+
+    if user_response is None:
+        raise Exception("Should never come here")
+
+    first_name = first_name.strip()
+    last_name = last_name.strip()
+    email = email.strip()
+    phone = phone.strip()
+
+    if first_name != "" or last_name != "":
+        is_recipe_initialized = False
+
+        try:
+            UserMetadataRecipe.get_instance()
+            is_recipe_initialized = True
+        except Exception:
+            pass
+
+        if is_recipe_initialized:
+            metadata_update: Dict[str, Any] = {}
+
+            if first_name != "":
+                metadata_update["first_name"] = first_name
+
+            if last_name != "":
+                metadata_update["last_name"] = last_name
+
+            await update_user_metadata(user_id, metadata_update, user_context)
+
+    if email != "":
+        email_update_response = await update_email_for_recipe_id(
+            user_response.recipe, user_id, email, tenant_id, user_context
+        )
+
+        if not isinstance(email_update_response, UserPutAPIOkResponse):
+            return email_update_response
+
+    if phone != "":
+        phone_update_response = await update_phone_for_recipe_id(
+            user_response.recipe, user_id, phone, tenant_id, user_context
+        )
+
+        if not isinstance(phone_update_response, UserPutAPIOkResponse):
+            return phone_update_response
+
+    return UserPutAPIOkResponse()
+
+
+
+async def update_email_for_recipe_id(recipe_id: str, user_id: str, email: str, tenant_id: str, user_context: Dict[str, Any]) ‑> Union[UserPutAPIOkResponseUserPutAPIInvalidEmailErrorResponseUserPutAPIEmailAlreadyExistsErrorResponse] +
+
+
+
+ +Expand source code + +
async def update_email_for_recipe_id(
+    recipe_id: str,
+    user_id: str,
+    email: str,
+    tenant_id: str,
+    user_context: Dict[str, Any],
+) -> Union[
+    UserPutAPIOkResponse,
+    UserPutAPIInvalidEmailErrorResponse,
+    UserPutAPIEmailAlreadyExistsErrorResponse,
+]:
+    validation_error: Optional[str] = None
+
+    if recipe_id == "emailpassword":
+        form_fields = (
+            EmailPasswordRecipe.get_instance().config.sign_up_feature.form_fields
+        )
+        email_form_fields = [
+            form_field
+            for form_field in form_fields
+            if form_field.id == FORM_FIELD_EMAIL_ID
+        ]
+
+        validation_error = await email_form_fields[0].validate(email, tenant_id)
+
+        if validation_error is not None:
+            return UserPutAPIInvalidEmailErrorResponse(validation_error)
+
+        email_update_response = await ep_update_email_or_password(
+            user_id, email, user_context=user_context
+        )
+
+        if isinstance(
+            email_update_response, UpdateEmailOrPasswordEmailAlreadyExistsError
+        ):
+            return UserPutAPIEmailAlreadyExistsErrorResponse()
+
+        return UserPutAPIOkResponse()
+
+    if recipe_id == "passwordless":
+        validation_error = None
+
+        passwordless_config = PasswordlessRecipe.get_instance().config.contact_config
+
+        if isinstance(passwordless_config.contact_method, ContactPhoneOnlyConfig):
+            validation_error = await default_validate_email(email, tenant_id)
+
+        elif isinstance(
+            passwordless_config, (ContactEmailOnlyConfig, ContactEmailOrPhoneConfig)
+        ):
+            validation_error = await passwordless_config.validate_email_address(
+                email, tenant_id
+            )
+
+        if validation_error is not None:
+            return UserPutAPIInvalidEmailErrorResponse(validation_error)
+
+        update_result = await pless_update_user(
+            user_id, email, user_context=user_context
+        )
+
+        if isinstance(update_result, PlessUpdateUserUnknownUserIdError):
+            raise Exception("Should never come here")
+
+        if isinstance(update_result, EmailAlreadyExistsErrorResponse):
+            return UserPutAPIEmailAlreadyExistsErrorResponse()
+
+        return UserPutAPIOkResponse()
+
+    # If it comes here then the user is a third party user in which case the UI should not have allowed this
+    raise Exception("Should never come here")
+
+
+
+async def update_phone_for_recipe_id(recipe_id: str, user_id: str, phone: str, tenant_id: str, user_context: Dict[str, Any]) ‑> Union[UserPutAPIOkResponseUserPutAPIInvalidPhoneErrorResponseUserPutPhoneAlreadyExistsAPIResponse] +
+
+
+
+ +Expand source code + +
async def update_phone_for_recipe_id(
+    recipe_id: str,
+    user_id: str,
+    phone: str,
+    tenant_id: str,
+    user_context: Dict[str, Any],
+) -> Union[
+    UserPutAPIOkResponse,
+    UserPutAPIInvalidPhoneErrorResponse,
+    UserPutPhoneAlreadyExistsAPIResponse,
+]:
+    validation_error: Optional[str] = None
+
+    if recipe_id == "passwordless":
+        validation_error = None
+
+        passwordless_config = PasswordlessRecipe.get_instance().config.contact_config
+
+        if isinstance(passwordless_config, ContactEmailOnlyConfig):
+            validation_error = await default_validate_phone_number(phone, tenant_id)
+        elif isinstance(
+            passwordless_config, (ContactPhoneOnlyConfig, ContactEmailOrPhoneConfig)
+        ):
+            validation_error = await passwordless_config.validate_phone_number(
+                phone, tenant_id
+            )
+
+        if validation_error is not None:
+            return UserPutAPIInvalidPhoneErrorResponse(validation_error)
+
+        update_result = await pless_update_user(
+            user_id, phone_number=phone, user_context=user_context
+        )
+
+        if isinstance(update_result, PlessUpdateUserUnknownUserIdError):
+            raise Exception("Should never come here")
+
+        if isinstance(update_result, PhoneNumberAlreadyExistsError):
+            return UserPutPhoneAlreadyExistsAPIResponse()
+
+        return UserPutAPIOkResponse()
+
+    # If it comes here then the user is a third party user in which case the UI should not have allowed this
+    raise Exception("Should never come here")
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/dashboard/api/userdetails/user_sessions_get.html b/html/supertokens_python/recipe/dashboard/api/userdetails/user_sessions_get.html new file mode 100644 index 000000000..593e2a594 --- /dev/null +++ b/html/supertokens_python/recipe/dashboard/api/userdetails/user_sessions_get.html @@ -0,0 +1,164 @@ + + + + + + +supertokens_python.recipe.dashboard.api.userdetails.user_sessions_get API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.dashboard.api.userdetails.user_sessions_get

+
+
+
+ +Expand source code + +
import asyncio
+from typing import List, Optional, Dict, Any
+
+from supertokens_python.exceptions import raise_bad_input_exception
+from supertokens_python.recipe.session.asyncio import (
+    get_all_session_handles_for_user,
+    get_session_information,
+)
+
+from ...interfaces import (
+    APIInterface,
+    APIOptions,
+    SessionInfo,
+    UserSessionsGetAPIResponse,
+)
+
+
+async def handle_sessions_get(
+    _api_interface: APIInterface,
+    _tenant_id: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+) -> UserSessionsGetAPIResponse:
+    user_id = api_options.request.get_query_param("userId")
+
+    if user_id is None:
+        raise_bad_input_exception("Missing required parameter 'userId'")
+
+    # Passing tenant id as None sets fetch_across_all_tenants to True
+    # which is what we want here.
+    session_handles = await get_all_session_handles_for_user(
+        user_id, None, user_context
+    )
+    sessions: List[Optional[SessionInfo]] = [None for _ in session_handles]
+
+    async def call_(i: int, session_handle: str):
+        try:
+            session_response = await get_session_information(
+                session_handle, user_context
+            )
+            if session_response is not None:
+                sessions[i] = SessionInfo(session_response)
+        except Exception:
+            sessions[i] = None
+
+    session_info_promises = [
+        call_(i, handle) for i, handle in enumerate(session_handles)
+    ]
+
+    await asyncio.gather(*session_info_promises)
+
+    return UserSessionsGetAPIResponse([s for s in sessions if s is not None])
+
+
+
+
+
+
+
+

Functions

+
+
+async def handle_sessions_get(_api_interface: APIInterface, _tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> UserSessionsGetAPIResponse +
+
+
+
+ +Expand source code + +
async def handle_sessions_get(
+    _api_interface: APIInterface,
+    _tenant_id: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+) -> UserSessionsGetAPIResponse:
+    user_id = api_options.request.get_query_param("userId")
+
+    if user_id is None:
+        raise_bad_input_exception("Missing required parameter 'userId'")
+
+    # Passing tenant id as None sets fetch_across_all_tenants to True
+    # which is what we want here.
+    session_handles = await get_all_session_handles_for_user(
+        user_id, None, user_context
+    )
+    sessions: List[Optional[SessionInfo]] = [None for _ in session_handles]
+
+    async def call_(i: int, session_handle: str):
+        try:
+            session_response = await get_session_information(
+                session_handle, user_context
+            )
+            if session_response is not None:
+                sessions[i] = SessionInfo(session_response)
+        except Exception:
+            sessions[i] = None
+
+    session_info_promises = [
+        call_(i, handle) for i, handle in enumerate(session_handles)
+    ]
+
+    await asyncio.gather(*session_info_promises)
+
+    return UserSessionsGetAPIResponse([s for s in sessions if s is not None])
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/dashboard/api/userdetails/user_sessions_post.html b/html/supertokens_python/recipe/dashboard/api/userdetails/user_sessions_post.html new file mode 100644 index 000000000..890241d67 --- /dev/null +++ b/html/supertokens_python/recipe/dashboard/api/userdetails/user_sessions_post.html @@ -0,0 +1,116 @@ + + + + + + +supertokens_python.recipe.dashboard.api.userdetails.user_sessions_post API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.dashboard.api.userdetails.user_sessions_post

+
+
+
+ +Expand source code + +
from typing import Any, Dict, List, Optional
+
+from supertokens_python.exceptions import raise_bad_input_exception
+from supertokens_python.recipe.session.asyncio import revoke_multiple_sessions
+from ...interfaces import APIInterface, APIOptions, UserSessionsPostAPIResponse
+
+
+async def handle_user_sessions_post(
+    _api_interface: APIInterface,
+    _tenant_id: str,
+    api_options: APIOptions,
+    _user_context: Dict[str, Any],
+) -> UserSessionsPostAPIResponse:
+    request_body = await api_options.request.json()  # type: ignore
+    session_handles: Optional[List[str]] = request_body.get("sessionHandles")  # type: ignore
+
+    if not isinstance(session_handles, list):
+        raise_bad_input_exception(
+            "Required parameter 'sessionHandles' is missing or has an invalid type"
+        )
+
+    await revoke_multiple_sessions(session_handles, _user_context)
+    return UserSessionsPostAPIResponse()
+
+
+
+
+
+
+
+

Functions

+
+
+async def handle_user_sessions_post(_api_interface: APIInterface, _tenant_id: str, api_options: APIOptions, _user_context: Dict[str, Any]) ‑> UserSessionsPostAPIResponse +
+
+
+
+ +Expand source code + +
async def handle_user_sessions_post(
+    _api_interface: APIInterface,
+    _tenant_id: str,
+    api_options: APIOptions,
+    _user_context: Dict[str, Any],
+) -> UserSessionsPostAPIResponse:
+    request_body = await api_options.request.json()  # type: ignore
+    session_handles: Optional[List[str]] = request_body.get("sessionHandles")  # type: ignore
+
+    if not isinstance(session_handles, list):
+        raise_bad_input_exception(
+            "Required parameter 'sessionHandles' is missing or has an invalid type"
+        )
+
+    await revoke_multiple_sessions(session_handles, _user_context)
+    return UserSessionsPostAPIResponse()
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/dashboard/api/users_count_get.html b/html/supertokens_python/recipe/dashboard/api/users_count_get.html new file mode 100644 index 000000000..5e7a5c477 --- /dev/null +++ b/html/supertokens_python/recipe/dashboard/api/users_count_get.html @@ -0,0 +1,126 @@ + + + + + + +supertokens_python.recipe.dashboard.api.users_count_get API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.dashboard.api.users_count_get

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Dict, Any
+
+from supertokens_python.supertokens import Supertokens
+from supertokens_python.recipe.dashboard.interfaces import UserCountGetAPIResponse
+
+if TYPE_CHECKING:
+    from supertokens_python.recipe.dashboard.interfaces import (
+        APIOptions,
+        APIInterface,
+    )
+
+
+async def handle_users_count_get_api(
+    _: APIInterface,
+    tenant_id: str,
+    _api_options: APIOptions,
+    _user_context: Dict[str, Any],
+) -> UserCountGetAPIResponse:
+    count = await Supertokens.get_instance().get_user_count(
+        None,
+        tenant_id,
+    )
+    return UserCountGetAPIResponse(count=count)
+
+
+
+
+
+
+
+

Functions

+
+
+async def handle_users_count_get_api(_: APIInterface, tenant_id: str, _api_options: APIOptions, _user_context: Dict[str, Any]) ‑> UserCountGetAPIResponse +
+
+
+
+ +Expand source code + +
async def handle_users_count_get_api(
+    _: APIInterface,
+    tenant_id: str,
+    _api_options: APIOptions,
+    _user_context: Dict[str, Any],
+) -> UserCountGetAPIResponse:
+    count = await Supertokens.get_instance().get_user_count(
+        None,
+        tenant_id,
+    )
+    return UserCountGetAPIResponse(count=count)
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/dashboard/api/users_get.html b/html/supertokens_python/recipe/dashboard/api/users_get.html new file mode 100644 index 000000000..fc984b181 --- /dev/null +++ b/html/supertokens_python/recipe/dashboard/api/users_get.html @@ -0,0 +1,275 @@ + + + + + + +supertokens_python.recipe.dashboard.api.users_get API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.dashboard.api.users_get

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+import asyncio
+from typing import TYPE_CHECKING, Any, Awaitable, List, Dict
+from typing_extensions import Literal
+
+from supertokens_python.supertokens import Supertokens
+
+from ...usermetadata import UserMetadataRecipe
+from ...usermetadata.asyncio import get_user_metadata
+from ..interfaces import DashboardUsersGetResponse
+from ..utils import UserWithMetadata
+
+if TYPE_CHECKING:
+    from supertokens_python.recipe.dashboard.interfaces import (
+        APIOptions,
+        APIInterface,
+    )
+    from supertokens_python.types import APIResponse
+
+from supertokens_python.exceptions import GeneralError, raise_bad_input_exception
+
+
+async def handle_users_get_api(
+    api_implementation: APIInterface,
+    tenant_id: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+) -> APIResponse:
+    _ = api_implementation
+
+    limit = api_options.request.get_query_param("limit")
+    if limit is None:
+        raise_bad_input_exception("Missing required parameter 'limit'")
+
+    time_joined_order: Literal["ASC", "DESC"] = api_options.request.get_query_param(  # type: ignore
+        "timeJoinedOrder", "DESC"
+    )
+    if time_joined_order not in ["ASC", "DESC"]:
+        raise_bad_input_exception("Invalid value recieved for 'timeJoinedOrder'")
+
+    pagination_token = api_options.request.get_query_param("paginationToken")
+
+    users_response = await Supertokens.get_instance().get_users(
+        tenant_id,
+        time_joined_order=time_joined_order,
+        limit=int(limit),
+        pagination_token=pagination_token,
+        include_recipe_ids=None,
+        query=api_options.request.get_query_params(),
+        user_context=user_context,
+    )
+
+    # user metadata bulk fetch with batches:
+
+    try:
+        UserMetadataRecipe.get_instance()
+    except GeneralError:
+        return DashboardUsersGetResponse(
+            users_response.users, users_response.next_pagination_token
+        )
+
+    users_with_metadata: List[UserWithMetadata] = [
+        UserWithMetadata().from_user(user) for user in users_response.users
+    ]
+    metadata_fetch_awaitables: List[Awaitable[Any]] = []
+
+    async def get_user_metadata_and_update_user(user_idx: int) -> None:
+        user = users_response.users[user_idx]
+        user_metadata = await get_user_metadata(user.user_id, user_context)
+        first_name = user_metadata.metadata.get("first_name")
+        last_name = user_metadata.metadata.get("last_name")
+
+        # None becomes null which is acceptable for the dashboard.
+        users_with_metadata[user_idx].first_name = first_name
+        users_with_metadata[user_idx].last_name = last_name
+
+    # Batch calls to get user metadata:
+    for i, _ in enumerate(users_response.users):
+        metadata_fetch_awaitables.append(get_user_metadata_and_update_user(i))
+
+    promise_arr_start_position = 0
+    batch_size = 5
+
+    while promise_arr_start_position < len(metadata_fetch_awaitables):
+        # We want to query only 5 in parallel at a time
+        promises_to_call = [
+            metadata_fetch_awaitables[i]
+            for i in range(
+                promise_arr_start_position,
+                min(
+                    promise_arr_start_position + batch_size,
+                    len(metadata_fetch_awaitables),
+                ),
+            )
+        ]
+        await asyncio.gather(*promises_to_call)
+
+        promise_arr_start_position += batch_size
+
+    return DashboardUsersGetResponse(
+        users_with_metadata,
+        users_response.next_pagination_token,
+    )
+
+
+
+
+
+
+
+

Functions

+
+
+async def handle_users_get_api(api_implementation: APIInterface, tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> APIResponse +
+
+
+
+ +Expand source code + +
async def handle_users_get_api(
+    api_implementation: APIInterface,
+    tenant_id: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+) -> APIResponse:
+    _ = api_implementation
+
+    limit = api_options.request.get_query_param("limit")
+    if limit is None:
+        raise_bad_input_exception("Missing required parameter 'limit'")
+
+    time_joined_order: Literal["ASC", "DESC"] = api_options.request.get_query_param(  # type: ignore
+        "timeJoinedOrder", "DESC"
+    )
+    if time_joined_order not in ["ASC", "DESC"]:
+        raise_bad_input_exception("Invalid value recieved for 'timeJoinedOrder'")
+
+    pagination_token = api_options.request.get_query_param("paginationToken")
+
+    users_response = await Supertokens.get_instance().get_users(
+        tenant_id,
+        time_joined_order=time_joined_order,
+        limit=int(limit),
+        pagination_token=pagination_token,
+        include_recipe_ids=None,
+        query=api_options.request.get_query_params(),
+        user_context=user_context,
+    )
+
+    # user metadata bulk fetch with batches:
+
+    try:
+        UserMetadataRecipe.get_instance()
+    except GeneralError:
+        return DashboardUsersGetResponse(
+            users_response.users, users_response.next_pagination_token
+        )
+
+    users_with_metadata: List[UserWithMetadata] = [
+        UserWithMetadata().from_user(user) for user in users_response.users
+    ]
+    metadata_fetch_awaitables: List[Awaitable[Any]] = []
+
+    async def get_user_metadata_and_update_user(user_idx: int) -> None:
+        user = users_response.users[user_idx]
+        user_metadata = await get_user_metadata(user.user_id, user_context)
+        first_name = user_metadata.metadata.get("first_name")
+        last_name = user_metadata.metadata.get("last_name")
+
+        # None becomes null which is acceptable for the dashboard.
+        users_with_metadata[user_idx].first_name = first_name
+        users_with_metadata[user_idx].last_name = last_name
+
+    # Batch calls to get user metadata:
+    for i, _ in enumerate(users_response.users):
+        metadata_fetch_awaitables.append(get_user_metadata_and_update_user(i))
+
+    promise_arr_start_position = 0
+    batch_size = 5
+
+    while promise_arr_start_position < len(metadata_fetch_awaitables):
+        # We want to query only 5 in parallel at a time
+        promises_to_call = [
+            metadata_fetch_awaitables[i]
+            for i in range(
+                promise_arr_start_position,
+                min(
+                    promise_arr_start_position + batch_size,
+                    len(metadata_fetch_awaitables),
+                ),
+            )
+        ]
+        await asyncio.gather(*promises_to_call)
+
+        promise_arr_start_position += batch_size
+
+    return DashboardUsersGetResponse(
+        users_with_metadata,
+        users_response.next_pagination_token,
+    )
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/dashboard/api/validate_key.html b/html/supertokens_python/recipe/dashboard/api/validate_key.html new file mode 100644 index 000000000..ffeab15b9 --- /dev/null +++ b/html/supertokens_python/recipe/dashboard/api/validate_key.html @@ -0,0 +1,134 @@ + + + + + + +supertokens_python.recipe.dashboard.api.validate_key API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.dashboard.api.validate_key

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Dict, Any
+
+if TYPE_CHECKING:
+    from supertokens_python.recipe.dashboard.interfaces import (
+        APIOptions,
+        APIInterface,
+    )
+
+from supertokens_python.utils import (
+    send_200_response,
+    send_non_200_response_with_message,
+)
+
+from ..utils import validate_api_key
+
+
+async def handle_validate_key_api(
+    _api_implementation: APIInterface,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+):
+
+    is_valid_key = await validate_api_key(
+        api_options.request, api_options.config, user_context
+    )
+
+    if is_valid_key:
+        return send_200_response({"status": "OK"}, api_options.response)
+    return send_non_200_response_with_message("Unauthorised", 401, api_options.response)
+
+
+
+
+
+
+
+

Functions

+
+
+async def handle_validate_key_api(_api_implementation: APIInterface, api_options: APIOptions, user_context: Dict[str, Any]) +
+
+
+
+ +Expand source code + +
async def handle_validate_key_api(
+    _api_implementation: APIInterface,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+):
+
+    is_valid_key = await validate_api_key(
+        api_options.request, api_options.config, user_context
+    )
+
+    if is_valid_key:
+        return send_200_response({"status": "OK"}, api_options.response)
+    return send_non_200_response_with_message("Unauthorised", 401, api_options.response)
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/dashboard/constants.html b/html/supertokens_python/recipe/dashboard/constants.html new file mode 100644 index 000000000..d4762020b --- /dev/null +++ b/html/supertokens_python/recipe/dashboard/constants.html @@ -0,0 +1,73 @@ + + + + + + +supertokens_python.recipe.dashboard.constants API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.dashboard.constants

+
+
+
+ +Expand source code + +
DASHBOARD_API = "/dashboard"
+VALIDATE_KEY_API = "/api/key/validate"
+USERS_LIST_GET_API = "/api/users"
+USERS_COUNT_API = "/api/users/count"
+USER_API = "/api/user"
+USER_EMAIL_VERIFY_API = "/api/user/email/verify"
+USER_METADATA_API = "/api/user/metadata"
+USER_SESSION_API = "/api/user/sessions"
+USER_PASSWORD_API = "/api/user/password"
+USER_EMAIL_VERIFY_TOKEN_API = "/api/user/email/verify/token"
+SIGN_IN_API = "/api/signin"
+SIGN_OUT_API = "/api/signout"
+SEARCH_TAGS_API = "/api/search/tags"
+DASHBOARD_ANALYTICS_API = "/api/analytics"
+TENANTS_LIST_API = "/api/tenants/list"
+
+
+
+
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/dashboard/exceptions.html b/html/supertokens_python/recipe/dashboard/exceptions.html new file mode 100644 index 000000000..55cfffe76 --- /dev/null +++ b/html/supertokens_python/recipe/dashboard/exceptions.html @@ -0,0 +1,125 @@ + + + + + + +supertokens_python.recipe.dashboard.exceptions API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.dashboard.exceptions

+
+
+
+ +Expand source code + +
from supertokens_python.exceptions import SuperTokensError
+
+
+class SuperTokensDashboardError(SuperTokensError):
+    pass
+
+
+class DashboardOperationNotAllowedError(SuperTokensDashboardError):
+    pass
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class DashboardOperationNotAllowedError +(*args, **kwargs) +
+
+

Common base class for all non-exit exceptions.

+
+ +Expand source code + +
class DashboardOperationNotAllowedError(SuperTokensDashboardError):
+    pass
+
+

Ancestors

+ +
+
+class SuperTokensDashboardError +(*args, **kwargs) +
+
+

Common base class for all non-exit exceptions.

+
+ +Expand source code + +
class SuperTokensDashboardError(SuperTokensError):
+    pass
+
+

Ancestors

+ +

Subclasses

+ +
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/dashboard/index.html b/html/supertokens_python/recipe/dashboard/index.html new file mode 100644 index 000000000..f5c97daf0 --- /dev/null +++ b/html/supertokens_python/recipe/dashboard/index.html @@ -0,0 +1,166 @@ + + + + + + +supertokens_python.recipe.dashboard API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.dashboard

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from __future__ import annotations
+
+from typing import Callable, Optional, List
+
+from supertokens_python import AppInfo, RecipeModule
+
+from .recipe import DashboardRecipe
+
+from supertokens_python.recipe.dashboard import utils
+
+InputOverrideConfig = utils.InputOverrideConfig
+
+
+def init(
+    api_key: Optional[str] = None,
+    admins: Optional[List[str]] = None,
+    override: Optional[InputOverrideConfig] = None,
+) -> Callable[[AppInfo], RecipeModule]:
+    return DashboardRecipe.init(
+        api_key,
+        admins,
+        override,
+    )
+
+
+
+

Sub-modules

+
+
supertokens_python.recipe.dashboard.api
+
+
+
+
supertokens_python.recipe.dashboard.constants
+
+
+
+
supertokens_python.recipe.dashboard.exceptions
+
+
+
+
supertokens_python.recipe.dashboard.interfaces
+
+
+
+
supertokens_python.recipe.dashboard.recipe
+
+
+
+
supertokens_python.recipe.dashboard.recipe_implementation
+
+
+
+
supertokens_python.recipe.dashboard.utils
+
+
+
+
+
+
+
+
+

Functions

+
+
+def init(api_key: Optional[str] = None, admins: Optional[List[str]] = None, override: Optional[InputOverrideConfig] = None) ‑> Callable[[AppInfo], RecipeModule] +
+
+
+
+ +Expand source code + +
def init(
+    api_key: Optional[str] = None,
+    admins: Optional[List[str]] = None,
+    override: Optional[InputOverrideConfig] = None,
+) -> Callable[[AppInfo], RecipeModule]:
+    return DashboardRecipe.init(
+        api_key,
+        admins,
+        override,
+    )
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/dashboard/interfaces.html b/html/supertokens_python/recipe/dashboard/interfaces.html new file mode 100644 index 000000000..04238799a --- /dev/null +++ b/html/supertokens_python/recipe/dashboard/interfaces.html @@ -0,0 +1,2084 @@ + + + + + + +supertokens_python.recipe.dashboard.interfaces API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.dashboard.interfaces

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from abc import ABC, abstractmethod
+from typing import TYPE_CHECKING, Any, Awaitable, Callable, Dict, List, Optional, Union
+
+from supertokens_python.types import User
+
+from ...types import APIResponse
+
+if TYPE_CHECKING:
+    from ...supertokens import AppInfo
+    from .utils import DashboardConfig, UserWithMetadata
+
+    from supertokens_python.recipe.session.interfaces import SessionInformationResult
+    from supertokens_python.framework import BaseRequest, BaseResponse
+
+    from supertokens_python.recipe.multitenancy.interfaces import (
+        EmailPasswordConfig,
+        PasswordlessConfig,
+        ThirdPartyConfig,
+    )
+
+
+class SessionInfo:
+    def __init__(self, info: SessionInformationResult) -> None:
+        self.session_handle = info.session_handle
+        self.user_id = info.user_id
+        self.session_data_in_database = info.session_data_in_database
+        self.expiry = info.expiry
+        self.access_token_payload = info.custom_claims_in_access_token_payload
+        self.time_created = info.time_created
+        self.tenant_id = info.tenant_id
+
+
+class RecipeInterface(ABC):
+    def __init__(self):
+        pass
+
+    @abstractmethod
+    async def get_dashboard_bundle_location(self, user_context: Dict[str, Any]) -> str:
+        pass
+
+    @abstractmethod
+    async def should_allow_access(
+        self,
+        request: BaseRequest,
+        config: DashboardConfig,
+        user_context: Dict[str, Any],
+    ) -> bool:
+        pass
+
+
+class APIOptions:
+    def __init__(
+        self,
+        request: BaseRequest,
+        response: BaseResponse,
+        recipe_id: str,
+        config: DashboardConfig,
+        recipe_implementation: RecipeInterface,
+        app_info: AppInfo,
+    ):
+        self.request: BaseRequest = request
+        self.response: BaseResponse = response
+        self.recipe_id: str = recipe_id
+        self.config: DashboardConfig = config
+        self.recipe_implementation: RecipeInterface = recipe_implementation
+        self.app_info = app_info
+
+
+class APIInterface:
+    def __init__(self):
+        # undefined should be allowed
+        self.dashboard_get: Optional[
+            Callable[[APIOptions, Dict[str, Any]], Awaitable[str]]
+        ] = None
+
+
+class DashboardUsersGetResponse(APIResponse):
+    status: str = "OK"
+
+    def __init__(
+        self,
+        users: Union[List[User], List[UserWithMetadata]],
+        next_pagination_token: Optional[str],
+    ):
+        self.users = users
+        self.next_pagination_token = next_pagination_token
+
+    def to_json(self) -> Dict[str, Any]:
+        return {
+            "status": self.status,
+            "users": [u.to_json() for u in self.users],
+            "nextPaginationToken": self.next_pagination_token,
+        }
+
+
+class DashboardListTenantItem:
+    def __init__(
+        self,
+        tenant_id: str,
+        emailpassword: EmailPasswordConfig,
+        passwordless: PasswordlessConfig,
+        third_party: ThirdPartyConfig,
+    ):
+        self.tenant_id = tenant_id
+        self.emailpassword = emailpassword
+        self.passwordless = passwordless
+        self.third_party = third_party
+
+    def to_json(self):
+        res = {
+            "tenantId": self.tenant_id,
+            "emailPassword": self.emailpassword.to_json(),
+            "passwordless": self.passwordless.to_json(),
+            "thirdParty": self.third_party.to_json(),
+        }
+
+        return res
+
+
+class DashboardListTenantsGetResponse(APIResponse):
+    status: str = "OK"
+
+    def __init__(self, tenants: List[DashboardListTenantItem]) -> None:
+        self.tenants = tenants
+
+    def to_json(self) -> Dict[str, Any]:
+        return {
+            "status": self.status,
+            "tenants": [t.to_json() for t in self.tenants],
+        }
+
+
+class UserCountGetAPIResponse(APIResponse):
+    status: str = "OK"
+
+    def __init__(self, count: int):
+        self.count = count
+
+    def to_json(self) -> Dict[str, Any]:
+        return {"status": self.status, "count": self.count}
+
+
+class UserGetAPIOkResponse(APIResponse):
+    status: str = "OK"
+
+    def __init__(self, recipe_id: str, user: UserWithMetadata):
+        self.recipe_id = recipe_id
+        self.user = user
+
+    def to_json(self) -> Dict[str, Any]:
+        return {
+            "status": self.status,
+            "recipeId": self.recipe_id,
+            "user": self.user.to_json(),
+        }
+
+
+class UserGetAPINoUserFoundError(APIResponse):
+    status: str = "NO_USER_FOUND_ERROR"
+
+    def to_json(self) -> Dict[str, Any]:
+        return {"status": self.status}
+
+
+class UserGetAPIRecipeNotInitialisedError(APIResponse):
+    status: str = "RECIPE_NOT_INITIALISED"
+
+    def to_json(self) -> Dict[str, Any]:
+        return {"status": self.status}
+
+
+class FeatureNotEnabledError(APIResponse):
+    status: str = "FEATURE_NOT_ENABLED_ERROR"
+
+    def to_json(self) -> Dict[str, Any]:
+        return {"status": self.status}
+
+
+class UserMetadataGetAPIOkResponse(APIResponse):
+    status: str = "OK"
+
+    def __init__(self, data: Dict[str, Any]) -> None:
+        self.data = data
+
+    def to_json(self) -> Dict[str, Any]:
+        return {"status": "OK", "data": self.data}
+
+
+class UserSessionsGetAPIResponse(APIResponse):
+    status: str = "OK"
+
+    def __init__(self, sessions: List[SessionInfo]):
+        self.sessions = [
+            {
+                "accessTokenPayload": s.access_token_payload,
+                "expiry": s.expiry,
+                "sessionDataInDatabase": s.session_data_in_database,
+                "tenantId": s.tenant_id,
+                "timeCreated": s.time_created,
+                "userId": s.user_id,
+                "sessionHandle": s.session_handle,
+            }
+            for s in sessions
+        ]
+
+    def to_json(self) -> Dict[str, Any]:
+        return {"status": self.status, "sessions": self.sessions}
+
+
+class UserEmailVerifyGetAPIResponse(APIResponse):
+    status: str = "OK"
+
+    def __init__(self, is_verified: bool):
+        self.is_verified = is_verified
+
+    def to_json(self) -> Dict[str, Any]:
+        return {"status": self.status, "isVerified": self.is_verified}
+
+
+class UserDeleteAPIResponse(APIResponse):
+    status: str = "OK"
+
+    def to_json(self) -> Dict[str, Any]:
+        return {"status": self.status}
+
+
+class UserEmailVerifyPutAPIResponse(APIResponse):
+    status: str = "OK"
+
+    def to_json(self) -> Dict[str, Any]:
+        return {"status": self.status}
+
+
+class UserPasswordPutAPIResponse(APIResponse):
+    status: str = "OK"
+
+    def to_json(self) -> Dict[str, Any]:
+        return {"status": self.status}
+
+
+class UserPasswordPutAPIInvalidPasswordErrorResponse(APIResponse):
+    status: str = "INVALID_PASSWORD_ERROR"
+
+    def __init__(self, error: str) -> None:
+        self.error = error
+
+    def to_json(self) -> Dict[str, Any]:
+        return {"status": self.status, "error": self.error}
+
+
+class UserSessionsPostAPIResponse(APIResponse):
+    status: str = "OK"
+
+    def to_json(self) -> Dict[str, Any]:
+        return {"status": self.status}
+
+
+class UserEmailVerifyTokenPostAPIOkResponse(APIResponse):
+    status: str = "OK"
+
+    def to_json(self) -> Dict[str, Any]:
+        return {"status": self.status}
+
+
+class UserEmailVerifyTokenPostAPIEmailAlreadyVerifiedErrorResponse(APIResponse):
+    status: str = "EMAIL_ALREADY_VERIFIED_ERROR"
+
+    def to_json(self) -> Dict[str, Any]:
+        return {"status": self.status}
+
+
+class UserMetadataPutAPIResponse(APIResponse):
+    status: str = "OK"
+
+    def to_json(self) -> Dict[str, Any]:
+        return {"status": self.status}
+
+
+class UserPutAPIOkResponse(APIResponse):
+    status: str = "OK"
+
+    def to_json(self) -> Dict[str, Any]:
+        return {"status": self.status}
+
+
+class UserPutAPIInvalidEmailErrorResponse(APIResponse):
+    status: str = "INVALID_EMAIL_ERROR"
+
+    def __init__(self, error: str) -> None:
+        self.error = error
+
+    def to_json(self) -> Dict[str, Any]:
+        return {"status": self.status, "error": self.error}
+
+
+class UserPutAPIEmailAlreadyExistsErrorResponse(APIResponse):
+    status: str = "EMAIL_ALREADY_EXISTS_ERROR"
+
+    def to_json(self) -> Dict[str, Any]:
+        return {"status": self.status}
+
+
+class UserPutPhoneAlreadyExistsAPIResponse(APIResponse):
+    status: str = "PHONE_ALREADY_EXISTS_ERROR"
+
+    def to_json(self) -> Dict[str, Any]:
+        return {"status": self.status}
+
+
+class UserPutAPIInvalidPhoneErrorResponse(APIResponse):
+    status: str = "INVALID_PHONE_ERROR"
+
+    def __init__(self, error: str) -> None:
+        self.error = error
+
+    def to_json(self) -> Dict[str, Any]:
+        return {"status": self.status, "error": self.error}
+
+
+class SignOutOK(APIResponse):
+    status: str = "OK"
+
+    def to_json(self):
+        return {"status": self.status}
+
+
+class SearchTagsOK(APIResponse):
+    status: str = "OK"
+    tags: List[str]
+
+    def __init__(self, tags: List[str]) -> None:
+        self.tags = tags
+
+    def to_json(self):
+        return {"status": self.status, "tags": self.tags}
+
+
+class AnalyticsResponse(APIResponse):
+    status: str = "OK"
+
+    def to_json(self) -> Dict[str, Any]:
+        return {"status": self.status}
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class APIInterface +
+
+
+
+ +Expand source code + +
class APIInterface:
+    def __init__(self):
+        # undefined should be allowed
+        self.dashboard_get: Optional[
+            Callable[[APIOptions, Dict[str, Any]], Awaitable[str]]
+        ] = None
+
+

Subclasses

+ +
+
+class APIOptions +(request: BaseRequest, response: BaseResponse, recipe_id: str, config: DashboardConfig, recipe_implementation: RecipeInterface, app_info: AppInfo) +
+
+
+
+ +Expand source code + +
class APIOptions:
+    def __init__(
+        self,
+        request: BaseRequest,
+        response: BaseResponse,
+        recipe_id: str,
+        config: DashboardConfig,
+        recipe_implementation: RecipeInterface,
+        app_info: AppInfo,
+    ):
+        self.request: BaseRequest = request
+        self.response: BaseResponse = response
+        self.recipe_id: str = recipe_id
+        self.config: DashboardConfig = config
+        self.recipe_implementation: RecipeInterface = recipe_implementation
+        self.app_info = app_info
+
+
+
+class AnalyticsResponse +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class AnalyticsResponse(APIResponse):
+    status: str = "OK"
+
+    def to_json(self) -> Dict[str, Any]:
+        return {"status": self.status}
+
+

Ancestors

+ +

Class variables

+
+
var status : str
+
+
+
+
+

Methods

+
+
+def to_json(self) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
def to_json(self) -> Dict[str, Any]:
+    return {"status": self.status}
+
+
+
+
+
+class DashboardListTenantItem +(tenant_id: str, emailpassword: EmailPasswordConfig, passwordless: PasswordlessConfig, third_party: ThirdPartyConfig) +
+
+
+
+ +Expand source code + +
class DashboardListTenantItem:
+    def __init__(
+        self,
+        tenant_id: str,
+        emailpassword: EmailPasswordConfig,
+        passwordless: PasswordlessConfig,
+        third_party: ThirdPartyConfig,
+    ):
+        self.tenant_id = tenant_id
+        self.emailpassword = emailpassword
+        self.passwordless = passwordless
+        self.third_party = third_party
+
+    def to_json(self):
+        res = {
+            "tenantId": self.tenant_id,
+            "emailPassword": self.emailpassword.to_json(),
+            "passwordless": self.passwordless.to_json(),
+            "thirdParty": self.third_party.to_json(),
+        }
+
+        return res
+
+

Methods

+
+
+def to_json(self) +
+
+
+
+ +Expand source code + +
def to_json(self):
+    res = {
+        "tenantId": self.tenant_id,
+        "emailPassword": self.emailpassword.to_json(),
+        "passwordless": self.passwordless.to_json(),
+        "thirdParty": self.third_party.to_json(),
+    }
+
+    return res
+
+
+
+
+
+class DashboardListTenantsGetResponse +(tenants: List[DashboardListTenantItem]) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class DashboardListTenantsGetResponse(APIResponse):
+    status: str = "OK"
+
+    def __init__(self, tenants: List[DashboardListTenantItem]) -> None:
+        self.tenants = tenants
+
+    def to_json(self) -> Dict[str, Any]:
+        return {
+            "status": self.status,
+            "tenants": [t.to_json() for t in self.tenants],
+        }
+
+

Ancestors

+ +

Class variables

+
+
var status : str
+
+
+
+
+

Methods

+
+
+def to_json(self) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
def to_json(self) -> Dict[str, Any]:
+    return {
+        "status": self.status,
+        "tenants": [t.to_json() for t in self.tenants],
+    }
+
+
+
+
+
+class DashboardUsersGetResponse +(users: Union[List[User], List[UserWithMetadata]], next_pagination_token: Optional[str]) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class DashboardUsersGetResponse(APIResponse):
+    status: str = "OK"
+
+    def __init__(
+        self,
+        users: Union[List[User], List[UserWithMetadata]],
+        next_pagination_token: Optional[str],
+    ):
+        self.users = users
+        self.next_pagination_token = next_pagination_token
+
+    def to_json(self) -> Dict[str, Any]:
+        return {
+            "status": self.status,
+            "users": [u.to_json() for u in self.users],
+            "nextPaginationToken": self.next_pagination_token,
+        }
+
+

Ancestors

+ +

Class variables

+
+
var status : str
+
+
+
+
+

Methods

+
+
+def to_json(self) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
def to_json(self) -> Dict[str, Any]:
+    return {
+        "status": self.status,
+        "users": [u.to_json() for u in self.users],
+        "nextPaginationToken": self.next_pagination_token,
+    }
+
+
+
+
+
+class FeatureNotEnabledError +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class FeatureNotEnabledError(APIResponse):
+    status: str = "FEATURE_NOT_ENABLED_ERROR"
+
+    def to_json(self) -> Dict[str, Any]:
+        return {"status": self.status}
+
+

Ancestors

+ +

Class variables

+
+
var status : str
+
+
+
+
+

Methods

+
+
+def to_json(self) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
def to_json(self) -> Dict[str, Any]:
+    return {"status": self.status}
+
+
+
+
+
+class RecipeInterface +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class RecipeInterface(ABC):
+    def __init__(self):
+        pass
+
+    @abstractmethod
+    async def get_dashboard_bundle_location(self, user_context: Dict[str, Any]) -> str:
+        pass
+
+    @abstractmethod
+    async def should_allow_access(
+        self,
+        request: BaseRequest,
+        config: DashboardConfig,
+        user_context: Dict[str, Any],
+    ) -> bool:
+        pass
+
+

Ancestors

+
    +
  • abc.ABC
  • +
+

Subclasses

+ +

Methods

+
+
+async def get_dashboard_bundle_location(self, user_context: Dict[str, Any]) ‑> str +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def get_dashboard_bundle_location(self, user_context: Dict[str, Any]) -> str:
+    pass
+
+
+
+async def should_allow_access(self, request: BaseRequest, config: DashboardConfig, user_context: Dict[str, Any]) ‑> bool +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def should_allow_access(
+    self,
+    request: BaseRequest,
+    config: DashboardConfig,
+    user_context: Dict[str, Any],
+) -> bool:
+    pass
+
+
+
+
+
+class SearchTagsOK +(tags: List[str]) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class SearchTagsOK(APIResponse):
+    status: str = "OK"
+    tags: List[str]
+
+    def __init__(self, tags: List[str]) -> None:
+        self.tags = tags
+
+    def to_json(self):
+        return {"status": self.status, "tags": self.tags}
+
+

Ancestors

+ +

Class variables

+
+
var status : str
+
+
+
+
var tags : List[str]
+
+
+
+
+

Methods

+
+
+def to_json(self) +
+
+
+
+ +Expand source code + +
def to_json(self):
+    return {"status": self.status, "tags": self.tags}
+
+
+
+
+
+class SessionInfo +(info: SessionInformationResult) +
+
+
+
+ +Expand source code + +
class SessionInfo:
+    def __init__(self, info: SessionInformationResult) -> None:
+        self.session_handle = info.session_handle
+        self.user_id = info.user_id
+        self.session_data_in_database = info.session_data_in_database
+        self.expiry = info.expiry
+        self.access_token_payload = info.custom_claims_in_access_token_payload
+        self.time_created = info.time_created
+        self.tenant_id = info.tenant_id
+
+
+
+class SignOutOK +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class SignOutOK(APIResponse):
+    status: str = "OK"
+
+    def to_json(self):
+        return {"status": self.status}
+
+

Ancestors

+ +

Class variables

+
+
var status : str
+
+
+
+
+

Methods

+
+
+def to_json(self) +
+
+
+
+ +Expand source code + +
def to_json(self):
+    return {"status": self.status}
+
+
+
+
+
+class UserCountGetAPIResponse +(count: int) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class UserCountGetAPIResponse(APIResponse):
+    status: str = "OK"
+
+    def __init__(self, count: int):
+        self.count = count
+
+    def to_json(self) -> Dict[str, Any]:
+        return {"status": self.status, "count": self.count}
+
+

Ancestors

+ +

Class variables

+
+
var status : str
+
+
+
+
+

Methods

+
+
+def to_json(self) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
def to_json(self) -> Dict[str, Any]:
+    return {"status": self.status, "count": self.count}
+
+
+
+
+
+class UserDeleteAPIResponse +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class UserDeleteAPIResponse(APIResponse):
+    status: str = "OK"
+
+    def to_json(self) -> Dict[str, Any]:
+        return {"status": self.status}
+
+

Ancestors

+ +

Class variables

+
+
var status : str
+
+
+
+
+

Methods

+
+
+def to_json(self) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
def to_json(self) -> Dict[str, Any]:
+    return {"status": self.status}
+
+
+
+
+
+class UserEmailVerifyGetAPIResponse +(is_verified: bool) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class UserEmailVerifyGetAPIResponse(APIResponse):
+    status: str = "OK"
+
+    def __init__(self, is_verified: bool):
+        self.is_verified = is_verified
+
+    def to_json(self) -> Dict[str, Any]:
+        return {"status": self.status, "isVerified": self.is_verified}
+
+

Ancestors

+ +

Class variables

+
+
var status : str
+
+
+
+
+

Methods

+
+
+def to_json(self) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
def to_json(self) -> Dict[str, Any]:
+    return {"status": self.status, "isVerified": self.is_verified}
+
+
+
+
+
+class UserEmailVerifyPutAPIResponse +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class UserEmailVerifyPutAPIResponse(APIResponse):
+    status: str = "OK"
+
+    def to_json(self) -> Dict[str, Any]:
+        return {"status": self.status}
+
+

Ancestors

+ +

Class variables

+
+
var status : str
+
+
+
+
+

Methods

+
+
+def to_json(self) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
def to_json(self) -> Dict[str, Any]:
+    return {"status": self.status}
+
+
+
+
+
+class UserEmailVerifyTokenPostAPIEmailAlreadyVerifiedErrorResponse +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class UserEmailVerifyTokenPostAPIEmailAlreadyVerifiedErrorResponse(APIResponse):
+    status: str = "EMAIL_ALREADY_VERIFIED_ERROR"
+
+    def to_json(self) -> Dict[str, Any]:
+        return {"status": self.status}
+
+

Ancestors

+ +

Class variables

+
+
var status : str
+
+
+
+
+

Methods

+
+
+def to_json(self) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
def to_json(self) -> Dict[str, Any]:
+    return {"status": self.status}
+
+
+
+
+
+class UserEmailVerifyTokenPostAPIOkResponse +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class UserEmailVerifyTokenPostAPIOkResponse(APIResponse):
+    status: str = "OK"
+
+    def to_json(self) -> Dict[str, Any]:
+        return {"status": self.status}
+
+

Ancestors

+ +

Class variables

+
+
var status : str
+
+
+
+
+

Methods

+
+
+def to_json(self) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
def to_json(self) -> Dict[str, Any]:
+    return {"status": self.status}
+
+
+
+
+
+class UserGetAPINoUserFoundError +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class UserGetAPINoUserFoundError(APIResponse):
+    status: str = "NO_USER_FOUND_ERROR"
+
+    def to_json(self) -> Dict[str, Any]:
+        return {"status": self.status}
+
+

Ancestors

+ +

Class variables

+
+
var status : str
+
+
+
+
+

Methods

+
+
+def to_json(self) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
def to_json(self) -> Dict[str, Any]:
+    return {"status": self.status}
+
+
+
+
+
+class UserGetAPIOkResponse +(recipe_id: str, user: UserWithMetadata) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class UserGetAPIOkResponse(APIResponse):
+    status: str = "OK"
+
+    def __init__(self, recipe_id: str, user: UserWithMetadata):
+        self.recipe_id = recipe_id
+        self.user = user
+
+    def to_json(self) -> Dict[str, Any]:
+        return {
+            "status": self.status,
+            "recipeId": self.recipe_id,
+            "user": self.user.to_json(),
+        }
+
+

Ancestors

+ +

Class variables

+
+
var status : str
+
+
+
+
+

Methods

+
+
+def to_json(self) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
def to_json(self) -> Dict[str, Any]:
+    return {
+        "status": self.status,
+        "recipeId": self.recipe_id,
+        "user": self.user.to_json(),
+    }
+
+
+
+
+
+class UserGetAPIRecipeNotInitialisedError +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class UserGetAPIRecipeNotInitialisedError(APIResponse):
+    status: str = "RECIPE_NOT_INITIALISED"
+
+    def to_json(self) -> Dict[str, Any]:
+        return {"status": self.status}
+
+

Ancestors

+ +

Class variables

+
+
var status : str
+
+
+
+
+

Methods

+
+
+def to_json(self) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
def to_json(self) -> Dict[str, Any]:
+    return {"status": self.status}
+
+
+
+
+
+class UserMetadataGetAPIOkResponse +(data: Dict[str, Any]) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class UserMetadataGetAPIOkResponse(APIResponse):
+    status: str = "OK"
+
+    def __init__(self, data: Dict[str, Any]) -> None:
+        self.data = data
+
+    def to_json(self) -> Dict[str, Any]:
+        return {"status": "OK", "data": self.data}
+
+

Ancestors

+ +

Class variables

+
+
var status : str
+
+
+
+
+

Methods

+
+
+def to_json(self) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
def to_json(self) -> Dict[str, Any]:
+    return {"status": "OK", "data": self.data}
+
+
+
+
+
+class UserMetadataPutAPIResponse +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class UserMetadataPutAPIResponse(APIResponse):
+    status: str = "OK"
+
+    def to_json(self) -> Dict[str, Any]:
+        return {"status": self.status}
+
+

Ancestors

+ +

Class variables

+
+
var status : str
+
+
+
+
+

Methods

+
+
+def to_json(self) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
def to_json(self) -> Dict[str, Any]:
+    return {"status": self.status}
+
+
+
+
+
+class UserPasswordPutAPIInvalidPasswordErrorResponse +(error: str) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class UserPasswordPutAPIInvalidPasswordErrorResponse(APIResponse):
+    status: str = "INVALID_PASSWORD_ERROR"
+
+    def __init__(self, error: str) -> None:
+        self.error = error
+
+    def to_json(self) -> Dict[str, Any]:
+        return {"status": self.status, "error": self.error}
+
+

Ancestors

+ +

Class variables

+
+
var status : str
+
+
+
+
+

Methods

+
+
+def to_json(self) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
def to_json(self) -> Dict[str, Any]:
+    return {"status": self.status, "error": self.error}
+
+
+
+
+
+class UserPasswordPutAPIResponse +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class UserPasswordPutAPIResponse(APIResponse):
+    status: str = "OK"
+
+    def to_json(self) -> Dict[str, Any]:
+        return {"status": self.status}
+
+

Ancestors

+ +

Class variables

+
+
var status : str
+
+
+
+
+

Methods

+
+
+def to_json(self) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
def to_json(self) -> Dict[str, Any]:
+    return {"status": self.status}
+
+
+
+
+
+class UserPutAPIEmailAlreadyExistsErrorResponse +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class UserPutAPIEmailAlreadyExistsErrorResponse(APIResponse):
+    status: str = "EMAIL_ALREADY_EXISTS_ERROR"
+
+    def to_json(self) -> Dict[str, Any]:
+        return {"status": self.status}
+
+

Ancestors

+ +

Class variables

+
+
var status : str
+
+
+
+
+

Methods

+
+
+def to_json(self) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
def to_json(self) -> Dict[str, Any]:
+    return {"status": self.status}
+
+
+
+
+
+class UserPutAPIInvalidEmailErrorResponse +(error: str) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class UserPutAPIInvalidEmailErrorResponse(APIResponse):
+    status: str = "INVALID_EMAIL_ERROR"
+
+    def __init__(self, error: str) -> None:
+        self.error = error
+
+    def to_json(self) -> Dict[str, Any]:
+        return {"status": self.status, "error": self.error}
+
+

Ancestors

+ +

Class variables

+
+
var status : str
+
+
+
+
+

Methods

+
+
+def to_json(self) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
def to_json(self) -> Dict[str, Any]:
+    return {"status": self.status, "error": self.error}
+
+
+
+
+
+class UserPutAPIInvalidPhoneErrorResponse +(error: str) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class UserPutAPIInvalidPhoneErrorResponse(APIResponse):
+    status: str = "INVALID_PHONE_ERROR"
+
+    def __init__(self, error: str) -> None:
+        self.error = error
+
+    def to_json(self) -> Dict[str, Any]:
+        return {"status": self.status, "error": self.error}
+
+

Ancestors

+ +

Class variables

+
+
var status : str
+
+
+
+
+

Methods

+
+
+def to_json(self) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
def to_json(self) -> Dict[str, Any]:
+    return {"status": self.status, "error": self.error}
+
+
+
+
+
+class UserPutAPIOkResponse +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class UserPutAPIOkResponse(APIResponse):
+    status: str = "OK"
+
+    def to_json(self) -> Dict[str, Any]:
+        return {"status": self.status}
+
+

Ancestors

+ +

Class variables

+
+
var status : str
+
+
+
+
+

Methods

+
+
+def to_json(self) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
def to_json(self) -> Dict[str, Any]:
+    return {"status": self.status}
+
+
+
+
+
+class UserPutPhoneAlreadyExistsAPIResponse +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class UserPutPhoneAlreadyExistsAPIResponse(APIResponse):
+    status: str = "PHONE_ALREADY_EXISTS_ERROR"
+
+    def to_json(self) -> Dict[str, Any]:
+        return {"status": self.status}
+
+

Ancestors

+ +

Class variables

+
+
var status : str
+
+
+
+
+

Methods

+
+
+def to_json(self) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
def to_json(self) -> Dict[str, Any]:
+    return {"status": self.status}
+
+
+
+
+
+class UserSessionsGetAPIResponse +(sessions: List[SessionInfo]) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class UserSessionsGetAPIResponse(APIResponse):
+    status: str = "OK"
+
+    def __init__(self, sessions: List[SessionInfo]):
+        self.sessions = [
+            {
+                "accessTokenPayload": s.access_token_payload,
+                "expiry": s.expiry,
+                "sessionDataInDatabase": s.session_data_in_database,
+                "tenantId": s.tenant_id,
+                "timeCreated": s.time_created,
+                "userId": s.user_id,
+                "sessionHandle": s.session_handle,
+            }
+            for s in sessions
+        ]
+
+    def to_json(self) -> Dict[str, Any]:
+        return {"status": self.status, "sessions": self.sessions}
+
+

Ancestors

+ +

Class variables

+
+
var status : str
+
+
+
+
+

Methods

+
+
+def to_json(self) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
def to_json(self) -> Dict[str, Any]:
+    return {"status": self.status, "sessions": self.sessions}
+
+
+
+
+
+class UserSessionsPostAPIResponse +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class UserSessionsPostAPIResponse(APIResponse):
+    status: str = "OK"
+
+    def to_json(self) -> Dict[str, Any]:
+        return {"status": self.status}
+
+

Ancestors

+ +

Class variables

+
+
var status : str
+
+
+
+
+

Methods

+
+
+def to_json(self) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
def to_json(self) -> Dict[str, Any]:
+    return {"status": self.status}
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/dashboard/recipe.html b/html/supertokens_python/recipe/dashboard/recipe.html new file mode 100644 index 000000000..a907b23b8 --- /dev/null +++ b/html/supertokens_python/recipe/dashboard/recipe.html @@ -0,0 +1,1187 @@ + + + + + + +supertokens_python.recipe.dashboard.recipe API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.dashboard.recipe

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from os import environ
+from typing import TYPE_CHECKING, Awaitable, Callable, List, Optional, Dict, Any
+
+from supertokens_python.normalised_url_path import NormalisedURLPath
+from supertokens_python.recipe_module import APIHandled, RecipeModule
+
+from .api import (
+    api_key_protector,
+    handle_analytics_post,
+    handle_dashboard_api,
+    handle_email_verify_token_post,
+    handle_emailpassword_signin_api,
+    handle_emailpassword_signout_api,
+    handle_get_tags,
+    handle_metadata_get,
+    handle_metadata_put,
+    handle_sessions_get,
+    handle_user_delete,
+    handle_user_email_verify_get,
+    handle_user_email_verify_put,
+    handle_user_get,
+    handle_user_password_put,
+    handle_user_put,
+    handle_user_sessions_post,
+    handle_users_count_get_api,
+    handle_users_get_api,
+    handle_validate_key_api,
+    handle_list_tenants_api,
+)
+from .api.implementation import APIImplementation
+from .exceptions import SuperTokensDashboardError
+from .interfaces import APIInterface, APIOptions
+from .recipe_implementation import RecipeImplementation
+
+if TYPE_CHECKING:
+    from supertokens_python.framework.request import BaseRequest
+    from supertokens_python.framework.response import BaseResponse
+    from supertokens_python.supertokens import AppInfo
+    from supertokens_python.types import APIResponse
+
+from supertokens_python.exceptions import SuperTokensError, raise_general_exception
+from supertokens_python.recipe.dashboard.utils import get_api_path_with_dashboard_base
+
+from .constants import (
+    DASHBOARD_ANALYTICS_API,
+    DASHBOARD_API,
+    SIGN_OUT_API,
+    SIGN_IN_API,
+    SEARCH_TAGS_API,
+    USER_API,
+    USER_EMAIL_VERIFY_API,
+    USER_EMAIL_VERIFY_TOKEN_API,
+    USER_METADATA_API,
+    USER_PASSWORD_API,
+    USER_SESSION_API,
+    USERS_COUNT_API,
+    USERS_LIST_GET_API,
+    VALIDATE_KEY_API,
+    TENANTS_LIST_API,
+)
+from .utils import (
+    InputOverrideConfig,
+    validate_and_normalise_user_input,
+)
+
+
+class DashboardRecipe(RecipeModule):
+    recipe_id = "dashboard"
+    __instance = None
+
+    def __init__(
+        self,
+        recipe_id: str,
+        app_info: AppInfo,
+        api_key: Optional[str],
+        admins: Optional[List[str]],
+        override: Optional[InputOverrideConfig] = None,
+    ):
+        super().__init__(recipe_id, app_info)
+        self.config = validate_and_normalise_user_input(
+            api_key,
+            admins,
+            override,
+        )
+        recipe_implementation = RecipeImplementation()
+        self.recipe_implementation = (
+            recipe_implementation
+            if self.config.override.functions is None
+            else self.config.override.functions(recipe_implementation)
+        )
+
+        api_implementation = APIImplementation()
+        self.api_implementation = (
+            api_implementation
+            if self.config.override.apis is None
+            else self.config.override.apis(api_implementation)
+        )
+
+    def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
+        return isinstance(err, SuperTokensError) and (
+            isinstance(err, SuperTokensDashboardError)
+        )
+
+    def get_apis_handled(self) -> List[APIHandled]:
+        return [
+            APIHandled(
+                NormalisedURLPath(get_api_path_with_dashboard_base("/")),
+                "get",
+                DASHBOARD_API,
+                False,
+            ),
+            APIHandled(
+                NormalisedURLPath(get_api_path_with_dashboard_base(SIGN_IN_API)),
+                "post",
+                SIGN_IN_API,
+                False,
+            ),
+            APIHandled(
+                NormalisedURLPath(get_api_path_with_dashboard_base(VALIDATE_KEY_API)),
+                "post",
+                VALIDATE_KEY_API,
+                False,
+            ),
+            APIHandled(
+                NormalisedURLPath(get_api_path_with_dashboard_base(SIGN_OUT_API)),
+                "post",
+                SIGN_OUT_API,
+                False,
+            ),
+            APIHandled(
+                NormalisedURLPath(get_api_path_with_dashboard_base(USERS_LIST_GET_API)),
+                "get",
+                USERS_LIST_GET_API,
+                False,
+            ),
+            APIHandled(
+                NormalisedURLPath(get_api_path_with_dashboard_base(USERS_COUNT_API)),
+                "get",
+                USERS_COUNT_API,
+                False,
+            ),
+            APIHandled(
+                NormalisedURLPath(get_api_path_with_dashboard_base(USER_API)),
+                "get",
+                USER_API,
+                False,
+            ),
+            APIHandled(
+                NormalisedURLPath(get_api_path_with_dashboard_base(USER_API)),
+                "post",
+                USER_API,
+                False,
+            ),
+            APIHandled(
+                NormalisedURLPath(get_api_path_with_dashboard_base(USER_API)),
+                "put",
+                USER_API,
+                False,
+            ),
+            APIHandled(
+                NormalisedURLPath(get_api_path_with_dashboard_base(USER_API)),
+                "delete",
+                USER_API,
+                False,
+            ),
+            APIHandled(
+                NormalisedURLPath(
+                    get_api_path_with_dashboard_base(USER_EMAIL_VERIFY_API)
+                ),
+                "get",
+                USER_EMAIL_VERIFY_API,
+                False,
+            ),
+            APIHandled(
+                NormalisedURLPath(
+                    get_api_path_with_dashboard_base(USER_EMAIL_VERIFY_API)
+                ),
+                "put",
+                USER_EMAIL_VERIFY_API,
+                False,
+            ),
+            APIHandled(
+                NormalisedURLPath(get_api_path_with_dashboard_base(USER_METADATA_API)),
+                "get",
+                USER_METADATA_API,
+                False,
+            ),
+            APIHandled(
+                NormalisedURLPath(get_api_path_with_dashboard_base(USER_METADATA_API)),
+                "put",
+                USER_METADATA_API,
+                False,
+            ),
+            APIHandled(
+                NormalisedURLPath(get_api_path_with_dashboard_base(USER_SESSION_API)),
+                "get",
+                USER_SESSION_API,
+                False,
+            ),
+            APIHandled(
+                NormalisedURLPath(get_api_path_with_dashboard_base(USER_SESSION_API)),
+                "post",
+                USER_SESSION_API,
+                False,
+            ),
+            APIHandled(
+                NormalisedURLPath(get_api_path_with_dashboard_base(USER_PASSWORD_API)),
+                "put",
+                USER_PASSWORD_API,
+                False,
+            ),
+            APIHandled(
+                NormalisedURLPath(
+                    get_api_path_with_dashboard_base(USER_EMAIL_VERIFY_TOKEN_API)
+                ),
+                "post",
+                USER_EMAIL_VERIFY_TOKEN_API,
+                False,
+            ),
+            APIHandled(
+                NormalisedURLPath(get_api_path_with_dashboard_base(SEARCH_TAGS_API)),
+                "get",
+                SEARCH_TAGS_API,
+                False,
+            ),
+            APIHandled(
+                NormalisedURLPath(
+                    get_api_path_with_dashboard_base(DASHBOARD_ANALYTICS_API)
+                ),
+                "post",
+                DASHBOARD_ANALYTICS_API,
+                False,
+            ),
+            APIHandled(
+                NormalisedURLPath(get_api_path_with_dashboard_base(TENANTS_LIST_API)),
+                "get",
+                TENANTS_LIST_API,
+                False,
+            ),
+        ]
+
+    async def handle_api_request(
+        self,
+        request_id: str,
+        tenant_id: str,
+        request: BaseRequest,
+        path: NormalisedURLPath,
+        method: str,
+        response: BaseResponse,
+        user_context: Dict[str, Any],
+    ) -> Optional[BaseResponse]:
+        api_options = APIOptions(
+            request,
+            response,
+            self.recipe_id,
+            self.config,
+            self.recipe_implementation,
+            self.get_app_info(),
+        )
+        # For these APIs we dont need API key validation
+        if request_id == DASHBOARD_API:
+            return await handle_dashboard_api(
+                self.api_implementation, api_options, user_context
+            )
+        if request_id == VALIDATE_KEY_API:
+            return await handle_validate_key_api(
+                self.api_implementation, api_options, user_context
+            )
+        if request_id == SIGN_IN_API:
+            return await handle_emailpassword_signin_api(
+                self.api_implementation, api_options, user_context
+            )
+
+        # Do API key validation for the remaining APIs
+        api_function: Optional[
+            Callable[
+                [APIInterface, str, APIOptions, Dict[str, Any]], Awaitable[APIResponse]
+            ]
+        ] = None
+        if request_id == USERS_LIST_GET_API:
+            api_function = handle_users_get_api
+        elif request_id == USERS_COUNT_API:
+            api_function = handle_users_count_get_api
+        elif request_id == USER_API:
+            if method == "get":
+                api_function = handle_user_get
+            if method == "delete":
+                api_function = handle_user_delete
+            if method == "put":
+                api_function = handle_user_put
+        elif request_id == USER_EMAIL_VERIFY_API:
+            if method == "get":
+                api_function = handle_user_email_verify_get
+            if method == "put":
+                api_function = handle_user_email_verify_put
+        elif request_id == USER_METADATA_API:
+            if method == "get":
+                api_function = handle_metadata_get
+            if method == "put":
+                api_function = handle_metadata_put
+        elif request_id == USER_SESSION_API:
+            if method == "get":
+                api_function = handle_sessions_get
+            if method == "post":
+                api_function = handle_user_sessions_post
+        elif request_id == USER_PASSWORD_API:
+            api_function = handle_user_password_put
+        elif request_id == USER_EMAIL_VERIFY_TOKEN_API:
+            api_function = handle_email_verify_token_post
+        elif request_id == SIGN_OUT_API:
+            api_function = handle_emailpassword_signout_api
+        elif request_id == SEARCH_TAGS_API:
+            api_function = handle_get_tags
+        elif request_id == DASHBOARD_ANALYTICS_API:
+            if method == "post":
+                api_function = handle_analytics_post
+        elif request_id == TENANTS_LIST_API:
+            api_function = handle_list_tenants_api
+
+        if api_function is not None:
+            return await api_key_protector(
+                self.api_implementation,
+                tenant_id,
+                api_options,
+                api_function,
+                user_context,
+            )
+
+        return None
+
+    async def handle_error(
+        self,
+        request: BaseRequest,
+        err: SuperTokensError,
+        response: BaseResponse,
+        user_context: Dict[str, Any],
+    ) -> BaseResponse:
+        raise err
+
+    def get_all_cors_headers(self) -> List[str]:
+        return []
+
+    @staticmethod
+    def init(
+        api_key: Optional[str],
+        admins: Optional[List[str]] = None,
+        override: Optional[InputOverrideConfig] = None,
+    ):
+        def func(app_info: AppInfo):
+            if DashboardRecipe.__instance is None:
+                DashboardRecipe.__instance = DashboardRecipe(
+                    DashboardRecipe.recipe_id,
+                    app_info,
+                    api_key,
+                    admins,
+                    override,
+                )
+                return DashboardRecipe.__instance
+            raise Exception(
+                None,
+                "Dashboard recipe has already been initialised. Please check your code for bugs.",
+            )
+
+        return func
+
+    @staticmethod
+    def get_instance() -> DashboardRecipe:
+        if DashboardRecipe.__instance is not None:
+            return DashboardRecipe.__instance
+        raise_general_exception(
+            "Initialisation not done. Did you forget to call the SuperTokens.init function?"
+        )
+
+    @staticmethod
+    def reset():
+        if ("SUPERTOKENS_ENV" not in environ) or (
+            environ["SUPERTOKENS_ENV"] != "testing"
+        ):
+            raise_general_exception("calling testing function in non testing env")
+        DashboardRecipe.__instance = None
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class DashboardRecipe +(recipe_id: str, app_info: AppInfo, api_key: Optional[str], admins: Optional[List[str]], override: Optional[InputOverrideConfig] = None) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class DashboardRecipe(RecipeModule):
+    recipe_id = "dashboard"
+    __instance = None
+
+    def __init__(
+        self,
+        recipe_id: str,
+        app_info: AppInfo,
+        api_key: Optional[str],
+        admins: Optional[List[str]],
+        override: Optional[InputOverrideConfig] = None,
+    ):
+        super().__init__(recipe_id, app_info)
+        self.config = validate_and_normalise_user_input(
+            api_key,
+            admins,
+            override,
+        )
+        recipe_implementation = RecipeImplementation()
+        self.recipe_implementation = (
+            recipe_implementation
+            if self.config.override.functions is None
+            else self.config.override.functions(recipe_implementation)
+        )
+
+        api_implementation = APIImplementation()
+        self.api_implementation = (
+            api_implementation
+            if self.config.override.apis is None
+            else self.config.override.apis(api_implementation)
+        )
+
+    def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
+        return isinstance(err, SuperTokensError) and (
+            isinstance(err, SuperTokensDashboardError)
+        )
+
+    def get_apis_handled(self) -> List[APIHandled]:
+        return [
+            APIHandled(
+                NormalisedURLPath(get_api_path_with_dashboard_base("/")),
+                "get",
+                DASHBOARD_API,
+                False,
+            ),
+            APIHandled(
+                NormalisedURLPath(get_api_path_with_dashboard_base(SIGN_IN_API)),
+                "post",
+                SIGN_IN_API,
+                False,
+            ),
+            APIHandled(
+                NormalisedURLPath(get_api_path_with_dashboard_base(VALIDATE_KEY_API)),
+                "post",
+                VALIDATE_KEY_API,
+                False,
+            ),
+            APIHandled(
+                NormalisedURLPath(get_api_path_with_dashboard_base(SIGN_OUT_API)),
+                "post",
+                SIGN_OUT_API,
+                False,
+            ),
+            APIHandled(
+                NormalisedURLPath(get_api_path_with_dashboard_base(USERS_LIST_GET_API)),
+                "get",
+                USERS_LIST_GET_API,
+                False,
+            ),
+            APIHandled(
+                NormalisedURLPath(get_api_path_with_dashboard_base(USERS_COUNT_API)),
+                "get",
+                USERS_COUNT_API,
+                False,
+            ),
+            APIHandled(
+                NormalisedURLPath(get_api_path_with_dashboard_base(USER_API)),
+                "get",
+                USER_API,
+                False,
+            ),
+            APIHandled(
+                NormalisedURLPath(get_api_path_with_dashboard_base(USER_API)),
+                "post",
+                USER_API,
+                False,
+            ),
+            APIHandled(
+                NormalisedURLPath(get_api_path_with_dashboard_base(USER_API)),
+                "put",
+                USER_API,
+                False,
+            ),
+            APIHandled(
+                NormalisedURLPath(get_api_path_with_dashboard_base(USER_API)),
+                "delete",
+                USER_API,
+                False,
+            ),
+            APIHandled(
+                NormalisedURLPath(
+                    get_api_path_with_dashboard_base(USER_EMAIL_VERIFY_API)
+                ),
+                "get",
+                USER_EMAIL_VERIFY_API,
+                False,
+            ),
+            APIHandled(
+                NormalisedURLPath(
+                    get_api_path_with_dashboard_base(USER_EMAIL_VERIFY_API)
+                ),
+                "put",
+                USER_EMAIL_VERIFY_API,
+                False,
+            ),
+            APIHandled(
+                NormalisedURLPath(get_api_path_with_dashboard_base(USER_METADATA_API)),
+                "get",
+                USER_METADATA_API,
+                False,
+            ),
+            APIHandled(
+                NormalisedURLPath(get_api_path_with_dashboard_base(USER_METADATA_API)),
+                "put",
+                USER_METADATA_API,
+                False,
+            ),
+            APIHandled(
+                NormalisedURLPath(get_api_path_with_dashboard_base(USER_SESSION_API)),
+                "get",
+                USER_SESSION_API,
+                False,
+            ),
+            APIHandled(
+                NormalisedURLPath(get_api_path_with_dashboard_base(USER_SESSION_API)),
+                "post",
+                USER_SESSION_API,
+                False,
+            ),
+            APIHandled(
+                NormalisedURLPath(get_api_path_with_dashboard_base(USER_PASSWORD_API)),
+                "put",
+                USER_PASSWORD_API,
+                False,
+            ),
+            APIHandled(
+                NormalisedURLPath(
+                    get_api_path_with_dashboard_base(USER_EMAIL_VERIFY_TOKEN_API)
+                ),
+                "post",
+                USER_EMAIL_VERIFY_TOKEN_API,
+                False,
+            ),
+            APIHandled(
+                NormalisedURLPath(get_api_path_with_dashboard_base(SEARCH_TAGS_API)),
+                "get",
+                SEARCH_TAGS_API,
+                False,
+            ),
+            APIHandled(
+                NormalisedURLPath(
+                    get_api_path_with_dashboard_base(DASHBOARD_ANALYTICS_API)
+                ),
+                "post",
+                DASHBOARD_ANALYTICS_API,
+                False,
+            ),
+            APIHandled(
+                NormalisedURLPath(get_api_path_with_dashboard_base(TENANTS_LIST_API)),
+                "get",
+                TENANTS_LIST_API,
+                False,
+            ),
+        ]
+
+    async def handle_api_request(
+        self,
+        request_id: str,
+        tenant_id: str,
+        request: BaseRequest,
+        path: NormalisedURLPath,
+        method: str,
+        response: BaseResponse,
+        user_context: Dict[str, Any],
+    ) -> Optional[BaseResponse]:
+        api_options = APIOptions(
+            request,
+            response,
+            self.recipe_id,
+            self.config,
+            self.recipe_implementation,
+            self.get_app_info(),
+        )
+        # For these APIs we dont need API key validation
+        if request_id == DASHBOARD_API:
+            return await handle_dashboard_api(
+                self.api_implementation, api_options, user_context
+            )
+        if request_id == VALIDATE_KEY_API:
+            return await handle_validate_key_api(
+                self.api_implementation, api_options, user_context
+            )
+        if request_id == SIGN_IN_API:
+            return await handle_emailpassword_signin_api(
+                self.api_implementation, api_options, user_context
+            )
+
+        # Do API key validation for the remaining APIs
+        api_function: Optional[
+            Callable[
+                [APIInterface, str, APIOptions, Dict[str, Any]], Awaitable[APIResponse]
+            ]
+        ] = None
+        if request_id == USERS_LIST_GET_API:
+            api_function = handle_users_get_api
+        elif request_id == USERS_COUNT_API:
+            api_function = handle_users_count_get_api
+        elif request_id == USER_API:
+            if method == "get":
+                api_function = handle_user_get
+            if method == "delete":
+                api_function = handle_user_delete
+            if method == "put":
+                api_function = handle_user_put
+        elif request_id == USER_EMAIL_VERIFY_API:
+            if method == "get":
+                api_function = handle_user_email_verify_get
+            if method == "put":
+                api_function = handle_user_email_verify_put
+        elif request_id == USER_METADATA_API:
+            if method == "get":
+                api_function = handle_metadata_get
+            if method == "put":
+                api_function = handle_metadata_put
+        elif request_id == USER_SESSION_API:
+            if method == "get":
+                api_function = handle_sessions_get
+            if method == "post":
+                api_function = handle_user_sessions_post
+        elif request_id == USER_PASSWORD_API:
+            api_function = handle_user_password_put
+        elif request_id == USER_EMAIL_VERIFY_TOKEN_API:
+            api_function = handle_email_verify_token_post
+        elif request_id == SIGN_OUT_API:
+            api_function = handle_emailpassword_signout_api
+        elif request_id == SEARCH_TAGS_API:
+            api_function = handle_get_tags
+        elif request_id == DASHBOARD_ANALYTICS_API:
+            if method == "post":
+                api_function = handle_analytics_post
+        elif request_id == TENANTS_LIST_API:
+            api_function = handle_list_tenants_api
+
+        if api_function is not None:
+            return await api_key_protector(
+                self.api_implementation,
+                tenant_id,
+                api_options,
+                api_function,
+                user_context,
+            )
+
+        return None
+
+    async def handle_error(
+        self,
+        request: BaseRequest,
+        err: SuperTokensError,
+        response: BaseResponse,
+        user_context: Dict[str, Any],
+    ) -> BaseResponse:
+        raise err
+
+    def get_all_cors_headers(self) -> List[str]:
+        return []
+
+    @staticmethod
+    def init(
+        api_key: Optional[str],
+        admins: Optional[List[str]] = None,
+        override: Optional[InputOverrideConfig] = None,
+    ):
+        def func(app_info: AppInfo):
+            if DashboardRecipe.__instance is None:
+                DashboardRecipe.__instance = DashboardRecipe(
+                    DashboardRecipe.recipe_id,
+                    app_info,
+                    api_key,
+                    admins,
+                    override,
+                )
+                return DashboardRecipe.__instance
+            raise Exception(
+                None,
+                "Dashboard recipe has already been initialised. Please check your code for bugs.",
+            )
+
+        return func
+
+    @staticmethod
+    def get_instance() -> DashboardRecipe:
+        if DashboardRecipe.__instance is not None:
+            return DashboardRecipe.__instance
+        raise_general_exception(
+            "Initialisation not done. Did you forget to call the SuperTokens.init function?"
+        )
+
+    @staticmethod
+    def reset():
+        if ("SUPERTOKENS_ENV" not in environ) or (
+            environ["SUPERTOKENS_ENV"] != "testing"
+        ):
+            raise_general_exception("calling testing function in non testing env")
+        DashboardRecipe.__instance = None
+
+

Ancestors

+ +

Class variables

+
+
var get_tenant_id : Optional[Callable[[str, Dict[str, Any]], Awaitable[str]]]
+
+
+
+
var recipe_id
+
+
+
+
+

Static methods

+
+
+def get_instance() ‑> DashboardRecipe +
+
+
+
+ +Expand source code + +
@staticmethod
+def get_instance() -> DashboardRecipe:
+    if DashboardRecipe.__instance is not None:
+        return DashboardRecipe.__instance
+    raise_general_exception(
+        "Initialisation not done. Did you forget to call the SuperTokens.init function?"
+    )
+
+
+
+def init(api_key: Optional[str], admins: Optional[List[str]] = None, override: Optional[InputOverrideConfig] = None) +
+
+
+
+ +Expand source code + +
@staticmethod
+def init(
+    api_key: Optional[str],
+    admins: Optional[List[str]] = None,
+    override: Optional[InputOverrideConfig] = None,
+):
+    def func(app_info: AppInfo):
+        if DashboardRecipe.__instance is None:
+            DashboardRecipe.__instance = DashboardRecipe(
+                DashboardRecipe.recipe_id,
+                app_info,
+                api_key,
+                admins,
+                override,
+            )
+            return DashboardRecipe.__instance
+        raise Exception(
+            None,
+            "Dashboard recipe has already been initialised. Please check your code for bugs.",
+        )
+
+    return func
+
+
+
+def reset() +
+
+
+
+ +Expand source code + +
@staticmethod
+def reset():
+    if ("SUPERTOKENS_ENV" not in environ) or (
+        environ["SUPERTOKENS_ENV"] != "testing"
+    ):
+        raise_general_exception("calling testing function in non testing env")
+    DashboardRecipe.__instance = None
+
+
+
+

Methods

+
+
+def get_all_cors_headers(self) ‑> List[str] +
+
+
+
+ +Expand source code + +
def get_all_cors_headers(self) -> List[str]:
+    return []
+
+
+
+def get_apis_handled(self) ‑> List[APIHandled] +
+
+
+
+ +Expand source code + +
def get_apis_handled(self) -> List[APIHandled]:
+    return [
+        APIHandled(
+            NormalisedURLPath(get_api_path_with_dashboard_base("/")),
+            "get",
+            DASHBOARD_API,
+            False,
+        ),
+        APIHandled(
+            NormalisedURLPath(get_api_path_with_dashboard_base(SIGN_IN_API)),
+            "post",
+            SIGN_IN_API,
+            False,
+        ),
+        APIHandled(
+            NormalisedURLPath(get_api_path_with_dashboard_base(VALIDATE_KEY_API)),
+            "post",
+            VALIDATE_KEY_API,
+            False,
+        ),
+        APIHandled(
+            NormalisedURLPath(get_api_path_with_dashboard_base(SIGN_OUT_API)),
+            "post",
+            SIGN_OUT_API,
+            False,
+        ),
+        APIHandled(
+            NormalisedURLPath(get_api_path_with_dashboard_base(USERS_LIST_GET_API)),
+            "get",
+            USERS_LIST_GET_API,
+            False,
+        ),
+        APIHandled(
+            NormalisedURLPath(get_api_path_with_dashboard_base(USERS_COUNT_API)),
+            "get",
+            USERS_COUNT_API,
+            False,
+        ),
+        APIHandled(
+            NormalisedURLPath(get_api_path_with_dashboard_base(USER_API)),
+            "get",
+            USER_API,
+            False,
+        ),
+        APIHandled(
+            NormalisedURLPath(get_api_path_with_dashboard_base(USER_API)),
+            "post",
+            USER_API,
+            False,
+        ),
+        APIHandled(
+            NormalisedURLPath(get_api_path_with_dashboard_base(USER_API)),
+            "put",
+            USER_API,
+            False,
+        ),
+        APIHandled(
+            NormalisedURLPath(get_api_path_with_dashboard_base(USER_API)),
+            "delete",
+            USER_API,
+            False,
+        ),
+        APIHandled(
+            NormalisedURLPath(
+                get_api_path_with_dashboard_base(USER_EMAIL_VERIFY_API)
+            ),
+            "get",
+            USER_EMAIL_VERIFY_API,
+            False,
+        ),
+        APIHandled(
+            NormalisedURLPath(
+                get_api_path_with_dashboard_base(USER_EMAIL_VERIFY_API)
+            ),
+            "put",
+            USER_EMAIL_VERIFY_API,
+            False,
+        ),
+        APIHandled(
+            NormalisedURLPath(get_api_path_with_dashboard_base(USER_METADATA_API)),
+            "get",
+            USER_METADATA_API,
+            False,
+        ),
+        APIHandled(
+            NormalisedURLPath(get_api_path_with_dashboard_base(USER_METADATA_API)),
+            "put",
+            USER_METADATA_API,
+            False,
+        ),
+        APIHandled(
+            NormalisedURLPath(get_api_path_with_dashboard_base(USER_SESSION_API)),
+            "get",
+            USER_SESSION_API,
+            False,
+        ),
+        APIHandled(
+            NormalisedURLPath(get_api_path_with_dashboard_base(USER_SESSION_API)),
+            "post",
+            USER_SESSION_API,
+            False,
+        ),
+        APIHandled(
+            NormalisedURLPath(get_api_path_with_dashboard_base(USER_PASSWORD_API)),
+            "put",
+            USER_PASSWORD_API,
+            False,
+        ),
+        APIHandled(
+            NormalisedURLPath(
+                get_api_path_with_dashboard_base(USER_EMAIL_VERIFY_TOKEN_API)
+            ),
+            "post",
+            USER_EMAIL_VERIFY_TOKEN_API,
+            False,
+        ),
+        APIHandled(
+            NormalisedURLPath(get_api_path_with_dashboard_base(SEARCH_TAGS_API)),
+            "get",
+            SEARCH_TAGS_API,
+            False,
+        ),
+        APIHandled(
+            NormalisedURLPath(
+                get_api_path_with_dashboard_base(DASHBOARD_ANALYTICS_API)
+            ),
+            "post",
+            DASHBOARD_ANALYTICS_API,
+            False,
+        ),
+        APIHandled(
+            NormalisedURLPath(get_api_path_with_dashboard_base(TENANTS_LIST_API)),
+            "get",
+            TENANTS_LIST_API,
+            False,
+        ),
+    ]
+
+
+
+async def handle_api_request(self, request_id: str, tenant_id: str, request: BaseRequest, path: NormalisedURLPath, method: str, response: BaseResponse, user_context: Dict[str, Any]) ‑> Optional[BaseResponse] +
+
+
+
+ +Expand source code + +
async def handle_api_request(
+    self,
+    request_id: str,
+    tenant_id: str,
+    request: BaseRequest,
+    path: NormalisedURLPath,
+    method: str,
+    response: BaseResponse,
+    user_context: Dict[str, Any],
+) -> Optional[BaseResponse]:
+    api_options = APIOptions(
+        request,
+        response,
+        self.recipe_id,
+        self.config,
+        self.recipe_implementation,
+        self.get_app_info(),
+    )
+    # For these APIs we dont need API key validation
+    if request_id == DASHBOARD_API:
+        return await handle_dashboard_api(
+            self.api_implementation, api_options, user_context
+        )
+    if request_id == VALIDATE_KEY_API:
+        return await handle_validate_key_api(
+            self.api_implementation, api_options, user_context
+        )
+    if request_id == SIGN_IN_API:
+        return await handle_emailpassword_signin_api(
+            self.api_implementation, api_options, user_context
+        )
+
+    # Do API key validation for the remaining APIs
+    api_function: Optional[
+        Callable[
+            [APIInterface, str, APIOptions, Dict[str, Any]], Awaitable[APIResponse]
+        ]
+    ] = None
+    if request_id == USERS_LIST_GET_API:
+        api_function = handle_users_get_api
+    elif request_id == USERS_COUNT_API:
+        api_function = handle_users_count_get_api
+    elif request_id == USER_API:
+        if method == "get":
+            api_function = handle_user_get
+        if method == "delete":
+            api_function = handle_user_delete
+        if method == "put":
+            api_function = handle_user_put
+    elif request_id == USER_EMAIL_VERIFY_API:
+        if method == "get":
+            api_function = handle_user_email_verify_get
+        if method == "put":
+            api_function = handle_user_email_verify_put
+    elif request_id == USER_METADATA_API:
+        if method == "get":
+            api_function = handle_metadata_get
+        if method == "put":
+            api_function = handle_metadata_put
+    elif request_id == USER_SESSION_API:
+        if method == "get":
+            api_function = handle_sessions_get
+        if method == "post":
+            api_function = handle_user_sessions_post
+    elif request_id == USER_PASSWORD_API:
+        api_function = handle_user_password_put
+    elif request_id == USER_EMAIL_VERIFY_TOKEN_API:
+        api_function = handle_email_verify_token_post
+    elif request_id == SIGN_OUT_API:
+        api_function = handle_emailpassword_signout_api
+    elif request_id == SEARCH_TAGS_API:
+        api_function = handle_get_tags
+    elif request_id == DASHBOARD_ANALYTICS_API:
+        if method == "post":
+            api_function = handle_analytics_post
+    elif request_id == TENANTS_LIST_API:
+        api_function = handle_list_tenants_api
+
+    if api_function is not None:
+        return await api_key_protector(
+            self.api_implementation,
+            tenant_id,
+            api_options,
+            api_function,
+            user_context,
+        )
+
+    return None
+
+
+
+async def handle_error(self, request: BaseRequest, err: SuperTokensError, response: BaseResponse, user_context: Dict[str, Any]) ‑> BaseResponse +
+
+
+
+ +Expand source code + +
async def handle_error(
+    self,
+    request: BaseRequest,
+    err: SuperTokensError,
+    response: BaseResponse,
+    user_context: Dict[str, Any],
+) -> BaseResponse:
+    raise err
+
+
+
+def is_error_from_this_recipe_based_on_instance(self, err: Exception) ‑> bool +
+
+
+
+ +Expand source code + +
def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
+    return isinstance(err, SuperTokensError) and (
+        isinstance(err, SuperTokensDashboardError)
+    )
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/dashboard/recipe_implementation.html b/html/supertokens_python/recipe/dashboard/recipe_implementation.html new file mode 100644 index 000000000..c44d25aa6 --- /dev/null +++ b/html/supertokens_python/recipe/dashboard/recipe_implementation.html @@ -0,0 +1,342 @@ + + + + + + +supertokens_python.recipe.dashboard.recipe_implementation API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.dashboard.recipe_implementation

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from typing import Any, Dict
+
+from supertokens_python.constants import DASHBOARD_VERSION
+from supertokens_python.framework import BaseRequest
+from supertokens_python.normalised_url_path import NormalisedURLPath
+from supertokens_python.utils import log_debug_message, normalise_http_method
+from supertokens_python.querier import Querier
+from supertokens_python.recipe.dashboard.constants import (
+    DASHBOARD_ANALYTICS_API,
+    SIGN_OUT_API,
+)
+
+from .interfaces import RecipeInterface
+from .utils import DashboardConfig, validate_api_key
+from .exceptions import DashboardOperationNotAllowedError
+
+
+class RecipeImplementation(RecipeInterface):
+    async def get_dashboard_bundle_location(self, user_context: Dict[str, Any]) -> str:
+        return f"https://cdn.jsdelivr.net/gh/supertokens/dashboard@v{DASHBOARD_VERSION}/build/"
+
+    async def should_allow_access(
+        self,
+        request: BaseRequest,
+        config: DashboardConfig,
+        user_context: Dict[str, Any],
+    ) -> bool:
+        # For cases where we're not using the API key, the JWT is being used; we allow their access by default
+        if config.api_key is None:
+            auth_header_value = request.get_header("authorization")
+            if not auth_header_value:
+                return False
+
+            auth_header_value = auth_header_value.split()[1]
+            session_verification_response = (
+                await Querier.get_instance().send_post_request(
+                    NormalisedURLPath("/recipe/dashboard/session/verify"),
+                    {"sessionId": auth_header_value},
+                    user_context=user_context,
+                )
+            )
+            if session_verification_response.get("status") != "OK":
+                return False
+
+            # For all non GET requests we also want to check if the
+            # user is allowed to perform this operation
+            if normalise_http_method(request.method()) != "get":
+                # We dont want to block the analytics API
+                if request.get_original_url().endswith(DASHBOARD_ANALYTICS_API):
+                    return True
+
+                # We do not want to block the sign out request
+                if request.get_original_url().endswith(SIGN_OUT_API):
+                    return True
+
+                admins = config.admins
+
+                if admins is None:
+                    return True
+
+                if len(admins) == 0:
+                    log_debug_message(
+                        "User Dashboard: Throwing OPERATION_NOT_ALLOWED because user is not an admin"
+                    )
+                    raise DashboardOperationNotAllowedError()
+
+                user_email = session_verification_response.get("email")
+
+                if user_email is None or not isinstance(user_email, str):
+                    log_debug_message(
+                        "User Dashboard: Returning UNAUTHORISED_ERROR because no email was provided in headers"
+                    )
+                    return False
+
+                if user_email not in admins:
+                    log_debug_message(
+                        "User Dashboard: Throwing OPERATION_NOT_ALLOWED because user is not an admin"
+                    )
+                    raise DashboardOperationNotAllowedError()
+
+            return True
+
+        return await validate_api_key(request, config, user_context)
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class RecipeImplementation +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class RecipeImplementation(RecipeInterface):
+    async def get_dashboard_bundle_location(self, user_context: Dict[str, Any]) -> str:
+        return f"https://cdn.jsdelivr.net/gh/supertokens/dashboard@v{DASHBOARD_VERSION}/build/"
+
+    async def should_allow_access(
+        self,
+        request: BaseRequest,
+        config: DashboardConfig,
+        user_context: Dict[str, Any],
+    ) -> bool:
+        # For cases where we're not using the API key, the JWT is being used; we allow their access by default
+        if config.api_key is None:
+            auth_header_value = request.get_header("authorization")
+            if not auth_header_value:
+                return False
+
+            auth_header_value = auth_header_value.split()[1]
+            session_verification_response = (
+                await Querier.get_instance().send_post_request(
+                    NormalisedURLPath("/recipe/dashboard/session/verify"),
+                    {"sessionId": auth_header_value},
+                    user_context=user_context,
+                )
+            )
+            if session_verification_response.get("status") != "OK":
+                return False
+
+            # For all non GET requests we also want to check if the
+            # user is allowed to perform this operation
+            if normalise_http_method(request.method()) != "get":
+                # We dont want to block the analytics API
+                if request.get_original_url().endswith(DASHBOARD_ANALYTICS_API):
+                    return True
+
+                # We do not want to block the sign out request
+                if request.get_original_url().endswith(SIGN_OUT_API):
+                    return True
+
+                admins = config.admins
+
+                if admins is None:
+                    return True
+
+                if len(admins) == 0:
+                    log_debug_message(
+                        "User Dashboard: Throwing OPERATION_NOT_ALLOWED because user is not an admin"
+                    )
+                    raise DashboardOperationNotAllowedError()
+
+                user_email = session_verification_response.get("email")
+
+                if user_email is None or not isinstance(user_email, str):
+                    log_debug_message(
+                        "User Dashboard: Returning UNAUTHORISED_ERROR because no email was provided in headers"
+                    )
+                    return False
+
+                if user_email not in admins:
+                    log_debug_message(
+                        "User Dashboard: Throwing OPERATION_NOT_ALLOWED because user is not an admin"
+                    )
+                    raise DashboardOperationNotAllowedError()
+
+            return True
+
+        return await validate_api_key(request, config, user_context)
+
+

Ancestors

+ +

Methods

+
+
+async def get_dashboard_bundle_location(self, user_context: Dict[str, Any]) ‑> str +
+
+
+
+ +Expand source code + +
async def get_dashboard_bundle_location(self, user_context: Dict[str, Any]) -> str:
+    return f"https://cdn.jsdelivr.net/gh/supertokens/dashboard@v{DASHBOARD_VERSION}/build/"
+
+
+
+async def should_allow_access(self, request: BaseRequest, config: DashboardConfig, user_context: Dict[str, Any]) ‑> bool +
+
+
+
+ +Expand source code + +
async def should_allow_access(
+    self,
+    request: BaseRequest,
+    config: DashboardConfig,
+    user_context: Dict[str, Any],
+) -> bool:
+    # For cases where we're not using the API key, the JWT is being used; we allow their access by default
+    if config.api_key is None:
+        auth_header_value = request.get_header("authorization")
+        if not auth_header_value:
+            return False
+
+        auth_header_value = auth_header_value.split()[1]
+        session_verification_response = (
+            await Querier.get_instance().send_post_request(
+                NormalisedURLPath("/recipe/dashboard/session/verify"),
+                {"sessionId": auth_header_value},
+                user_context=user_context,
+            )
+        )
+        if session_verification_response.get("status") != "OK":
+            return False
+
+        # For all non GET requests we also want to check if the
+        # user is allowed to perform this operation
+        if normalise_http_method(request.method()) != "get":
+            # We dont want to block the analytics API
+            if request.get_original_url().endswith(DASHBOARD_ANALYTICS_API):
+                return True
+
+            # We do not want to block the sign out request
+            if request.get_original_url().endswith(SIGN_OUT_API):
+                return True
+
+            admins = config.admins
+
+            if admins is None:
+                return True
+
+            if len(admins) == 0:
+                log_debug_message(
+                    "User Dashboard: Throwing OPERATION_NOT_ALLOWED because user is not an admin"
+                )
+                raise DashboardOperationNotAllowedError()
+
+            user_email = session_verification_response.get("email")
+
+            if user_email is None or not isinstance(user_email, str):
+                log_debug_message(
+                    "User Dashboard: Returning UNAUTHORISED_ERROR because no email was provided in headers"
+                )
+                return False
+
+            if user_email not in admins:
+                log_debug_message(
+                    "User Dashboard: Throwing OPERATION_NOT_ALLOWED because user is not an admin"
+                )
+                raise DashboardOperationNotAllowedError()
+
+        return True
+
+    return await validate_api_key(request, config, user_context)
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/dashboard/utils.html b/html/supertokens_python/recipe/dashboard/utils.html new file mode 100644 index 000000000..e7f79748c --- /dev/null +++ b/html/supertokens_python/recipe/dashboard/utils.html @@ -0,0 +1,1060 @@ + + + + + + +supertokens_python.recipe.dashboard.utils API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.dashboard.utils

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Any, Callable, Dict, Optional, Union, List
+
+if TYPE_CHECKING:
+    from supertokens_python.framework.request import BaseRequest
+
+from supertokens_python.recipe.emailpassword import EmailPasswordRecipe
+from supertokens_python.recipe.emailpassword.asyncio import (
+    get_user_by_id as ep_get_user_by_id,
+)
+from supertokens_python.recipe.passwordless import PasswordlessRecipe
+from supertokens_python.recipe.passwordless.asyncio import (
+    get_user_by_id as pless_get_user_by_id,
+)
+from supertokens_python.recipe.thirdparty import ThirdPartyRecipe
+from supertokens_python.recipe.thirdparty.asyncio import (
+    get_user_by_id as tp_get_user_by_idx,
+)
+from supertokens_python.types import User
+from supertokens_python.utils import Awaitable, log_debug_message, normalise_email
+
+from ...normalised_url_path import NormalisedURLPath
+from .constants import (
+    DASHBOARD_ANALYTICS_API,
+    DASHBOARD_API,
+    SIGN_OUT_API,
+    SIGN_IN_API,
+    SEARCH_TAGS_API,
+    USER_API,
+    USER_EMAIL_VERIFY_API,
+    USER_EMAIL_VERIFY_TOKEN_API,
+    USER_METADATA_API,
+    USER_PASSWORD_API,
+    USER_SESSION_API,
+    USERS_COUNT_API,
+    USERS_LIST_GET_API,
+    VALIDATE_KEY_API,
+)
+
+if TYPE_CHECKING:
+    from .interfaces import APIInterface, RecipeInterface
+
+
+class UserWithMetadata:
+    user_id: str
+    time_joined: int
+    recipe_id: Optional[str] = None
+    email: Optional[str] = None
+    phone_number: Optional[str] = None
+    tp_info: Optional[Dict[str, Any]] = None
+    first_name: Optional[str] = None
+    last_name: Optional[str] = None
+    tenant_ids: List[str]
+
+    def from_user(
+        self,
+        user: User,
+        first_name: Optional[str] = None,
+        last_name: Optional[str] = None,
+    ):
+        self.first_name = first_name
+        self.last_name = last_name
+
+        self.user_id = user.user_id
+        # from_user() is called in /api/users (note extra s)
+        # here we DashboardUsersGetResponse() doesn't maintain
+        # recipe id for each user on its own. That's why we need
+        # to set self.recipe_id here.
+        self.recipe_id = user.recipe_id
+        self.time_joined = user.time_joined
+        self.email = user.email
+        self.phone_number = user.phone_number
+        self.tp_info = (
+            None if user.third_party_info is None else user.third_party_info.__dict__
+        )
+        self.tenant_ids = user.tenant_ids
+
+        return self
+
+    def from_dict(
+        self,
+        user_obj_dict: Dict[str, Any],
+        first_name: Optional[str] = None,
+        last_name: Optional[str] = None,
+    ):
+        self.first_name = first_name
+        self.last_name = last_name
+
+        self.user_id = user_obj_dict["user_id"]
+        # from_dict() is used in `/api/user` where
+        # recipe_id is already passed seperately to
+        # GetUserForRecipeIdResult object
+        # So we set recipe_id to None here
+        self.recipe_id = None
+        self.time_joined = user_obj_dict["time_joined"]
+        self.tenant_ids = user_obj_dict.get("tenant_ids", [])
+
+        self.email = user_obj_dict.get("email")
+        self.phone_number = user_obj_dict.get("phone_number")
+        self.tp_info = (
+            None
+            if user_obj_dict.get("third_party_info") is None
+            else user_obj_dict["third_party_info"].__dict__
+        )
+
+        return self
+
+    def to_json(self) -> Dict[str, Any]:
+        user_json: Dict[str, Any] = {
+            "id": self.user_id,
+            "timeJoined": self.time_joined,
+            "tenantIds": self.tenant_ids,
+        }
+        if self.tp_info is not None:
+            user_json["thirdParty"] = {
+                "id": self.tp_info["id"],
+                "userId": self.tp_info["user_id"],
+            }
+        if self.phone_number is not None:
+            user_json["phoneNumber"] = self.phone_number
+        if self.email is not None:
+            user_json["email"] = self.email
+        if self.first_name is not None:
+            user_json["firstName"] = self.first_name
+        if self.last_name is not None:
+            user_json["lastName"] = self.last_name
+
+        if self.recipe_id is not None:
+            return {
+                "recipeId": self.recipe_id,
+                "user": user_json,
+            }
+        return user_json
+
+
+class InputOverrideConfig:
+    def __init__(
+        self,
+        functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None,
+        apis: Union[Callable[[APIInterface], APIInterface], None] = None,
+    ):
+        self.functions = functions
+        self.apis = apis
+
+
+class OverrideConfig:
+    def __init__(
+        self,
+        functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None,
+        apis: Union[Callable[[APIInterface], APIInterface], None] = None,
+    ):
+        self.functions = functions
+        self.apis = apis
+
+
+class DashboardConfig:
+    def __init__(
+        self,
+        api_key: Optional[str],
+        admins: Optional[List[str]],
+        override: OverrideConfig,
+        auth_mode: str,
+    ):
+        self.api_key = api_key
+        self.admins = admins
+        self.override = override
+        self.auth_mode = auth_mode
+
+
+def validate_and_normalise_user_input(
+    # app_info: AppInfo,
+    api_key: Union[str, None],
+    admins: Optional[List[str]],
+    override: Optional[InputOverrideConfig] = None,
+) -> DashboardConfig:
+
+    if override is None:
+        override = InputOverrideConfig()
+
+    if api_key is not None and admins is not None:
+        log_debug_message(
+            "User Dashboard: Providing 'admins' has no effect when using an api key."
+        )
+
+    admins = [normalise_email(a) for a in admins] if admins is not None else None
+
+    return DashboardConfig(
+        api_key,
+        admins,
+        OverrideConfig(
+            functions=override.functions,
+            apis=override.apis,
+        ),
+        "api-key" if api_key else "email-password",
+    )
+
+
+def is_api_path(path: NormalisedURLPath, base_path: NormalisedURLPath) -> bool:
+    dashboard_recipe_base_path = base_path.append(NormalisedURLPath(DASHBOARD_API))
+
+    if not path.startswith(dashboard_recipe_base_path):
+        return False
+
+    path_without_dashboard_path = path.get_as_string_dangerous().split(DASHBOARD_API)[1]
+
+    if len(path_without_dashboard_path) > 0 and path_without_dashboard_path[0] == "/":
+        path_without_dashboard_path = path_without_dashboard_path[1:]
+
+    if path_without_dashboard_path.split("/")[0] == "api":
+        return True
+
+    return False
+
+
+def get_api_if_matched(path: NormalisedURLPath, method: str) -> Optional[str]:
+    path_str = path.get_as_string_dangerous()
+
+    if path_str.endswith(VALIDATE_KEY_API) and method == "post":
+        return VALIDATE_KEY_API
+    if path_str.endswith(USERS_LIST_GET_API) and method == "get":
+        return USERS_LIST_GET_API
+    if path_str.endswith(USERS_COUNT_API) and method == "get":
+        return USERS_COUNT_API
+    if path_str.endswith(USER_API) and method in ("get", "delete", "put"):
+        return USER_API
+    if path_str.endswith(USER_EMAIL_VERIFY_API) and method in ("get", "put"):
+        return USER_EMAIL_VERIFY_API
+    if path_str.endswith(USER_METADATA_API) and method in ("get", "put"):
+        return USER_METADATA_API
+    if path_str.endswith(USER_SESSION_API) and method in ("get", "post"):
+        return USER_SESSION_API
+    if path_str.endswith(USER_PASSWORD_API) and method == "put":
+        return USER_PASSWORD_API
+    if path_str.endswith(USER_EMAIL_VERIFY_TOKEN_API) and method == "post":
+        return USER_EMAIL_VERIFY_TOKEN_API
+    if path_str.endswith(SIGN_IN_API) and method == "post":
+        return SIGN_IN_API
+    if path_str.endswith(SIGN_OUT_API) and method == "post":
+        return SIGN_OUT_API
+    if path_str.endswith(SEARCH_TAGS_API) and method == "get":
+        return SEARCH_TAGS_API
+    if path_str.endswith(DASHBOARD_ANALYTICS_API) and method == "post":
+        return DASHBOARD_ANALYTICS_API
+
+    return None
+
+
+def is_valid_recipe_id(recipe_id: str) -> bool:
+    return recipe_id in ("emailpassword", "thirdparty", "passwordless")
+
+
+class GetUserForRecipeIdResult:
+    def __init__(self, user: UserWithMetadata, recipe: str):
+        self.user = user
+        self.recipe = recipe
+
+
+if TYPE_CHECKING:
+    from supertokens_python.recipe.emailpassword.types import User as EmailPasswordUser
+    from supertokens_python.recipe.passwordless.types import User as PasswordlessUser
+    from supertokens_python.recipe.thirdparty.types import User as ThirdPartyUser
+
+    GetUserResult = Union[
+        EmailPasswordUser,
+        ThirdPartyUser,
+        PasswordlessUser,
+        None,
+    ]
+
+
+async def get_user_for_recipe_id(
+    user_id: str, recipe_id: str
+) -> Optional[GetUserForRecipeIdResult]:
+    user: Optional[UserWithMetadata] = None
+    recipe: Optional[str] = None
+
+    async def update_user_dict(
+        get_user_funcs: List[Callable[[str], Awaitable[GetUserResult]]],
+        recipes: List[str],
+    ):
+        nonlocal user, user_id, recipe
+
+        for get_user_func, recipe_id in zip(get_user_funcs, recipes):
+            try:
+                recipe_user = await get_user_func(user_id)  # type: ignore
+
+                if recipe_user is not None:
+                    user = UserWithMetadata().from_dict(
+                        recipe_user.__dict__, first_name="", last_name=""
+                    )
+                    recipe = recipe_id
+                    break
+            except Exception:
+                pass
+
+    if recipe_id == EmailPasswordRecipe.recipe_id:
+        await update_user_dict(
+            [ep_get_user_by_id],
+            ["emailpassword"],
+        )
+
+    elif recipe_id == ThirdPartyRecipe.recipe_id:
+        await update_user_dict(
+            [tp_get_user_by_idx],
+            ["thirdparty"],
+        )
+
+    elif recipe_id == PasswordlessRecipe.recipe_id:
+        await update_user_dict(
+            [pless_get_user_by_id],
+            ["passwordless"],
+        )
+
+    if user is not None and recipe is not None:
+        return GetUserForRecipeIdResult(user, recipe)
+
+    return None
+
+
+def is_recipe_initialised(recipeId: str) -> bool:
+    isRecipeInitialised: bool = False
+
+    if recipeId == EmailPasswordRecipe.recipe_id:
+        try:
+            EmailPasswordRecipe.get_instance()
+            isRecipeInitialised = True
+        except Exception:
+            pass
+
+    elif recipeId == PasswordlessRecipe.recipe_id:
+        try:
+            PasswordlessRecipe.get_instance()
+            isRecipeInitialised = True
+        except Exception:
+            pass
+
+    elif recipeId == ThirdPartyRecipe.recipe_id:
+        try:
+            ThirdPartyRecipe.get_instance()
+            isRecipeInitialised = True
+        except Exception:
+            pass
+
+    return isRecipeInitialised
+
+
+async def validate_api_key(
+    req: BaseRequest, config: DashboardConfig, _user_context: Dict[str, Any]
+) -> bool:
+    api_key_header_value = req.get_header("authorization")
+    if not api_key_header_value:
+        return False
+    # We receieve the api key as `Bearer API_KEY`, this retrieves just the key
+    api_key_header_value = api_key_header_value.split(" ")[1]
+    return api_key_header_value == config.api_key
+
+
+def get_api_path_with_dashboard_base(path: str) -> str:
+    return DASHBOARD_API + path
+
+
+
+
+
+
+
+

Functions

+
+
+def get_api_if_matched(path: NormalisedURLPath, method: str) ‑> Optional[str] +
+
+
+
+ +Expand source code + +
def get_api_if_matched(path: NormalisedURLPath, method: str) -> Optional[str]:
+    path_str = path.get_as_string_dangerous()
+
+    if path_str.endswith(VALIDATE_KEY_API) and method == "post":
+        return VALIDATE_KEY_API
+    if path_str.endswith(USERS_LIST_GET_API) and method == "get":
+        return USERS_LIST_GET_API
+    if path_str.endswith(USERS_COUNT_API) and method == "get":
+        return USERS_COUNT_API
+    if path_str.endswith(USER_API) and method in ("get", "delete", "put"):
+        return USER_API
+    if path_str.endswith(USER_EMAIL_VERIFY_API) and method in ("get", "put"):
+        return USER_EMAIL_VERIFY_API
+    if path_str.endswith(USER_METADATA_API) and method in ("get", "put"):
+        return USER_METADATA_API
+    if path_str.endswith(USER_SESSION_API) and method in ("get", "post"):
+        return USER_SESSION_API
+    if path_str.endswith(USER_PASSWORD_API) and method == "put":
+        return USER_PASSWORD_API
+    if path_str.endswith(USER_EMAIL_VERIFY_TOKEN_API) and method == "post":
+        return USER_EMAIL_VERIFY_TOKEN_API
+    if path_str.endswith(SIGN_IN_API) and method == "post":
+        return SIGN_IN_API
+    if path_str.endswith(SIGN_OUT_API) and method == "post":
+        return SIGN_OUT_API
+    if path_str.endswith(SEARCH_TAGS_API) and method == "get":
+        return SEARCH_TAGS_API
+    if path_str.endswith(DASHBOARD_ANALYTICS_API) and method == "post":
+        return DASHBOARD_ANALYTICS_API
+
+    return None
+
+
+
+def get_api_path_with_dashboard_base(path: str) ‑> str +
+
+
+
+ +Expand source code + +
def get_api_path_with_dashboard_base(path: str) -> str:
+    return DASHBOARD_API + path
+
+
+
+async def get_user_for_recipe_id(user_id: str, recipe_id: str) ‑> Optional[GetUserForRecipeIdResult] +
+
+
+
+ +Expand source code + +
async def get_user_for_recipe_id(
+    user_id: str, recipe_id: str
+) -> Optional[GetUserForRecipeIdResult]:
+    user: Optional[UserWithMetadata] = None
+    recipe: Optional[str] = None
+
+    async def update_user_dict(
+        get_user_funcs: List[Callable[[str], Awaitable[GetUserResult]]],
+        recipes: List[str],
+    ):
+        nonlocal user, user_id, recipe
+
+        for get_user_func, recipe_id in zip(get_user_funcs, recipes):
+            try:
+                recipe_user = await get_user_func(user_id)  # type: ignore
+
+                if recipe_user is not None:
+                    user = UserWithMetadata().from_dict(
+                        recipe_user.__dict__, first_name="", last_name=""
+                    )
+                    recipe = recipe_id
+                    break
+            except Exception:
+                pass
+
+    if recipe_id == EmailPasswordRecipe.recipe_id:
+        await update_user_dict(
+            [ep_get_user_by_id],
+            ["emailpassword"],
+        )
+
+    elif recipe_id == ThirdPartyRecipe.recipe_id:
+        await update_user_dict(
+            [tp_get_user_by_idx],
+            ["thirdparty"],
+        )
+
+    elif recipe_id == PasswordlessRecipe.recipe_id:
+        await update_user_dict(
+            [pless_get_user_by_id],
+            ["passwordless"],
+        )
+
+    if user is not None and recipe is not None:
+        return GetUserForRecipeIdResult(user, recipe)
+
+    return None
+
+
+
+def is_api_path(path: NormalisedURLPath, base_path: NormalisedURLPath) ‑> bool +
+
+
+
+ +Expand source code + +
def is_api_path(path: NormalisedURLPath, base_path: NormalisedURLPath) -> bool:
+    dashboard_recipe_base_path = base_path.append(NormalisedURLPath(DASHBOARD_API))
+
+    if not path.startswith(dashboard_recipe_base_path):
+        return False
+
+    path_without_dashboard_path = path.get_as_string_dangerous().split(DASHBOARD_API)[1]
+
+    if len(path_without_dashboard_path) > 0 and path_without_dashboard_path[0] == "/":
+        path_without_dashboard_path = path_without_dashboard_path[1:]
+
+    if path_without_dashboard_path.split("/")[0] == "api":
+        return True
+
+    return False
+
+
+
+def is_recipe_initialised(recipeId: str) ‑> bool +
+
+
+
+ +Expand source code + +
def is_recipe_initialised(recipeId: str) -> bool:
+    isRecipeInitialised: bool = False
+
+    if recipeId == EmailPasswordRecipe.recipe_id:
+        try:
+            EmailPasswordRecipe.get_instance()
+            isRecipeInitialised = True
+        except Exception:
+            pass
+
+    elif recipeId == PasswordlessRecipe.recipe_id:
+        try:
+            PasswordlessRecipe.get_instance()
+            isRecipeInitialised = True
+        except Exception:
+            pass
+
+    elif recipeId == ThirdPartyRecipe.recipe_id:
+        try:
+            ThirdPartyRecipe.get_instance()
+            isRecipeInitialised = True
+        except Exception:
+            pass
+
+    return isRecipeInitialised
+
+
+
+def is_valid_recipe_id(recipe_id: str) ‑> bool +
+
+
+
+ +Expand source code + +
def is_valid_recipe_id(recipe_id: str) -> bool:
+    return recipe_id in ("emailpassword", "thirdparty", "passwordless")
+
+
+
+def validate_and_normalise_user_input(api_key: Union[str, None], admins: Optional[List[str]], override: Optional[InputOverrideConfig] = None) ‑> DashboardConfig +
+
+
+
+ +Expand source code + +
def validate_and_normalise_user_input(
+    # app_info: AppInfo,
+    api_key: Union[str, None],
+    admins: Optional[List[str]],
+    override: Optional[InputOverrideConfig] = None,
+) -> DashboardConfig:
+
+    if override is None:
+        override = InputOverrideConfig()
+
+    if api_key is not None and admins is not None:
+        log_debug_message(
+            "User Dashboard: Providing 'admins' has no effect when using an api key."
+        )
+
+    admins = [normalise_email(a) for a in admins] if admins is not None else None
+
+    return DashboardConfig(
+        api_key,
+        admins,
+        OverrideConfig(
+            functions=override.functions,
+            apis=override.apis,
+        ),
+        "api-key" if api_key else "email-password",
+    )
+
+
+
+async def validate_api_key(req: BaseRequest, config: DashboardConfig, _user_context: Dict[str, Any]) ‑> bool +
+
+
+
+ +Expand source code + +
async def validate_api_key(
+    req: BaseRequest, config: DashboardConfig, _user_context: Dict[str, Any]
+) -> bool:
+    api_key_header_value = req.get_header("authorization")
+    if not api_key_header_value:
+        return False
+    # We receieve the api key as `Bearer API_KEY`, this retrieves just the key
+    api_key_header_value = api_key_header_value.split(" ")[1]
+    return api_key_header_value == config.api_key
+
+
+
+
+
+

Classes

+
+
+class DashboardConfig +(api_key: Optional[str], admins: Optional[List[str]], override: OverrideConfig, auth_mode: str) +
+
+
+
+ +Expand source code + +
class DashboardConfig:
+    def __init__(
+        self,
+        api_key: Optional[str],
+        admins: Optional[List[str]],
+        override: OverrideConfig,
+        auth_mode: str,
+    ):
+        self.api_key = api_key
+        self.admins = admins
+        self.override = override
+        self.auth_mode = auth_mode
+
+
+
+class GetUserForRecipeIdResult +(user: UserWithMetadata, recipe: str) +
+
+
+
+ +Expand source code + +
class GetUserForRecipeIdResult:
+    def __init__(self, user: UserWithMetadata, recipe: str):
+        self.user = user
+        self.recipe = recipe
+
+
+
+class InputOverrideConfig +(functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None, apis: Union[Callable[[APIInterface], APIInterface], None] = None) +
+
+
+
+ +Expand source code + +
class InputOverrideConfig:
+    def __init__(
+        self,
+        functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None,
+        apis: Union[Callable[[APIInterface], APIInterface], None] = None,
+    ):
+        self.functions = functions
+        self.apis = apis
+
+
+
+class OverrideConfig +(functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None, apis: Union[Callable[[APIInterface], APIInterface], None] = None) +
+
+
+
+ +Expand source code + +
class OverrideConfig:
+    def __init__(
+        self,
+        functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None,
+        apis: Union[Callable[[APIInterface], APIInterface], None] = None,
+    ):
+        self.functions = functions
+        self.apis = apis
+
+
+
+class UserWithMetadata +
+
+
+
+ +Expand source code + +
class UserWithMetadata:
+    user_id: str
+    time_joined: int
+    recipe_id: Optional[str] = None
+    email: Optional[str] = None
+    phone_number: Optional[str] = None
+    tp_info: Optional[Dict[str, Any]] = None
+    first_name: Optional[str] = None
+    last_name: Optional[str] = None
+    tenant_ids: List[str]
+
+    def from_user(
+        self,
+        user: User,
+        first_name: Optional[str] = None,
+        last_name: Optional[str] = None,
+    ):
+        self.first_name = first_name
+        self.last_name = last_name
+
+        self.user_id = user.user_id
+        # from_user() is called in /api/users (note extra s)
+        # here we DashboardUsersGetResponse() doesn't maintain
+        # recipe id for each user on its own. That's why we need
+        # to set self.recipe_id here.
+        self.recipe_id = user.recipe_id
+        self.time_joined = user.time_joined
+        self.email = user.email
+        self.phone_number = user.phone_number
+        self.tp_info = (
+            None if user.third_party_info is None else user.third_party_info.__dict__
+        )
+        self.tenant_ids = user.tenant_ids
+
+        return self
+
+    def from_dict(
+        self,
+        user_obj_dict: Dict[str, Any],
+        first_name: Optional[str] = None,
+        last_name: Optional[str] = None,
+    ):
+        self.first_name = first_name
+        self.last_name = last_name
+
+        self.user_id = user_obj_dict["user_id"]
+        # from_dict() is used in `/api/user` where
+        # recipe_id is already passed seperately to
+        # GetUserForRecipeIdResult object
+        # So we set recipe_id to None here
+        self.recipe_id = None
+        self.time_joined = user_obj_dict["time_joined"]
+        self.tenant_ids = user_obj_dict.get("tenant_ids", [])
+
+        self.email = user_obj_dict.get("email")
+        self.phone_number = user_obj_dict.get("phone_number")
+        self.tp_info = (
+            None
+            if user_obj_dict.get("third_party_info") is None
+            else user_obj_dict["third_party_info"].__dict__
+        )
+
+        return self
+
+    def to_json(self) -> Dict[str, Any]:
+        user_json: Dict[str, Any] = {
+            "id": self.user_id,
+            "timeJoined": self.time_joined,
+            "tenantIds": self.tenant_ids,
+        }
+        if self.tp_info is not None:
+            user_json["thirdParty"] = {
+                "id": self.tp_info["id"],
+                "userId": self.tp_info["user_id"],
+            }
+        if self.phone_number is not None:
+            user_json["phoneNumber"] = self.phone_number
+        if self.email is not None:
+            user_json["email"] = self.email
+        if self.first_name is not None:
+            user_json["firstName"] = self.first_name
+        if self.last_name is not None:
+            user_json["lastName"] = self.last_name
+
+        if self.recipe_id is not None:
+            return {
+                "recipeId": self.recipe_id,
+                "user": user_json,
+            }
+        return user_json
+
+

Class variables

+
+
var email : Optional[str]
+
+
+
+
var first_name : Optional[str]
+
+
+
+
var last_name : Optional[str]
+
+
+
+
var phone_number : Optional[str]
+
+
+
+
var recipe_id : Optional[str]
+
+
+
+
var tenant_ids : List[str]
+
+
+
+
var time_joined : int
+
+
+
+
var tp_info : Optional[Dict[str, Any]]
+
+
+
+
var user_id : str
+
+
+
+
+

Methods

+
+
+def from_dict(self, user_obj_dict: Dict[str, Any], first_name: Optional[str] = None, last_name: Optional[str] = None) +
+
+
+
+ +Expand source code + +
def from_dict(
+    self,
+    user_obj_dict: Dict[str, Any],
+    first_name: Optional[str] = None,
+    last_name: Optional[str] = None,
+):
+    self.first_name = first_name
+    self.last_name = last_name
+
+    self.user_id = user_obj_dict["user_id"]
+    # from_dict() is used in `/api/user` where
+    # recipe_id is already passed seperately to
+    # GetUserForRecipeIdResult object
+    # So we set recipe_id to None here
+    self.recipe_id = None
+    self.time_joined = user_obj_dict["time_joined"]
+    self.tenant_ids = user_obj_dict.get("tenant_ids", [])
+
+    self.email = user_obj_dict.get("email")
+    self.phone_number = user_obj_dict.get("phone_number")
+    self.tp_info = (
+        None
+        if user_obj_dict.get("third_party_info") is None
+        else user_obj_dict["third_party_info"].__dict__
+    )
+
+    return self
+
+
+
+def from_user(self, user: User, first_name: Optional[str] = None, last_name: Optional[str] = None) +
+
+
+
+ +Expand source code + +
def from_user(
+    self,
+    user: User,
+    first_name: Optional[str] = None,
+    last_name: Optional[str] = None,
+):
+    self.first_name = first_name
+    self.last_name = last_name
+
+    self.user_id = user.user_id
+    # from_user() is called in /api/users (note extra s)
+    # here we DashboardUsersGetResponse() doesn't maintain
+    # recipe id for each user on its own. That's why we need
+    # to set self.recipe_id here.
+    self.recipe_id = user.recipe_id
+    self.time_joined = user.time_joined
+    self.email = user.email
+    self.phone_number = user.phone_number
+    self.tp_info = (
+        None if user.third_party_info is None else user.third_party_info.__dict__
+    )
+    self.tenant_ids = user.tenant_ids
+
+    return self
+
+
+
+def to_json(self) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
def to_json(self) -> Dict[str, Any]:
+    user_json: Dict[str, Any] = {
+        "id": self.user_id,
+        "timeJoined": self.time_joined,
+        "tenantIds": self.tenant_ids,
+    }
+    if self.tp_info is not None:
+        user_json["thirdParty"] = {
+            "id": self.tp_info["id"],
+            "userId": self.tp_info["user_id"],
+        }
+    if self.phone_number is not None:
+        user_json["phoneNumber"] = self.phone_number
+    if self.email is not None:
+        user_json["email"] = self.email
+    if self.first_name is not None:
+        user_json["firstName"] = self.first_name
+    if self.last_name is not None:
+        user_json["lastName"] = self.last_name
+
+    if self.recipe_id is not None:
+        return {
+            "recipeId": self.recipe_id,
+            "user": user_json,
+        }
+    return user_json
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailpassword/api/email_exists.html b/html/supertokens_python/recipe/emailpassword/api/email_exists.html new file mode 100644 index 000000000..f0809b0e6 --- /dev/null +++ b/html/supertokens_python/recipe/emailpassword/api/email_exists.html @@ -0,0 +1,136 @@ + + + + + + +supertokens_python.recipe.emailpassword.api.email_exists API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.emailpassword.api.email_exists

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Dict, Any
+
+if TYPE_CHECKING:
+    from supertokens_python.recipe.emailpassword.interfaces import (
+        APIOptions,
+        APIInterface,
+    )
+
+from supertokens_python.exceptions import raise_bad_input_exception
+from supertokens_python.utils import send_200_response
+
+
+async def handle_email_exists_api(
+    tenant_id: str,
+    api_implementation: APIInterface,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+):
+    if api_implementation.disable_email_exists_get:
+        return None
+    email = api_options.request.get_query_param("email")
+    if email is None:
+        raise_bad_input_exception("Please provide the email as a GET param")
+
+    response = await api_implementation.email_exists_get(
+        email, tenant_id, api_options, user_context
+    )
+    return send_200_response(response.to_json(), api_options.response)
+
+
+
+
+
+
+
+

Functions

+
+
+async def handle_email_exists_api(tenant_id: str, api_implementation: APIInterface, api_options: APIOptions, user_context: Dict[str, Any]) +
+
+
+
+ +Expand source code + +
async def handle_email_exists_api(
+    tenant_id: str,
+    api_implementation: APIInterface,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+):
+    if api_implementation.disable_email_exists_get:
+        return None
+    email = api_options.request.get_query_param("email")
+    if email is None:
+        raise_bad_input_exception("Please provide the email as a GET param")
+
+    response = await api_implementation.email_exists_get(
+        email, tenant_id, api_options, user_context
+    )
+    return send_200_response(response.to_json(), api_options.response)
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailpassword/api/generate_password_reset_token.html b/html/supertokens_python/recipe/emailpassword/api/generate_password_reset_token.html new file mode 100644 index 000000000..b9453ade5 --- /dev/null +++ b/html/supertokens_python/recipe/emailpassword/api/generate_password_reset_token.html @@ -0,0 +1,150 @@ + + + + + + +supertokens_python.recipe.emailpassword.api.generate_password_reset_token API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.emailpassword.api.generate_password_reset_token

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Any, Dict
+
+if TYPE_CHECKING:
+    from supertokens_python.recipe.emailpassword.interfaces import (
+        APIOptions,
+        APIInterface,
+    )
+
+from supertokens_python.exceptions import raise_bad_input_exception
+from supertokens_python.utils import send_200_response
+
+from .utils import validate_form_fields_or_throw_error
+
+
+async def handle_generate_password_reset_token_api(
+    tenant_id: str,
+    api_implementation: APIInterface,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+):
+    if api_implementation.disable_generate_password_reset_token_post:
+        return None
+    body = await api_options.request.json()
+    if body is None:
+        raise_bad_input_exception("Please provide a JSON body")
+    form_fields_raw: Any = body["formFields"] if "formFields" in body else []
+    form_fields = await validate_form_fields_or_throw_error(
+        api_options.config.reset_password_using_token_feature.form_fields_for_generate_token_form,
+        form_fields_raw,
+        tenant_id,
+    )
+
+    response = await api_implementation.generate_password_reset_token_post(
+        form_fields, tenant_id, api_options, user_context
+    )
+    return send_200_response(response.to_json(), api_options.response)
+
+
+
+
+
+
+
+

Functions

+
+
+async def handle_generate_password_reset_token_api(tenant_id: str, api_implementation: APIInterface, api_options: APIOptions, user_context: Dict[str, Any]) +
+
+
+
+ +Expand source code + +
async def handle_generate_password_reset_token_api(
+    tenant_id: str,
+    api_implementation: APIInterface,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+):
+    if api_implementation.disable_generate_password_reset_token_post:
+        return None
+    body = await api_options.request.json()
+    if body is None:
+        raise_bad_input_exception("Please provide a JSON body")
+    form_fields_raw: Any = body["formFields"] if "formFields" in body else []
+    form_fields = await validate_form_fields_or_throw_error(
+        api_options.config.reset_password_using_token_feature.form_fields_for_generate_token_form,
+        form_fields_raw,
+        tenant_id,
+    )
+
+    response = await api_implementation.generate_password_reset_token_post(
+        form_fields, tenant_id, api_options, user_context
+    )
+    return send_200_response(response.to_json(), api_options.response)
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailpassword/api/implementation.html b/html/supertokens_python/recipe/emailpassword/api/implementation.html new file mode 100644 index 000000000..f8b1081ee --- /dev/null +++ b/html/supertokens_python/recipe/emailpassword/api/implementation.html @@ -0,0 +1,720 @@ + + + + + + +supertokens_python.recipe.emailpassword.api.implementation API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.emailpassword.api.implementation

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Any, Dict, List, Union
+
+from supertokens_python.logger import log_debug_message
+from supertokens_python.recipe.emailpassword.constants import (
+    FORM_FIELD_EMAIL_ID,
+    FORM_FIELD_PASSWORD_ID,
+)
+from supertokens_python.recipe.emailpassword.interfaces import (
+    APIInterface,
+    CreateResetPasswordWrongUserIdError,
+    EmailExistsGetOkResult,
+    GeneratePasswordResetTokenPostOkResult,
+    PasswordResetPostInvalidTokenResponse,
+    PasswordResetPostOkResult,
+    ResetPasswordUsingTokenInvalidTokenError,
+    SignInPostOkResult,
+    SignInPostWrongCredentialsError,
+    SignInWrongCredentialsError,
+    SignUpEmailAlreadyExistsError,
+    SignUpPostEmailAlreadyExistsError,
+    SignUpPostOkResult,
+)
+from supertokens_python.recipe.emailpassword.types import (
+    FormField,
+    PasswordResetEmailTemplateVars,
+    PasswordResetEmailTemplateVarsUser,
+)
+from ..utils import get_password_reset_link
+from supertokens_python.recipe.session.asyncio import create_new_session
+from supertokens_python.utils import find_first_occurrence_in_list
+
+if TYPE_CHECKING:
+    from supertokens_python.recipe.emailpassword.interfaces import APIOptions
+
+from supertokens_python.types import GeneralErrorResponse
+
+
+class APIImplementation(APIInterface):
+    async def email_exists_get(
+        self,
+        email: str,
+        tenant_id: str,
+        api_options: APIOptions,
+        user_context: Dict[str, Any],
+    ) -> Union[EmailExistsGetOkResult, GeneralErrorResponse]:
+        user = await api_options.recipe_implementation.get_user_by_email(
+            email, tenant_id, user_context
+        )
+        return EmailExistsGetOkResult(user is not None)
+
+    async def generate_password_reset_token_post(
+        self,
+        form_fields: List[FormField],
+        tenant_id: str,
+        api_options: APIOptions,
+        user_context: Dict[str, Any],
+    ) -> Union[GeneratePasswordResetTokenPostOkResult, GeneralErrorResponse]:
+        emailFormField = find_first_occurrence_in_list(
+            lambda x: x.id == FORM_FIELD_EMAIL_ID, form_fields
+        )
+        if emailFormField is None:
+            raise Exception("Should never come here")
+        email = emailFormField.value
+
+        user = await api_options.recipe_implementation.get_user_by_email(
+            email, tenant_id, user_context
+        )
+
+        if user is None:
+            return GeneratePasswordResetTokenPostOkResult()
+
+        token_result = (
+            await api_options.recipe_implementation.create_reset_password_token(
+                user.user_id, tenant_id, user_context
+            )
+        )
+
+        if isinstance(token_result, CreateResetPasswordWrongUserIdError):
+            log_debug_message(
+                "Password reset email not sent, unknown user id: %s", user.user_id
+            )
+            return GeneratePasswordResetTokenPostOkResult()
+
+        password_reset_link = get_password_reset_link(
+            api_options.app_info,
+            token_result.token,
+            tenant_id,
+            api_options.request,
+            user_context,
+        )
+
+        log_debug_message("Sending password reset email to %s", email)
+        send_email_input = PasswordResetEmailTemplateVars(
+            user=PasswordResetEmailTemplateVarsUser(user.user_id, user.email),
+            password_reset_link=password_reset_link,
+            tenant_id=tenant_id,
+        )
+        await api_options.email_delivery.ingredient_interface_impl.send_email(
+            send_email_input, user_context
+        )
+
+        return GeneratePasswordResetTokenPostOkResult()
+
+    async def password_reset_post(
+        self,
+        form_fields: List[FormField],
+        token: str,
+        tenant_id: str,
+        api_options: APIOptions,
+        user_context: Dict[str, Any],
+    ) -> Union[
+        PasswordResetPostOkResult,
+        PasswordResetPostInvalidTokenResponse,
+        GeneralErrorResponse,
+    ]:
+        new_password_for_field = find_first_occurrence_in_list(
+            lambda x: x.id == FORM_FIELD_PASSWORD_ID, form_fields
+        )
+        if new_password_for_field is None:
+            raise Exception("Should never come here")
+        new_password = new_password_for_field.value
+
+        result = await api_options.recipe_implementation.reset_password_using_token(
+            token, new_password, tenant_id, user_context
+        )
+
+        if isinstance(result, ResetPasswordUsingTokenInvalidTokenError):
+            return PasswordResetPostInvalidTokenResponse()
+
+        return PasswordResetPostOkResult(result.user_id)
+
+    async def sign_in_post(
+        self,
+        form_fields: List[FormField],
+        tenant_id: str,
+        api_options: APIOptions,
+        user_context: Dict[str, Any],
+    ) -> Union[
+        SignInPostOkResult, SignInPostWrongCredentialsError, GeneralErrorResponse
+    ]:
+        password_form_field = find_first_occurrence_in_list(
+            lambda x: x.id == FORM_FIELD_PASSWORD_ID, form_fields
+        )
+        if password_form_field is None:
+            raise Exception("Should never come here")
+        password = password_form_field.value
+
+        email_form_field = find_first_occurrence_in_list(
+            lambda x: x.id == FORM_FIELD_EMAIL_ID, form_fields
+        )
+        if email_form_field is None:
+            raise Exception("Should never come here")
+        email = email_form_field.value
+
+        result = await api_options.recipe_implementation.sign_in(
+            email, password, tenant_id, user_context
+        )
+
+        if isinstance(result, SignInWrongCredentialsError):
+            return SignInPostWrongCredentialsError()
+
+        user = result.user
+        session = await create_new_session(
+            tenant_id=tenant_id,
+            request=api_options.request,
+            user_id=user.user_id,
+            access_token_payload={},
+            session_data_in_database={},
+            user_context=user_context,
+        )
+        return SignInPostOkResult(user, session)
+
+    async def sign_up_post(
+        self,
+        form_fields: List[FormField],
+        tenant_id: str,
+        api_options: APIOptions,
+        user_context: Dict[str, Any],
+    ) -> Union[
+        SignUpPostOkResult, SignUpPostEmailAlreadyExistsError, GeneralErrorResponse
+    ]:
+        password_form_field = find_first_occurrence_in_list(
+            lambda x: x.id == FORM_FIELD_PASSWORD_ID, form_fields
+        )
+        if password_form_field is None:
+            raise Exception("Should never come here")
+        password = password_form_field.value
+
+        email_form_field = find_first_occurrence_in_list(
+            lambda x: x.id == FORM_FIELD_EMAIL_ID, form_fields
+        )
+        if email_form_field is None:
+            raise Exception("Should never come here")
+        email = email_form_field.value
+
+        result = await api_options.recipe_implementation.sign_up(
+            email, password, tenant_id, user_context
+        )
+
+        if isinstance(result, SignUpEmailAlreadyExistsError):
+            return SignUpPostEmailAlreadyExistsError()
+
+        user = result.user
+        session = await create_new_session(
+            tenant_id=tenant_id,
+            request=api_options.request,
+            user_id=user.user_id,
+            access_token_payload={},
+            session_data_in_database={},
+            user_context=user_context,
+        )
+        return SignUpPostOkResult(user, session)
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class APIImplementation +
+
+
+
+ +Expand source code + +
class APIImplementation(APIInterface):
+    async def email_exists_get(
+        self,
+        email: str,
+        tenant_id: str,
+        api_options: APIOptions,
+        user_context: Dict[str, Any],
+    ) -> Union[EmailExistsGetOkResult, GeneralErrorResponse]:
+        user = await api_options.recipe_implementation.get_user_by_email(
+            email, tenant_id, user_context
+        )
+        return EmailExistsGetOkResult(user is not None)
+
+    async def generate_password_reset_token_post(
+        self,
+        form_fields: List[FormField],
+        tenant_id: str,
+        api_options: APIOptions,
+        user_context: Dict[str, Any],
+    ) -> Union[GeneratePasswordResetTokenPostOkResult, GeneralErrorResponse]:
+        emailFormField = find_first_occurrence_in_list(
+            lambda x: x.id == FORM_FIELD_EMAIL_ID, form_fields
+        )
+        if emailFormField is None:
+            raise Exception("Should never come here")
+        email = emailFormField.value
+
+        user = await api_options.recipe_implementation.get_user_by_email(
+            email, tenant_id, user_context
+        )
+
+        if user is None:
+            return GeneratePasswordResetTokenPostOkResult()
+
+        token_result = (
+            await api_options.recipe_implementation.create_reset_password_token(
+                user.user_id, tenant_id, user_context
+            )
+        )
+
+        if isinstance(token_result, CreateResetPasswordWrongUserIdError):
+            log_debug_message(
+                "Password reset email not sent, unknown user id: %s", user.user_id
+            )
+            return GeneratePasswordResetTokenPostOkResult()
+
+        password_reset_link = get_password_reset_link(
+            api_options.app_info,
+            token_result.token,
+            tenant_id,
+            api_options.request,
+            user_context,
+        )
+
+        log_debug_message("Sending password reset email to %s", email)
+        send_email_input = PasswordResetEmailTemplateVars(
+            user=PasswordResetEmailTemplateVarsUser(user.user_id, user.email),
+            password_reset_link=password_reset_link,
+            tenant_id=tenant_id,
+        )
+        await api_options.email_delivery.ingredient_interface_impl.send_email(
+            send_email_input, user_context
+        )
+
+        return GeneratePasswordResetTokenPostOkResult()
+
+    async def password_reset_post(
+        self,
+        form_fields: List[FormField],
+        token: str,
+        tenant_id: str,
+        api_options: APIOptions,
+        user_context: Dict[str, Any],
+    ) -> Union[
+        PasswordResetPostOkResult,
+        PasswordResetPostInvalidTokenResponse,
+        GeneralErrorResponse,
+    ]:
+        new_password_for_field = find_first_occurrence_in_list(
+            lambda x: x.id == FORM_FIELD_PASSWORD_ID, form_fields
+        )
+        if new_password_for_field is None:
+            raise Exception("Should never come here")
+        new_password = new_password_for_field.value
+
+        result = await api_options.recipe_implementation.reset_password_using_token(
+            token, new_password, tenant_id, user_context
+        )
+
+        if isinstance(result, ResetPasswordUsingTokenInvalidTokenError):
+            return PasswordResetPostInvalidTokenResponse()
+
+        return PasswordResetPostOkResult(result.user_id)
+
+    async def sign_in_post(
+        self,
+        form_fields: List[FormField],
+        tenant_id: str,
+        api_options: APIOptions,
+        user_context: Dict[str, Any],
+    ) -> Union[
+        SignInPostOkResult, SignInPostWrongCredentialsError, GeneralErrorResponse
+    ]:
+        password_form_field = find_first_occurrence_in_list(
+            lambda x: x.id == FORM_FIELD_PASSWORD_ID, form_fields
+        )
+        if password_form_field is None:
+            raise Exception("Should never come here")
+        password = password_form_field.value
+
+        email_form_field = find_first_occurrence_in_list(
+            lambda x: x.id == FORM_FIELD_EMAIL_ID, form_fields
+        )
+        if email_form_field is None:
+            raise Exception("Should never come here")
+        email = email_form_field.value
+
+        result = await api_options.recipe_implementation.sign_in(
+            email, password, tenant_id, user_context
+        )
+
+        if isinstance(result, SignInWrongCredentialsError):
+            return SignInPostWrongCredentialsError()
+
+        user = result.user
+        session = await create_new_session(
+            tenant_id=tenant_id,
+            request=api_options.request,
+            user_id=user.user_id,
+            access_token_payload={},
+            session_data_in_database={},
+            user_context=user_context,
+        )
+        return SignInPostOkResult(user, session)
+
+    async def sign_up_post(
+        self,
+        form_fields: List[FormField],
+        tenant_id: str,
+        api_options: APIOptions,
+        user_context: Dict[str, Any],
+    ) -> Union[
+        SignUpPostOkResult, SignUpPostEmailAlreadyExistsError, GeneralErrorResponse
+    ]:
+        password_form_field = find_first_occurrence_in_list(
+            lambda x: x.id == FORM_FIELD_PASSWORD_ID, form_fields
+        )
+        if password_form_field is None:
+            raise Exception("Should never come here")
+        password = password_form_field.value
+
+        email_form_field = find_first_occurrence_in_list(
+            lambda x: x.id == FORM_FIELD_EMAIL_ID, form_fields
+        )
+        if email_form_field is None:
+            raise Exception("Should never come here")
+        email = email_form_field.value
+
+        result = await api_options.recipe_implementation.sign_up(
+            email, password, tenant_id, user_context
+        )
+
+        if isinstance(result, SignUpEmailAlreadyExistsError):
+            return SignUpPostEmailAlreadyExistsError()
+
+        user = result.user
+        session = await create_new_session(
+            tenant_id=tenant_id,
+            request=api_options.request,
+            user_id=user.user_id,
+            access_token_payload={},
+            session_data_in_database={},
+            user_context=user_context,
+        )
+        return SignUpPostOkResult(user, session)
+
+

Ancestors

+ +

Methods

+
+
+async def email_exists_get(self, email: str, tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[EmailExistsGetOkResult, GeneralErrorResponse] +
+
+
+
+ +Expand source code + +
async def email_exists_get(
+    self,
+    email: str,
+    tenant_id: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+) -> Union[EmailExistsGetOkResult, GeneralErrorResponse]:
+    user = await api_options.recipe_implementation.get_user_by_email(
+        email, tenant_id, user_context
+    )
+    return EmailExistsGetOkResult(user is not None)
+
+
+
+async def generate_password_reset_token_post(self, form_fields: List[FormField], tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[GeneratePasswordResetTokenPostOkResult, GeneralErrorResponse] +
+
+
+
+ +Expand source code + +
async def generate_password_reset_token_post(
+    self,
+    form_fields: List[FormField],
+    tenant_id: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+) -> Union[GeneratePasswordResetTokenPostOkResult, GeneralErrorResponse]:
+    emailFormField = find_first_occurrence_in_list(
+        lambda x: x.id == FORM_FIELD_EMAIL_ID, form_fields
+    )
+    if emailFormField is None:
+        raise Exception("Should never come here")
+    email = emailFormField.value
+
+    user = await api_options.recipe_implementation.get_user_by_email(
+        email, tenant_id, user_context
+    )
+
+    if user is None:
+        return GeneratePasswordResetTokenPostOkResult()
+
+    token_result = (
+        await api_options.recipe_implementation.create_reset_password_token(
+            user.user_id, tenant_id, user_context
+        )
+    )
+
+    if isinstance(token_result, CreateResetPasswordWrongUserIdError):
+        log_debug_message(
+            "Password reset email not sent, unknown user id: %s", user.user_id
+        )
+        return GeneratePasswordResetTokenPostOkResult()
+
+    password_reset_link = get_password_reset_link(
+        api_options.app_info,
+        token_result.token,
+        tenant_id,
+        api_options.request,
+        user_context,
+    )
+
+    log_debug_message("Sending password reset email to %s", email)
+    send_email_input = PasswordResetEmailTemplateVars(
+        user=PasswordResetEmailTemplateVarsUser(user.user_id, user.email),
+        password_reset_link=password_reset_link,
+        tenant_id=tenant_id,
+    )
+    await api_options.email_delivery.ingredient_interface_impl.send_email(
+        send_email_input, user_context
+    )
+
+    return GeneratePasswordResetTokenPostOkResult()
+
+
+
+async def password_reset_post(self, form_fields: List[FormField], token: str, tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[PasswordResetPostOkResult, PasswordResetPostInvalidTokenResponse, GeneralErrorResponse] +
+
+
+
+ +Expand source code + +
async def password_reset_post(
+    self,
+    form_fields: List[FormField],
+    token: str,
+    tenant_id: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+) -> Union[
+    PasswordResetPostOkResult,
+    PasswordResetPostInvalidTokenResponse,
+    GeneralErrorResponse,
+]:
+    new_password_for_field = find_first_occurrence_in_list(
+        lambda x: x.id == FORM_FIELD_PASSWORD_ID, form_fields
+    )
+    if new_password_for_field is None:
+        raise Exception("Should never come here")
+    new_password = new_password_for_field.value
+
+    result = await api_options.recipe_implementation.reset_password_using_token(
+        token, new_password, tenant_id, user_context
+    )
+
+    if isinstance(result, ResetPasswordUsingTokenInvalidTokenError):
+        return PasswordResetPostInvalidTokenResponse()
+
+    return PasswordResetPostOkResult(result.user_id)
+
+
+
+async def sign_in_post(self, form_fields: List[FormField], tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[SignInPostOkResult, SignInPostWrongCredentialsError, GeneralErrorResponse] +
+
+
+
+ +Expand source code + +
async def sign_in_post(
+    self,
+    form_fields: List[FormField],
+    tenant_id: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+) -> Union[
+    SignInPostOkResult, SignInPostWrongCredentialsError, GeneralErrorResponse
+]:
+    password_form_field = find_first_occurrence_in_list(
+        lambda x: x.id == FORM_FIELD_PASSWORD_ID, form_fields
+    )
+    if password_form_field is None:
+        raise Exception("Should never come here")
+    password = password_form_field.value
+
+    email_form_field = find_first_occurrence_in_list(
+        lambda x: x.id == FORM_FIELD_EMAIL_ID, form_fields
+    )
+    if email_form_field is None:
+        raise Exception("Should never come here")
+    email = email_form_field.value
+
+    result = await api_options.recipe_implementation.sign_in(
+        email, password, tenant_id, user_context
+    )
+
+    if isinstance(result, SignInWrongCredentialsError):
+        return SignInPostWrongCredentialsError()
+
+    user = result.user
+    session = await create_new_session(
+        tenant_id=tenant_id,
+        request=api_options.request,
+        user_id=user.user_id,
+        access_token_payload={},
+        session_data_in_database={},
+        user_context=user_context,
+    )
+    return SignInPostOkResult(user, session)
+
+
+
+async def sign_up_post(self, form_fields: List[FormField], tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[SignUpPostOkResult, SignUpPostEmailAlreadyExistsError, GeneralErrorResponse] +
+
+
+
+ +Expand source code + +
async def sign_up_post(
+    self,
+    form_fields: List[FormField],
+    tenant_id: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+) -> Union[
+    SignUpPostOkResult, SignUpPostEmailAlreadyExistsError, GeneralErrorResponse
+]:
+    password_form_field = find_first_occurrence_in_list(
+        lambda x: x.id == FORM_FIELD_PASSWORD_ID, form_fields
+    )
+    if password_form_field is None:
+        raise Exception("Should never come here")
+    password = password_form_field.value
+
+    email_form_field = find_first_occurrence_in_list(
+        lambda x: x.id == FORM_FIELD_EMAIL_ID, form_fields
+    )
+    if email_form_field is None:
+        raise Exception("Should never come here")
+    email = email_form_field.value
+
+    result = await api_options.recipe_implementation.sign_up(
+        email, password, tenant_id, user_context
+    )
+
+    if isinstance(result, SignUpEmailAlreadyExistsError):
+        return SignUpPostEmailAlreadyExistsError()
+
+    user = result.user
+    session = await create_new_session(
+        tenant_id=tenant_id,
+        request=api_options.request,
+        user_id=user.user_id,
+        access_token_payload={},
+        session_data_in_database={},
+        user_context=user_context,
+    )
+    return SignUpPostOkResult(user, session)
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailpassword/api/index.html b/html/supertokens_python/recipe/emailpassword/api/index.html new file mode 100644 index 000000000..c18ea51c5 --- /dev/null +++ b/html/supertokens_python/recipe/emailpassword/api/index.html @@ -0,0 +1,120 @@ + + + + + + +supertokens_python.recipe.emailpassword.api API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.emailpassword.api

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from .email_exists import handle_email_exists_api  # type: ignore
+from .generate_password_reset_token import (
+    handle_generate_password_reset_token_api,  # type: ignore
+)
+from .password_reset import handle_password_reset_api  # type: ignore
+from .signin import handle_sign_in_api  # type: ignore
+from .signup import handle_sign_up_api  # type: ignore
+
+
+
+

Sub-modules

+
+
supertokens_python.recipe.emailpassword.api.email_exists
+
+
+
+
supertokens_python.recipe.emailpassword.api.generate_password_reset_token
+
+
+
+
supertokens_python.recipe.emailpassword.api.implementation
+
+
+
+
supertokens_python.recipe.emailpassword.api.password_reset
+
+
+
+
supertokens_python.recipe.emailpassword.api.signin
+
+
+
+
supertokens_python.recipe.emailpassword.api.signup
+
+
+
+
supertokens_python.recipe.emailpassword.api.utils
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailpassword/api/password_reset.html b/html/supertokens_python/recipe/emailpassword/api/password_reset.html new file mode 100644 index 000000000..7a9d15af0 --- /dev/null +++ b/html/supertokens_python/recipe/emailpassword/api/password_reset.html @@ -0,0 +1,164 @@ + + + + + + +supertokens_python.recipe.emailpassword.api.password_reset API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.emailpassword.api.password_reset

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Any, Dict
+
+if TYPE_CHECKING:
+    from supertokens_python.recipe.emailpassword.interfaces import (
+        APIOptions,
+        APIInterface,
+    )
+
+from supertokens_python.exceptions import raise_bad_input_exception
+from supertokens_python.utils import send_200_response
+
+from .utils import validate_form_fields_or_throw_error
+
+
+async def handle_password_reset_api(
+    tenant_id: str,
+    api_implementation: APIInterface,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+):
+    if api_implementation.disable_generate_password_reset_token_post:
+        return None
+    body = await api_options.request.json()
+    if body is None:
+        raise_bad_input_exception("Please provide a JSON body")
+    form_fields_raw: Any = body["formFields"] if "formFields" in body else []
+    form_fields = await validate_form_fields_or_throw_error(
+        api_options.config.reset_password_using_token_feature.form_fields_for_password_reset_form,
+        form_fields_raw,
+        tenant_id,
+    )
+
+    if "token" not in body:
+        raise_bad_input_exception("Please provide the password reset token")
+    if not isinstance(body["token"], str):
+        raise_bad_input_exception("The password reset token must be a string")
+
+    token = body["token"]
+
+    response = await api_implementation.password_reset_post(
+        form_fields, token, tenant_id, api_options, user_context
+    )
+    return send_200_response(response.to_json(), api_options.response)
+
+
+
+
+
+
+
+

Functions

+
+
+async def handle_password_reset_api(tenant_id: str, api_implementation: APIInterface, api_options: APIOptions, user_context: Dict[str, Any]) +
+
+
+
+ +Expand source code + +
async def handle_password_reset_api(
+    tenant_id: str,
+    api_implementation: APIInterface,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+):
+    if api_implementation.disable_generate_password_reset_token_post:
+        return None
+    body = await api_options.request.json()
+    if body is None:
+        raise_bad_input_exception("Please provide a JSON body")
+    form_fields_raw: Any = body["formFields"] if "formFields" in body else []
+    form_fields = await validate_form_fields_or_throw_error(
+        api_options.config.reset_password_using_token_feature.form_fields_for_password_reset_form,
+        form_fields_raw,
+        tenant_id,
+    )
+
+    if "token" not in body:
+        raise_bad_input_exception("Please provide the password reset token")
+    if not isinstance(body["token"], str):
+        raise_bad_input_exception("The password reset token must be a string")
+
+    token = body["token"]
+
+    response = await api_implementation.password_reset_post(
+        form_fields, token, tenant_id, api_options, user_context
+    )
+    return send_200_response(response.to_json(), api_options.response)
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailpassword/api/signin.html b/html/supertokens_python/recipe/emailpassword/api/signin.html new file mode 100644 index 000000000..cdb344600 --- /dev/null +++ b/html/supertokens_python/recipe/emailpassword/api/signin.html @@ -0,0 +1,148 @@ + + + + + + +supertokens_python.recipe.emailpassword.api.signin API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.emailpassword.api.signin

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Any, Dict
+
+if TYPE_CHECKING:
+    from supertokens_python.recipe.emailpassword.interfaces import (
+        APIOptions,
+        APIInterface,
+    )
+
+from supertokens_python.exceptions import raise_bad_input_exception
+from supertokens_python.utils import send_200_response
+
+from .utils import validate_form_fields_or_throw_error
+
+
+async def handle_sign_in_api(
+    tenant_id: str,
+    api_implementation: APIInterface,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+):
+    if api_implementation.disable_sign_in_post:
+        return None
+    body = await api_options.request.json()
+    if body is None:
+        raise_bad_input_exception("Please provide a JSON body")
+    form_fields_raw: Any = body["formFields"] if "formFields" in body else []
+    form_fields = await validate_form_fields_or_throw_error(
+        api_options.config.sign_in_feature.form_fields, form_fields_raw, tenant_id
+    )
+
+    response = await api_implementation.sign_in_post(
+        form_fields, tenant_id, api_options, user_context
+    )
+
+    return send_200_response(response.to_json(), api_options.response)
+
+
+
+
+
+
+
+

Functions

+
+
+async def handle_sign_in_api(tenant_id: str, api_implementation: APIInterface, api_options: APIOptions, user_context: Dict[str, Any]) +
+
+
+
+ +Expand source code + +
async def handle_sign_in_api(
+    tenant_id: str,
+    api_implementation: APIInterface,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+):
+    if api_implementation.disable_sign_in_post:
+        return None
+    body = await api_options.request.json()
+    if body is None:
+        raise_bad_input_exception("Please provide a JSON body")
+    form_fields_raw: Any = body["formFields"] if "formFields" in body else []
+    form_fields = await validate_form_fields_or_throw_error(
+        api_options.config.sign_in_feature.form_fields, form_fields_raw, tenant_id
+    )
+
+    response = await api_implementation.sign_in_post(
+        form_fields, tenant_id, api_options, user_context
+    )
+
+    return send_200_response(response.to_json(), api_options.response)
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailpassword/api/signup.html b/html/supertokens_python/recipe/emailpassword/api/signup.html new file mode 100644 index 000000000..5146f4f59 --- /dev/null +++ b/html/supertokens_python/recipe/emailpassword/api/signup.html @@ -0,0 +1,180 @@ + + + + + + +supertokens_python.recipe.emailpassword.api.signup API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.emailpassword.api.signup

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Any, Dict
+
+from supertokens_python.recipe.emailpassword.interfaces import SignUpPostOkResult
+from supertokens_python.types import GeneralErrorResponse
+
+from ..exceptions import raise_form_field_exception
+from ..types import ErrorFormField
+
+if TYPE_CHECKING:
+    from supertokens_python.recipe.emailpassword.interfaces import (
+        APIOptions,
+        APIInterface,
+    )
+
+from supertokens_python.exceptions import raise_bad_input_exception
+from supertokens_python.utils import send_200_response
+
+from .utils import validate_form_fields_or_throw_error
+
+
+async def handle_sign_up_api(
+    tenant_id: str,
+    api_implementation: APIInterface,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+):
+    if api_implementation.disable_sign_up_post:
+        return None
+    body = await api_options.request.json()
+    if body is None:
+        raise_bad_input_exception("Please provide a JSON body")
+    form_fields_raw: Any = body["formFields"] if "formFields" in body else []
+    form_fields = await validate_form_fields_or_throw_error(
+        api_options.config.sign_up_feature.form_fields, form_fields_raw, tenant_id
+    )
+
+    response = await api_implementation.sign_up_post(
+        form_fields, tenant_id, api_options, user_context
+    )
+
+    if isinstance(response, SignUpPostOkResult):
+        return send_200_response(response.to_json(), api_options.response)
+    if isinstance(response, GeneralErrorResponse):
+        return send_200_response(response.to_json(), api_options.response)
+
+    return raise_form_field_exception(
+        "EMAIL_ALREADY_EXISTS_ERROR",
+        [
+            ErrorFormField(
+                id="email",
+                error="This email already exists. Please sign in instead.",
+            )
+        ],
+    )
+
+
+
+
+
+
+
+

Functions

+
+
+async def handle_sign_up_api(tenant_id: str, api_implementation: APIInterface, api_options: APIOptions, user_context: Dict[str, Any]) +
+
+
+
+ +Expand source code + +
async def handle_sign_up_api(
+    tenant_id: str,
+    api_implementation: APIInterface,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+):
+    if api_implementation.disable_sign_up_post:
+        return None
+    body = await api_options.request.json()
+    if body is None:
+        raise_bad_input_exception("Please provide a JSON body")
+    form_fields_raw: Any = body["formFields"] if "formFields" in body else []
+    form_fields = await validate_form_fields_or_throw_error(
+        api_options.config.sign_up_feature.form_fields, form_fields_raw, tenant_id
+    )
+
+    response = await api_implementation.sign_up_post(
+        form_fields, tenant_id, api_options, user_context
+    )
+
+    if isinstance(response, SignUpPostOkResult):
+        return send_200_response(response.to_json(), api_options.response)
+    if isinstance(response, GeneralErrorResponse):
+        return send_200_response(response.to_json(), api_options.response)
+
+    return raise_form_field_exception(
+        "EMAIL_ALREADY_EXISTS_ERROR",
+        [
+            ErrorFormField(
+                id="email",
+                error="This email already exists. Please sign in instead.",
+            )
+        ],
+    )
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailpassword/api/utils.html b/html/supertokens_python/recipe/emailpassword/api/utils.html new file mode 100644 index 000000000..d3e575a12 --- /dev/null +++ b/html/supertokens_python/recipe/emailpassword/api/utils.html @@ -0,0 +1,270 @@ + + + + + + +supertokens_python.recipe.emailpassword.api.utils API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.emailpassword.api.utils

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from typing import Any, Dict, List, Union
+
+from supertokens_python.exceptions import raise_bad_input_exception
+from supertokens_python.recipe.emailpassword.constants import (
+    FORM_FIELD_EMAIL_ID,
+    FORM_FIELD_PASSWORD_ID,
+)
+from supertokens_python.recipe.emailpassword.exceptions import (
+    raise_form_field_exception,
+)
+from supertokens_python.recipe.emailpassword.types import (
+    ErrorFormField,
+    FormField,
+    NormalisedFormField,
+)
+from supertokens_python.utils import find_first_occurrence_in_list
+
+
+async def validate_form_or_throw_error(
+    inputs: List[FormField],
+    config_form_fields: List[NormalisedFormField],
+    tenant_id: str,
+):
+    validation_errors: List[ErrorFormField] = []
+    if len(config_form_fields) < len(inputs):
+        raise_bad_input_exception("Are you sending too many formFields?")
+
+    for field in config_form_fields:
+        input_field: Union[None, FormField] = find_first_occurrence_in_list(
+            lambda x: x.id == field.id, inputs
+        )
+        is_invalid_value = input_field is None or (
+            isinstance(input_field.value, str) and input_field.value == ""
+        )
+        if not field.optional and is_invalid_value:
+            validation_errors.append(ErrorFormField(field.id, "Field is not optional"))
+            continue
+
+        # If the field was invalid and not optional, execution won't reach here.
+        # so we need to skip it if  the value is invalid and optional.
+        if is_invalid_value:
+            continue
+
+        assert input_field is not None
+
+        error = await field.validate(input_field.value, tenant_id)
+        if error is not None:
+            validation_errors.append(ErrorFormField(field.id, error))
+
+    if len(validation_errors) != 0:
+        # raise BadInputError(msg="Error in input formFields")
+        raise_form_field_exception("Error in input formFields", validation_errors)
+
+
+async def validate_form_fields_or_throw_error(
+    config_form_fields: List[NormalisedFormField], form_fields_raw: Any, tenant_id: str
+) -> List[FormField]:
+    if form_fields_raw is None:
+        raise_bad_input_exception("Missing input param: formFields")
+
+    if not isinstance(form_fields_raw, List):
+        raise_bad_input_exception("formFields must be an array")
+
+    form_fields: List[FormField] = []
+
+    form_fields_list_raw: List[Dict[str, Any]] = form_fields_raw
+    for current_form_field in form_fields_list_raw:
+        if (
+            "id" not in current_form_field
+            or not isinstance(current_form_field["id"], str)
+            or "value" not in current_form_field
+        ):
+            raise_bad_input_exception(
+                "All elements of formFields must contain an 'id' and 'value' field"
+            )
+
+        value = current_form_field["value"]
+        if current_form_field["id"] in [
+            FORM_FIELD_EMAIL_ID,
+            FORM_FIELD_PASSWORD_ID,
+        ] and not isinstance(value, str):
+            # Ensure that the type is string else we will throw a bad input
+            # error.
+            raise_bad_input_exception(
+                f"{current_form_field['id']} value must be a string"
+            )
+
+        if current_form_field["id"] == FORM_FIELD_EMAIL_ID and isinstance(value, str):
+            value = value.strip()
+        form_fields.append(FormField(current_form_field["id"], value))
+
+    await validate_form_or_throw_error(form_fields, config_form_fields, tenant_id)
+    return form_fields
+
+
+
+
+
+
+
+

Functions

+
+
+async def validate_form_fields_or_throw_error(config_form_fields: List[NormalisedFormField], form_fields_raw: Any, tenant_id: str) ‑> List[FormField] +
+
+
+
+ +Expand source code + +
async def validate_form_fields_or_throw_error(
+    config_form_fields: List[NormalisedFormField], form_fields_raw: Any, tenant_id: str
+) -> List[FormField]:
+    if form_fields_raw is None:
+        raise_bad_input_exception("Missing input param: formFields")
+
+    if not isinstance(form_fields_raw, List):
+        raise_bad_input_exception("formFields must be an array")
+
+    form_fields: List[FormField] = []
+
+    form_fields_list_raw: List[Dict[str, Any]] = form_fields_raw
+    for current_form_field in form_fields_list_raw:
+        if (
+            "id" not in current_form_field
+            or not isinstance(current_form_field["id"], str)
+            or "value" not in current_form_field
+        ):
+            raise_bad_input_exception(
+                "All elements of formFields must contain an 'id' and 'value' field"
+            )
+
+        value = current_form_field["value"]
+        if current_form_field["id"] in [
+            FORM_FIELD_EMAIL_ID,
+            FORM_FIELD_PASSWORD_ID,
+        ] and not isinstance(value, str):
+            # Ensure that the type is string else we will throw a bad input
+            # error.
+            raise_bad_input_exception(
+                f"{current_form_field['id']} value must be a string"
+            )
+
+        if current_form_field["id"] == FORM_FIELD_EMAIL_ID and isinstance(value, str):
+            value = value.strip()
+        form_fields.append(FormField(current_form_field["id"], value))
+
+    await validate_form_or_throw_error(form_fields, config_form_fields, tenant_id)
+    return form_fields
+
+
+
+async def validate_form_or_throw_error(inputs: List[FormField], config_form_fields: List[NormalisedFormField], tenant_id: str) +
+
+
+
+ +Expand source code + +
async def validate_form_or_throw_error(
+    inputs: List[FormField],
+    config_form_fields: List[NormalisedFormField],
+    tenant_id: str,
+):
+    validation_errors: List[ErrorFormField] = []
+    if len(config_form_fields) < len(inputs):
+        raise_bad_input_exception("Are you sending too many formFields?")
+
+    for field in config_form_fields:
+        input_field: Union[None, FormField] = find_first_occurrence_in_list(
+            lambda x: x.id == field.id, inputs
+        )
+        is_invalid_value = input_field is None or (
+            isinstance(input_field.value, str) and input_field.value == ""
+        )
+        if not field.optional and is_invalid_value:
+            validation_errors.append(ErrorFormField(field.id, "Field is not optional"))
+            continue
+
+        # If the field was invalid and not optional, execution won't reach here.
+        # so we need to skip it if  the value is invalid and optional.
+        if is_invalid_value:
+            continue
+
+        assert input_field is not None
+
+        error = await field.validate(input_field.value, tenant_id)
+        if error is not None:
+            validation_errors.append(ErrorFormField(field.id, error))
+
+    if len(validation_errors) != 0:
+        # raise BadInputError(msg="Error in input formFields")
+        raise_form_field_exception("Error in input formFields", validation_errors)
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailpassword/asyncio/index.html b/html/supertokens_python/recipe/emailpassword/asyncio/index.html new file mode 100644 index 000000000..7ea32049a --- /dev/null +++ b/html/supertokens_python/recipe/emailpassword/asyncio/index.html @@ -0,0 +1,490 @@ + + + + + + +supertokens_python.recipe.emailpassword.asyncio API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.emailpassword.asyncio

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from typing import Any, Dict, Union, Optional
+from supertokens_python import get_request_from_user_context
+
+from supertokens_python.recipe.emailpassword import EmailPasswordRecipe
+
+from ..types import EmailTemplateVars, User
+from ...multitenancy.constants import DEFAULT_TENANT_ID
+
+from supertokens_python.recipe.emailpassword.interfaces import (
+    CreateResetPasswordWrongUserIdError,
+    CreateResetPasswordLinkUnknownUserIdError,
+    CreateResetPasswordLinkOkResult,
+    SendResetPasswordEmailOkResult,
+    SendResetPasswordEmailUnknownUserIdError,
+)
+from supertokens_python.recipe.emailpassword.utils import get_password_reset_link
+from supertokens_python.recipe.emailpassword.types import (
+    PasswordResetEmailTemplateVars,
+    PasswordResetEmailTemplateVarsUser,
+)
+
+
+async def update_email_or_password(
+    user_id: str,
+    email: Union[str, None] = None,
+    password: Union[str, None] = None,
+    apply_password_policy: Union[bool, None] = None,
+    tenant_id_for_password_policy: Optional[str] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+):
+    if user_context is None:
+        user_context = {}
+    return await EmailPasswordRecipe.get_instance().recipe_implementation.update_email_or_password(
+        user_id,
+        email,
+        password,
+        apply_password_policy,
+        tenant_id_for_password_policy or DEFAULT_TENANT_ID,
+        user_context,
+    )
+
+
+async def get_user_by_id(
+    user_id: str, user_context: Union[None, Dict[str, Any]] = None
+) -> Union[None, User]:
+    if user_context is None:
+        user_context = {}
+    return (
+        await EmailPasswordRecipe.get_instance().recipe_implementation.get_user_by_id(
+            user_id, user_context
+        )
+    )
+
+
+async def get_user_by_email(
+    tenant_id: str, email: str, user_context: Union[None, Dict[str, Any]] = None
+) -> Union[User, None]:
+    if user_context is None:
+        user_context = {}
+    return await EmailPasswordRecipe.get_instance().recipe_implementation.get_user_by_email(
+        email, tenant_id, user_context
+    )
+
+
+async def create_reset_password_token(
+    tenant_id: str, user_id: str, user_context: Union[None, Dict[str, Any]] = None
+):
+    if user_context is None:
+        user_context = {}
+    return await EmailPasswordRecipe.get_instance().recipe_implementation.create_reset_password_token(
+        user_id, tenant_id, user_context
+    )
+
+
+async def reset_password_using_token(
+    tenant_id: str,
+    token: str,
+    new_password: str,
+    user_context: Union[None, Dict[str, Any]] = None,
+):
+    if user_context is None:
+        user_context = {}
+    return await EmailPasswordRecipe.get_instance().recipe_implementation.reset_password_using_token(
+        token, new_password, tenant_id, user_context
+    )
+
+
+async def sign_in(
+    tenant_id: str,
+    email: str,
+    password: str,
+    user_context: Union[None, Dict[str, Any]] = None,
+):
+    if user_context is None:
+        user_context = {}
+    return await EmailPasswordRecipe.get_instance().recipe_implementation.sign_in(
+        email, password, tenant_id, user_context
+    )
+
+
+async def sign_up(
+    tenant_id: str,
+    email: str,
+    password: str,
+    user_context: Union[None, Dict[str, Any]] = None,
+):
+    if user_context is None:
+        user_context = {}
+    return await EmailPasswordRecipe.get_instance().recipe_implementation.sign_up(
+        email, password, tenant_id, user_context
+    )
+
+
+async def send_email(
+    input_: EmailTemplateVars,
+    user_context: Union[None, Dict[str, Any]] = None,
+):
+    if user_context is None:
+        user_context = {}
+    return await EmailPasswordRecipe.get_instance().email_delivery.ingredient_interface_impl.send_email(
+        input_, user_context
+    )
+
+
+async def create_reset_password_link(
+    tenant_id: str, user_id: str, user_context: Optional[Dict[str, Any]] = None
+):
+    if user_context is None:
+        user_context = {}
+    token = await create_reset_password_token(tenant_id, user_id, user_context)
+    if isinstance(token, CreateResetPasswordWrongUserIdError):
+        return CreateResetPasswordLinkUnknownUserIdError()
+
+    recipe_instance = EmailPasswordRecipe.get_instance()
+    request = get_request_from_user_context(user_context)
+    return CreateResetPasswordLinkOkResult(
+        link=get_password_reset_link(
+            recipe_instance.get_app_info(),
+            token.token,
+            tenant_id,
+            request,
+            user_context,
+        )
+    )
+
+
+async def send_reset_password_email(
+    tenant_id: str, user_id: str, user_context: Optional[Dict[str, Any]] = None
+):
+    link = await create_reset_password_link(tenant_id, user_id, user_context)
+    if isinstance(link, CreateResetPasswordLinkUnknownUserIdError):
+        return SendResetPasswordEmailUnknownUserIdError()
+
+    user = await get_user_by_id(user_id, user_context)
+    assert user is not None
+
+    await send_email(
+        PasswordResetEmailTemplateVars(
+            PasswordResetEmailTemplateVarsUser(user.user_id, user.email),
+            link.link,
+            tenant_id,
+        ),
+        user_context,
+    )
+
+    return SendResetPasswordEmailOkResult()
+
+
+
+
+
+
+
+

Functions

+
+ +
+
+
+ +Expand source code + +
async def create_reset_password_link(
+    tenant_id: str, user_id: str, user_context: Optional[Dict[str, Any]] = None
+):
+    if user_context is None:
+        user_context = {}
+    token = await create_reset_password_token(tenant_id, user_id, user_context)
+    if isinstance(token, CreateResetPasswordWrongUserIdError):
+        return CreateResetPasswordLinkUnknownUserIdError()
+
+    recipe_instance = EmailPasswordRecipe.get_instance()
+    request = get_request_from_user_context(user_context)
+    return CreateResetPasswordLinkOkResult(
+        link=get_password_reset_link(
+            recipe_instance.get_app_info(),
+            token.token,
+            tenant_id,
+            request,
+            user_context,
+        )
+    )
+
+
+
+async def create_reset_password_token(tenant_id: str, user_id: str, user_context: Optional[Dict[str, Any]] = None) +
+
+
+
+ +Expand source code + +
async def create_reset_password_token(
+    tenant_id: str, user_id: str, user_context: Union[None, Dict[str, Any]] = None
+):
+    if user_context is None:
+        user_context = {}
+    return await EmailPasswordRecipe.get_instance().recipe_implementation.create_reset_password_token(
+        user_id, tenant_id, user_context
+    )
+
+
+
+async def get_user_by_email(tenant_id: str, email: str, user_context: Optional[Dict[str, Any]] = None) ‑> Optional[User] +
+
+
+
+ +Expand source code + +
async def get_user_by_email(
+    tenant_id: str, email: str, user_context: Union[None, Dict[str, Any]] = None
+) -> Union[User, None]:
+    if user_context is None:
+        user_context = {}
+    return await EmailPasswordRecipe.get_instance().recipe_implementation.get_user_by_email(
+        email, tenant_id, user_context
+    )
+
+
+
+async def get_user_by_id(user_id: str, user_context: Optional[Dict[str, Any]] = None) ‑> Optional[User] +
+
+
+
+ +Expand source code + +
async def get_user_by_id(
+    user_id: str, user_context: Union[None, Dict[str, Any]] = None
+) -> Union[None, User]:
+    if user_context is None:
+        user_context = {}
+    return (
+        await EmailPasswordRecipe.get_instance().recipe_implementation.get_user_by_id(
+            user_id, user_context
+        )
+    )
+
+
+
+async def reset_password_using_token(tenant_id: str, token: str, new_password: str, user_context: Optional[Dict[str, Any]] = None) +
+
+
+
+ +Expand source code + +
async def reset_password_using_token(
+    tenant_id: str,
+    token: str,
+    new_password: str,
+    user_context: Union[None, Dict[str, Any]] = None,
+):
+    if user_context is None:
+        user_context = {}
+    return await EmailPasswordRecipe.get_instance().recipe_implementation.reset_password_using_token(
+        token, new_password, tenant_id, user_context
+    )
+
+
+
+async def send_email(input_: PasswordResetEmailTemplateVars, user_context: Optional[Dict[str, Any]] = None) +
+
+
+
+ +Expand source code + +
async def send_email(
+    input_: EmailTemplateVars,
+    user_context: Union[None, Dict[str, Any]] = None,
+):
+    if user_context is None:
+        user_context = {}
+    return await EmailPasswordRecipe.get_instance().email_delivery.ingredient_interface_impl.send_email(
+        input_, user_context
+    )
+
+
+
+async def send_reset_password_email(tenant_id: str, user_id: str, user_context: Optional[Dict[str, Any]] = None) +
+
+
+
+ +Expand source code + +
async def send_reset_password_email(
+    tenant_id: str, user_id: str, user_context: Optional[Dict[str, Any]] = None
+):
+    link = await create_reset_password_link(tenant_id, user_id, user_context)
+    if isinstance(link, CreateResetPasswordLinkUnknownUserIdError):
+        return SendResetPasswordEmailUnknownUserIdError()
+
+    user = await get_user_by_id(user_id, user_context)
+    assert user is not None
+
+    await send_email(
+        PasswordResetEmailTemplateVars(
+            PasswordResetEmailTemplateVarsUser(user.user_id, user.email),
+            link.link,
+            tenant_id,
+        ),
+        user_context,
+    )
+
+    return SendResetPasswordEmailOkResult()
+
+
+
+async def sign_in(tenant_id: str, email: str, password: str, user_context: Optional[Dict[str, Any]] = None) +
+
+
+
+ +Expand source code + +
async def sign_in(
+    tenant_id: str,
+    email: str,
+    password: str,
+    user_context: Union[None, Dict[str, Any]] = None,
+):
+    if user_context is None:
+        user_context = {}
+    return await EmailPasswordRecipe.get_instance().recipe_implementation.sign_in(
+        email, password, tenant_id, user_context
+    )
+
+
+
+async def sign_up(tenant_id: str, email: str, password: str, user_context: Optional[Dict[str, Any]] = None) +
+
+
+
+ +Expand source code + +
async def sign_up(
+    tenant_id: str,
+    email: str,
+    password: str,
+    user_context: Union[None, Dict[str, Any]] = None,
+):
+    if user_context is None:
+        user_context = {}
+    return await EmailPasswordRecipe.get_instance().recipe_implementation.sign_up(
+        email, password, tenant_id, user_context
+    )
+
+
+
+async def update_email_or_password(user_id: str, email: Optional[str] = None, password: Optional[str] = None, apply_password_policy: Optional[bool] = None, tenant_id_for_password_policy: Optional[str] = None, user_context: Optional[Dict[str, Any]] = None) +
+
+
+
+ +Expand source code + +
async def update_email_or_password(
+    user_id: str,
+    email: Union[str, None] = None,
+    password: Union[str, None] = None,
+    apply_password_policy: Union[bool, None] = None,
+    tenant_id_for_password_policy: Optional[str] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+):
+    if user_context is None:
+        user_context = {}
+    return await EmailPasswordRecipe.get_instance().recipe_implementation.update_email_or_password(
+        user_id,
+        email,
+        password,
+        apply_password_policy,
+        tenant_id_for_password_policy or DEFAULT_TENANT_ID,
+        user_context,
+    )
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailpassword/constants.html b/html/supertokens_python/recipe/emailpassword/constants.html new file mode 100644 index 000000000..5b30aefb2 --- /dev/null +++ b/html/supertokens_python/recipe/emailpassword/constants.html @@ -0,0 +1,80 @@ + + + + + + +supertokens_python.recipe.emailpassword.constants API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.emailpassword.constants

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+FORM_FIELD_PASSWORD_ID = "password"
+FORM_FIELD_EMAIL_ID = "email"
+SIGNUP = "/signup"
+SIGNIN = "/signin"
+USER_PASSWORD_RESET_TOKEN = "/user/password/reset/token"
+USER_PASSWORD_RESET = "/user/password/reset"
+SIGNUP_EMAIL_EXISTS_OLD = "/signup/email/exists"
+SIGNUP_EMAIL_EXISTS = "/emailpassword/email/exists"
+RESET_PASSWORD = "/reset-password"
+
+
+
+
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailpassword/emaildelivery/index.html b/html/supertokens_python/recipe/emailpassword/emaildelivery/index.html new file mode 100644 index 000000000..cd73e83cd --- /dev/null +++ b/html/supertokens_python/recipe/emailpassword/emaildelivery/index.html @@ -0,0 +1,83 @@ + + + + + + +supertokens_python.recipe.emailpassword.emaildelivery API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.emailpassword.emaildelivery

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+
+
+

Sub-modules

+
+
supertokens_python.recipe.emailpassword.emaildelivery.services
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailpassword/emaildelivery/services/backward_compatibility/index.html b/html/supertokens_python/recipe/emailpassword/emaildelivery/services/backward_compatibility/index.html new file mode 100644 index 000000000..c05100626 --- /dev/null +++ b/html/supertokens_python/recipe/emailpassword/emaildelivery/services/backward_compatibility/index.html @@ -0,0 +1,287 @@ + + + + + + +supertokens_python.recipe.emailpassword.emaildelivery.services.backward_compatibility API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.emailpassword.emaildelivery.services.backward_compatibility

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from os import environ
+from typing import Any, Dict
+
+from httpx import AsyncClient
+
+from supertokens_python.ingredients.emaildelivery.types import EmailDeliveryInterface
+from supertokens_python.logger import log_debug_message
+from supertokens_python.recipe.emailpassword.interfaces import (
+    EmailTemplateVars,
+    RecipeInterface,
+)
+from supertokens_python.recipe.emailpassword.types import User
+from supertokens_python.supertokens import AppInfo
+from supertokens_python.utils import handle_httpx_client_exceptions
+
+
+async def create_and_send_email_using_supertokens_service(
+    app_info: AppInfo, user: User, password_reset_url_with_token: str
+) -> None:
+    if ("SUPERTOKENS_ENV" in environ) and (environ["SUPERTOKENS_ENV"] == "testing"):
+        return
+
+    data = {
+        "email": user.email,
+        "appName": app_info.app_name,
+        "passwordResetURL": password_reset_url_with_token,
+    }
+    try:
+        async with AsyncClient(timeout=30.0) as client:
+            resp = await client.post("https://api.supertokens.io/0/st/auth/password/reset", json=data, headers={"api-version": "0"})  # type: ignore
+            resp.raise_for_status()
+            log_debug_message("Password reset email sent to %s", user.email)
+    except Exception as e:
+        log_debug_message("Error sending password reset email")
+        handle_httpx_client_exceptions(e, data)
+
+
+class BackwardCompatibilityService(EmailDeliveryInterface[EmailTemplateVars]):
+    app_info: AppInfo
+
+    def __init__(
+        self,
+        app_info: AppInfo,
+        recipe_interface_impl: RecipeInterface,
+    ) -> None:
+        self.recipe_interface_impl = recipe_interface_impl
+        self.app_info = app_info
+
+    async def send_email(
+        self,
+        template_vars: EmailTemplateVars,
+        user_context: Dict[str, Any],
+    ) -> None:
+        user = await self.recipe_interface_impl.get_user_by_id(
+            user_id=template_vars.user.id, user_context=user_context
+        )
+        if user is None:
+            raise Exception("Should never come here")
+
+        # we add this here cause the user may have overridden the sendEmail function
+        # to change the input email and if we don't do this, the input email
+        # will get reset by the getUserById call above.
+        user.email = template_vars.user.email
+        try:
+            await create_and_send_email_using_supertokens_service(
+                self.app_info, user, template_vars.password_reset_link
+            )
+        except Exception:
+            pass
+
+
+
+
+
+
+
+

Functions

+
+
+async def create_and_send_email_using_supertokens_service(app_info: AppInfo, user: User, password_reset_url_with_token: str) ‑> None +
+
+
+
+ +Expand source code + +
async def create_and_send_email_using_supertokens_service(
+    app_info: AppInfo, user: User, password_reset_url_with_token: str
+) -> None:
+    if ("SUPERTOKENS_ENV" in environ) and (environ["SUPERTOKENS_ENV"] == "testing"):
+        return
+
+    data = {
+        "email": user.email,
+        "appName": app_info.app_name,
+        "passwordResetURL": password_reset_url_with_token,
+    }
+    try:
+        async with AsyncClient(timeout=30.0) as client:
+            resp = await client.post("https://api.supertokens.io/0/st/auth/password/reset", json=data, headers={"api-version": "0"})  # type: ignore
+            resp.raise_for_status()
+            log_debug_message("Password reset email sent to %s", user.email)
+    except Exception as e:
+        log_debug_message("Error sending password reset email")
+        handle_httpx_client_exceptions(e, data)
+
+
+
+
+
+

Classes

+
+
+class BackwardCompatibilityService +(app_info: AppInfo, recipe_interface_impl: RecipeInterface) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class BackwardCompatibilityService(EmailDeliveryInterface[EmailTemplateVars]):
+    app_info: AppInfo
+
+    def __init__(
+        self,
+        app_info: AppInfo,
+        recipe_interface_impl: RecipeInterface,
+    ) -> None:
+        self.recipe_interface_impl = recipe_interface_impl
+        self.app_info = app_info
+
+    async def send_email(
+        self,
+        template_vars: EmailTemplateVars,
+        user_context: Dict[str, Any],
+    ) -> None:
+        user = await self.recipe_interface_impl.get_user_by_id(
+            user_id=template_vars.user.id, user_context=user_context
+        )
+        if user is None:
+            raise Exception("Should never come here")
+
+        # we add this here cause the user may have overridden the sendEmail function
+        # to change the input email and if we don't do this, the input email
+        # will get reset by the getUserById call above.
+        user.email = template_vars.user.email
+        try:
+            await create_and_send_email_using_supertokens_service(
+                self.app_info, user, template_vars.password_reset_link
+            )
+        except Exception:
+            pass
+
+

Ancestors

+ +

Class variables

+
+
var app_infoAppInfo
+
+
+
+
+

Methods

+
+
+async def send_email(self, template_vars: EmailTemplateVars, user_context: Dict[str, Any]) ‑> None +
+
+
+
+ +Expand source code + +
async def send_email(
+    self,
+    template_vars: EmailTemplateVars,
+    user_context: Dict[str, Any],
+) -> None:
+    user = await self.recipe_interface_impl.get_user_by_id(
+        user_id=template_vars.user.id, user_context=user_context
+    )
+    if user is None:
+        raise Exception("Should never come here")
+
+    # we add this here cause the user may have overridden the sendEmail function
+    # to change the input email and if we don't do this, the input email
+    # will get reset by the getUserById call above.
+    user.email = template_vars.user.email
+    try:
+        await create_and_send_email_using_supertokens_service(
+            self.app_info, user, template_vars.password_reset_link
+        )
+    except Exception:
+        pass
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailpassword/emaildelivery/services/index.html b/html/supertokens_python/recipe/emailpassword/emaildelivery/services/index.html new file mode 100644 index 000000000..41760fca0 --- /dev/null +++ b/html/supertokens_python/recipe/emailpassword/emaildelivery/services/index.html @@ -0,0 +1,92 @@ + + + + + + +supertokens_python.recipe.emailpassword.emaildelivery.services API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.emailpassword.emaildelivery.services

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from . import smtp
+
+SMTPService = smtp.SMTPService
+
+
+
+

Sub-modules

+
+
supertokens_python.recipe.emailpassword.emaildelivery.services.backward_compatibility
+
+
+
+
supertokens_python.recipe.emailpassword.emaildelivery.services.smtp
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailpassword/emaildelivery/services/smtp/index.html b/html/supertokens_python/recipe/emailpassword/emaildelivery/services/smtp/index.html new file mode 100644 index 000000000..be9fba3b1 --- /dev/null +++ b/html/supertokens_python/recipe/emailpassword/emaildelivery/services/smtp/index.html @@ -0,0 +1,217 @@ + + + + + + +supertokens_python.recipe.emailpassword.emaildelivery.services.smtp API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.emailpassword.emaildelivery.services.smtp

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from typing import Any, Dict, Callable, Union
+
+from supertokens_python.ingredients.emaildelivery.services.smtp import Transporter
+from supertokens_python.ingredients.emaildelivery.types import (
+    EmailDeliveryInterface,
+    SMTPServiceInterface,
+    SMTPSettings,
+)
+from supertokens_python.recipe.emailpassword.types import (
+    EmailTemplateVars,
+    SMTPOverrideInput,
+)
+
+from .service_implementation import ServiceImplementation
+
+
+class SMTPService(EmailDeliveryInterface[EmailTemplateVars]):
+    service_implementation: SMTPServiceInterface[EmailTemplateVars]
+
+    def __init__(
+        self,
+        smtp_settings: SMTPSettings,
+        override: Union[Callable[[SMTPOverrideInput], SMTPOverrideInput], None] = None,
+    ) -> None:
+        transporter = Transporter(smtp_settings)
+
+        oi = ServiceImplementation(transporter)
+        self.service_implementation = oi if override is None else override(oi)
+
+    async def send_email(
+        self,
+        template_vars: EmailTemplateVars,
+        user_context: Dict[str, Any],
+    ) -> None:
+        content = await self.service_implementation.get_content(
+            template_vars, user_context
+        )
+        await self.service_implementation.send_raw_email(content, user_context)
+
+
+
+

Sub-modules

+
+
supertokens_python.recipe.emailpassword.emaildelivery.services.smtp.password_reset
+
+
+
+
supertokens_python.recipe.emailpassword.emaildelivery.services.smtp.password_reset_email
+
+
+
+
supertokens_python.recipe.emailpassword.emaildelivery.services.smtp.service_implementation
+
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class SMTPService +(smtp_settings: SMTPSettings, override: Optional[Callable[[SMTPServiceInterface[PasswordResetEmailTemplateVars]], SMTPServiceInterface[PasswordResetEmailTemplateVars]]] = None) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class SMTPService(EmailDeliveryInterface[EmailTemplateVars]):
+    service_implementation: SMTPServiceInterface[EmailTemplateVars]
+
+    def __init__(
+        self,
+        smtp_settings: SMTPSettings,
+        override: Union[Callable[[SMTPOverrideInput], SMTPOverrideInput], None] = None,
+    ) -> None:
+        transporter = Transporter(smtp_settings)
+
+        oi = ServiceImplementation(transporter)
+        self.service_implementation = oi if override is None else override(oi)
+
+    async def send_email(
+        self,
+        template_vars: EmailTemplateVars,
+        user_context: Dict[str, Any],
+    ) -> None:
+        content = await self.service_implementation.get_content(
+            template_vars, user_context
+        )
+        await self.service_implementation.send_raw_email(content, user_context)
+
+

Ancestors

+ +

Class variables

+
+
var service_implementationSMTPServiceInterface[PasswordResetEmailTemplateVars]
+
+
+
+
+

Methods

+
+
+async def send_email(self, template_vars: PasswordResetEmailTemplateVars, user_context: Dict[str, Any]) ‑> None +
+
+
+
+ +Expand source code + +
async def send_email(
+    self,
+    template_vars: EmailTemplateVars,
+    user_context: Dict[str, Any],
+) -> None:
+    content = await self.service_implementation.get_content(
+        template_vars, user_context
+    )
+    await self.service_implementation.send_raw_email(content, user_context)
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailpassword/emaildelivery/services/smtp/password_reset.html b/html/supertokens_python/recipe/emailpassword/emaildelivery/services/smtp/password_reset.html new file mode 100644 index 000000000..016ca7c08 --- /dev/null +++ b/html/supertokens_python/recipe/emailpassword/emaildelivery/services/smtp/password_reset.html @@ -0,0 +1,146 @@ + + + + + + +supertokens_python.recipe.emailpassword.emaildelivery.services.smtp.password_reset API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.emailpassword.emaildelivery.services.smtp.password_reset

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from string import Template
+
+from supertokens_python.ingredients.emaildelivery.types import EmailContent
+from supertokens_python.recipe.emailpassword.types import PasswordResetEmailTemplateVars
+from supertokens_python.supertokens import Supertokens
+
+from .password_reset_email import html_template
+
+
+def get_password_reset_email_content(
+    email_input: PasswordResetEmailTemplateVars,
+) -> EmailContent:
+    supertokens = Supertokens.get_instance()
+    app_name = supertokens.app_info.app_name
+    body = get_password_reset_email_html(
+        app_name, email_input.user.email, email_input.password_reset_link
+    )
+    content_result = EmailContent(
+        body, "Password reset instructions", email_input.user.email, is_html=True
+    )
+    return content_result
+
+
+def get_password_reset_email_html(app_name: str, email: str, reset_link: str):
+    return Template(html_template).substitute(
+        appname=app_name, resetLink=reset_link, toEmail=email
+    )
+
+
+
+
+
+
+
+

Functions

+
+
+def get_password_reset_email_content(email_input: PasswordResetEmailTemplateVars) ‑> EmailContent +
+
+
+
+ +Expand source code + +
def get_password_reset_email_content(
+    email_input: PasswordResetEmailTemplateVars,
+) -> EmailContent:
+    supertokens = Supertokens.get_instance()
+    app_name = supertokens.app_info.app_name
+    body = get_password_reset_email_html(
+        app_name, email_input.user.email, email_input.password_reset_link
+    )
+    content_result = EmailContent(
+        body, "Password reset instructions", email_input.user.email, is_html=True
+    )
+    return content_result
+
+
+
+def get_password_reset_email_html(app_name: str, email: str, reset_link: str) +
+
+
+
+ +Expand source code + +
def get_password_reset_email_html(app_name: str, email: str, reset_link: str):
+    return Template(html_template).substitute(
+        appname=app_name, resetLink=reset_link, toEmail=email
+    )
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailpassword/emaildelivery/services/smtp/password_reset_email.html b/html/supertokens_python/recipe/emailpassword/emaildelivery/services/smtp/password_reset_email.html new file mode 100644 index 000000000..bcee905c2 --- /dev/null +++ b/html/supertokens_python/recipe/emailpassword/emaildelivery/services/smtp/password_reset_email.html @@ -0,0 +1,969 @@ + + + + + + +supertokens_python.recipe.emailpassword.emaildelivery.services.smtp.password_reset_email API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.emailpassword.emaildelivery.services.smtp.password_reset_email

+
+
+
+ +Expand source code + +
html_template = """
+<!doctype html>
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml"
+        xmlns:o="urn:schemas-microsoft-com:office:office">
+
+<head>
+        <meta charset="UTF-8">
+        <meta http-equiv="X-UA-Compatible" content="IE=edge">
+        <meta name="viewport" content="width=device-width, initial-scale=1">
+        <title>*|MC:SUBJECT|*</title>
+
+        <style type="text/css">
+            body {
+                        max-width: 100vw;
+                        overflow: hidden;
+                }
+                p {
+                        margin: 10px 0;
+                        padding: 0;
+                }
+
+                table {
+                        border-collapse: collapse;
+                }
+
+                h1,
+                h2,
+                h3,
+                h4,
+                h5,
+                h6 {
+                        display: block;
+                        margin: 0;
+                        padding: 0;
+                }
+
+                img,
+                a img {
+                        border: 0;
+                        height: auto;
+                        outline: none;
+                        text-decoration: none;
+                }
+
+                body,
+                #bodyTable,
+                #bodyCell {
+                        height: 100%;
+                        margin: 0;
+                        padding: 0;
+                        width: 100%;
+                }
+
+                .mcnPreviewText {
+                        display: none !important;
+                }
+
+                #outlook a {
+                        padding: 0;
+                }
+
+                img {
+                        -ms-interpolation-mode: bicubic;
+                }
+
+                table {
+                        mso-table-lspace: 0pt;
+                        mso-table-rspace: 0pt;
+                }
+
+                .ReadMsgBody {
+                        width: 100%;
+                }
+
+                .ExternalClass {
+                        width: 100%;
+                }
+
+                p,
+                a,
+                li,
+                td,
+                blockquote {
+                        mso-line-height-rule: exactly;
+                }
+
+                a[href^=tel],
+                a[href^=sms] {
+                        color: inherit;
+                        cursor: default;
+                        text-decoration: none;
+                }
+
+                p,
+                a,
+                li,
+                td,
+                body,
+                table,
+                blockquote {
+                        -ms-text-size-adjust: 100%;
+                        -webkit-text-size-adjust: 100%;
+                }
+
+                .ExternalClass,
+                .ExternalClass p,
+                .ExternalClass td,
+                .ExternalClass div,
+                .ExternalClass span,
+                .ExternalClass font {
+                        line-height: 100%;
+                }
+
+                a[x-apple-data-detectors] {
+                        color: inherit !important;
+                        text-decoration: none !important;
+                        font-size: inherit !important;
+                        font-family: inherit !important;
+                        font-weight: inherit !important;
+                        line-height: inherit !important;
+                }
+
+                .templateContainer {
+                        max-width: 600px !important;
+                }
+
+                a.mcnButton {
+                        display: block;
+                }
+
+                .mcnImage,
+                .mcnRetinaImage {
+                        vertical-align: bottom;
+                }
+
+                .mcnTextContent {
+                        word-break: break-word;
+                }
+
+                .mcnTextContent img {
+                        height: auto !important;
+                }
+
+                .mcnDividerBlock {
+                        table-layout: fixed !important;
+                }
+
+                /*
+        @tab Page
+        @section Heading 1
+        @style heading 1
+        */
+                h1 {
+                        /*@editable*/
+                        color: #222222;
+                        /*@editable*/
+                        font-family: 'Open Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
+                        /*@editable*/
+                        font-size: 40px;
+                        /*@editable*/
+                        font-style: normal;
+                        /*@editable*/
+                        font-weight: bold;
+                        /*@editable*/
+                        line-height: 150%;
+                        /*@editable*/
+                        letter-spacing: normal;
+                        /*@editable*/
+                        text-align: center;
+                }
+
+                /*
+        @tab Page
+        @section Heading 2
+        @style heading 2
+        */
+                h2 {
+                        /*@editable*/
+                        color: #222222;
+                        /*@editable*/
+                        font-family: Helvetica;
+                        /*@editable*/
+                        font-size: 34px;
+                        /*@editable*/
+                        font-style: normal;
+                        /*@editable*/
+                        font-weight: bold;
+                        /*@editable*/
+                        line-height: 150%;
+                        /*@editable*/
+                        letter-spacing: normal;
+                        /*@editable*/
+                        text-align: left;
+                }
+
+                /*
+        @tab Page
+        @section Heading 3
+        @style heading 3
+        */
+                h3 {
+                        /*@editable*/
+                        color: #444444;
+                        /*@editable*/
+                        font-family: Helvetica;
+                        /*@editable*/
+                        font-size: 22px;
+                        /*@editable*/
+                        font-style: normal;
+                        /*@editable*/
+                        font-weight: bold;
+                        /*@editable*/
+                        line-height: 150%;
+                        /*@editable*/
+                        letter-spacing: normal;
+                        /*@editable*/
+                        text-align: left;
+                }
+
+                /*
+        @tab Page
+        @section Heading 4
+        @style heading 4
+        */
+                h4 {
+                        /*@editable*/
+                        color: #949494;
+                        /*@editable*/
+                        font-family: Georgia;
+                        /*@editable*/
+                        font-size: 20px;
+                        /*@editable*/
+                        font-style: italic;
+                        /*@editable*/
+                        font-weight: normal;
+                        /*@editable*/
+                        line-height: 125%;
+                        /*@editable*/
+                        letter-spacing: normal;
+                        /*@editable*/
+                        text-align: left;
+                }
+
+                /*
+        @tab Header
+        @section Header Container Style
+        */
+                #templateHeader {
+                        /*@editable*/
+                        background-color: #f4f4f4;
+                        /*@editable*/
+                        background-image: none;
+                        /*@editable*/
+                        background-repeat: no-repeat;
+                        /*@editable*/
+                        background-position: center;
+                        /*@editable*/
+                        background-size: cover;
+                        /*@editable*/
+                        border-top: 0;
+                        /*@editable*/
+                        border-bottom: 0;
+                        /*@editable*/
+                        padding-top: 0px;
+                        /*@editable*/
+                        padding-bottom: 0px;
+                }
+
+                /*
+        @tab Header
+        @section Header Interior Style
+        */
+                .headerContainer {
+                        /*@editable*/
+                        background-color: #transparent;
+                        /*@editable*/
+                        background-image: none;
+                        /*@editable*/
+                        background-repeat: no-repeat;
+                        /*@editable*/
+                        background-position: center;
+                        /*@editable*/
+                        background-size: cover;
+                        /*@editable*/
+                        border-top: 0;
+                        /*@editable*/
+                        border-bottom: 0;
+                        /*@editable*/
+                        padding-top: 0;
+                        /*@editable*/
+                        padding-bottom: 0;
+                }
+
+                /*
+        @tab Header
+        @section Header Text
+        */
+                .headerContainer .mcnTextContent,
+                .headerContainer .mcnTextContent p {
+                        /*@editable*/
+                        color: #757575;
+                        /*@editable*/
+                        font-family: Helvetica;
+                        /*@editable*/
+                        font-size: 16px;
+                        /*@editable*/
+                        line-height: 150%;
+                        /*@editable*/
+                        text-align: left;
+                }
+
+                /*
+        @tab Header
+        @section Header Link
+        */
+                .headerContainer .mcnTextContent a,
+                .headerContainer .mcnTextContent p a {
+                        /*@editable*/
+                        color: #007C89;
+                        /*@editable*/
+                        font-weight: normal;
+                        /*@editable*/
+                        text-decoration: underline;
+                }
+
+                /*
+        @tab Body
+        @section Body Container Style
+        */
+                #templateBody {
+                        /*@editable*/
+                        background-color: #f4f4f4;
+                        /*@editable*/
+                        background-image: none;
+                        /*@editable*/
+                        background-repeat: no-repeat;
+                        /*@editable*/
+                        background-position: center;
+                        /*@editable*/
+                        background-size: cover;
+                        /*@editable*/
+                        border-top: 0;
+                        /*@editable*/
+                        border-bottom: 0;
+                        /*@editable*/
+                        padding-top: 0px;
+                        /*@editable*/
+                        padding-bottom: 20px;
+                }
+
+                /*
+        @tab Body
+        @section Body Interior Style
+        */
+                .bodyContainer {
+                        /*@editable*/
+                        background-color: #f4f4f4;
+                        /*@editable*/
+                        background-image: none;
+                        /*@editable*/
+                        background-repeat: no-repeat;
+                        /*@editable*/
+                        background-position: center;
+                        /*@editable*/
+                        background-size: cover;
+                        /*@editable*/
+                        border-top: 2px none #ff9933;
+                        /*@editable*/
+                        border-bottom: 2px none #ff9933;
+                        /*@editable*/
+                        padding-top: 10px;
+                        /*@editable*/
+                        padding-bottom: 10px;
+                }
+
+                /*
+        @tab Body
+        @section Body Text
+        */
+                .bodyContainer .mcnTextContent,
+                .bodyContainer .mcnTextContent p {
+                        /*@editable*/
+                        color: #757575;
+                        /*@editable*/
+                        font-family: Helvetica;
+                        /*@editable*/
+                        font-size: 16px;
+                        /*@editable*/
+                        line-height: 150%;
+                        /*@editable*/
+                        text-align: left;
+                }
+
+                /*
+        @tab Body
+        @section Body Link
+        */
+                .bodyContainer .mcnTextContent a,
+                .bodyContainer .mcnTextContent p a {
+                        /*@editable*/
+                        color: #222222;
+                        /*@editable*/
+                        font-weight: normal;
+                        /*@editable*/
+                        text-decoration: underline;
+                }
+
+                /*
+        @tab Footer
+        @section Footer Style
+        */
+                #templateFooter {
+                        /*@editable*/
+                        background-color: #f4f4f4;
+                        /*@editable*/
+                        background-image: none;
+                        /*@editable*/
+                        background-repeat: no-repeat;
+                        /*@editable*/
+                        background-position: center;
+                        /*@editable*/
+                        background-size: cover;
+                        /*@editable*/
+                        border-top: 0;
+                        /*@editable*/
+                        border-bottom: 0;
+                        /*@editable*/
+                        padding-top: 0px;
+                        /*@editable*/
+                        padding-bottom: 20px;
+                }
+
+                /*
+        @tab Footer
+        @section Footer Interior Style
+        */
+                .footerContainer {
+                        /*@editable*/
+                        background-color: #transparent;
+                        /*@editable*/
+                        background-image: none;
+                        /*@editable*/
+                        background-repeat: no-repeat;
+                        /*@editable*/
+                        background-position: center;
+                        /*@editable*/
+                        background-size: cover;
+                        /*@editable*/
+                        border-top: 0;
+                        /*@editable*/
+                        border-bottom: 0;
+                        /*@editable*/
+                        padding-top: 0;
+                        /*@editable*/
+                        padding-bottom: 0;
+                }
+
+                /*
+        @tab Footer
+        @section Footer Text
+        */
+                .footerContainer .mcnTextContent,
+                .footerContainer .mcnTextContent p {
+                        /*@editable*/
+                        color: #FFFFFF;
+                        /*@editable*/
+                        font-family: 'Helvetica Neue', Helvetica, Arial, Verdana, sans-serif;
+                        /*@editable*/
+                        font-size: 12px;
+                        /*@editable*/
+                        line-height: 150%;
+                        /*@editable*/
+                        text-align: center;
+                }
+
+                /*
+        @tab Footer
+        @section Footer Link
+        */
+                .footerContainer .mcnTextContent a,
+                .footerContainer .mcnTextContent p a {
+                        /*@editable*/
+                        color: #FFFFFF;
+                        /*@editable*/
+                        font-weight: normal;
+                        /*@editable*/
+                        text-decoration: underline;
+                }
+
+                @media only screen and (max-width: 480px) {
+
+                        body,
+                        table,
+                        td,
+                        p,
+                        a,
+                        li,
+                        blockquote {
+                                -webkit-text-size-adjust: none !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+                        body {
+                                width: 100% !important;
+                                min-width: 100% !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+                        .mcnRetinaImage {
+                                max-width: 100% !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+                        .mcnImage {
+                                width: 100% !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+
+                        .mcnCartContainer,
+                        .mcnCaptionTopContent,
+                        .mcnRecContentContainer,
+                        .mcnCaptionBottomContent,
+                        .mcnTextContentContainer,
+                        .mcnBoxedTextContentContainer,
+                        .mcnImageGroupContentContainer,
+                        .mcnCaptionLeftTextContentContainer,
+                        .mcnCaptionRightTextContentContainer,
+                        .mcnCaptionLeftImageContentContainer,
+                        .mcnCaptionRightImageContentContainer,
+                        .mcnImageCardLeftTextContentContainer,
+                        .mcnImageCardRightTextContentContainer,
+                        .mcnImageCardLeftImageContentContainer,
+                        .mcnImageCardRightImageContentContainer {
+                                max-width: 100% !important;
+                                width: 100% !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+                        .mcnBoxedTextContentContainer {
+                                min-width: 100% !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+                        .mcnImageGroupContent {
+                                padding: 9px !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+
+                        .mcnCaptionLeftContentOuter .mcnTextContent,
+                        .mcnCaptionRightContentOuter .mcnTextContent {
+                                padding-top: 9px !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+
+                        .mcnImageCardTopImageContent,
+                        .mcnCaptionBottomContent:last-child .mcnCaptionBottomImageContent,
+                        .mcnCaptionBlockInner .mcnCaptionTopContent:last-child .mcnTextContent {
+                                padding-top: 18px !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+                        .mcnImageCardBottomImageContent {
+                                padding-bottom: 9px !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+                        .mcnImageGroupBlockInner {
+                                padding-top: 0 !important;
+                                padding-bottom: 0 !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+                        .mcnImageGroupBlockOuter {
+                                padding-top: 9px !important;
+                                padding-bottom: 9px !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+
+                        .mcnTextContent,
+                        .mcnBoxedTextContentColumn {
+                                padding-right: 18px !important;
+                                padding-left: 18px !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+
+                        .mcnImageCardLeftImageContent,
+                        .mcnImageCardRightImageContent {
+                                padding-right: 18px !important;
+                                padding-bottom: 0 !important;
+                                padding-left: 18px !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+                        .mcpreview-image-uploader {
+                                display: none !important;
+                                width: 100% !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+
+                        /*
+        @tab Mobile Styles
+        @section Heading 1
+        @tip Make the first-level headings larger in size for better readability on small screens.
+        */
+                        h1 {
+                                /*@editable*/
+                                font-size: 30px !important;
+                                /*@editable*/
+                                line-height: 125% !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+
+                        /*
+        @tab Mobile Styles
+        @section Heading 2
+        @tip Make the second-level headings larger in size for better readability on small screens.
+        */
+                        h2 {
+                                /*@editable*/
+                                font-size: 26px !important;
+                                /*@editable*/
+                                line-height: 125% !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+
+                        /*
+        @tab Mobile Styles
+        @section Heading 3
+        @tip Make the third-level headings larger in size for better readability on small screens.
+        */
+                        h3 {
+                                /*@editable*/
+                                font-size: 20px !important;
+                                /*@editable*/
+                                line-height: 150% !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+
+                        /*
+        @tab Mobile Styles
+        @section Heading 4
+        @tip Make the fourth-level headings larger in size for better readability on small screens.
+        */
+                        h4 {
+                                /*@editable*/
+                                font-size: 18px !important;
+                                /*@editable*/
+                                line-height: 150% !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+
+                        /*
+        @tab Mobile Styles
+        @section Boxed Text
+        @tip Make the boxed text larger in size for better readability on small screens. We recommend a font size of at least 16px.
+        */
+                        .mcnBoxedTextContentContainer .mcnTextContent,
+                        .mcnBoxedTextContentContainer .mcnTextContent p {
+                                /*@editable*/
+                                font-size: 14px !important;
+                                /*@editable*/
+                                line-height: 150% !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+
+                        /*
+        @tab Mobile Styles
+        @section Header Text
+        @tip Make the header text larger in size for better readability on small screens.
+        */
+                        .headerContainer .mcnTextContent,
+                        .headerContainer .mcnTextContent p {
+                                /*@editable*/
+                                font-size: 16px !important;
+                                /*@editable*/
+                                line-height: 150% !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+
+                        /*
+        @tab Mobile Styles
+        @section Body Text
+        @tip Make the body text larger in size for better readability on small screens. We recommend a font size of at least 16px.
+        */
+                        .bodyContainer .mcnTextContent,
+                        .bodyContainer .mcnTextContent p {
+                                /*@editable*/
+                                font-size: 16px !important;
+                                /*@editable*/
+                                line-height: 150% !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+
+                        /*
+        @tab Mobile Styles
+        @section Footer Text
+        @tip Make the footer content text larger in size for better readability on small screens.
+        */
+                        .footerContainer .mcnTextContent,
+                        .footerContainer .mcnTextContent p {
+                                /*@editable*/
+                                font-size: 14px !important;
+                                /*@editable*/
+                                line-height: 150% !important;
+                        }
+
+                }
+        </style>
+</head>
+
+<body>
+        <!--*|IF:MC_PREVIEW_TEXT|*-->
+        <!--[if !gte mso 9]><!----><span class="mcnPreviewText"
+                style="display:none; font-size:0px; line-height:0px; max-height:0px; max-width:0px; opacity:0; overflow:hidden; visibility:hidden; mso-hide:all;"></span>
+        <!--<![endif]-->
+        <!--*|END:IF|*-->
+        <center>
+                <table align="center" border="0" cellpadding="0" cellspacing="0" height="100%" width="100%" id="bodyTable">
+                        <tr>
+                                <td align="center" valign="top" id="bodyCell">
+                                        <!-- BEGIN TEMPLATE // -->
+                                        <table border="0" cellpadding="0" cellspacing="0" width="100%">
+                                                <tr>
+                                                        <td align="center" valign="top" id="templateHeader" data-template-container>
+                                                                <!--[if (gte mso 9)|(IE)]>
+                                    <table align="center" border="0" cellspacing="0" cellpadding="0" width="600" style="width:600px;">
+                                    <tr>
+                                    <td align="center" valign="top" width="600" style="width:600px;">
+                                    <![endif]-->
+                                                                <table align="center" border="0" cellpadding="0" cellspacing="0" width="100%"
+                                                                        class="templateContainer">
+                                                                        <tr>
+                                                                                <td valign="top" class="headerContainer"></td>
+                                                                        </tr>
+                                                                </table>
+                                                                <!--[if (gte mso 9)|(IE)]>
+                                    </td>
+                                    </tr>
+                                    </table>
+                                    <![endif]-->
+                                                        </td>
+                                                </tr>
+                                                <tr>
+                                                        <td align="center" valign="top" id="templateBody" data-template-container>
+                                                                <!--[if (gte mso 9)|(IE)]>
+                                    <table align="center" border="0" cellspacing="0" cellpadding="0" width="600" style="width:600px;">
+                                    <tr>
+                                    <td align="center" valign="top" width="600" style="width:600px;">
+                                    <![endif]-->
+                                                                <table align="center" border="0" cellpadding="0" cellspacing="0" width="100%"
+                                                                        class="templateContainer">
+                                                                        <tr>
+                                                                                <td valign="top" class="bodyContainer">
+                                                                                        <table border="0" cellpadding="0" cellspacing="0" width="100%"
+                                                                                                class="mcnCodeBlock">
+                                                                                                <tbody class="mcnTextBlockOuter">
+                                                                                                        <tr>
+                                                                                                                <td valign="top" class="mcnTextBlockInner">
+
+
+                                                                                                                        <div
+                                                                                                                                style="background-color:#fff; margin-left: 3%; margin-right: 3%; border: 1px solid #ddd; margin-top: 40px; border-radius: 6px;">
+                                                                                                                                <div style="padding-left: 15%; padding-right: 15%;">
+
+                                                                                                                                        <p
+                                                                                                                                                style="font-family:'Helvetica'; font-size: 16px; line-height: 26px; font-weight:700; text-align: center; padding-top: 24px; padding-bottom: 24px; padding-left: 8%; padding-right: 8%; ">
+                                                                                                                                                A password reset request for your account on
+                                                                                                                                                ${appname} has been received.
+                                                                                                                                        </p>
+
+                                                                                                                                        <div class="button-td button-td-primary"
+                                                                                                                                                style="border-radius: 6px; margin-bottom: 50px; display: block; text-align: center;">
+                                                                                                                                                <a class="button-a button-a-primary"
+                                                                                                                                                        href="${resetLink}" target="_blank"
+                                                                                                                                                        style="background: #52B56E;font-size: 17px;line-height: 24px;font-weight: 700;font-family: 'Helvetica', sans-serif;text-decoration: none;padding: 9px 25px 9px 25px;color: #ffffff;display: block;border-radius: 6px;width: fit-content;margin: 0 auto;">Reset
+                                                                                                                                                        Password</a>
+                                                                                                                                        </div>
+                                                                                                                                </div>
+                                                                                                                                <div
+                                                                                                                                        style="background-color:#fafafa; border-top: 1px solid #ddd; padding-left: 15%; padding-right: 15%; padding-bottom: 24px; padding-top: 24px">
+                                                                                                                                        <p
+                                                                                                                                                style="font-family: 'Hevetica', sans-serif; font-size: 14px; line-height: 23px; font-weight:400;  text-align: center; color: #808080;">
+                                                                                                                                                Alternatively, you can directly paste this link
+                                                                                                                                                in your browser <br>
+                                                                                                                                                <a style="font-family: 'Helvetica', sans-serif, sans-serif; text-align: center; word-break: break-all; font-weight: 400; font-size: 14px; line-height: 23px; color: #007aff !important;"
+                                                                                                                                                        target="_blank"
+                                                                                                                                                        href="${resetLink}">${resetLink}</a>
+                                                                                                                                        </p>
+                                                                                                                                </div>
+                                                                                                                        </div>
+
+
+
+
+                                                                                                                </td>
+                                                                                                        </tr>
+                                                                                                </tbody>
+                                                                                        </table>
+                                                                                        <table border="0" cellpadding="0" cellspacing="0" width="100%"
+                                                                                                class="mcnCodeBlock">
+                                                                                                <tbody class="mcnTextBlockOuter">
+                                                                                                        <tr>
+                                                                                                                <td valign="top" class="mcnTextBlockInner">
+                                                                                                                        <p
+                                                                                                                                style="font-family: 'Helvetica', sans-serif; font-size: 16px; line-height: 26px; font-weight:400; text-align: center; color: #808080">
+                                                                                                                                This email is meant for <a
+                                                                                                                                        style="font-family: 'Helvetica', sans-serif; text-align: center; word-break: break-all; font-weight: 400; font-size: 16px; line-height: 26px; color: #808080 !important;"
+                                                                                                                                        target="_blank"
+                                                                                                                                        href="mailto:${toEmail}">${toEmail}</a>
+                                                                                                                        </p>
+                                                                                                                </td>
+                                                                                                        </tr>
+                                                                                                </tbody>
+                                                                                        </table>
+                                                                                </td>
+                                                                        </tr>
+                                                                </table>
+                                                                <!--[if (gte mso 9)|(IE)]>
+                                    </td>
+                                    </tr>
+                                    </table>
+                                    <![endif]-->
+                                                        </td>
+                                                </tr>
+                                                <tr>
+                                                        <td align="center" valign="top" id="templateFooter" data-template-container>
+                                                                <!--[if (gte mso 9)|(IE)]>
+                                    <table align="center" border="0" cellspacing="0" cellpadding="0" width="600" style="width:600px;">
+                                    <tr>
+                                    <td align="center" valign="top" width="600" style="width:600px;">
+                                    <![endif]-->
+                                                                <table align="center" border="0" cellpadding="0" cellspacing="0" width="100%"
+                                                                        class="templateContainer">
+                                                                        <tr>
+                                                                                <td valign="top" class="footerContainer"></td>
+                                                                        </tr>
+                                                                </table>
+                                                                <!--[if (gte mso 9)|(IE)]>
+                                    </td>
+                                    </tr>
+                                    </table>
+                                    <![endif]-->
+                                                        </td>
+                                                </tr>
+                                        </table>
+                                        <!-- // END TEMPLATE -->
+                                </td>
+                        </tr>
+                </table>
+        </center>
+</body>
+
+</html>
+"""
+
+
+
+
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailpassword/emaildelivery/services/smtp/service_implementation/index.html b/html/supertokens_python/recipe/emailpassword/emaildelivery/services/smtp/service_implementation/index.html new file mode 100644 index 000000000..0f67374d2 --- /dev/null +++ b/html/supertokens_python/recipe/emailpassword/emaildelivery/services/smtp/service_implementation/index.html @@ -0,0 +1,170 @@ + + + + + + +supertokens_python.recipe.emailpassword.emaildelivery.services.smtp.service_implementation API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.emailpassword.emaildelivery.services.smtp.service_implementation

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from typing import Any, Dict
+
+from supertokens_python.ingredients.emaildelivery.types import (
+    EmailContent,
+    SMTPServiceInterface,
+)
+from supertokens_python.recipe.emailpassword.emaildelivery.services.smtp.password_reset import (
+    get_password_reset_email_content,
+)
+from supertokens_python.recipe.emailpassword.types import EmailTemplateVars
+
+
+class ServiceImplementation(SMTPServiceInterface[EmailTemplateVars]):
+    async def send_raw_email(
+        self, content: EmailContent, user_context: Dict[str, Any]
+    ) -> None:
+        await self.transporter.send_email(content, user_context)
+
+    async def get_content(
+        self, template_vars: EmailTemplateVars, user_context: Dict[str, Any]
+    ) -> EmailContent:
+        return get_password_reset_email_content(template_vars)
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class ServiceImplementation +(transporter: Transporter) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class ServiceImplementation(SMTPServiceInterface[EmailTemplateVars]):
+    async def send_raw_email(
+        self, content: EmailContent, user_context: Dict[str, Any]
+    ) -> None:
+        await self.transporter.send_email(content, user_context)
+
+    async def get_content(
+        self, template_vars: EmailTemplateVars, user_context: Dict[str, Any]
+    ) -> EmailContent:
+        return get_password_reset_email_content(template_vars)
+
+

Ancestors

+ +

Methods

+
+
+async def get_content(self, template_vars: PasswordResetEmailTemplateVars, user_context: Dict[str, Any]) ‑> EmailContent +
+
+
+
+ +Expand source code + +
async def get_content(
+    self, template_vars: EmailTemplateVars, user_context: Dict[str, Any]
+) -> EmailContent:
+    return get_password_reset_email_content(template_vars)
+
+
+
+async def send_raw_email(self, content: EmailContent, user_context: Dict[str, Any]) ‑> None +
+
+
+
+ +Expand source code + +
async def send_raw_email(
+    self, content: EmailContent, user_context: Dict[str, Any]
+) -> None:
+    await self.transporter.send_email(content, user_context)
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailpassword/exceptions.html b/html/supertokens_python/recipe/emailpassword/exceptions.html new file mode 100644 index 000000000..cfe6ad462 --- /dev/null +++ b/html/supertokens_python/recipe/emailpassword/exceptions.html @@ -0,0 +1,208 @@ + + + + + + +supertokens_python.recipe.emailpassword.exceptions API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.emailpassword.exceptions

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Any, Dict, List, NoReturn
+
+from supertokens_python.exceptions import SuperTokensError
+
+if TYPE_CHECKING:
+    from .types import ErrorFormField
+
+
+def raise_form_field_exception(msg: str, form_fields: List[ErrorFormField]) -> NoReturn:
+    raise FieldError(msg, form_fields)
+
+
+class SuperTokensEmailPasswordError(SuperTokensError):
+    pass
+
+
+class FieldError(SuperTokensEmailPasswordError):
+    def __init__(self, msg: str, form_fields: List[ErrorFormField]):
+        super().__init__(msg)
+        self.form_fields = form_fields
+
+    def get_json_form_fields(self) -> List[Dict[str, Any]]:
+        form_fields: List[Dict[str, Any]] = []
+        for form_field in self.form_fields:
+            form_fields.append({"id": form_field.id, "error": form_field.error})
+        return form_fields
+
+
+
+
+
+
+
+

Functions

+
+
+def raise_form_field_exception(msg: str, form_fields: List[ErrorFormField]) ‑> NoReturn +
+
+
+
+ +Expand source code + +
def raise_form_field_exception(msg: str, form_fields: List[ErrorFormField]) -> NoReturn:
+    raise FieldError(msg, form_fields)
+
+
+
+
+
+

Classes

+
+
+class FieldError +(msg: str, form_fields: List[ErrorFormField]) +
+
+

Common base class for all non-exit exceptions.

+
+ +Expand source code + +
class FieldError(SuperTokensEmailPasswordError):
+    def __init__(self, msg: str, form_fields: List[ErrorFormField]):
+        super().__init__(msg)
+        self.form_fields = form_fields
+
+    def get_json_form_fields(self) -> List[Dict[str, Any]]:
+        form_fields: List[Dict[str, Any]] = []
+        for form_field in self.form_fields:
+            form_fields.append({"id": form_field.id, "error": form_field.error})
+        return form_fields
+
+

Ancestors

+ +

Methods

+
+
+def get_json_form_fields(self) ‑> List[Dict[str, Any]] +
+
+
+
+ +Expand source code + +
def get_json_form_fields(self) -> List[Dict[str, Any]]:
+    form_fields: List[Dict[str, Any]] = []
+    for form_field in self.form_fields:
+        form_fields.append({"id": form_field.id, "error": form_field.error})
+    return form_fields
+
+
+
+
+
+class SuperTokensEmailPasswordError +(*args, **kwargs) +
+
+

Common base class for all non-exit exceptions.

+
+ +Expand source code + +
class SuperTokensEmailPasswordError(SuperTokensError):
+    pass
+
+

Ancestors

+ +

Subclasses

+ +
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailpassword/index.html b/html/supertokens_python/recipe/emailpassword/index.html new file mode 100644 index 000000000..0dfb72456 --- /dev/null +++ b/html/supertokens_python/recipe/emailpassword/index.html @@ -0,0 +1,198 @@ + + + + + + +supertokens_python.recipe.emailpassword API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.emailpassword

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Callable, Union
+
+from supertokens_python.ingredients.emaildelivery.types import EmailDeliveryConfig
+from supertokens_python.recipe.emailpassword.types import EmailTemplateVars
+from supertokens_python.ingredients.emaildelivery import types as emaildelivery_types
+
+from . import exceptions as ex
+from . import utils
+from .emaildelivery import services as emaildelivery_services
+from .recipe import EmailPasswordRecipe
+
+exceptions = ex
+InputOverrideConfig = utils.InputOverrideConfig
+InputSignUpFeature = utils.InputSignUpFeature
+InputFormField = utils.InputFormField
+SMTPService = emaildelivery_services.SMTPService
+EmailDeliveryInterface = emaildelivery_types.EmailDeliveryInterface
+
+if TYPE_CHECKING:
+    from supertokens_python.supertokens import AppInfo
+
+    from ...recipe_module import RecipeModule
+
+
+def init(
+    sign_up_feature: Union[utils.InputSignUpFeature, None] = None,
+    override: Union[utils.InputOverrideConfig, None] = None,
+    email_delivery: Union[EmailDeliveryConfig[EmailTemplateVars], None] = None,
+) -> Callable[[AppInfo], RecipeModule]:
+    return EmailPasswordRecipe.init(
+        sign_up_feature,
+        override,
+        email_delivery,
+    )
+
+
+
+

Sub-modules

+
+
supertokens_python.recipe.emailpassword.api
+
+
+
+
supertokens_python.recipe.emailpassword.asyncio
+
+
+
+
supertokens_python.recipe.emailpassword.constants
+
+
+
+
supertokens_python.recipe.emailpassword.emaildelivery
+
+
+
+
supertokens_python.recipe.emailpassword.exceptions
+
+
+
+
supertokens_python.recipe.emailpassword.interfaces
+
+
+
+
supertokens_python.recipe.emailpassword.recipe
+
+
+
+
supertokens_python.recipe.emailpassword.recipe_implementation
+
+
+
+
supertokens_python.recipe.emailpassword.syncio
+
+
+
+
supertokens_python.recipe.emailpassword.types
+
+
+
+
supertokens_python.recipe.emailpassword.utils
+
+
+
+
+
+
+
+
+

Functions

+
+
+def init(sign_up_feature: Union[InputSignUpFeature, None] = None, override: Union[InputOverrideConfig, None] = None, email_delivery: Union[EmailDeliveryConfig[EmailTemplateVars], None] = None) ‑> Callable[[AppInfo], RecipeModule] +
+
+
+
+ +Expand source code + +
def init(
+    sign_up_feature: Union[utils.InputSignUpFeature, None] = None,
+    override: Union[utils.InputOverrideConfig, None] = None,
+    email_delivery: Union[EmailDeliveryConfig[EmailTemplateVars], None] = None,
+) -> Callable[[AppInfo], RecipeModule]:
+    return EmailPasswordRecipe.init(
+        sign_up_feature,
+        override,
+        email_delivery,
+    )
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailpassword/interfaces.html b/html/supertokens_python/recipe/emailpassword/interfaces.html new file mode 100644 index 000000000..a38b8f6f7 --- /dev/null +++ b/html/supertokens_python/recipe/emailpassword/interfaces.html @@ -0,0 +1,1607 @@ + + + + + + +supertokens_python.recipe.emailpassword.interfaces API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.emailpassword.interfaces

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from abc import ABC, abstractmethod
+from typing import TYPE_CHECKING, Any, Dict, List, Union
+
+from supertokens_python.ingredients.emaildelivery import EmailDeliveryIngredient
+from supertokens_python.recipe.emailpassword.types import EmailTemplateVars
+from ...supertokens import AppInfo
+
+from ...types import APIResponse, GeneralErrorResponse
+
+if TYPE_CHECKING:
+    from supertokens_python.framework import BaseRequest, BaseResponse
+    from supertokens_python.recipe.session import SessionContainer
+
+    from .types import FormField, User
+    from .utils import EmailPasswordConfig
+
+
+class SignUpOkResult:
+    def __init__(self, user: User):
+        self.user = user
+
+
+class SignUpEmailAlreadyExistsError:
+    pass
+
+
+class SignInOkResult:
+    def __init__(self, user: User):
+        self.user = user
+
+
+class SignInWrongCredentialsError:
+    pass
+
+
+class CreateResetPasswordOkResult:
+    def __init__(self, token: str):
+        self.token = token
+
+
+class CreateResetPasswordWrongUserIdError:
+    pass
+
+
+class CreateResetPasswordLinkOkResult:
+    def __init__(self, link: str):
+        self.link = link
+
+
+class CreateResetPasswordLinkUnknownUserIdError:
+    pass
+
+
+class SendResetPasswordEmailOkResult:
+    pass
+
+
+class SendResetPasswordEmailUnknownUserIdError:
+    pass
+
+
+class ResetPasswordUsingTokenOkResult:
+    def __init__(self, user_id: Union[str, None]):
+        self.user_id = user_id
+
+
+class ResetPasswordUsingTokenInvalidTokenError:
+    pass
+
+
+class UpdateEmailOrPasswordOkResult:
+    pass
+
+
+class UpdateEmailOrPasswordEmailAlreadyExistsError:
+    pass
+
+
+class UpdateEmailOrPasswordUnknownUserIdError:
+    pass
+
+
+class UpdateEmailOrPasswordPasswordPolicyViolationError:
+    failure_reason: str
+
+    def __init__(self, failure_reason: str):
+        self.failure_reason = failure_reason
+
+
+class RecipeInterface(ABC):
+    def __init__(self):
+        pass
+
+    @abstractmethod
+    async def get_user_by_id(
+        self, user_id: str, user_context: Dict[str, Any]
+    ) -> Union[User, None]:
+        pass
+
+    @abstractmethod
+    async def get_user_by_email(
+        self, email: str, tenant_id: str, user_context: Dict[str, Any]
+    ) -> Union[User, None]:
+        pass
+
+    @abstractmethod
+    async def create_reset_password_token(
+        self, user_id: str, tenant_id: str, user_context: Dict[str, Any]
+    ) -> Union[CreateResetPasswordOkResult, CreateResetPasswordWrongUserIdError]:
+        pass
+
+    @abstractmethod
+    async def reset_password_using_token(
+        self,
+        token: str,
+        new_password: str,
+        tenant_id: str,
+        user_context: Dict[str, Any],
+    ) -> Union[
+        ResetPasswordUsingTokenOkResult, ResetPasswordUsingTokenInvalidTokenError
+    ]:
+        pass
+
+    @abstractmethod
+    async def sign_in(
+        self, email: str, password: str, tenant_id: str, user_context: Dict[str, Any]
+    ) -> Union[SignInOkResult, SignInWrongCredentialsError]:
+        pass
+
+    @abstractmethod
+    async def sign_up(
+        self, email: str, password: str, tenant_id: str, user_context: Dict[str, Any]
+    ) -> Union[SignUpOkResult, SignUpEmailAlreadyExistsError]:
+        pass
+
+    @abstractmethod
+    async def update_email_or_password(
+        self,
+        user_id: str,
+        email: Union[str, None],
+        password: Union[str, None],
+        apply_password_policy: Union[bool, None],
+        tenant_id_for_password_policy: str,
+        user_context: Dict[str, Any],
+    ) -> Union[
+        UpdateEmailOrPasswordOkResult,
+        UpdateEmailOrPasswordEmailAlreadyExistsError,
+        UpdateEmailOrPasswordUnknownUserIdError,
+        UpdateEmailOrPasswordPasswordPolicyViolationError,
+    ]:
+        pass
+
+
+class APIOptions:
+    def __init__(
+        self,
+        request: BaseRequest,
+        response: BaseResponse,
+        recipe_id: str,
+        config: EmailPasswordConfig,
+        recipe_implementation: RecipeInterface,
+        app_info: AppInfo,
+        email_delivery: EmailDeliveryIngredient[EmailTemplateVars],
+    ):
+        self.request: BaseRequest = request
+        self.response: BaseResponse = response
+        self.recipe_id: str = recipe_id
+        self.config: EmailPasswordConfig = config
+        self.recipe_implementation: RecipeInterface = recipe_implementation
+        self.app_info = app_info
+        self.email_delivery = email_delivery
+
+
+class EmailExistsGetOkResult(APIResponse):
+    status: str = "OK"
+
+    def __init__(self, exists: bool):
+        self.exists = exists
+
+    def to_json(self) -> Dict[str, Any]:
+        return {"status": self.status, "exists": self.exists}
+
+
+class GeneratePasswordResetTokenPostOkResult(APIResponse):
+    status: str = "OK"
+
+    def to_json(self) -> Dict[str, Any]:
+        return {"status": self.status}
+
+
+class PasswordResetPostOkResult(APIResponse):
+    status: str = "OK"
+
+    def __init__(self, user_id: Union[str, None]):
+        self.user_id = user_id
+
+    def to_json(self) -> Dict[str, Any]:
+        return {"status": self.status}
+
+
+class PasswordResetPostInvalidTokenResponse(APIResponse):
+    status: str = "RESET_PASSWORD_INVALID_TOKEN_ERROR"
+
+    def to_json(self) -> Dict[str, Any]:
+        return {"status": self.status}
+
+
+class SignInPostOkResult(APIResponse):
+    status: str = "OK"
+
+    def __init__(self, user: User, session: SessionContainer):
+        self.user = user
+        self.session = session
+
+    def to_json(self) -> Dict[str, Any]:
+        return {
+            "status": self.status,
+            "user": {
+                "id": self.user.user_id,
+                "email": self.user.email,
+                "timeJoined": self.user.time_joined,
+            },
+        }
+
+
+class SignInPostWrongCredentialsError(APIResponse):
+    status: str = "WRONG_CREDENTIALS_ERROR"
+
+    def to_json(self) -> Dict[str, Any]:
+        return {"status": self.status}
+
+
+class SignUpPostOkResult(APIResponse):
+    status: str = "OK"
+
+    def __init__(self, user: User, session: SessionContainer):
+        self.user = user
+        self.session = session
+
+    def to_json(self) -> Dict[str, Any]:
+        return {
+            "status": self.status,
+            "user": {
+                "id": self.user.user_id,
+                "email": self.user.email,
+                "timeJoined": self.user.time_joined,
+            },
+        }
+
+
+class SignUpPostEmailAlreadyExistsError(APIResponse):
+    status: str = "EMAIL_ALREADY_EXISTS_ERROR"
+
+    def to_json(self) -> Dict[str, Any]:
+        return {"status": self.status}
+
+
+class APIInterface:
+    def __init__(self):
+        self.disable_email_exists_get = False
+        self.disable_generate_password_reset_token_post = False
+        self.disable_password_reset_post = False
+        self.disable_sign_in_post = False
+        self.disable_sign_up_post = False
+
+    @abstractmethod
+    async def email_exists_get(
+        self,
+        email: str,
+        tenant_id: str,
+        api_options: APIOptions,
+        user_context: Dict[str, Any],
+    ) -> Union[EmailExistsGetOkResult, GeneralErrorResponse]:
+        pass
+
+    @abstractmethod
+    async def generate_password_reset_token_post(
+        self,
+        form_fields: List[FormField],
+        tenant_id: str,
+        api_options: APIOptions,
+        user_context: Dict[str, Any],
+    ) -> Union[GeneratePasswordResetTokenPostOkResult, GeneralErrorResponse]:
+        pass
+
+    @abstractmethod
+    async def password_reset_post(
+        self,
+        form_fields: List[FormField],
+        token: str,
+        tenant_id: str,
+        api_options: APIOptions,
+        user_context: Dict[str, Any],
+    ) -> Union[
+        PasswordResetPostOkResult,
+        PasswordResetPostInvalidTokenResponse,
+        GeneralErrorResponse,
+    ]:
+        pass
+
+    @abstractmethod
+    async def sign_in_post(
+        self,
+        form_fields: List[FormField],
+        tenant_id: str,
+        api_options: APIOptions,
+        user_context: Dict[str, Any],
+    ) -> Union[
+        SignInPostOkResult, SignInPostWrongCredentialsError, GeneralErrorResponse
+    ]:
+        pass
+
+    @abstractmethod
+    async def sign_up_post(
+        self,
+        form_fields: List[FormField],
+        tenant_id: str,
+        api_options: APIOptions,
+        user_context: Dict[str, Any],
+    ) -> Union[
+        SignUpPostOkResult, SignUpPostEmailAlreadyExistsError, GeneralErrorResponse
+    ]:
+        pass
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class APIInterface +
+
+
+
+ +Expand source code + +
class APIInterface:
+    def __init__(self):
+        self.disable_email_exists_get = False
+        self.disable_generate_password_reset_token_post = False
+        self.disable_password_reset_post = False
+        self.disable_sign_in_post = False
+        self.disable_sign_up_post = False
+
+    @abstractmethod
+    async def email_exists_get(
+        self,
+        email: str,
+        tenant_id: str,
+        api_options: APIOptions,
+        user_context: Dict[str, Any],
+    ) -> Union[EmailExistsGetOkResult, GeneralErrorResponse]:
+        pass
+
+    @abstractmethod
+    async def generate_password_reset_token_post(
+        self,
+        form_fields: List[FormField],
+        tenant_id: str,
+        api_options: APIOptions,
+        user_context: Dict[str, Any],
+    ) -> Union[GeneratePasswordResetTokenPostOkResult, GeneralErrorResponse]:
+        pass
+
+    @abstractmethod
+    async def password_reset_post(
+        self,
+        form_fields: List[FormField],
+        token: str,
+        tenant_id: str,
+        api_options: APIOptions,
+        user_context: Dict[str, Any],
+    ) -> Union[
+        PasswordResetPostOkResult,
+        PasswordResetPostInvalidTokenResponse,
+        GeneralErrorResponse,
+    ]:
+        pass
+
+    @abstractmethod
+    async def sign_in_post(
+        self,
+        form_fields: List[FormField],
+        tenant_id: str,
+        api_options: APIOptions,
+        user_context: Dict[str, Any],
+    ) -> Union[
+        SignInPostOkResult, SignInPostWrongCredentialsError, GeneralErrorResponse
+    ]:
+        pass
+
+    @abstractmethod
+    async def sign_up_post(
+        self,
+        form_fields: List[FormField],
+        tenant_id: str,
+        api_options: APIOptions,
+        user_context: Dict[str, Any],
+    ) -> Union[
+        SignUpPostOkResult, SignUpPostEmailAlreadyExistsError, GeneralErrorResponse
+    ]:
+        pass
+
+

Subclasses

+ +

Methods

+
+
+async def email_exists_get(self, email: str, tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[EmailExistsGetOkResultGeneralErrorResponse] +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def email_exists_get(
+    self,
+    email: str,
+    tenant_id: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+) -> Union[EmailExistsGetOkResult, GeneralErrorResponse]:
+    pass
+
+
+
+async def generate_password_reset_token_post(self, form_fields: List[FormField], tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[GeneratePasswordResetTokenPostOkResult, GeneralErrorResponse] +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def generate_password_reset_token_post(
+    self,
+    form_fields: List[FormField],
+    tenant_id: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+) -> Union[GeneratePasswordResetTokenPostOkResult, GeneralErrorResponse]:
+    pass
+
+
+
+async def password_reset_post(self, form_fields: List[FormField], token: str, tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[PasswordResetPostOkResultPasswordResetPostInvalidTokenResponse, GeneralErrorResponse] +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def password_reset_post(
+    self,
+    form_fields: List[FormField],
+    token: str,
+    tenant_id: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+) -> Union[
+    PasswordResetPostOkResult,
+    PasswordResetPostInvalidTokenResponse,
+    GeneralErrorResponse,
+]:
+    pass
+
+
+
+async def sign_in_post(self, form_fields: List[FormField], tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[SignInPostOkResultSignInPostWrongCredentialsError, GeneralErrorResponse] +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def sign_in_post(
+    self,
+    form_fields: List[FormField],
+    tenant_id: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+) -> Union[
+    SignInPostOkResult, SignInPostWrongCredentialsError, GeneralErrorResponse
+]:
+    pass
+
+
+
+async def sign_up_post(self, form_fields: List[FormField], tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[SignUpPostOkResultSignUpPostEmailAlreadyExistsError, GeneralErrorResponse] +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def sign_up_post(
+    self,
+    form_fields: List[FormField],
+    tenant_id: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+) -> Union[
+    SignUpPostOkResult, SignUpPostEmailAlreadyExistsError, GeneralErrorResponse
+]:
+    pass
+
+
+
+
+
+class APIOptions +(request: BaseRequest, response: BaseResponse, recipe_id: str, config: EmailPasswordConfig, recipe_implementation: RecipeInterface, app_info: AppInfo, email_delivery: EmailDeliveryIngredient[EmailTemplateVars]) +
+
+
+
+ +Expand source code + +
class APIOptions:
+    def __init__(
+        self,
+        request: BaseRequest,
+        response: BaseResponse,
+        recipe_id: str,
+        config: EmailPasswordConfig,
+        recipe_implementation: RecipeInterface,
+        app_info: AppInfo,
+        email_delivery: EmailDeliveryIngredient[EmailTemplateVars],
+    ):
+        self.request: BaseRequest = request
+        self.response: BaseResponse = response
+        self.recipe_id: str = recipe_id
+        self.config: EmailPasswordConfig = config
+        self.recipe_implementation: RecipeInterface = recipe_implementation
+        self.app_info = app_info
+        self.email_delivery = email_delivery
+
+
+
+class CreateResetPasswordLinkOkResult +(link: str) +
+
+
+
+ +Expand source code + +
class CreateResetPasswordLinkOkResult:
+    def __init__(self, link: str):
+        self.link = link
+
+
+
+class CreateResetPasswordLinkUnknownUserIdError +
+
+
+
+ +Expand source code + +
class CreateResetPasswordLinkUnknownUserIdError:
+    pass
+
+
+
+class CreateResetPasswordOkResult +(token: str) +
+
+
+
+ +Expand source code + +
class CreateResetPasswordOkResult:
+    def __init__(self, token: str):
+        self.token = token
+
+
+
+class CreateResetPasswordWrongUserIdError +
+
+
+
+ +Expand source code + +
class CreateResetPasswordWrongUserIdError:
+    pass
+
+
+
+class EmailExistsGetOkResult +(exists: bool) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class EmailExistsGetOkResult(APIResponse):
+    status: str = "OK"
+
+    def __init__(self, exists: bool):
+        self.exists = exists
+
+    def to_json(self) -> Dict[str, Any]:
+        return {"status": self.status, "exists": self.exists}
+
+

Ancestors

+ +

Class variables

+
+
var status : str
+
+
+
+
+

Methods

+
+
+def to_json(self) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
def to_json(self) -> Dict[str, Any]:
+    return {"status": self.status, "exists": self.exists}
+
+
+
+
+
+class GeneratePasswordResetTokenPostOkResult +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class GeneratePasswordResetTokenPostOkResult(APIResponse):
+    status: str = "OK"
+
+    def to_json(self) -> Dict[str, Any]:
+        return {"status": self.status}
+
+

Ancestors

+ +

Class variables

+
+
var status : str
+
+
+
+
+

Methods

+
+
+def to_json(self) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
def to_json(self) -> Dict[str, Any]:
+    return {"status": self.status}
+
+
+
+
+
+class PasswordResetPostInvalidTokenResponse +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class PasswordResetPostInvalidTokenResponse(APIResponse):
+    status: str = "RESET_PASSWORD_INVALID_TOKEN_ERROR"
+
+    def to_json(self) -> Dict[str, Any]:
+        return {"status": self.status}
+
+

Ancestors

+ +

Class variables

+
+
var status : str
+
+
+
+
+

Methods

+
+
+def to_json(self) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
def to_json(self) -> Dict[str, Any]:
+    return {"status": self.status}
+
+
+
+
+
+class PasswordResetPostOkResult +(user_id: Union[str, None]) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class PasswordResetPostOkResult(APIResponse):
+    status: str = "OK"
+
+    def __init__(self, user_id: Union[str, None]):
+        self.user_id = user_id
+
+    def to_json(self) -> Dict[str, Any]:
+        return {"status": self.status}
+
+

Ancestors

+ +

Class variables

+
+
var status : str
+
+
+
+
+

Methods

+
+
+def to_json(self) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
def to_json(self) -> Dict[str, Any]:
+    return {"status": self.status}
+
+
+
+
+
+class RecipeInterface +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class RecipeInterface(ABC):
+    def __init__(self):
+        pass
+
+    @abstractmethod
+    async def get_user_by_id(
+        self, user_id: str, user_context: Dict[str, Any]
+    ) -> Union[User, None]:
+        pass
+
+    @abstractmethod
+    async def get_user_by_email(
+        self, email: str, tenant_id: str, user_context: Dict[str, Any]
+    ) -> Union[User, None]:
+        pass
+
+    @abstractmethod
+    async def create_reset_password_token(
+        self, user_id: str, tenant_id: str, user_context: Dict[str, Any]
+    ) -> Union[CreateResetPasswordOkResult, CreateResetPasswordWrongUserIdError]:
+        pass
+
+    @abstractmethod
+    async def reset_password_using_token(
+        self,
+        token: str,
+        new_password: str,
+        tenant_id: str,
+        user_context: Dict[str, Any],
+    ) -> Union[
+        ResetPasswordUsingTokenOkResult, ResetPasswordUsingTokenInvalidTokenError
+    ]:
+        pass
+
+    @abstractmethod
+    async def sign_in(
+        self, email: str, password: str, tenant_id: str, user_context: Dict[str, Any]
+    ) -> Union[SignInOkResult, SignInWrongCredentialsError]:
+        pass
+
+    @abstractmethod
+    async def sign_up(
+        self, email: str, password: str, tenant_id: str, user_context: Dict[str, Any]
+    ) -> Union[SignUpOkResult, SignUpEmailAlreadyExistsError]:
+        pass
+
+    @abstractmethod
+    async def update_email_or_password(
+        self,
+        user_id: str,
+        email: Union[str, None],
+        password: Union[str, None],
+        apply_password_policy: Union[bool, None],
+        tenant_id_for_password_policy: str,
+        user_context: Dict[str, Any],
+    ) -> Union[
+        UpdateEmailOrPasswordOkResult,
+        UpdateEmailOrPasswordEmailAlreadyExistsError,
+        UpdateEmailOrPasswordUnknownUserIdError,
+        UpdateEmailOrPasswordPasswordPolicyViolationError,
+    ]:
+        pass
+
+

Ancestors

+
    +
  • abc.ABC
  • +
+

Subclasses

+ +

Methods

+
+
+async def create_reset_password_token(self, user_id: str, tenant_id: str, user_context: Dict[str, Any]) ‑> Union[CreateResetPasswordOkResultCreateResetPasswordWrongUserIdError] +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def create_reset_password_token(
+    self, user_id: str, tenant_id: str, user_context: Dict[str, Any]
+) -> Union[CreateResetPasswordOkResult, CreateResetPasswordWrongUserIdError]:
+    pass
+
+
+
+async def get_user_by_email(self, email: str, tenant_id: str, user_context: Dict[str, Any]) ‑> Union[User, None] +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def get_user_by_email(
+    self, email: str, tenant_id: str, user_context: Dict[str, Any]
+) -> Union[User, None]:
+    pass
+
+
+
+async def get_user_by_id(self, user_id: str, user_context: Dict[str, Any]) ‑> Union[User, None] +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def get_user_by_id(
+    self, user_id: str, user_context: Dict[str, Any]
+) -> Union[User, None]:
+    pass
+
+
+
+async def reset_password_using_token(self, token: str, new_password: str, tenant_id: str, user_context: Dict[str, Any]) ‑> Union[ResetPasswordUsingTokenOkResultResetPasswordUsingTokenInvalidTokenError] +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def reset_password_using_token(
+    self,
+    token: str,
+    new_password: str,
+    tenant_id: str,
+    user_context: Dict[str, Any],
+) -> Union[
+    ResetPasswordUsingTokenOkResult, ResetPasswordUsingTokenInvalidTokenError
+]:
+    pass
+
+
+
+async def sign_in(self, email: str, password: str, tenant_id: str, user_context: Dict[str, Any]) ‑> Union[SignInOkResultSignInWrongCredentialsError] +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def sign_in(
+    self, email: str, password: str, tenant_id: str, user_context: Dict[str, Any]
+) -> Union[SignInOkResult, SignInWrongCredentialsError]:
+    pass
+
+
+
+async def sign_up(self, email: str, password: str, tenant_id: str, user_context: Dict[str, Any]) ‑> Union[SignUpOkResultSignUpEmailAlreadyExistsError] +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def sign_up(
+    self, email: str, password: str, tenant_id: str, user_context: Dict[str, Any]
+) -> Union[SignUpOkResult, SignUpEmailAlreadyExistsError]:
+    pass
+
+
+
+async def update_email_or_password(self, user_id: str, email: Union[str, None], password: Union[str, None], apply_password_policy: Union[bool, None], tenant_id_for_password_policy: str, user_context: Dict[str, Any]) ‑> Union[UpdateEmailOrPasswordOkResultUpdateEmailOrPasswordEmailAlreadyExistsErrorUpdateEmailOrPasswordUnknownUserIdErrorUpdateEmailOrPasswordPasswordPolicyViolationError] +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def update_email_or_password(
+    self,
+    user_id: str,
+    email: Union[str, None],
+    password: Union[str, None],
+    apply_password_policy: Union[bool, None],
+    tenant_id_for_password_policy: str,
+    user_context: Dict[str, Any],
+) -> Union[
+    UpdateEmailOrPasswordOkResult,
+    UpdateEmailOrPasswordEmailAlreadyExistsError,
+    UpdateEmailOrPasswordUnknownUserIdError,
+    UpdateEmailOrPasswordPasswordPolicyViolationError,
+]:
+    pass
+
+
+
+
+
+class ResetPasswordUsingTokenInvalidTokenError +
+
+
+
+ +Expand source code + +
class ResetPasswordUsingTokenInvalidTokenError:
+    pass
+
+
+
+class ResetPasswordUsingTokenOkResult +(user_id: Union[str, None]) +
+
+
+
+ +Expand source code + +
class ResetPasswordUsingTokenOkResult:
+    def __init__(self, user_id: Union[str, None]):
+        self.user_id = user_id
+
+
+
+class SendResetPasswordEmailOkResult +
+
+
+
+ +Expand source code + +
class SendResetPasswordEmailOkResult:
+    pass
+
+
+
+class SendResetPasswordEmailUnknownUserIdError +
+
+
+
+ +Expand source code + +
class SendResetPasswordEmailUnknownUserIdError:
+    pass
+
+
+
+class SignInOkResult +(user: User) +
+
+
+
+ +Expand source code + +
class SignInOkResult:
+    def __init__(self, user: User):
+        self.user = user
+
+
+
+class SignInPostOkResult +(user: User, session: SessionContainer) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class SignInPostOkResult(APIResponse):
+    status: str = "OK"
+
+    def __init__(self, user: User, session: SessionContainer):
+        self.user = user
+        self.session = session
+
+    def to_json(self) -> Dict[str, Any]:
+        return {
+            "status": self.status,
+            "user": {
+                "id": self.user.user_id,
+                "email": self.user.email,
+                "timeJoined": self.user.time_joined,
+            },
+        }
+
+

Ancestors

+ +

Class variables

+
+
var status : str
+
+
+
+
+

Methods

+
+
+def to_json(self) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
def to_json(self) -> Dict[str, Any]:
+    return {
+        "status": self.status,
+        "user": {
+            "id": self.user.user_id,
+            "email": self.user.email,
+            "timeJoined": self.user.time_joined,
+        },
+    }
+
+
+
+
+
+class SignInPostWrongCredentialsError +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class SignInPostWrongCredentialsError(APIResponse):
+    status: str = "WRONG_CREDENTIALS_ERROR"
+
+    def to_json(self) -> Dict[str, Any]:
+        return {"status": self.status}
+
+

Ancestors

+ +

Class variables

+
+
var status : str
+
+
+
+
+

Methods

+
+
+def to_json(self) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
def to_json(self) -> Dict[str, Any]:
+    return {"status": self.status}
+
+
+
+
+
+class SignInWrongCredentialsError +
+
+
+
+ +Expand source code + +
class SignInWrongCredentialsError:
+    pass
+
+
+
+class SignUpEmailAlreadyExistsError +
+
+
+
+ +Expand source code + +
class SignUpEmailAlreadyExistsError:
+    pass
+
+
+
+class SignUpOkResult +(user: User) +
+
+
+
+ +Expand source code + +
class SignUpOkResult:
+    def __init__(self, user: User):
+        self.user = user
+
+
+
+class SignUpPostEmailAlreadyExistsError +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class SignUpPostEmailAlreadyExistsError(APIResponse):
+    status: str = "EMAIL_ALREADY_EXISTS_ERROR"
+
+    def to_json(self) -> Dict[str, Any]:
+        return {"status": self.status}
+
+

Ancestors

+ +

Class variables

+
+
var status : str
+
+
+
+
+

Methods

+
+
+def to_json(self) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
def to_json(self) -> Dict[str, Any]:
+    return {"status": self.status}
+
+
+
+
+
+class SignUpPostOkResult +(user: User, session: SessionContainer) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class SignUpPostOkResult(APIResponse):
+    status: str = "OK"
+
+    def __init__(self, user: User, session: SessionContainer):
+        self.user = user
+        self.session = session
+
+    def to_json(self) -> Dict[str, Any]:
+        return {
+            "status": self.status,
+            "user": {
+                "id": self.user.user_id,
+                "email": self.user.email,
+                "timeJoined": self.user.time_joined,
+            },
+        }
+
+

Ancestors

+ +

Class variables

+
+
var status : str
+
+
+
+
+

Methods

+
+
+def to_json(self) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
def to_json(self) -> Dict[str, Any]:
+    return {
+        "status": self.status,
+        "user": {
+            "id": self.user.user_id,
+            "email": self.user.email,
+            "timeJoined": self.user.time_joined,
+        },
+    }
+
+
+
+
+
+class UpdateEmailOrPasswordEmailAlreadyExistsError +
+
+
+
+ +Expand source code + +
class UpdateEmailOrPasswordEmailAlreadyExistsError:
+    pass
+
+
+
+class UpdateEmailOrPasswordOkResult +
+
+
+
+ +Expand source code + +
class UpdateEmailOrPasswordOkResult:
+    pass
+
+
+
+class UpdateEmailOrPasswordPasswordPolicyViolationError +(failure_reason: str) +
+
+
+
+ +Expand source code + +
class UpdateEmailOrPasswordPasswordPolicyViolationError:
+    failure_reason: str
+
+    def __init__(self, failure_reason: str):
+        self.failure_reason = failure_reason
+
+

Class variables

+
+
var failure_reason : str
+
+
+
+
+
+
+class UpdateEmailOrPasswordUnknownUserIdError +
+
+
+
+ +Expand source code + +
class UpdateEmailOrPasswordUnknownUserIdError:
+    pass
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailpassword/recipe.html b/html/supertokens_python/recipe/emailpassword/recipe.html new file mode 100644 index 000000000..ad1b2ef1d --- /dev/null +++ b/html/supertokens_python/recipe/emailpassword/recipe.html @@ -0,0 +1,861 @@ + + + + + + +supertokens_python.recipe.emailpassword.recipe API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.emailpassword.recipe

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from os import environ
+from typing import TYPE_CHECKING, Any, Dict, List, Union
+
+from supertokens_python.ingredients.emaildelivery import EmailDeliveryIngredient
+from supertokens_python.ingredients.emaildelivery.types import EmailDeliveryConfig
+from supertokens_python.normalised_url_path import NormalisedURLPath
+from supertokens_python.recipe.emailpassword.types import (
+    EmailPasswordIngredients,
+    EmailTemplateVars,
+)
+from supertokens_python.recipe_module import APIHandled, RecipeModule
+from ..emailverification.interfaces import (
+    UnknownUserIdError,
+    GetEmailForUserIdOkResult,
+    EmailDoesNotExistError,
+)
+
+from .api.implementation import APIImplementation
+from .exceptions import FieldError, SuperTokensEmailPasswordError
+from .interfaces import APIOptions
+from .recipe_implementation import RecipeImplementation
+from ...post_init_callbacks import PostSTInitCallbacks
+
+if TYPE_CHECKING:
+    from supertokens_python.framework.request import BaseRequest
+    from supertokens_python.framework.response import BaseResponse
+    from supertokens_python.supertokens import AppInfo
+
+from supertokens_python.exceptions import SuperTokensError, raise_general_exception
+from supertokens_python.querier import Querier
+from supertokens_python.recipe.emailverification import EmailVerificationRecipe
+
+from .api import (
+    handle_email_exists_api,
+    handle_generate_password_reset_token_api,
+    handle_password_reset_api,
+    handle_sign_in_api,
+    handle_sign_up_api,
+)
+from .constants import (
+    SIGNIN,
+    SIGNUP,
+    SIGNUP_EMAIL_EXISTS,
+    USER_PASSWORD_RESET,
+    USER_PASSWORD_RESET_TOKEN,
+    SIGNUP_EMAIL_EXISTS_OLD,
+)
+from .utils import (
+    InputOverrideConfig,
+    InputSignUpFeature,
+    validate_and_normalise_user_input,
+    EmailPasswordConfig,
+)
+
+
+class EmailPasswordRecipe(RecipeModule):
+    recipe_id = "emailpassword"
+    __instance = None
+    email_delivery: EmailDeliveryIngredient[EmailTemplateVars]
+
+    def __init__(
+        self,
+        recipe_id: str,
+        app_info: AppInfo,
+        ingredients: EmailPasswordIngredients,
+        sign_up_feature: Union[InputSignUpFeature, None] = None,
+        override: Union[InputOverrideConfig, None] = None,
+        email_delivery: Union[EmailDeliveryConfig[EmailTemplateVars], None] = None,
+    ):
+        super().__init__(recipe_id, app_info)
+        self.config = validate_and_normalise_user_input(
+            app_info,
+            sign_up_feature,
+            override,
+            email_delivery,
+        )
+
+        def get_emailpassword_config() -> EmailPasswordConfig:
+            return self.config
+
+        recipe_implementation = RecipeImplementation(
+            Querier.get_instance(recipe_id), get_emailpassword_config
+        )
+        self.recipe_implementation = (
+            recipe_implementation
+            if self.config.override.functions is None
+            else self.config.override.functions(recipe_implementation)
+        )
+
+        email_delivery_ingredient = ingredients.email_delivery
+        if email_delivery_ingredient is None:
+            self.email_delivery = EmailDeliveryIngredient(
+                self.config.get_email_delivery_config(self.recipe_implementation)
+            )
+        else:
+            self.email_delivery = email_delivery_ingredient
+
+        api_implementation = APIImplementation()
+        self.api_implementation = (
+            api_implementation
+            if self.config.override.apis is None
+            else self.config.override.apis(api_implementation)
+        )
+
+        def callback():
+            ev_recipe = EmailVerificationRecipe.get_instance_optional()
+            if ev_recipe:
+                ev_recipe.add_get_email_for_user_id_func(self.get_email_for_user_id)
+
+        PostSTInitCallbacks.add_post_init_callback(callback)
+
+    def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
+        return isinstance(err, SuperTokensError) and (
+            isinstance(err, SuperTokensEmailPasswordError)
+        )
+
+    def get_apis_handled(self) -> List[APIHandled]:
+        return [
+            APIHandled(
+                NormalisedURLPath(SIGNUP),
+                "post",
+                SIGNUP,
+                self.api_implementation.disable_sign_up_post,
+            ),
+            APIHandled(
+                NormalisedURLPath(SIGNIN),
+                "post",
+                SIGNIN,
+                self.api_implementation.disable_sign_in_post,
+            ),
+            APIHandled(
+                NormalisedURLPath(USER_PASSWORD_RESET_TOKEN),
+                "post",
+                USER_PASSWORD_RESET_TOKEN,
+                self.api_implementation.disable_generate_password_reset_token_post,
+            ),
+            APIHandled(
+                NormalisedURLPath(USER_PASSWORD_RESET),
+                "post",
+                USER_PASSWORD_RESET,
+                self.api_implementation.disable_password_reset_post,
+            ),
+            APIHandled(
+                NormalisedURLPath(SIGNUP_EMAIL_EXISTS_OLD),
+                "get",
+                SIGNUP_EMAIL_EXISTS_OLD,
+                self.api_implementation.disable_email_exists_get,
+            ),
+            APIHandled(
+                NormalisedURLPath(SIGNUP_EMAIL_EXISTS),
+                "get",
+                SIGNUP_EMAIL_EXISTS,
+                self.api_implementation.disable_email_exists_get,
+            ),
+        ]
+
+    async def handle_api_request(
+        self,
+        request_id: str,
+        tenant_id: str,
+        request: BaseRequest,
+        path: NormalisedURLPath,
+        method: str,
+        response: BaseResponse,
+        user_context: Dict[str, Any],
+    ):
+        api_options = APIOptions(
+            request,
+            response,
+            self.recipe_id,
+            self.config,
+            self.recipe_implementation,
+            self.get_app_info(),
+            self.email_delivery,
+        )
+        if request_id == SIGNUP:
+            return await handle_sign_up_api(
+                tenant_id, self.api_implementation, api_options, user_context
+            )
+        if request_id == SIGNIN:
+            return await handle_sign_in_api(
+                tenant_id, self.api_implementation, api_options, user_context
+            )
+        if request_id in (SIGNUP_EMAIL_EXISTS, SIGNUP_EMAIL_EXISTS_OLD):
+            return await handle_email_exists_api(
+                tenant_id, self.api_implementation, api_options, user_context
+            )
+        if request_id == USER_PASSWORD_RESET_TOKEN:
+            return await handle_generate_password_reset_token_api(
+                tenant_id, self.api_implementation, api_options, user_context
+            )
+        if request_id == USER_PASSWORD_RESET:
+            return await handle_password_reset_api(
+                tenant_id, self.api_implementation, api_options, user_context
+            )
+
+        return None
+
+    async def handle_error(
+        self,
+        request: BaseRequest,
+        err: SuperTokensError,
+        response: BaseResponse,
+        user_context: Dict[str, Any],
+    ) -> BaseResponse:
+        if isinstance(err, SuperTokensEmailPasswordError):
+            if isinstance(err, FieldError):
+                response.set_json_content(
+                    {"status": "FIELD_ERROR", "formFields": err.get_json_form_fields()}
+                )
+                return response
+        raise err
+
+    def get_all_cors_headers(self) -> List[str]:
+        return []
+
+    @staticmethod
+    def init(
+        sign_up_feature: Union[InputSignUpFeature, None] = None,
+        override: Union[InputOverrideConfig, None] = None,
+        email_delivery: Union[EmailDeliveryConfig[EmailTemplateVars], None] = None,
+    ):
+        def func(app_info: AppInfo):
+            if EmailPasswordRecipe.__instance is None:
+                ingredients = EmailPasswordIngredients(None)
+                EmailPasswordRecipe.__instance = EmailPasswordRecipe(
+                    EmailPasswordRecipe.recipe_id,
+                    app_info,
+                    ingredients,
+                    sign_up_feature,
+                    override,
+                    email_delivery=email_delivery,
+                )
+                return EmailPasswordRecipe.__instance
+            raise Exception(
+                None,
+                "Emailpassword recipe has already been initialised. Please check your code for bugs.",
+            )
+
+        return func
+
+    @staticmethod
+    def get_instance() -> EmailPasswordRecipe:
+        if EmailPasswordRecipe.__instance is not None:
+            return EmailPasswordRecipe.__instance
+        raise_general_exception(
+            "Initialisation not done. Did you forget to call the SuperTokens.init function?"
+        )
+
+    @staticmethod
+    def reset():
+        if ("SUPERTOKENS_ENV" not in environ) or (
+            environ["SUPERTOKENS_ENV"] != "testing"
+        ):
+            raise_general_exception("calling testing function in non testing env")
+        EmailPasswordRecipe.__instance = None
+
+    # instance functions below...............
+
+    async def get_email_for_user_id(
+        self, user_id: str, user_context: Dict[str, Any]
+    ) -> Union[UnknownUserIdError, GetEmailForUserIdOkResult, EmailDoesNotExistError]:
+        user_info = await self.recipe_implementation.get_user_by_id(
+            user_id, user_context
+        )
+        if user_info is not None:
+            return GetEmailForUserIdOkResult(user_info.email)
+
+        return UnknownUserIdError()
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class EmailPasswordRecipe +(recipe_id: str, app_info: AppInfo, ingredients: EmailPasswordIngredients, sign_up_feature: Union[InputSignUpFeature, None] = None, override: Union[InputOverrideConfig, None] = None, email_delivery: Union[EmailDeliveryConfig[EmailTemplateVars], None] = None) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class EmailPasswordRecipe(RecipeModule):
+    recipe_id = "emailpassword"
+    __instance = None
+    email_delivery: EmailDeliveryIngredient[EmailTemplateVars]
+
+    def __init__(
+        self,
+        recipe_id: str,
+        app_info: AppInfo,
+        ingredients: EmailPasswordIngredients,
+        sign_up_feature: Union[InputSignUpFeature, None] = None,
+        override: Union[InputOverrideConfig, None] = None,
+        email_delivery: Union[EmailDeliveryConfig[EmailTemplateVars], None] = None,
+    ):
+        super().__init__(recipe_id, app_info)
+        self.config = validate_and_normalise_user_input(
+            app_info,
+            sign_up_feature,
+            override,
+            email_delivery,
+        )
+
+        def get_emailpassword_config() -> EmailPasswordConfig:
+            return self.config
+
+        recipe_implementation = RecipeImplementation(
+            Querier.get_instance(recipe_id), get_emailpassword_config
+        )
+        self.recipe_implementation = (
+            recipe_implementation
+            if self.config.override.functions is None
+            else self.config.override.functions(recipe_implementation)
+        )
+
+        email_delivery_ingredient = ingredients.email_delivery
+        if email_delivery_ingredient is None:
+            self.email_delivery = EmailDeliveryIngredient(
+                self.config.get_email_delivery_config(self.recipe_implementation)
+            )
+        else:
+            self.email_delivery = email_delivery_ingredient
+
+        api_implementation = APIImplementation()
+        self.api_implementation = (
+            api_implementation
+            if self.config.override.apis is None
+            else self.config.override.apis(api_implementation)
+        )
+
+        def callback():
+            ev_recipe = EmailVerificationRecipe.get_instance_optional()
+            if ev_recipe:
+                ev_recipe.add_get_email_for_user_id_func(self.get_email_for_user_id)
+
+        PostSTInitCallbacks.add_post_init_callback(callback)
+
+    def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
+        return isinstance(err, SuperTokensError) and (
+            isinstance(err, SuperTokensEmailPasswordError)
+        )
+
+    def get_apis_handled(self) -> List[APIHandled]:
+        return [
+            APIHandled(
+                NormalisedURLPath(SIGNUP),
+                "post",
+                SIGNUP,
+                self.api_implementation.disable_sign_up_post,
+            ),
+            APIHandled(
+                NormalisedURLPath(SIGNIN),
+                "post",
+                SIGNIN,
+                self.api_implementation.disable_sign_in_post,
+            ),
+            APIHandled(
+                NormalisedURLPath(USER_PASSWORD_RESET_TOKEN),
+                "post",
+                USER_PASSWORD_RESET_TOKEN,
+                self.api_implementation.disable_generate_password_reset_token_post,
+            ),
+            APIHandled(
+                NormalisedURLPath(USER_PASSWORD_RESET),
+                "post",
+                USER_PASSWORD_RESET,
+                self.api_implementation.disable_password_reset_post,
+            ),
+            APIHandled(
+                NormalisedURLPath(SIGNUP_EMAIL_EXISTS_OLD),
+                "get",
+                SIGNUP_EMAIL_EXISTS_OLD,
+                self.api_implementation.disable_email_exists_get,
+            ),
+            APIHandled(
+                NormalisedURLPath(SIGNUP_EMAIL_EXISTS),
+                "get",
+                SIGNUP_EMAIL_EXISTS,
+                self.api_implementation.disable_email_exists_get,
+            ),
+        ]
+
+    async def handle_api_request(
+        self,
+        request_id: str,
+        tenant_id: str,
+        request: BaseRequest,
+        path: NormalisedURLPath,
+        method: str,
+        response: BaseResponse,
+        user_context: Dict[str, Any],
+    ):
+        api_options = APIOptions(
+            request,
+            response,
+            self.recipe_id,
+            self.config,
+            self.recipe_implementation,
+            self.get_app_info(),
+            self.email_delivery,
+        )
+        if request_id == SIGNUP:
+            return await handle_sign_up_api(
+                tenant_id, self.api_implementation, api_options, user_context
+            )
+        if request_id == SIGNIN:
+            return await handle_sign_in_api(
+                tenant_id, self.api_implementation, api_options, user_context
+            )
+        if request_id in (SIGNUP_EMAIL_EXISTS, SIGNUP_EMAIL_EXISTS_OLD):
+            return await handle_email_exists_api(
+                tenant_id, self.api_implementation, api_options, user_context
+            )
+        if request_id == USER_PASSWORD_RESET_TOKEN:
+            return await handle_generate_password_reset_token_api(
+                tenant_id, self.api_implementation, api_options, user_context
+            )
+        if request_id == USER_PASSWORD_RESET:
+            return await handle_password_reset_api(
+                tenant_id, self.api_implementation, api_options, user_context
+            )
+
+        return None
+
+    async def handle_error(
+        self,
+        request: BaseRequest,
+        err: SuperTokensError,
+        response: BaseResponse,
+        user_context: Dict[str, Any],
+    ) -> BaseResponse:
+        if isinstance(err, SuperTokensEmailPasswordError):
+            if isinstance(err, FieldError):
+                response.set_json_content(
+                    {"status": "FIELD_ERROR", "formFields": err.get_json_form_fields()}
+                )
+                return response
+        raise err
+
+    def get_all_cors_headers(self) -> List[str]:
+        return []
+
+    @staticmethod
+    def init(
+        sign_up_feature: Union[InputSignUpFeature, None] = None,
+        override: Union[InputOverrideConfig, None] = None,
+        email_delivery: Union[EmailDeliveryConfig[EmailTemplateVars], None] = None,
+    ):
+        def func(app_info: AppInfo):
+            if EmailPasswordRecipe.__instance is None:
+                ingredients = EmailPasswordIngredients(None)
+                EmailPasswordRecipe.__instance = EmailPasswordRecipe(
+                    EmailPasswordRecipe.recipe_id,
+                    app_info,
+                    ingredients,
+                    sign_up_feature,
+                    override,
+                    email_delivery=email_delivery,
+                )
+                return EmailPasswordRecipe.__instance
+            raise Exception(
+                None,
+                "Emailpassword recipe has already been initialised. Please check your code for bugs.",
+            )
+
+        return func
+
+    @staticmethod
+    def get_instance() -> EmailPasswordRecipe:
+        if EmailPasswordRecipe.__instance is not None:
+            return EmailPasswordRecipe.__instance
+        raise_general_exception(
+            "Initialisation not done. Did you forget to call the SuperTokens.init function?"
+        )
+
+    @staticmethod
+    def reset():
+        if ("SUPERTOKENS_ENV" not in environ) or (
+            environ["SUPERTOKENS_ENV"] != "testing"
+        ):
+            raise_general_exception("calling testing function in non testing env")
+        EmailPasswordRecipe.__instance = None
+
+    # instance functions below...............
+
+    async def get_email_for_user_id(
+        self, user_id: str, user_context: Dict[str, Any]
+    ) -> Union[UnknownUserIdError, GetEmailForUserIdOkResult, EmailDoesNotExistError]:
+        user_info = await self.recipe_implementation.get_user_by_id(
+            user_id, user_context
+        )
+        if user_info is not None:
+            return GetEmailForUserIdOkResult(user_info.email)
+
+        return UnknownUserIdError()
+
+

Ancestors

+ +

Class variables

+
+
var email_deliveryEmailDeliveryIngredient[PasswordResetEmailTemplateVars]
+
+
+
+
var recipe_id
+
+
+
+
+

Static methods

+
+
+def get_instance() ‑> EmailPasswordRecipe +
+
+
+
+ +Expand source code + +
@staticmethod
+def get_instance() -> EmailPasswordRecipe:
+    if EmailPasswordRecipe.__instance is not None:
+        return EmailPasswordRecipe.__instance
+    raise_general_exception(
+        "Initialisation not done. Did you forget to call the SuperTokens.init function?"
+    )
+
+
+
+def init(sign_up_feature: Union[InputSignUpFeature, None] = None, override: Union[InputOverrideConfig, None] = None, email_delivery: Union[EmailDeliveryConfig[EmailTemplateVars], None] = None) +
+
+
+
+ +Expand source code + +
@staticmethod
+def init(
+    sign_up_feature: Union[InputSignUpFeature, None] = None,
+    override: Union[InputOverrideConfig, None] = None,
+    email_delivery: Union[EmailDeliveryConfig[EmailTemplateVars], None] = None,
+):
+    def func(app_info: AppInfo):
+        if EmailPasswordRecipe.__instance is None:
+            ingredients = EmailPasswordIngredients(None)
+            EmailPasswordRecipe.__instance = EmailPasswordRecipe(
+                EmailPasswordRecipe.recipe_id,
+                app_info,
+                ingredients,
+                sign_up_feature,
+                override,
+                email_delivery=email_delivery,
+            )
+            return EmailPasswordRecipe.__instance
+        raise Exception(
+            None,
+            "Emailpassword recipe has already been initialised. Please check your code for bugs.",
+        )
+
+    return func
+
+
+
+def reset() +
+
+
+
+ +Expand source code + +
@staticmethod
+def reset():
+    if ("SUPERTOKENS_ENV" not in environ) or (
+        environ["SUPERTOKENS_ENV"] != "testing"
+    ):
+        raise_general_exception("calling testing function in non testing env")
+    EmailPasswordRecipe.__instance = None
+
+
+
+

Methods

+
+
+def get_all_cors_headers(self) ‑> List[str] +
+
+
+
+ +Expand source code + +
def get_all_cors_headers(self) -> List[str]:
+    return []
+
+
+
+def get_apis_handled(self) ‑> List[APIHandled] +
+
+
+
+ +Expand source code + +
def get_apis_handled(self) -> List[APIHandled]:
+    return [
+        APIHandled(
+            NormalisedURLPath(SIGNUP),
+            "post",
+            SIGNUP,
+            self.api_implementation.disable_sign_up_post,
+        ),
+        APIHandled(
+            NormalisedURLPath(SIGNIN),
+            "post",
+            SIGNIN,
+            self.api_implementation.disable_sign_in_post,
+        ),
+        APIHandled(
+            NormalisedURLPath(USER_PASSWORD_RESET_TOKEN),
+            "post",
+            USER_PASSWORD_RESET_TOKEN,
+            self.api_implementation.disable_generate_password_reset_token_post,
+        ),
+        APIHandled(
+            NormalisedURLPath(USER_PASSWORD_RESET),
+            "post",
+            USER_PASSWORD_RESET,
+            self.api_implementation.disable_password_reset_post,
+        ),
+        APIHandled(
+            NormalisedURLPath(SIGNUP_EMAIL_EXISTS_OLD),
+            "get",
+            SIGNUP_EMAIL_EXISTS_OLD,
+            self.api_implementation.disable_email_exists_get,
+        ),
+        APIHandled(
+            NormalisedURLPath(SIGNUP_EMAIL_EXISTS),
+            "get",
+            SIGNUP_EMAIL_EXISTS,
+            self.api_implementation.disable_email_exists_get,
+        ),
+    ]
+
+
+
+async def get_email_for_user_id(self, user_id: str, user_context: Dict[str, Any]) ‑> Union[UnknownUserIdErrorGetEmailForUserIdOkResultEmailDoesNotExistError] +
+
+
+
+ +Expand source code + +
async def get_email_for_user_id(
+    self, user_id: str, user_context: Dict[str, Any]
+) -> Union[UnknownUserIdError, GetEmailForUserIdOkResult, EmailDoesNotExistError]:
+    user_info = await self.recipe_implementation.get_user_by_id(
+        user_id, user_context
+    )
+    if user_info is not None:
+        return GetEmailForUserIdOkResult(user_info.email)
+
+    return UnknownUserIdError()
+
+
+
+async def handle_api_request(self, request_id: str, tenant_id: str, request: BaseRequest, path: NormalisedURLPath, method: str, response: BaseResponse, user_context: Dict[str, Any]) +
+
+
+
+ +Expand source code + +
async def handle_api_request(
+    self,
+    request_id: str,
+    tenant_id: str,
+    request: BaseRequest,
+    path: NormalisedURLPath,
+    method: str,
+    response: BaseResponse,
+    user_context: Dict[str, Any],
+):
+    api_options = APIOptions(
+        request,
+        response,
+        self.recipe_id,
+        self.config,
+        self.recipe_implementation,
+        self.get_app_info(),
+        self.email_delivery,
+    )
+    if request_id == SIGNUP:
+        return await handle_sign_up_api(
+            tenant_id, self.api_implementation, api_options, user_context
+        )
+    if request_id == SIGNIN:
+        return await handle_sign_in_api(
+            tenant_id, self.api_implementation, api_options, user_context
+        )
+    if request_id in (SIGNUP_EMAIL_EXISTS, SIGNUP_EMAIL_EXISTS_OLD):
+        return await handle_email_exists_api(
+            tenant_id, self.api_implementation, api_options, user_context
+        )
+    if request_id == USER_PASSWORD_RESET_TOKEN:
+        return await handle_generate_password_reset_token_api(
+            tenant_id, self.api_implementation, api_options, user_context
+        )
+    if request_id == USER_PASSWORD_RESET:
+        return await handle_password_reset_api(
+            tenant_id, self.api_implementation, api_options, user_context
+        )
+
+    return None
+
+
+
+async def handle_error(self, request: BaseRequest, err: SuperTokensError, response: BaseResponse, user_context: Dict[str, Any]) ‑> BaseResponse +
+
+
+
+ +Expand source code + +
async def handle_error(
+    self,
+    request: BaseRequest,
+    err: SuperTokensError,
+    response: BaseResponse,
+    user_context: Dict[str, Any],
+) -> BaseResponse:
+    if isinstance(err, SuperTokensEmailPasswordError):
+        if isinstance(err, FieldError):
+            response.set_json_content(
+                {"status": "FIELD_ERROR", "formFields": err.get_json_form_fields()}
+            )
+            return response
+    raise err
+
+
+
+def is_error_from_this_recipe_based_on_instance(self, err: Exception) ‑> bool +
+
+
+
+ +Expand source code + +
def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
+    return isinstance(err, SuperTokensError) and (
+        isinstance(err, SuperTokensEmailPasswordError)
+    )
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailpassword/recipe_implementation.html b/html/supertokens_python/recipe/emailpassword/recipe_implementation.html new file mode 100644 index 000000000..07603572a --- /dev/null +++ b/html/supertokens_python/recipe/emailpassword/recipe_implementation.html @@ -0,0 +1,675 @@ + + + + + + +supertokens_python.recipe.emailpassword.recipe_implementation API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.emailpassword.recipe_implementation

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Any, Dict, Union, Callable
+
+from supertokens_python.normalised_url_path import NormalisedURLPath
+
+from .interfaces import (
+    CreateResetPasswordOkResult,
+    CreateResetPasswordWrongUserIdError,
+    RecipeInterface,
+    ResetPasswordUsingTokenOkResult,
+    ResetPasswordUsingTokenInvalidTokenError,
+    SignInOkResult,
+    SignInWrongCredentialsError,
+    SignUpEmailAlreadyExistsError,
+    SignUpOkResult,
+    UpdateEmailOrPasswordEmailAlreadyExistsError,
+    UpdateEmailOrPasswordOkResult,
+    UpdateEmailOrPasswordUnknownUserIdError,
+    UpdateEmailOrPasswordPasswordPolicyViolationError,
+)
+from .types import User
+from .utils import EmailPasswordConfig
+from .constants import FORM_FIELD_PASSWORD_ID
+
+if TYPE_CHECKING:
+    from supertokens_python.querier import Querier
+
+
+class RecipeImplementation(RecipeInterface):
+    def __init__(
+        self,
+        querier: Querier,
+        get_emailpassword_config: Callable[[], EmailPasswordConfig],
+    ):
+        super().__init__()
+        self.querier = querier
+        self.get_emailpassword_config = get_emailpassword_config
+
+    async def get_user_by_id(
+        self, user_id: str, user_context: Dict[str, Any]
+    ) -> Union[User, None]:
+        params = {"userId": user_id}
+        response = await self.querier.send_get_request(
+            NormalisedURLPath("/recipe/user"), params, user_context
+        )
+        if "status" in response and response["status"] == "OK":
+            return User(
+                response["user"]["id"],
+                response["user"]["email"],
+                response["user"]["timeJoined"],
+                response["user"]["tenantIds"],
+            )
+        return None
+
+    async def get_user_by_email(
+        self, email: str, tenant_id: str, user_context: Dict[str, Any]
+    ) -> Union[User, None]:
+        params = {"email": email}
+        response = await self.querier.send_get_request(
+            NormalisedURLPath(f"{tenant_id}/recipe/user"), params, user_context
+        )
+        if "status" in response and response["status"] == "OK":
+            return User(
+                response["user"]["id"],
+                response["user"]["email"],
+                response["user"]["timeJoined"],
+                response["user"]["tenantIds"],
+            )
+        return None
+
+    async def create_reset_password_token(
+        self, user_id: str, tenant_id: str, user_context: Dict[str, Any]
+    ) -> Union[CreateResetPasswordOkResult, CreateResetPasswordWrongUserIdError]:
+        data = {"userId": user_id}
+        response = await self.querier.send_post_request(
+            NormalisedURLPath(f"{tenant_id}/recipe/user/password/reset/token"),
+            data,
+            user_context=user_context,
+        )
+        if "status" in response and response["status"] == "OK":
+            return CreateResetPasswordOkResult(response["token"])
+        return CreateResetPasswordWrongUserIdError()
+
+    async def reset_password_using_token(
+        self,
+        token: str,
+        new_password: str,
+        tenant_id: str,
+        user_context: Dict[str, Any],
+    ) -> Union[
+        ResetPasswordUsingTokenOkResult, ResetPasswordUsingTokenInvalidTokenError
+    ]:
+        data = {"method": "token", "token": token, "newPassword": new_password}
+        response = await self.querier.send_post_request(
+            NormalisedURLPath(f"{tenant_id}/recipe/user/password/reset"),
+            data,
+            user_context=user_context,
+        )
+        if "status" not in response or response["status"] != "OK":
+            return ResetPasswordUsingTokenInvalidTokenError()
+        user_id = None
+        if "userId" in response:
+            user_id = response["userId"]
+        return ResetPasswordUsingTokenOkResult(user_id)
+
+    async def sign_in(
+        self, email: str, password: str, tenant_id: str, user_context: Dict[str, Any]
+    ) -> Union[SignInOkResult, SignInWrongCredentialsError]:
+        data = {"password": password, "email": email}
+        response = await self.querier.send_post_request(
+            NormalisedURLPath(f"{tenant_id}/recipe/signin"),
+            data,
+            user_context=user_context,
+        )
+        if "status" in response and response["status"] == "OK":
+            return SignInOkResult(
+                User(
+                    response["user"]["id"],
+                    response["user"]["email"],
+                    response["user"]["timeJoined"],
+                    response["user"]["tenantIds"],
+                )
+            )
+        return SignInWrongCredentialsError()
+
+    async def sign_up(
+        self, email: str, password: str, tenant_id: str, user_context: Dict[str, Any]
+    ) -> Union[SignUpOkResult, SignUpEmailAlreadyExistsError]:
+        data = {"password": password, "email": email}
+        response = await self.querier.send_post_request(
+            NormalisedURLPath(f"{tenant_id}/recipe/signup"),
+            data,
+            user_context=user_context,
+        )
+        if "status" in response and response["status"] == "OK":
+            return SignUpOkResult(
+                User(
+                    response["user"]["id"],
+                    response["user"]["email"],
+                    response["user"]["timeJoined"],
+                    response["user"]["tenantIds"],
+                )
+            )
+        return SignUpEmailAlreadyExistsError()
+
+    async def update_email_or_password(
+        self,
+        user_id: str,
+        email: Union[str, None],
+        password: Union[str, None],
+        apply_password_policy: Union[bool, None],
+        tenant_id_for_password_policy: str,
+        user_context: Dict[str, Any],
+    ) -> Union[
+        UpdateEmailOrPasswordOkResult,
+        UpdateEmailOrPasswordEmailAlreadyExistsError,
+        UpdateEmailOrPasswordUnknownUserIdError,
+        UpdateEmailOrPasswordPasswordPolicyViolationError,
+    ]:
+        data = {"userId": user_id}
+        if email is not None:
+            data = {"email": email, **data}
+        if password is not None:
+            if apply_password_policy is None or apply_password_policy:
+                form_fields = (
+                    self.get_emailpassword_config().sign_up_feature.form_fields
+                )
+                password_field = list(
+                    filter(lambda x: x.id == FORM_FIELD_PASSWORD_ID, form_fields)
+                )[0]
+                error = await password_field.validate(
+                    password, tenant_id_for_password_policy
+                )
+                if error is not None:
+                    return UpdateEmailOrPasswordPasswordPolicyViolationError(error)
+            data = {"password": password, **data}
+        response = await self.querier.send_put_request(
+            NormalisedURLPath("/recipe/user"),
+            data,
+            user_context=user_context,
+        )
+        if "status" in response and response["status"] == "OK":
+            return UpdateEmailOrPasswordOkResult()
+        if "status" in response and response["status"] == "EMAIL_ALREADY_EXISTS_ERROR":
+            return UpdateEmailOrPasswordEmailAlreadyExistsError()
+        return UpdateEmailOrPasswordUnknownUserIdError()
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class RecipeImplementation +(querier: Querier, get_emailpassword_config: Callable[[], EmailPasswordConfig]) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class RecipeImplementation(RecipeInterface):
+    def __init__(
+        self,
+        querier: Querier,
+        get_emailpassword_config: Callable[[], EmailPasswordConfig],
+    ):
+        super().__init__()
+        self.querier = querier
+        self.get_emailpassword_config = get_emailpassword_config
+
+    async def get_user_by_id(
+        self, user_id: str, user_context: Dict[str, Any]
+    ) -> Union[User, None]:
+        params = {"userId": user_id}
+        response = await self.querier.send_get_request(
+            NormalisedURLPath("/recipe/user"), params, user_context
+        )
+        if "status" in response and response["status"] == "OK":
+            return User(
+                response["user"]["id"],
+                response["user"]["email"],
+                response["user"]["timeJoined"],
+                response["user"]["tenantIds"],
+            )
+        return None
+
+    async def get_user_by_email(
+        self, email: str, tenant_id: str, user_context: Dict[str, Any]
+    ) -> Union[User, None]:
+        params = {"email": email}
+        response = await self.querier.send_get_request(
+            NormalisedURLPath(f"{tenant_id}/recipe/user"), params, user_context
+        )
+        if "status" in response and response["status"] == "OK":
+            return User(
+                response["user"]["id"],
+                response["user"]["email"],
+                response["user"]["timeJoined"],
+                response["user"]["tenantIds"],
+            )
+        return None
+
+    async def create_reset_password_token(
+        self, user_id: str, tenant_id: str, user_context: Dict[str, Any]
+    ) -> Union[CreateResetPasswordOkResult, CreateResetPasswordWrongUserIdError]:
+        data = {"userId": user_id}
+        response = await self.querier.send_post_request(
+            NormalisedURLPath(f"{tenant_id}/recipe/user/password/reset/token"),
+            data,
+            user_context=user_context,
+        )
+        if "status" in response and response["status"] == "OK":
+            return CreateResetPasswordOkResult(response["token"])
+        return CreateResetPasswordWrongUserIdError()
+
+    async def reset_password_using_token(
+        self,
+        token: str,
+        new_password: str,
+        tenant_id: str,
+        user_context: Dict[str, Any],
+    ) -> Union[
+        ResetPasswordUsingTokenOkResult, ResetPasswordUsingTokenInvalidTokenError
+    ]:
+        data = {"method": "token", "token": token, "newPassword": new_password}
+        response = await self.querier.send_post_request(
+            NormalisedURLPath(f"{tenant_id}/recipe/user/password/reset"),
+            data,
+            user_context=user_context,
+        )
+        if "status" not in response or response["status"] != "OK":
+            return ResetPasswordUsingTokenInvalidTokenError()
+        user_id = None
+        if "userId" in response:
+            user_id = response["userId"]
+        return ResetPasswordUsingTokenOkResult(user_id)
+
+    async def sign_in(
+        self, email: str, password: str, tenant_id: str, user_context: Dict[str, Any]
+    ) -> Union[SignInOkResult, SignInWrongCredentialsError]:
+        data = {"password": password, "email": email}
+        response = await self.querier.send_post_request(
+            NormalisedURLPath(f"{tenant_id}/recipe/signin"),
+            data,
+            user_context=user_context,
+        )
+        if "status" in response and response["status"] == "OK":
+            return SignInOkResult(
+                User(
+                    response["user"]["id"],
+                    response["user"]["email"],
+                    response["user"]["timeJoined"],
+                    response["user"]["tenantIds"],
+                )
+            )
+        return SignInWrongCredentialsError()
+
+    async def sign_up(
+        self, email: str, password: str, tenant_id: str, user_context: Dict[str, Any]
+    ) -> Union[SignUpOkResult, SignUpEmailAlreadyExistsError]:
+        data = {"password": password, "email": email}
+        response = await self.querier.send_post_request(
+            NormalisedURLPath(f"{tenant_id}/recipe/signup"),
+            data,
+            user_context=user_context,
+        )
+        if "status" in response and response["status"] == "OK":
+            return SignUpOkResult(
+                User(
+                    response["user"]["id"],
+                    response["user"]["email"],
+                    response["user"]["timeJoined"],
+                    response["user"]["tenantIds"],
+                )
+            )
+        return SignUpEmailAlreadyExistsError()
+
+    async def update_email_or_password(
+        self,
+        user_id: str,
+        email: Union[str, None],
+        password: Union[str, None],
+        apply_password_policy: Union[bool, None],
+        tenant_id_for_password_policy: str,
+        user_context: Dict[str, Any],
+    ) -> Union[
+        UpdateEmailOrPasswordOkResult,
+        UpdateEmailOrPasswordEmailAlreadyExistsError,
+        UpdateEmailOrPasswordUnknownUserIdError,
+        UpdateEmailOrPasswordPasswordPolicyViolationError,
+    ]:
+        data = {"userId": user_id}
+        if email is not None:
+            data = {"email": email, **data}
+        if password is not None:
+            if apply_password_policy is None or apply_password_policy:
+                form_fields = (
+                    self.get_emailpassword_config().sign_up_feature.form_fields
+                )
+                password_field = list(
+                    filter(lambda x: x.id == FORM_FIELD_PASSWORD_ID, form_fields)
+                )[0]
+                error = await password_field.validate(
+                    password, tenant_id_for_password_policy
+                )
+                if error is not None:
+                    return UpdateEmailOrPasswordPasswordPolicyViolationError(error)
+            data = {"password": password, **data}
+        response = await self.querier.send_put_request(
+            NormalisedURLPath("/recipe/user"),
+            data,
+            user_context=user_context,
+        )
+        if "status" in response and response["status"] == "OK":
+            return UpdateEmailOrPasswordOkResult()
+        if "status" in response and response["status"] == "EMAIL_ALREADY_EXISTS_ERROR":
+            return UpdateEmailOrPasswordEmailAlreadyExistsError()
+        return UpdateEmailOrPasswordUnknownUserIdError()
+
+

Ancestors

+ +

Methods

+
+
+async def create_reset_password_token(self, user_id: str, tenant_id: str, user_context: Dict[str, Any]) ‑> Union[CreateResetPasswordOkResultCreateResetPasswordWrongUserIdError] +
+
+
+
+ +Expand source code + +
async def create_reset_password_token(
+    self, user_id: str, tenant_id: str, user_context: Dict[str, Any]
+) -> Union[CreateResetPasswordOkResult, CreateResetPasswordWrongUserIdError]:
+    data = {"userId": user_id}
+    response = await self.querier.send_post_request(
+        NormalisedURLPath(f"{tenant_id}/recipe/user/password/reset/token"),
+        data,
+        user_context=user_context,
+    )
+    if "status" in response and response["status"] == "OK":
+        return CreateResetPasswordOkResult(response["token"])
+    return CreateResetPasswordWrongUserIdError()
+
+
+
+async def get_user_by_email(self, email: str, tenant_id: str, user_context: Dict[str, Any]) ‑> Optional[User] +
+
+
+
+ +Expand source code + +
async def get_user_by_email(
+    self, email: str, tenant_id: str, user_context: Dict[str, Any]
+) -> Union[User, None]:
+    params = {"email": email}
+    response = await self.querier.send_get_request(
+        NormalisedURLPath(f"{tenant_id}/recipe/user"), params, user_context
+    )
+    if "status" in response and response["status"] == "OK":
+        return User(
+            response["user"]["id"],
+            response["user"]["email"],
+            response["user"]["timeJoined"],
+            response["user"]["tenantIds"],
+        )
+    return None
+
+
+
+async def get_user_by_id(self, user_id: str, user_context: Dict[str, Any]) ‑> Optional[User] +
+
+
+
+ +Expand source code + +
async def get_user_by_id(
+    self, user_id: str, user_context: Dict[str, Any]
+) -> Union[User, None]:
+    params = {"userId": user_id}
+    response = await self.querier.send_get_request(
+        NormalisedURLPath("/recipe/user"), params, user_context
+    )
+    if "status" in response and response["status"] == "OK":
+        return User(
+            response["user"]["id"],
+            response["user"]["email"],
+            response["user"]["timeJoined"],
+            response["user"]["tenantIds"],
+        )
+    return None
+
+
+
+async def reset_password_using_token(self, token: str, new_password: str, tenant_id: str, user_context: Dict[str, Any]) ‑> Union[ResetPasswordUsingTokenOkResultResetPasswordUsingTokenInvalidTokenError] +
+
+
+
+ +Expand source code + +
async def reset_password_using_token(
+    self,
+    token: str,
+    new_password: str,
+    tenant_id: str,
+    user_context: Dict[str, Any],
+) -> Union[
+    ResetPasswordUsingTokenOkResult, ResetPasswordUsingTokenInvalidTokenError
+]:
+    data = {"method": "token", "token": token, "newPassword": new_password}
+    response = await self.querier.send_post_request(
+        NormalisedURLPath(f"{tenant_id}/recipe/user/password/reset"),
+        data,
+        user_context=user_context,
+    )
+    if "status" not in response or response["status"] != "OK":
+        return ResetPasswordUsingTokenInvalidTokenError()
+    user_id = None
+    if "userId" in response:
+        user_id = response["userId"]
+    return ResetPasswordUsingTokenOkResult(user_id)
+
+
+
+async def sign_in(self, email: str, password: str, tenant_id: str, user_context: Dict[str, Any]) ‑> Union[SignInOkResultSignInWrongCredentialsError] +
+
+
+
+ +Expand source code + +
async def sign_in(
+    self, email: str, password: str, tenant_id: str, user_context: Dict[str, Any]
+) -> Union[SignInOkResult, SignInWrongCredentialsError]:
+    data = {"password": password, "email": email}
+    response = await self.querier.send_post_request(
+        NormalisedURLPath(f"{tenant_id}/recipe/signin"),
+        data,
+        user_context=user_context,
+    )
+    if "status" in response and response["status"] == "OK":
+        return SignInOkResult(
+            User(
+                response["user"]["id"],
+                response["user"]["email"],
+                response["user"]["timeJoined"],
+                response["user"]["tenantIds"],
+            )
+        )
+    return SignInWrongCredentialsError()
+
+
+
+async def sign_up(self, email: str, password: str, tenant_id: str, user_context: Dict[str, Any]) ‑> Union[SignUpOkResultSignUpEmailAlreadyExistsError] +
+
+
+
+ +Expand source code + +
async def sign_up(
+    self, email: str, password: str, tenant_id: str, user_context: Dict[str, Any]
+) -> Union[SignUpOkResult, SignUpEmailAlreadyExistsError]:
+    data = {"password": password, "email": email}
+    response = await self.querier.send_post_request(
+        NormalisedURLPath(f"{tenant_id}/recipe/signup"),
+        data,
+        user_context=user_context,
+    )
+    if "status" in response and response["status"] == "OK":
+        return SignUpOkResult(
+            User(
+                response["user"]["id"],
+                response["user"]["email"],
+                response["user"]["timeJoined"],
+                response["user"]["tenantIds"],
+            )
+        )
+    return SignUpEmailAlreadyExistsError()
+
+
+
+async def update_email_or_password(self, user_id: str, email: Union[str, None], password: Union[str, None], apply_password_policy: Union[bool, None], tenant_id_for_password_policy: str, user_context: Dict[str, Any]) ‑> Union[UpdateEmailOrPasswordOkResultUpdateEmailOrPasswordEmailAlreadyExistsErrorUpdateEmailOrPasswordUnknownUserIdErrorUpdateEmailOrPasswordPasswordPolicyViolationError] +
+
+
+
+ +Expand source code + +
async def update_email_or_password(
+    self,
+    user_id: str,
+    email: Union[str, None],
+    password: Union[str, None],
+    apply_password_policy: Union[bool, None],
+    tenant_id_for_password_policy: str,
+    user_context: Dict[str, Any],
+) -> Union[
+    UpdateEmailOrPasswordOkResult,
+    UpdateEmailOrPasswordEmailAlreadyExistsError,
+    UpdateEmailOrPasswordUnknownUserIdError,
+    UpdateEmailOrPasswordPasswordPolicyViolationError,
+]:
+    data = {"userId": user_id}
+    if email is not None:
+        data = {"email": email, **data}
+    if password is not None:
+        if apply_password_policy is None or apply_password_policy:
+            form_fields = (
+                self.get_emailpassword_config().sign_up_feature.form_fields
+            )
+            password_field = list(
+                filter(lambda x: x.id == FORM_FIELD_PASSWORD_ID, form_fields)
+            )[0]
+            error = await password_field.validate(
+                password, tenant_id_for_password_policy
+            )
+            if error is not None:
+                return UpdateEmailOrPasswordPasswordPolicyViolationError(error)
+        data = {"password": password, **data}
+    response = await self.querier.send_put_request(
+        NormalisedURLPath("/recipe/user"),
+        data,
+        user_context=user_context,
+    )
+    if "status" in response and response["status"] == "OK":
+        return UpdateEmailOrPasswordOkResult()
+    if "status" in response and response["status"] == "EMAIL_ALREADY_EXISTS_ERROR":
+        return UpdateEmailOrPasswordEmailAlreadyExistsError()
+    return UpdateEmailOrPasswordUnknownUserIdError()
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailpassword/syncio/index.html b/html/supertokens_python/recipe/emailpassword/syncio/index.html new file mode 100644 index 000000000..e46bdf972 --- /dev/null +++ b/html/supertokens_python/recipe/emailpassword/syncio/index.html @@ -0,0 +1,428 @@ + + + + + + +supertokens_python.recipe.emailpassword.syncio API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.emailpassword.syncio

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from typing import Any, Dict, Union, Optional
+
+from supertokens_python.async_to_sync_wrapper import sync
+
+from ..interfaces import SignInOkResult, SignInWrongCredentialsError
+from ..types import EmailTemplateVars, User
+
+
+def update_email_or_password(
+    user_id: str,
+    email: Union[str, None] = None,
+    password: Union[str, None] = None,
+    apply_password_policy: Union[bool, None] = None,
+    tenant_id_for_password_policy: Optional[str] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+):
+    from supertokens_python.recipe.emailpassword.asyncio import update_email_or_password
+
+    return sync(
+        update_email_or_password(
+            user_id,
+            email,
+            password,
+            apply_password_policy,
+            tenant_id_for_password_policy,
+            user_context,
+        )
+    )
+
+
+def get_user_by_id(
+    user_id: str, user_context: Union[None, Dict[str, Any]] = None
+) -> Union[None, User]:
+    from supertokens_python.recipe.emailpassword.asyncio import get_user_by_id
+
+    return sync(get_user_by_id(user_id, user_context))
+
+
+def get_user_by_email(
+    tenant_id: str,
+    email: str,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> Union[None, User]:
+    from supertokens_python.recipe.emailpassword.asyncio import get_user_by_email
+
+    return sync(get_user_by_email(tenant_id, email, user_context))
+
+
+def create_reset_password_token(
+    tenant_id: str,
+    user_id: str,
+    user_context: Union[None, Dict[str, Any]] = None,
+):
+    from supertokens_python.recipe.emailpassword.asyncio import (
+        create_reset_password_token,
+    )
+
+    return sync(create_reset_password_token(tenant_id, user_id, user_context))
+
+
+def reset_password_using_token(
+    tenant_id: str,
+    token: str,
+    new_password: str,
+    user_context: Union[None, Dict[str, Any]] = None,
+):
+    from supertokens_python.recipe.emailpassword.asyncio import (
+        reset_password_using_token,
+    )
+
+    return sync(
+        reset_password_using_token(tenant_id, token, new_password, user_context)
+    )
+
+
+def sign_in(
+    tenant_id: str,
+    email: str,
+    password: str,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> Union[SignInOkResult, SignInWrongCredentialsError]:
+    from supertokens_python.recipe.emailpassword.asyncio import sign_in
+
+    return sync(sign_in(tenant_id, email, password, user_context))
+
+
+def sign_up(
+    tenant_id: str,
+    email: str,
+    password: str,
+    user_context: Union[None, Dict[str, Any]] = None,
+):
+    from supertokens_python.recipe.emailpassword.asyncio import sign_up
+
+    return sync(sign_up(tenant_id, email, password, user_context))
+
+
+def send_email(
+    input_: EmailTemplateVars,
+    user_context: Union[None, Dict[str, Any]] = None,
+):
+    from supertokens_python.recipe.emailpassword.asyncio import send_email
+
+    return sync(send_email(input_, user_context))
+
+
+def create_reset_password_link(
+    tenant_id: str,
+    user_id: str,
+    user_context: Optional[Dict[str, Any]] = None,
+):
+    from supertokens_python.recipe.emailpassword.asyncio import (
+        create_reset_password_link,
+    )
+
+    return sync(create_reset_password_link(tenant_id, user_id, user_context))
+
+
+def send_reset_password_email(
+    tenant_id: str,
+    user_id: str,
+    user_context: Optional[Dict[str, Any]] = None,
+):
+    from supertokens_python.recipe.emailpassword.asyncio import (
+        send_reset_password_email,
+    )
+
+    return sync(send_reset_password_email(tenant_id, user_id, user_context))
+
+
+
+
+
+
+
+

Functions

+
+ +
+
+
+ +Expand source code + +
def create_reset_password_link(
+    tenant_id: str,
+    user_id: str,
+    user_context: Optional[Dict[str, Any]] = None,
+):
+    from supertokens_python.recipe.emailpassword.asyncio import (
+        create_reset_password_link,
+    )
+
+    return sync(create_reset_password_link(tenant_id, user_id, user_context))
+
+
+
+def create_reset_password_token(tenant_id: str, user_id: str, user_context: Optional[Dict[str, Any]] = None) +
+
+
+
+ +Expand source code + +
def create_reset_password_token(
+    tenant_id: str,
+    user_id: str,
+    user_context: Union[None, Dict[str, Any]] = None,
+):
+    from supertokens_python.recipe.emailpassword.asyncio import (
+        create_reset_password_token,
+    )
+
+    return sync(create_reset_password_token(tenant_id, user_id, user_context))
+
+
+
+def get_user_by_email(tenant_id: str, email: str, user_context: Optional[Dict[str, Any]] = None) ‑> Optional[User] +
+
+
+
+ +Expand source code + +
def get_user_by_email(
+    tenant_id: str,
+    email: str,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> Union[None, User]:
+    from supertokens_python.recipe.emailpassword.asyncio import get_user_by_email
+
+    return sync(get_user_by_email(tenant_id, email, user_context))
+
+
+
+def get_user_by_id(user_id: str, user_context: Optional[Dict[str, Any]] = None) ‑> Optional[User] +
+
+
+
+ +Expand source code + +
def get_user_by_id(
+    user_id: str, user_context: Union[None, Dict[str, Any]] = None
+) -> Union[None, User]:
+    from supertokens_python.recipe.emailpassword.asyncio import get_user_by_id
+
+    return sync(get_user_by_id(user_id, user_context))
+
+
+
+def reset_password_using_token(tenant_id: str, token: str, new_password: str, user_context: Optional[Dict[str, Any]] = None) +
+
+
+
+ +Expand source code + +
def reset_password_using_token(
+    tenant_id: str,
+    token: str,
+    new_password: str,
+    user_context: Union[None, Dict[str, Any]] = None,
+):
+    from supertokens_python.recipe.emailpassword.asyncio import (
+        reset_password_using_token,
+    )
+
+    return sync(
+        reset_password_using_token(tenant_id, token, new_password, user_context)
+    )
+
+
+
+def send_email(input_: PasswordResetEmailTemplateVars, user_context: Optional[Dict[str, Any]] = None) +
+
+
+
+ +Expand source code + +
def send_email(
+    input_: EmailTemplateVars,
+    user_context: Union[None, Dict[str, Any]] = None,
+):
+    from supertokens_python.recipe.emailpassword.asyncio import send_email
+
+    return sync(send_email(input_, user_context))
+
+
+
+def send_reset_password_email(tenant_id: str, user_id: str, user_context: Optional[Dict[str, Any]] = None) +
+
+
+
+ +Expand source code + +
def send_reset_password_email(
+    tenant_id: str,
+    user_id: str,
+    user_context: Optional[Dict[str, Any]] = None,
+):
+    from supertokens_python.recipe.emailpassword.asyncio import (
+        send_reset_password_email,
+    )
+
+    return sync(send_reset_password_email(tenant_id, user_id, user_context))
+
+
+
+def sign_in(tenant_id: str, email: str, password: str, user_context: Optional[Dict[str, Any]] = None) ‑> Union[SignInOkResultSignInWrongCredentialsError] +
+
+
+
+ +Expand source code + +
def sign_in(
+    tenant_id: str,
+    email: str,
+    password: str,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> Union[SignInOkResult, SignInWrongCredentialsError]:
+    from supertokens_python.recipe.emailpassword.asyncio import sign_in
+
+    return sync(sign_in(tenant_id, email, password, user_context))
+
+
+
+def sign_up(tenant_id: str, email: str, password: str, user_context: Optional[Dict[str, Any]] = None) +
+
+
+
+ +Expand source code + +
def sign_up(
+    tenant_id: str,
+    email: str,
+    password: str,
+    user_context: Union[None, Dict[str, Any]] = None,
+):
+    from supertokens_python.recipe.emailpassword.asyncio import sign_up
+
+    return sync(sign_up(tenant_id, email, password, user_context))
+
+
+
+def update_email_or_password(user_id: str, email: Optional[str] = None, password: Optional[str] = None, apply_password_policy: Optional[bool] = None, tenant_id_for_password_policy: Optional[str] = None, user_context: Optional[Dict[str, Any]] = None) +
+
+
+
+ +Expand source code + +
def update_email_or_password(
+    user_id: str,
+    email: Union[str, None] = None,
+    password: Union[str, None] = None,
+    apply_password_policy: Union[bool, None] = None,
+    tenant_id_for_password_policy: Optional[str] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+):
+    from supertokens_python.recipe.emailpassword.asyncio import update_email_or_password
+
+    return sync(
+        update_email_or_password(
+            user_id,
+            email,
+            password,
+            apply_password_policy,
+            tenant_id_for_password_policy,
+            user_context,
+        )
+    )
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailpassword/types.html b/html/supertokens_python/recipe/emailpassword/types.html new file mode 100644 index 000000000..c71226e44 --- /dev/null +++ b/html/supertokens_python/recipe/emailpassword/types.html @@ -0,0 +1,422 @@ + + + + + + +supertokens_python.recipe.emailpassword.types API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.emailpassword.types

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from typing import Any, Awaitable, Callable, List, TypeVar, Union
+
+from supertokens_python.ingredients.emaildelivery import EmailDeliveryIngredient
+from supertokens_python.ingredients.emaildelivery.types import (
+    EmailDeliveryInterface,
+    SMTPServiceInterface,
+)
+
+
+class User:
+    def __init__(
+        self, user_id: str, email: str, time_joined: int, tenant_ids: List[str]
+    ):
+        self.user_id = user_id
+        self.email = email
+        self.time_joined = time_joined
+        self.tenant_ids = tenant_ids
+
+    def __eq__(self, other: object):
+        return (
+            isinstance(other, self.__class__)
+            and self.user_id == other.user_id
+            and self.email == other.email
+            and self.time_joined == other.time_joined
+            and self.tenant_ids == other.tenant_ids
+        )
+
+
+class UsersResponse:
+    def __init__(self, users: List[User], next_pagination_token: Union[str, None]):
+        self.users = users
+        self.next_pagination_token = next_pagination_token
+
+
+class ErrorFormField:
+    def __init__(self, id: str, error: str):  # pylint: disable=redefined-builtin
+        self.id = id
+        self.error = error
+
+
+class FormField:
+    def __init__(self, id: str, value: Any):  # pylint: disable=redefined-builtin
+        self.id: str = id
+        self.value: Any = value
+
+
+class InputFormField:
+    def __init__(
+        self,
+        id: str,  # pylint: disable=redefined-builtin
+        validate: Union[
+            Callable[[str, str], Awaitable[Union[str, None]]],
+            None,
+        ] = None,
+        optional: Union[bool, None] = None,
+    ):
+        self.id = id
+        self.validate = validate
+        self.optional = optional
+
+
+class NormalisedFormField:
+    def __init__(
+        self,
+        id: str,  # pylint: disable=redefined-builtin
+        validate: Callable[[str, str], Awaitable[Union[str, None]]],
+        optional: bool,
+    ):
+        self.id = id
+        self.validate = validate
+        self.optional = optional
+
+
+_T = TypeVar("_T")
+
+
+class PasswordResetEmailTemplateVarsUser:
+    def __init__(self, user_id: str, email: str):
+        self.id = user_id
+        self.email = email
+
+
+class PasswordResetEmailTemplateVars:
+    def __init__(
+        self,
+        user: PasswordResetEmailTemplateVarsUser,
+        password_reset_link: str,
+        tenant_id: str,
+    ) -> None:
+        self.user = user
+        self.password_reset_link = password_reset_link
+        self.tenant_id = tenant_id
+
+
+# Export:
+EmailTemplateVars = PasswordResetEmailTemplateVars
+
+# PasswordResetEmailTemplateVars (Already exported because it's defined in the same)
+
+SMTPOverrideInput = SMTPServiceInterface[EmailTemplateVars]
+
+EmailDeliveryOverrideInput = EmailDeliveryInterface[EmailTemplateVars]
+
+
+class EmailPasswordIngredients:
+    def __init__(
+        self,
+        email_delivery: Union[EmailDeliveryIngredient[EmailTemplateVars], None] = None,
+    ) -> None:
+        self.email_delivery = email_delivery
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class EmailPasswordIngredients +(email_delivery: Union[EmailDeliveryIngredient[PasswordResetEmailTemplateVars], None] = None) +
+
+
+
+ +Expand source code + +
class EmailPasswordIngredients:
+    def __init__(
+        self,
+        email_delivery: Union[EmailDeliveryIngredient[EmailTemplateVars], None] = None,
+    ) -> None:
+        self.email_delivery = email_delivery
+
+
+
+class ErrorFormField +(id: str, error: str) +
+
+
+
+ +Expand source code + +
class ErrorFormField:
+    def __init__(self, id: str, error: str):  # pylint: disable=redefined-builtin
+        self.id = id
+        self.error = error
+
+
+
+class FormField +(id: str, value: Any) +
+
+
+
+ +Expand source code + +
class FormField:
+    def __init__(self, id: str, value: Any):  # pylint: disable=redefined-builtin
+        self.id: str = id
+        self.value: Any = value
+
+
+
+class InputFormField +(id: str, validate: Union[Callable[[str, str], Awaitable[Union[str, None]]], None] = None, optional: Union[bool, None] = None) +
+
+
+
+ +Expand source code + +
class InputFormField:
+    def __init__(
+        self,
+        id: str,  # pylint: disable=redefined-builtin
+        validate: Union[
+            Callable[[str, str], Awaitable[Union[str, None]]],
+            None,
+        ] = None,
+        optional: Union[bool, None] = None,
+    ):
+        self.id = id
+        self.validate = validate
+        self.optional = optional
+
+
+
+class NormalisedFormField +(id: str, validate: Callable[[str, str], Awaitable[Union[str, None]]], optional: bool) +
+
+
+
+ +Expand source code + +
class NormalisedFormField:
+    def __init__(
+        self,
+        id: str,  # pylint: disable=redefined-builtin
+        validate: Callable[[str, str], Awaitable[Union[str, None]]],
+        optional: bool,
+    ):
+        self.id = id
+        self.validate = validate
+        self.optional = optional
+
+
+
+class PasswordResetEmailTemplateVars +(user: PasswordResetEmailTemplateVarsUser, password_reset_link: str, tenant_id: str) +
+
+
+
+ +Expand source code + +
class PasswordResetEmailTemplateVars:
+    def __init__(
+        self,
+        user: PasswordResetEmailTemplateVarsUser,
+        password_reset_link: str,
+        tenant_id: str,
+    ) -> None:
+        self.user = user
+        self.password_reset_link = password_reset_link
+        self.tenant_id = tenant_id
+
+
+
+class EmailTemplateVars +(user: PasswordResetEmailTemplateVarsUser, password_reset_link: str, tenant_id: str) +
+
+
+
+ +Expand source code + +
class PasswordResetEmailTemplateVars:
+    def __init__(
+        self,
+        user: PasswordResetEmailTemplateVarsUser,
+        password_reset_link: str,
+        tenant_id: str,
+    ) -> None:
+        self.user = user
+        self.password_reset_link = password_reset_link
+        self.tenant_id = tenant_id
+
+
+
+class PasswordResetEmailTemplateVarsUser +(user_id: str, email: str) +
+
+
+
+ +Expand source code + +
class PasswordResetEmailTemplateVarsUser:
+    def __init__(self, user_id: str, email: str):
+        self.id = user_id
+        self.email = email
+
+
+
+class User +(user_id: str, email: str, time_joined: int, tenant_ids: List[str]) +
+
+
+
+ +Expand source code + +
class User:
+    def __init__(
+        self, user_id: str, email: str, time_joined: int, tenant_ids: List[str]
+    ):
+        self.user_id = user_id
+        self.email = email
+        self.time_joined = time_joined
+        self.tenant_ids = tenant_ids
+
+    def __eq__(self, other: object):
+        return (
+            isinstance(other, self.__class__)
+            and self.user_id == other.user_id
+            and self.email == other.email
+            and self.time_joined == other.time_joined
+            and self.tenant_ids == other.tenant_ids
+        )
+
+
+
+class UsersResponse +(users: List[User], next_pagination_token: Union[str, None]) +
+
+
+
+ +Expand source code + +
class UsersResponse:
+    def __init__(self, users: List[User], next_pagination_token: Union[str, None]):
+        self.users = users
+        self.next_pagination_token = next_pagination_token
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailpassword/utils.html b/html/supertokens_python/recipe/emailpassword/utils.html new file mode 100644 index 000000000..ed2bb11f5 --- /dev/null +++ b/html/supertokens_python/recipe/emailpassword/utils.html @@ -0,0 +1,839 @@ + + + + + + +supertokens_python.recipe.emailpassword.utils API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.emailpassword.utils

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from re import fullmatch
+from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Union
+
+from supertokens_python.framework import BaseRequest
+from supertokens_python.ingredients.emaildelivery.types import (
+    EmailDeliveryConfig,
+    EmailDeliveryConfigWithService,
+)
+from supertokens_python.recipe.emailpassword.emaildelivery.services.backward_compatibility import (
+    BackwardCompatibilityService,
+)
+
+from .interfaces import APIInterface, RecipeInterface
+from .types import EmailTemplateVars, InputFormField, NormalisedFormField
+
+if TYPE_CHECKING:
+    from supertokens_python.supertokens import AppInfo
+
+from supertokens_python.utils import get_filtered_list
+
+from .constants import FORM_FIELD_EMAIL_ID, FORM_FIELD_PASSWORD_ID
+
+
+async def default_validator(_: str, __: str) -> Union[str, None]:
+    return None
+
+
+async def default_password_validator(value: str, _tenant_id: str) -> Union[str, None]:
+    # length >= 8 && < 100
+    # must have a number and a character
+    # as per
+    # https://github.com/supertokens/supertokens-auth-react/issues/5#issuecomment-709512438
+    if len(value) < 8:
+        return "Password must contain at least 8 characters, including a number"
+
+    if len(value) >= 100:
+        return "Password's length must be lesser than 100 characters"
+
+    if fullmatch(r"^.*[A-Za-z]+.*$", value) is None:
+        return "Password must contain at least one alphabet"
+
+    if fullmatch(r"^.*[0-9]+.*$", value) is None:
+        return "Password must contain at least one number"
+
+    return None
+
+
+async def default_email_validator(value: Any, _tenant_id: str) -> Union[str, None]:
+    # We check if the email syntax is correct
+    # As per https://github.com/supertokens/supertokens-auth-react/issues/5#issuecomment-709512438
+    # Regex from https://stackoverflow.com/a/46181/3867175
+    if (not isinstance(value, str)) or (
+        fullmatch(
+            r'^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,'
+            r"3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$",
+            value,
+        )
+        is None
+    ):
+        return "Email is not valid"
+
+    return None
+
+
+class InputSignUpFeature:
+    def __init__(self, form_fields: Union[List[InputFormField], None] = None):
+        if form_fields is None:
+            form_fields = []
+        self.form_fields = normalise_sign_up_form_fields(form_fields)
+
+
+class SignUpFeature:
+    def __init__(self, form_fields: List[NormalisedFormField]):
+        self.form_fields = form_fields
+
+
+def normalise_sign_up_form_fields(
+    form_fields: List[InputFormField],
+) -> List[NormalisedFormField]:
+    normalised_form_fields: List[NormalisedFormField] = []
+    for field in form_fields:
+        if field.id == FORM_FIELD_PASSWORD_ID:
+            validator = (
+                field.validate
+                if field.validate is not None
+                else default_password_validator
+            )
+            normalised_form_fields.append(
+                NormalisedFormField(field.id, validator, False)
+            )
+        elif field.id == FORM_FIELD_EMAIL_ID:
+            validator = (
+                field.validate
+                if field.validate is not None
+                else default_email_validator
+            )
+            normalised_form_fields.append(
+                NormalisedFormField(field.id, validator, False)
+            )
+        else:
+            validator = (
+                field.validate if field.validate is not None else default_validator
+            )
+            optional = field.optional if field.optional is not None else False
+            normalised_form_fields.append(
+                NormalisedFormField(field.id, validator, optional)
+            )
+    if (
+        len(
+            get_filtered_list(
+                lambda x: x.id == FORM_FIELD_PASSWORD_ID, normalised_form_fields
+            )
+        )
+        == 0
+    ):
+        normalised_form_fields.append(
+            NormalisedFormField(
+                FORM_FIELD_PASSWORD_ID, default_password_validator, False
+            )
+        )
+    if (
+        len(
+            get_filtered_list(
+                lambda x: x.id == FORM_FIELD_EMAIL_ID, normalised_form_fields
+            )
+        )
+        == 0
+    ):
+        normalised_form_fields.append(
+            NormalisedFormField(FORM_FIELD_EMAIL_ID, default_email_validator, False)
+        )
+    return normalised_form_fields
+
+
+class SignInFeature:
+    def __init__(self, form_fields: List[NormalisedFormField]):
+        self.form_fields = form_fields
+
+
+def normalise_sign_in_form_fields(
+    form_fields: List[NormalisedFormField],
+) -> List[NormalisedFormField]:
+    return list(
+        map(
+            lambda y: NormalisedFormField(
+                y.id,
+                y.validate if y.id == FORM_FIELD_EMAIL_ID else default_validator,
+                False,
+            ),
+            get_filtered_list(
+                lambda x: x.id in (FORM_FIELD_PASSWORD_ID, FORM_FIELD_EMAIL_ID),
+                form_fields,
+            ),
+        )
+    )
+
+
+def validate_and_normalise_sign_in_config(
+    sign_up_config: SignUpFeature,
+) -> SignInFeature:
+    form_fields = normalise_sign_in_form_fields(sign_up_config.form_fields)
+    return SignInFeature(form_fields)
+
+
+class ResetPasswordUsingTokenFeature:
+    def __init__(
+        self,
+        form_fields_for_password_reset_form: List[NormalisedFormField],
+        form_fields_for_generate_token_form: List[NormalisedFormField],
+    ):
+        self.form_fields_for_password_reset_form = form_fields_for_password_reset_form
+        self.form_fields_for_generate_token_form = form_fields_for_generate_token_form
+
+
+def validate_and_normalise_reset_password_using_token_config(
+    sign_up_config: InputSignUpFeature,
+) -> ResetPasswordUsingTokenFeature:
+    form_fields_for_password_reset_form = list(
+        map(
+            lambda y: NormalisedFormField(y.id, y.validate, False),
+            get_filtered_list(
+                lambda x: x.id == FORM_FIELD_PASSWORD_ID, sign_up_config.form_fields
+            ),
+        )
+    )
+    form_fields_for_generate_token_form = list(
+        map(
+            lambda y: NormalisedFormField(y.id, y.validate, False),
+            get_filtered_list(
+                lambda x: x.id == FORM_FIELD_EMAIL_ID, sign_up_config.form_fields
+            ),
+        )
+    )
+
+    return ResetPasswordUsingTokenFeature(
+        form_fields_for_password_reset_form,
+        form_fields_for_generate_token_form,
+    )
+
+
+class InputOverrideConfig:
+    def __init__(
+        self,
+        functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None,
+        apis: Union[Callable[[APIInterface], APIInterface], None] = None,
+    ):
+        self.functions = functions
+        self.apis = apis
+
+
+class OverrideConfig:
+    def __init__(
+        self,
+        functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None,
+        apis: Union[Callable[[APIInterface], APIInterface], None] = None,
+    ):
+        self.functions = functions
+        self.apis = apis
+
+
+class EmailPasswordConfig:
+    def __init__(
+        self,
+        sign_up_feature: SignUpFeature,
+        sign_in_feature: SignInFeature,
+        reset_password_using_token_feature: ResetPasswordUsingTokenFeature,
+        override: OverrideConfig,
+        get_email_delivery_config: Callable[
+            [RecipeInterface], EmailDeliveryConfigWithService[EmailTemplateVars]
+        ],
+    ):
+        self.sign_up_feature = sign_up_feature
+        self.sign_in_feature = sign_in_feature
+        self.reset_password_using_token_feature = reset_password_using_token_feature
+        self.override = override
+        self.get_email_delivery_config = get_email_delivery_config
+
+
+def validate_and_normalise_user_input(
+    app_info: AppInfo,
+    sign_up_feature: Union[InputSignUpFeature, None] = None,
+    override: Union[InputOverrideConfig, None] = None,
+    email_delivery: Union[EmailDeliveryConfig[EmailTemplateVars], None] = None,
+) -> EmailPasswordConfig:
+
+    # NOTE: We don't need to check the instance of sign_up_feature and override
+    # as they will always be either None or the specified type.
+
+    if override is None:
+        override = InputOverrideConfig()
+
+    if sign_up_feature is None:
+        sign_up_feature = InputSignUpFeature()
+
+    def get_email_delivery_config(
+        ep_recipe: RecipeInterface,
+    ) -> EmailDeliveryConfigWithService[EmailTemplateVars]:
+        if email_delivery and email_delivery.service:
+            return EmailDeliveryConfigWithService(
+                service=email_delivery.service, override=email_delivery.override
+            )
+
+        email_service = BackwardCompatibilityService(
+            app_info=app_info,
+            recipe_interface_impl=ep_recipe,
+        )
+        if email_delivery is not None and email_delivery.override is not None:
+            override = email_delivery.override
+        else:
+            override = None
+        return EmailDeliveryConfigWithService(email_service, override=override)
+
+    return EmailPasswordConfig(
+        SignUpFeature(sign_up_feature.form_fields),
+        SignInFeature(normalise_sign_in_form_fields(sign_up_feature.form_fields)),
+        validate_and_normalise_reset_password_using_token_config(sign_up_feature),
+        OverrideConfig(functions=override.functions, apis=override.apis),
+        get_email_delivery_config=get_email_delivery_config,
+    )
+
+
+def get_password_reset_link(
+    app_info: AppInfo,
+    token: str,
+    tenant_id: str,
+    request: Optional[BaseRequest],
+    user_context: Dict[str, Any],
+) -> str:
+    return (
+        app_info.get_origin(request, user_context).get_as_string_dangerous()
+        + app_info.website_base_path.get_as_string_dangerous()
+        + "/reset-password?token="
+        + token
+        + "&tenantId="
+        + tenant_id
+    )
+
+
+
+
+
+
+
+

Functions

+
+
+async def default_email_validator(value: Any, _tenant_id: str) ‑> Optional[str] +
+
+
+
+ +Expand source code + +
async def default_email_validator(value: Any, _tenant_id: str) -> Union[str, None]:
+    # We check if the email syntax is correct
+    # As per https://github.com/supertokens/supertokens-auth-react/issues/5#issuecomment-709512438
+    # Regex from https://stackoverflow.com/a/46181/3867175
+    if (not isinstance(value, str)) or (
+        fullmatch(
+            r'^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,'
+            r"3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$",
+            value,
+        )
+        is None
+    ):
+        return "Email is not valid"
+
+    return None
+
+
+
+async def default_password_validator(value: str, _tenant_id: str) ‑> Optional[str] +
+
+
+
+ +Expand source code + +
async def default_password_validator(value: str, _tenant_id: str) -> Union[str, None]:
+    # length >= 8 && < 100
+    # must have a number and a character
+    # as per
+    # https://github.com/supertokens/supertokens-auth-react/issues/5#issuecomment-709512438
+    if len(value) < 8:
+        return "Password must contain at least 8 characters, including a number"
+
+    if len(value) >= 100:
+        return "Password's length must be lesser than 100 characters"
+
+    if fullmatch(r"^.*[A-Za-z]+.*$", value) is None:
+        return "Password must contain at least one alphabet"
+
+    if fullmatch(r"^.*[0-9]+.*$", value) is None:
+        return "Password must contain at least one number"
+
+    return None
+
+
+
+async def default_validator(_: str, __: str) ‑> Optional[str] +
+
+
+
+ +Expand source code + +
async def default_validator(_: str, __: str) -> Union[str, None]:
+    return None
+
+
+ +
+
+
+ +Expand source code + +
def get_password_reset_link(
+    app_info: AppInfo,
+    token: str,
+    tenant_id: str,
+    request: Optional[BaseRequest],
+    user_context: Dict[str, Any],
+) -> str:
+    return (
+        app_info.get_origin(request, user_context).get_as_string_dangerous()
+        + app_info.website_base_path.get_as_string_dangerous()
+        + "/reset-password?token="
+        + token
+        + "&tenantId="
+        + tenant_id
+    )
+
+
+
+def normalise_sign_in_form_fields(form_fields: List[NormalisedFormField]) ‑> List[NormalisedFormField] +
+
+
+
+ +Expand source code + +
def normalise_sign_in_form_fields(
+    form_fields: List[NormalisedFormField],
+) -> List[NormalisedFormField]:
+    return list(
+        map(
+            lambda y: NormalisedFormField(
+                y.id,
+                y.validate if y.id == FORM_FIELD_EMAIL_ID else default_validator,
+                False,
+            ),
+            get_filtered_list(
+                lambda x: x.id in (FORM_FIELD_PASSWORD_ID, FORM_FIELD_EMAIL_ID),
+                form_fields,
+            ),
+        )
+    )
+
+
+
+def normalise_sign_up_form_fields(form_fields: List[InputFormField]) ‑> List[NormalisedFormField] +
+
+
+
+ +Expand source code + +
def normalise_sign_up_form_fields(
+    form_fields: List[InputFormField],
+) -> List[NormalisedFormField]:
+    normalised_form_fields: List[NormalisedFormField] = []
+    for field in form_fields:
+        if field.id == FORM_FIELD_PASSWORD_ID:
+            validator = (
+                field.validate
+                if field.validate is not None
+                else default_password_validator
+            )
+            normalised_form_fields.append(
+                NormalisedFormField(field.id, validator, False)
+            )
+        elif field.id == FORM_FIELD_EMAIL_ID:
+            validator = (
+                field.validate
+                if field.validate is not None
+                else default_email_validator
+            )
+            normalised_form_fields.append(
+                NormalisedFormField(field.id, validator, False)
+            )
+        else:
+            validator = (
+                field.validate if field.validate is not None else default_validator
+            )
+            optional = field.optional if field.optional is not None else False
+            normalised_form_fields.append(
+                NormalisedFormField(field.id, validator, optional)
+            )
+    if (
+        len(
+            get_filtered_list(
+                lambda x: x.id == FORM_FIELD_PASSWORD_ID, normalised_form_fields
+            )
+        )
+        == 0
+    ):
+        normalised_form_fields.append(
+            NormalisedFormField(
+                FORM_FIELD_PASSWORD_ID, default_password_validator, False
+            )
+        )
+    if (
+        len(
+            get_filtered_list(
+                lambda x: x.id == FORM_FIELD_EMAIL_ID, normalised_form_fields
+            )
+        )
+        == 0
+    ):
+        normalised_form_fields.append(
+            NormalisedFormField(FORM_FIELD_EMAIL_ID, default_email_validator, False)
+        )
+    return normalised_form_fields
+
+
+
+def validate_and_normalise_reset_password_using_token_config(sign_up_config: InputSignUpFeature) ‑> ResetPasswordUsingTokenFeature +
+
+
+
+ +Expand source code + +
def validate_and_normalise_reset_password_using_token_config(
+    sign_up_config: InputSignUpFeature,
+) -> ResetPasswordUsingTokenFeature:
+    form_fields_for_password_reset_form = list(
+        map(
+            lambda y: NormalisedFormField(y.id, y.validate, False),
+            get_filtered_list(
+                lambda x: x.id == FORM_FIELD_PASSWORD_ID, sign_up_config.form_fields
+            ),
+        )
+    )
+    form_fields_for_generate_token_form = list(
+        map(
+            lambda y: NormalisedFormField(y.id, y.validate, False),
+            get_filtered_list(
+                lambda x: x.id == FORM_FIELD_EMAIL_ID, sign_up_config.form_fields
+            ),
+        )
+    )
+
+    return ResetPasswordUsingTokenFeature(
+        form_fields_for_password_reset_form,
+        form_fields_for_generate_token_form,
+    )
+
+
+
+def validate_and_normalise_sign_in_config(sign_up_config: SignUpFeature) ‑> SignInFeature +
+
+
+
+ +Expand source code + +
def validate_and_normalise_sign_in_config(
+    sign_up_config: SignUpFeature,
+) -> SignInFeature:
+    form_fields = normalise_sign_in_form_fields(sign_up_config.form_fields)
+    return SignInFeature(form_fields)
+
+
+
+def validate_and_normalise_user_input(app_info: AppInfo, sign_up_feature: Union[InputSignUpFeature, None] = None, override: Union[InputOverrideConfig, None] = None, email_delivery: Union[EmailDeliveryConfig[EmailTemplateVars], None] = None) ‑> EmailPasswordConfig +
+
+
+
+ +Expand source code + +
def validate_and_normalise_user_input(
+    app_info: AppInfo,
+    sign_up_feature: Union[InputSignUpFeature, None] = None,
+    override: Union[InputOverrideConfig, None] = None,
+    email_delivery: Union[EmailDeliveryConfig[EmailTemplateVars], None] = None,
+) -> EmailPasswordConfig:
+
+    # NOTE: We don't need to check the instance of sign_up_feature and override
+    # as they will always be either None or the specified type.
+
+    if override is None:
+        override = InputOverrideConfig()
+
+    if sign_up_feature is None:
+        sign_up_feature = InputSignUpFeature()
+
+    def get_email_delivery_config(
+        ep_recipe: RecipeInterface,
+    ) -> EmailDeliveryConfigWithService[EmailTemplateVars]:
+        if email_delivery and email_delivery.service:
+            return EmailDeliveryConfigWithService(
+                service=email_delivery.service, override=email_delivery.override
+            )
+
+        email_service = BackwardCompatibilityService(
+            app_info=app_info,
+            recipe_interface_impl=ep_recipe,
+        )
+        if email_delivery is not None and email_delivery.override is not None:
+            override = email_delivery.override
+        else:
+            override = None
+        return EmailDeliveryConfigWithService(email_service, override=override)
+
+    return EmailPasswordConfig(
+        SignUpFeature(sign_up_feature.form_fields),
+        SignInFeature(normalise_sign_in_form_fields(sign_up_feature.form_fields)),
+        validate_and_normalise_reset_password_using_token_config(sign_up_feature),
+        OverrideConfig(functions=override.functions, apis=override.apis),
+        get_email_delivery_config=get_email_delivery_config,
+    )
+
+
+
+
+
+

Classes

+
+
+class EmailPasswordConfig +(sign_up_feature: SignUpFeature, sign_in_feature: SignInFeature, reset_password_using_token_feature: ResetPasswordUsingTokenFeature, override: OverrideConfig, get_email_delivery_config: Callable[[RecipeInterface], EmailDeliveryConfigWithService[EmailTemplateVars]]) +
+
+
+
+ +Expand source code + +
class EmailPasswordConfig:
+    def __init__(
+        self,
+        sign_up_feature: SignUpFeature,
+        sign_in_feature: SignInFeature,
+        reset_password_using_token_feature: ResetPasswordUsingTokenFeature,
+        override: OverrideConfig,
+        get_email_delivery_config: Callable[
+            [RecipeInterface], EmailDeliveryConfigWithService[EmailTemplateVars]
+        ],
+    ):
+        self.sign_up_feature = sign_up_feature
+        self.sign_in_feature = sign_in_feature
+        self.reset_password_using_token_feature = reset_password_using_token_feature
+        self.override = override
+        self.get_email_delivery_config = get_email_delivery_config
+
+
+
+class InputOverrideConfig +(functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None, apis: Union[Callable[[APIInterface], APIInterface], None] = None) +
+
+
+
+ +Expand source code + +
class InputOverrideConfig:
+    def __init__(
+        self,
+        functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None,
+        apis: Union[Callable[[APIInterface], APIInterface], None] = None,
+    ):
+        self.functions = functions
+        self.apis = apis
+
+
+
+class InputSignUpFeature +(form_fields: Union[List[InputFormField], None] = None) +
+
+
+
+ +Expand source code + +
class InputSignUpFeature:
+    def __init__(self, form_fields: Union[List[InputFormField], None] = None):
+        if form_fields is None:
+            form_fields = []
+        self.form_fields = normalise_sign_up_form_fields(form_fields)
+
+
+
+class OverrideConfig +(functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None, apis: Union[Callable[[APIInterface], APIInterface], None] = None) +
+
+
+
+ +Expand source code + +
class OverrideConfig:
+    def __init__(
+        self,
+        functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None,
+        apis: Union[Callable[[APIInterface], APIInterface], None] = None,
+    ):
+        self.functions = functions
+        self.apis = apis
+
+
+
+class ResetPasswordUsingTokenFeature +(form_fields_for_password_reset_form: List[NormalisedFormField], form_fields_for_generate_token_form: List[NormalisedFormField]) +
+
+
+
+ +Expand source code + +
class ResetPasswordUsingTokenFeature:
+    def __init__(
+        self,
+        form_fields_for_password_reset_form: List[NormalisedFormField],
+        form_fields_for_generate_token_form: List[NormalisedFormField],
+    ):
+        self.form_fields_for_password_reset_form = form_fields_for_password_reset_form
+        self.form_fields_for_generate_token_form = form_fields_for_generate_token_form
+
+
+
+class SignInFeature +(form_fields: List[NormalisedFormField]) +
+
+
+
+ +Expand source code + +
class SignInFeature:
+    def __init__(self, form_fields: List[NormalisedFormField]):
+        self.form_fields = form_fields
+
+
+
+class SignUpFeature +(form_fields: List[NormalisedFormField]) +
+
+
+
+ +Expand source code + +
class SignUpFeature:
+    def __init__(self, form_fields: List[NormalisedFormField]):
+        self.form_fields = form_fields
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailverification/api/email_verify.html b/html/supertokens_python/recipe/emailverification/api/email_verify.html new file mode 100644 index 000000000..e29de5f96 --- /dev/null +++ b/html/supertokens_python/recipe/emailverification/api/email_verify.html @@ -0,0 +1,193 @@ + + + + + + +supertokens_python.recipe.emailverification.api.email_verify API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.emailverification.api.email_verify

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+from typing import Any, Dict
+
+from supertokens_python.exceptions import raise_bad_input_exception
+from supertokens_python.recipe.emailverification.interfaces import (
+    APIInterface,
+    APIOptions,
+)
+from supertokens_python.utils import (
+    normalise_http_method,
+    send_200_response,
+)
+from supertokens_python.recipe.session.asyncio import get_session
+
+
+async def handle_email_verify_api(
+    api_implementation: APIInterface,
+    tenant_id: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+):
+    if normalise_http_method(api_options.request.method()) == "post":
+        if api_implementation.disable_email_verify_post:
+            return None
+        body = await api_options.request.json()
+        if body is None:
+            raise_bad_input_exception("Please pass JSON input body")
+        if "token" not in body:
+            raise_bad_input_exception("Please provide the email verification token")
+        if not isinstance(body["token"], str):
+            raise_bad_input_exception("The email verification token must be a string")
+
+        token = body["token"]
+
+        session = await get_session(
+            api_options.request,
+            session_required=False,
+            override_global_claim_validators=lambda _, __, ___: [],
+            user_context=user_context,
+        )
+
+        result = await api_implementation.email_verify_post(
+            token, session, tenant_id, api_options, user_context
+        )
+    else:
+        if api_implementation.disable_is_email_verified_get:
+            return None
+
+        session = await get_session(
+            api_options.request,
+            override_global_claim_validators=lambda _, __, ___: [],
+            user_context=user_context,
+        )
+        assert session is not None
+        result = await api_implementation.is_email_verified_get(
+            session, api_options, user_context
+        )
+
+    return send_200_response(result.to_json(), api_options.response)
+
+
+
+
+
+
+
+

Functions

+
+
+async def handle_email_verify_api(api_implementation: APIInterface, tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) +
+
+
+
+ +Expand source code + +
async def handle_email_verify_api(
+    api_implementation: APIInterface,
+    tenant_id: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+):
+    if normalise_http_method(api_options.request.method()) == "post":
+        if api_implementation.disable_email_verify_post:
+            return None
+        body = await api_options.request.json()
+        if body is None:
+            raise_bad_input_exception("Please pass JSON input body")
+        if "token" not in body:
+            raise_bad_input_exception("Please provide the email verification token")
+        if not isinstance(body["token"], str):
+            raise_bad_input_exception("The email verification token must be a string")
+
+        token = body["token"]
+
+        session = await get_session(
+            api_options.request,
+            session_required=False,
+            override_global_claim_validators=lambda _, __, ___: [],
+            user_context=user_context,
+        )
+
+        result = await api_implementation.email_verify_post(
+            token, session, tenant_id, api_options, user_context
+        )
+    else:
+        if api_implementation.disable_is_email_verified_get:
+            return None
+
+        session = await get_session(
+            api_options.request,
+            override_global_claim_validators=lambda _, __, ___: [],
+            user_context=user_context,
+        )
+        assert session is not None
+        result = await api_implementation.is_email_verified_get(
+            session, api_options, user_context
+        )
+
+    return send_200_response(result.to_json(), api_options.response)
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailverification/api/generate_email_verify_token.html b/html/supertokens_python/recipe/emailverification/api/generate_email_verify_token.html new file mode 100644 index 000000000..f4be85daa --- /dev/null +++ b/html/supertokens_python/recipe/emailverification/api/generate_email_verify_token.html @@ -0,0 +1,137 @@ + + + + + + +supertokens_python.recipe.emailverification.api.generate_email_verify_token API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.emailverification.api.generate_email_verify_token

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+from typing import Any, Dict
+
+from supertokens_python.recipe.emailverification.interfaces import (
+    APIInterface,
+    APIOptions,
+)
+from supertokens_python.utils import send_200_response
+from supertokens_python.recipe.session.asyncio import get_session
+
+
+async def handle_generate_email_verify_token_api(
+    api_implementation: APIInterface,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+):
+    if api_implementation.disable_generate_email_verify_token_post:
+        return None
+    session = await get_session(
+        api_options.request,
+        override_global_claim_validators=lambda _, __, ___: [],
+        user_context=user_context,
+    )
+    assert session is not None
+
+    result = await api_implementation.generate_email_verify_token_post(
+        session, api_options, user_context
+    )
+    return send_200_response(result.to_json(), api_options.response)
+
+
+
+
+
+
+
+

Functions

+
+
+async def handle_generate_email_verify_token_api(api_implementation: APIInterface, api_options: APIOptions, user_context: Dict[str, Any]) +
+
+
+
+ +Expand source code + +
async def handle_generate_email_verify_token_api(
+    api_implementation: APIInterface,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+):
+    if api_implementation.disable_generate_email_verify_token_post:
+        return None
+    session = await get_session(
+        api_options.request,
+        override_global_claim_validators=lambda _, __, ___: [],
+        user_context=user_context,
+    )
+    assert session is not None
+
+    result = await api_implementation.generate_email_verify_token_post(
+        session, api_options, user_context
+    )
+    return send_200_response(result.to_json(), api_options.response)
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailverification/api/index.html b/html/supertokens_python/recipe/emailverification/api/index.html new file mode 100644 index 000000000..31309f3a8 --- /dev/null +++ b/html/supertokens_python/recipe/emailverification/api/index.html @@ -0,0 +1,92 @@ + + + + + + +supertokens_python.recipe.emailverification.api API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.emailverification.api

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from .email_verify import handle_email_verify_api  # type: ignore
+from .generate_email_verify_token import (
+    handle_generate_email_verify_token_api,  # type: ignore
+)
+
+
+
+

Sub-modules

+
+
supertokens_python.recipe.emailverification.api.email_verify
+
+
+
+
supertokens_python.recipe.emailverification.api.generate_email_verify_token
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailverification/asyncio/index.html b/html/supertokens_python/recipe/emailverification/asyncio/index.html new file mode 100644 index 000000000..58a325305 --- /dev/null +++ b/html/supertokens_python/recipe/emailverification/asyncio/index.html @@ -0,0 +1,577 @@ + + + + + + +supertokens_python.recipe.emailverification.asyncio API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.emailverification.asyncio

+
+
+
+ +Expand source code + +
#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from typing import Any, Dict, Union, Optional
+
+from supertokens_python import get_request_from_user_context
+
+from supertokens_python.recipe.emailverification.interfaces import (
+    GetEmailForUserIdOkResult,
+    EmailDoesNotExistError,
+    CreateEmailVerificationTokenEmailAlreadyVerifiedError,
+    CreateEmailVerificationLinkOkResult,
+    CreateEmailVerificationLinkEmailAlreadyVerifiedError,
+    SendEmailVerificationEmailOkResult,
+    SendEmailVerificationEmailAlreadyVerifiedError,
+    UnverifyEmailOkResult,
+    CreateEmailVerificationTokenOkResult,
+    RevokeEmailVerificationTokensOkResult,
+)
+from supertokens_python.recipe.emailverification.types import EmailTemplateVars
+from supertokens_python.recipe.emailverification.recipe import EmailVerificationRecipe
+
+from supertokens_python.recipe.emailverification.utils import get_email_verify_link
+from supertokens_python.recipe.emailverification.types import (
+    VerificationEmailTemplateVars,
+    VerificationEmailTemplateVarsUser,
+)
+
+
+async def create_email_verification_token(
+    tenant_id: str,
+    user_id: str,
+    email: Optional[str] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> Union[
+    CreateEmailVerificationTokenOkResult,
+    CreateEmailVerificationTokenEmailAlreadyVerifiedError,
+]:
+    if user_context is None:
+        user_context = {}
+    recipe = EmailVerificationRecipe.get_instance()
+    if email is None:
+        email_info = await recipe.get_email_for_user_id(user_id, user_context)
+        if isinstance(email_info, GetEmailForUserIdOkResult):
+            email = email_info.email
+        elif isinstance(email_info, EmailDoesNotExistError):
+            return CreateEmailVerificationTokenEmailAlreadyVerifiedError()
+        else:
+            raise Exception("Unknown User ID provided without email")
+
+    return await recipe.recipe_implementation.create_email_verification_token(
+        user_id, email, tenant_id, user_context
+    )
+
+
+async def verify_email_using_token(
+    tenant_id: str, token: str, user_context: Union[None, Dict[str, Any]] = None
+):
+    if user_context is None:
+        user_context = {}
+    return await EmailVerificationRecipe.get_instance().recipe_implementation.verify_email_using_token(
+        token, tenant_id, user_context
+    )
+
+
+async def is_email_verified(
+    user_id: str,
+    email: Optional[str] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+):
+    if user_context is None:
+        user_context = {}
+
+    recipe = EmailVerificationRecipe.get_instance()
+    if email is None:
+        email_info = await recipe.get_email_for_user_id(user_id, user_context)
+        if isinstance(email_info, GetEmailForUserIdOkResult):
+            email = email_info.email
+        elif isinstance(email_info, EmailDoesNotExistError):
+            return True
+        else:
+            raise Exception("Unknown User ID provided without email")
+
+    return await recipe.recipe_implementation.is_email_verified(
+        user_id, email, user_context
+    )
+
+
+async def revoke_email_verification_tokens(
+    tenant_id: str,
+    user_id: str,
+    email: Optional[str] = None,
+    user_context: Optional[Dict[str, Any]] = None,
+) -> RevokeEmailVerificationTokensOkResult:
+    if user_context is None:
+        user_context = {}
+
+    recipe = EmailVerificationRecipe.get_instance()
+    if email is None:
+        email_info = await recipe.get_email_for_user_id(user_id, user_context)
+        if isinstance(email_info, GetEmailForUserIdOkResult):
+            email = email_info.email
+        elif isinstance(email_info, EmailDoesNotExistError):
+            return RevokeEmailVerificationTokensOkResult()
+        else:
+            raise Exception("Unknown User ID provided without email")
+
+    return await EmailVerificationRecipe.get_instance().recipe_implementation.revoke_email_verification_tokens(
+        user_id, email, tenant_id, user_context
+    )
+
+
+async def unverify_email(
+    user_id: str,
+    email: Optional[str] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+):
+    if user_context is None:
+        user_context = {}
+
+    recipe = EmailVerificationRecipe.get_instance()
+    if email is None:
+        email_info = await recipe.get_email_for_user_id(user_id, user_context)
+        if isinstance(email_info, GetEmailForUserIdOkResult):
+            email = email_info.email
+        elif isinstance(email_info, EmailDoesNotExistError):
+            # Here we are returning OK since that's how it used to work, but a later call
+            # to is_verified will still return true
+            return UnverifyEmailOkResult
+        else:
+            raise Exception("Unknown User ID provided without email")
+
+    return await EmailVerificationRecipe.get_instance().recipe_implementation.unverify_email(
+        user_id, email, user_context
+    )
+
+
+async def send_email(
+    input_: EmailTemplateVars,
+    user_context: Union[None, Dict[str, Any]] = None,
+):
+    if user_context is None:
+        user_context = {}
+    return await EmailVerificationRecipe.get_instance().email_delivery.ingredient_interface_impl.send_email(
+        input_, user_context
+    )
+
+
+async def create_email_verification_link(
+    tenant_id: str,
+    user_id: str,
+    email: Optional[str],
+    user_context: Optional[Dict[str, Any]] = None,
+) -> Union[
+    CreateEmailVerificationLinkOkResult,
+    CreateEmailVerificationLinkEmailAlreadyVerifiedError,
+]:
+    if user_context is None:
+        user_context = {}
+
+    recipe_instance = EmailVerificationRecipe.get_instance()
+    app_info = recipe_instance.get_app_info()
+
+    email_verification_token = await create_email_verification_token(
+        tenant_id, user_id, email, user_context
+    )
+    if isinstance(
+        email_verification_token, CreateEmailVerificationTokenEmailAlreadyVerifiedError
+    ):
+        return CreateEmailVerificationLinkEmailAlreadyVerifiedError()
+
+    request = get_request_from_user_context(user_context)
+    return CreateEmailVerificationLinkOkResult(
+        link=get_email_verify_link(
+            app_info,
+            email_verification_token.token,
+            tenant_id,
+            request,
+            user_context,
+        )
+    )
+
+
+async def send_email_verification_email(
+    tenant_id: str,
+    user_id: str,
+    email: Optional[str],
+    user_context: Optional[Dict[str, Any]] = None,
+) -> Union[
+    SendEmailVerificationEmailOkResult,
+    SendEmailVerificationEmailAlreadyVerifiedError,
+]:
+    if user_context is None:
+        user_context = {}
+
+    if email is None:
+        recipe_instance = EmailVerificationRecipe.get_instance()
+
+        email_info = await recipe_instance.get_email_for_user_id(user_id, user_context)
+        if isinstance(email_info, GetEmailForUserIdOkResult):
+            email = email_info.email
+        elif isinstance(email_info, EmailDoesNotExistError):
+            return SendEmailVerificationEmailAlreadyVerifiedError()
+        else:
+            raise Exception("Unknown User ID provided without email")
+
+    email_verification_link = await create_email_verification_link(
+        tenant_id, user_id, email, user_context
+    )
+
+    if isinstance(
+        email_verification_link, CreateEmailVerificationLinkEmailAlreadyVerifiedError
+    ):
+        return SendEmailVerificationEmailAlreadyVerifiedError()
+
+    await send_email(
+        VerificationEmailTemplateVars(
+            user=VerificationEmailTemplateVarsUser(user_id, email),
+            email_verify_link=email_verification_link.link,
+            tenant_id=tenant_id,
+        ),
+        user_context,
+    )
+
+    return SendEmailVerificationEmailOkResult()
+
+
+
+
+
+
+
+

Functions

+
+ +
+
+
+ +Expand source code + +
async def create_email_verification_link(
+    tenant_id: str,
+    user_id: str,
+    email: Optional[str],
+    user_context: Optional[Dict[str, Any]] = None,
+) -> Union[
+    CreateEmailVerificationLinkOkResult,
+    CreateEmailVerificationLinkEmailAlreadyVerifiedError,
+]:
+    if user_context is None:
+        user_context = {}
+
+    recipe_instance = EmailVerificationRecipe.get_instance()
+    app_info = recipe_instance.get_app_info()
+
+    email_verification_token = await create_email_verification_token(
+        tenant_id, user_id, email, user_context
+    )
+    if isinstance(
+        email_verification_token, CreateEmailVerificationTokenEmailAlreadyVerifiedError
+    ):
+        return CreateEmailVerificationLinkEmailAlreadyVerifiedError()
+
+    request = get_request_from_user_context(user_context)
+    return CreateEmailVerificationLinkOkResult(
+        link=get_email_verify_link(
+            app_info,
+            email_verification_token.token,
+            tenant_id,
+            request,
+            user_context,
+        )
+    )
+
+
+
+async def create_email_verification_token(tenant_id: str, user_id: str, email: Optional[str] = None, user_context: Optional[Dict[str, Any]] = None) ‑> Union[CreateEmailVerificationTokenOkResultCreateEmailVerificationTokenEmailAlreadyVerifiedError] +
+
+
+
+ +Expand source code + +
async def create_email_verification_token(
+    tenant_id: str,
+    user_id: str,
+    email: Optional[str] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> Union[
+    CreateEmailVerificationTokenOkResult,
+    CreateEmailVerificationTokenEmailAlreadyVerifiedError,
+]:
+    if user_context is None:
+        user_context = {}
+    recipe = EmailVerificationRecipe.get_instance()
+    if email is None:
+        email_info = await recipe.get_email_for_user_id(user_id, user_context)
+        if isinstance(email_info, GetEmailForUserIdOkResult):
+            email = email_info.email
+        elif isinstance(email_info, EmailDoesNotExistError):
+            return CreateEmailVerificationTokenEmailAlreadyVerifiedError()
+        else:
+            raise Exception("Unknown User ID provided without email")
+
+    return await recipe.recipe_implementation.create_email_verification_token(
+        user_id, email, tenant_id, user_context
+    )
+
+
+
+async def is_email_verified(user_id: str, email: Optional[str] = None, user_context: Optional[Dict[str, Any]] = None) +
+
+
+
+ +Expand source code + +
async def is_email_verified(
+    user_id: str,
+    email: Optional[str] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+):
+    if user_context is None:
+        user_context = {}
+
+    recipe = EmailVerificationRecipe.get_instance()
+    if email is None:
+        email_info = await recipe.get_email_for_user_id(user_id, user_context)
+        if isinstance(email_info, GetEmailForUserIdOkResult):
+            email = email_info.email
+        elif isinstance(email_info, EmailDoesNotExistError):
+            return True
+        else:
+            raise Exception("Unknown User ID provided without email")
+
+    return await recipe.recipe_implementation.is_email_verified(
+        user_id, email, user_context
+    )
+
+
+
+async def revoke_email_verification_tokens(tenant_id: str, user_id: str, email: Optional[str] = None, user_context: Optional[Dict[str, Any]] = None) ‑> RevokeEmailVerificationTokensOkResult +
+
+
+
+ +Expand source code + +
async def revoke_email_verification_tokens(
+    tenant_id: str,
+    user_id: str,
+    email: Optional[str] = None,
+    user_context: Optional[Dict[str, Any]] = None,
+) -> RevokeEmailVerificationTokensOkResult:
+    if user_context is None:
+        user_context = {}
+
+    recipe = EmailVerificationRecipe.get_instance()
+    if email is None:
+        email_info = await recipe.get_email_for_user_id(user_id, user_context)
+        if isinstance(email_info, GetEmailForUserIdOkResult):
+            email = email_info.email
+        elif isinstance(email_info, EmailDoesNotExistError):
+            return RevokeEmailVerificationTokensOkResult()
+        else:
+            raise Exception("Unknown User ID provided without email")
+
+    return await EmailVerificationRecipe.get_instance().recipe_implementation.revoke_email_verification_tokens(
+        user_id, email, tenant_id, user_context
+    )
+
+
+
+async def send_email(input_: VerificationEmailTemplateVars, user_context: Optional[Dict[str, Any]] = None) +
+
+
+
+ +Expand source code + +
async def send_email(
+    input_: EmailTemplateVars,
+    user_context: Union[None, Dict[str, Any]] = None,
+):
+    if user_context is None:
+        user_context = {}
+    return await EmailVerificationRecipe.get_instance().email_delivery.ingredient_interface_impl.send_email(
+        input_, user_context
+    )
+
+
+
+async def send_email_verification_email(tenant_id: str, user_id: str, email: Optional[str], user_context: Optional[Dict[str, Any]] = None) ‑> Union[SendEmailVerificationEmailOkResultSendEmailVerificationEmailAlreadyVerifiedError] +
+
+
+
+ +Expand source code + +
async def send_email_verification_email(
+    tenant_id: str,
+    user_id: str,
+    email: Optional[str],
+    user_context: Optional[Dict[str, Any]] = None,
+) -> Union[
+    SendEmailVerificationEmailOkResult,
+    SendEmailVerificationEmailAlreadyVerifiedError,
+]:
+    if user_context is None:
+        user_context = {}
+
+    if email is None:
+        recipe_instance = EmailVerificationRecipe.get_instance()
+
+        email_info = await recipe_instance.get_email_for_user_id(user_id, user_context)
+        if isinstance(email_info, GetEmailForUserIdOkResult):
+            email = email_info.email
+        elif isinstance(email_info, EmailDoesNotExistError):
+            return SendEmailVerificationEmailAlreadyVerifiedError()
+        else:
+            raise Exception("Unknown User ID provided without email")
+
+    email_verification_link = await create_email_verification_link(
+        tenant_id, user_id, email, user_context
+    )
+
+    if isinstance(
+        email_verification_link, CreateEmailVerificationLinkEmailAlreadyVerifiedError
+    ):
+        return SendEmailVerificationEmailAlreadyVerifiedError()
+
+    await send_email(
+        VerificationEmailTemplateVars(
+            user=VerificationEmailTemplateVarsUser(user_id, email),
+            email_verify_link=email_verification_link.link,
+            tenant_id=tenant_id,
+        ),
+        user_context,
+    )
+
+    return SendEmailVerificationEmailOkResult()
+
+
+
+async def unverify_email(user_id: str, email: Optional[str] = None, user_context: Optional[Dict[str, Any]] = None) +
+
+
+
+ +Expand source code + +
async def unverify_email(
+    user_id: str,
+    email: Optional[str] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+):
+    if user_context is None:
+        user_context = {}
+
+    recipe = EmailVerificationRecipe.get_instance()
+    if email is None:
+        email_info = await recipe.get_email_for_user_id(user_id, user_context)
+        if isinstance(email_info, GetEmailForUserIdOkResult):
+            email = email_info.email
+        elif isinstance(email_info, EmailDoesNotExistError):
+            # Here we are returning OK since that's how it used to work, but a later call
+            # to is_verified will still return true
+            return UnverifyEmailOkResult
+        else:
+            raise Exception("Unknown User ID provided without email")
+
+    return await EmailVerificationRecipe.get_instance().recipe_implementation.unverify_email(
+        user_id, email, user_context
+    )
+
+
+
+async def verify_email_using_token(tenant_id: str, token: str, user_context: Optional[Dict[str, Any]] = None) +
+
+
+
+ +Expand source code + +
async def verify_email_using_token(
+    tenant_id: str, token: str, user_context: Union[None, Dict[str, Any]] = None
+):
+    if user_context is None:
+        user_context = {}
+    return await EmailVerificationRecipe.get_instance().recipe_implementation.verify_email_using_token(
+        token, tenant_id, user_context
+    )
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailverification/constants.html b/html/supertokens_python/recipe/emailverification/constants.html new file mode 100644 index 000000000..78a66a42c --- /dev/null +++ b/html/supertokens_python/recipe/emailverification/constants.html @@ -0,0 +1,73 @@ + + + + + + +supertokens_python.recipe.emailverification.constants API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.emailverification.constants

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+USER_EMAIL_VERIFY_TOKEN = "/user/email/verify/token"
+USER_EMAIL_VERIFY = "/user/email/verify"
+
+
+
+
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailverification/emaildelivery/index.html b/html/supertokens_python/recipe/emailverification/emaildelivery/index.html new file mode 100644 index 000000000..af56a4935 --- /dev/null +++ b/html/supertokens_python/recipe/emailverification/emaildelivery/index.html @@ -0,0 +1,83 @@ + + + + + + +supertokens_python.recipe.emailverification.emaildelivery API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.emailverification.emaildelivery

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+
+
+

Sub-modules

+
+
supertokens_python.recipe.emailverification.emaildelivery.services
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailverification/emaildelivery/services/backward_compatibility/index.html b/html/supertokens_python/recipe/emailverification/emaildelivery/services/backward_compatibility/index.html new file mode 100644 index 000000000..7c026ff1c --- /dev/null +++ b/html/supertokens_python/recipe/emailverification/emaildelivery/services/backward_compatibility/index.html @@ -0,0 +1,245 @@ + + + + + + +supertokens_python.recipe.emailverification.emaildelivery.services.backward_compatibility API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.emailverification.emaildelivery.services.backward_compatibility

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from os import environ
+from typing import Any, Dict
+
+from httpx import AsyncClient
+from supertokens_python.ingredients.emaildelivery.types import EmailDeliveryInterface
+from supertokens_python.logger import log_debug_message
+from supertokens_python.recipe.emailverification.types import (
+    User,
+    VerificationEmailTemplateVars,
+)
+from supertokens_python.supertokens import AppInfo
+from supertokens_python.utils import handle_httpx_client_exceptions
+
+
+async def create_and_send_email_using_supertokens_service(
+    app_info: AppInfo, user: User, email_verification_url: str
+) -> None:
+    if ("SUPERTOKENS_ENV" in environ) and (environ["SUPERTOKENS_ENV"] == "testing"):
+        return
+
+    data = {
+        "email": user.email,
+        "appName": app_info.app_name,
+        "emailVerifyURL": email_verification_url,
+    }
+    try:
+        async with AsyncClient(timeout=30.0) as client:
+            resp = await client.post("https://api.supertokens.io/0/st/auth/email/verify", json=data, headers={"api-version": "0"})  # type: ignore
+            resp.raise_for_status()
+            log_debug_message("Email verification email sent to %s", user.email)
+    except Exception as e:
+        log_debug_message("Error sending verification email")
+        handle_httpx_client_exceptions(e, data)
+
+
+class BackwardCompatibilityService(
+    EmailDeliveryInterface[VerificationEmailTemplateVars]
+):
+    def __init__(
+        self,
+        app_info: AppInfo,
+    ) -> None:
+        self.app_info = app_info
+
+    async def send_email(
+        self,
+        template_vars: VerificationEmailTemplateVars,
+        user_context: Dict[str, Any],
+    ) -> None:
+        try:
+            email_user = User(template_vars.user.id, template_vars.user.email)
+            await create_and_send_email_using_supertokens_service(
+                self.app_info, email_user, template_vars.email_verify_link
+            )
+        except Exception as _:
+            pass
+
+
+
+
+
+
+
+

Functions

+
+
+async def create_and_send_email_using_supertokens_service(app_info: AppInfo, user: User, email_verification_url: str) ‑> None +
+
+
+
+ +Expand source code + +
async def create_and_send_email_using_supertokens_service(
+    app_info: AppInfo, user: User, email_verification_url: str
+) -> None:
+    if ("SUPERTOKENS_ENV" in environ) and (environ["SUPERTOKENS_ENV"] == "testing"):
+        return
+
+    data = {
+        "email": user.email,
+        "appName": app_info.app_name,
+        "emailVerifyURL": email_verification_url,
+    }
+    try:
+        async with AsyncClient(timeout=30.0) as client:
+            resp = await client.post("https://api.supertokens.io/0/st/auth/email/verify", json=data, headers={"api-version": "0"})  # type: ignore
+            resp.raise_for_status()
+            log_debug_message("Email verification email sent to %s", user.email)
+    except Exception as e:
+        log_debug_message("Error sending verification email")
+        handle_httpx_client_exceptions(e, data)
+
+
+
+
+
+

Classes

+
+
+class BackwardCompatibilityService +(app_info: AppInfo) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class BackwardCompatibilityService(
+    EmailDeliveryInterface[VerificationEmailTemplateVars]
+):
+    def __init__(
+        self,
+        app_info: AppInfo,
+    ) -> None:
+        self.app_info = app_info
+
+    async def send_email(
+        self,
+        template_vars: VerificationEmailTemplateVars,
+        user_context: Dict[str, Any],
+    ) -> None:
+        try:
+            email_user = User(template_vars.user.id, template_vars.user.email)
+            await create_and_send_email_using_supertokens_service(
+                self.app_info, email_user, template_vars.email_verify_link
+            )
+        except Exception as _:
+            pass
+
+

Ancestors

+ +

Methods

+
+
+async def send_email(self, template_vars: VerificationEmailTemplateVars, user_context: Dict[str, Any]) ‑> None +
+
+
+
+ +Expand source code + +
async def send_email(
+    self,
+    template_vars: VerificationEmailTemplateVars,
+    user_context: Dict[str, Any],
+) -> None:
+    try:
+        email_user = User(template_vars.user.id, template_vars.user.email)
+        await create_and_send_email_using_supertokens_service(
+            self.app_info, email_user, template_vars.email_verify_link
+        )
+    except Exception as _:
+        pass
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailverification/emaildelivery/services/index.html b/html/supertokens_python/recipe/emailverification/emaildelivery/services/index.html new file mode 100644 index 000000000..ce7cad4f7 --- /dev/null +++ b/html/supertokens_python/recipe/emailverification/emaildelivery/services/index.html @@ -0,0 +1,92 @@ + + + + + + +supertokens_python.recipe.emailverification.emaildelivery.services API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.emailverification.emaildelivery.services

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from . import smtp
+
+SMTPService = smtp.SMTPService
+
+
+
+

Sub-modules

+
+
supertokens_python.recipe.emailverification.emaildelivery.services.backward_compatibility
+
+
+
+
supertokens_python.recipe.emailverification.emaildelivery.services.smtp
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailverification/emaildelivery/services/smtp/email_verify.html b/html/supertokens_python/recipe/emailverification/emaildelivery/services/smtp/email_verify.html new file mode 100644 index 000000000..662395f46 --- /dev/null +++ b/html/supertokens_python/recipe/emailverification/emaildelivery/services/smtp/email_verify.html @@ -0,0 +1,146 @@ + + + + + + +supertokens_python.recipe.emailverification.emaildelivery.services.smtp.email_verify API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.emailverification.emaildelivery.services.smtp.email_verify

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from string import Template
+
+from supertokens_python.ingredients.emaildelivery.types import EmailContent
+from supertokens_python.recipe.emailverification.types import (
+    VerificationEmailTemplateVars,
+)
+from supertokens_python.supertokens import Supertokens
+
+from .email_verify_email import html_template
+
+
+def get_email_verify_email_content(
+    email_input: VerificationEmailTemplateVars,
+) -> EmailContent:
+    supertokens = Supertokens.get_instance()
+    app_name = supertokens.app_info.app_name
+    body = get_email_verify_email_html(
+        app_name, email_input.user.email, email_input.email_verify_link
+    )
+    return EmailContent(
+        body, "Email verification instructions", email_input.user.email, is_html=True
+    )
+
+
+def get_email_verify_email_html(app_name: str, email: str, verification_link: str):
+    return Template(html_template).substitute(
+        appname=app_name, verificationLink=verification_link, toEmail=email
+    )
+
+
+
+
+
+
+
+

Functions

+
+
+def get_email_verify_email_content(email_input: VerificationEmailTemplateVars) ‑> EmailContent +
+
+
+
+ +Expand source code + +
def get_email_verify_email_content(
+    email_input: VerificationEmailTemplateVars,
+) -> EmailContent:
+    supertokens = Supertokens.get_instance()
+    app_name = supertokens.app_info.app_name
+    body = get_email_verify_email_html(
+        app_name, email_input.user.email, email_input.email_verify_link
+    )
+    return EmailContent(
+        body, "Email verification instructions", email_input.user.email, is_html=True
+    )
+
+
+
+def get_email_verify_email_html(app_name: str, email: str, verification_link: str) +
+
+
+
+ +Expand source code + +
def get_email_verify_email_html(app_name: str, email: str, verification_link: str):
+    return Template(html_template).substitute(
+        appname=app_name, verificationLink=verification_link, toEmail=email
+    )
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailverification/emaildelivery/services/smtp/email_verify_email.html b/html/supertokens_python/recipe/emailverification/emaildelivery/services/smtp/email_verify_email.html new file mode 100644 index 000000000..7cfd874d9 --- /dev/null +++ b/html/supertokens_python/recipe/emailverification/emaildelivery/services/smtp/email_verify_email.html @@ -0,0 +1,977 @@ + + + + + + +supertokens_python.recipe.emailverification.emaildelivery.services.smtp.email_verify_email API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.emailverification.emaildelivery.services.smtp.email_verify_email

+
+
+
+ +Expand source code + +
html_template = """
+<!doctype html>
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml"
+        xmlns:o="urn:schemas-microsoft-com:office:office">
+
+<head>
+        <meta charset="UTF-8">
+        <meta http-equiv="X-UA-Compatible" content="IE=edge">
+        <meta name="viewport" content="width=device-width, initial-scale=1">
+        <title>*|MC:SUBJECT|*</title>
+
+        <style type="text/css">
+                body {
+                        max-width: 100vw;
+                        overflow: hidden;
+                }
+                p {
+                        margin: 10px 0;
+                        padding: 0;
+                }
+
+                table {
+                        border-collapse: collapse;
+                }
+
+                h1,
+                h2,
+                h3,
+                h4,
+                h5,
+                h6 {
+                        display: block;
+                        margin: 0;
+                        padding: 0;
+                }
+
+                img,
+                a img {
+                        border: 0;
+                        height: auto;
+                        outline: none;
+                        text-decoration: none;
+                }
+
+                body,
+                #bodyTable,
+                #bodyCell {
+                        height: 100%;
+                        margin: 0;
+                        padding: 0;
+                        width: 100%;
+                }
+
+                .mcnPreviewText {
+                        display: none !important;
+                }
+
+                #outlook a {
+                        padding: 0;
+                }
+
+                img {
+                        -ms-interpolation-mode: bicubic;
+                }
+
+                table {
+                        mso-table-lspace: 0pt;
+                        mso-table-rspace: 0pt;
+                }
+
+                .ReadMsgBody {
+                        width: 100%;
+                }
+
+                .ExternalClass {
+                        width: 100%;
+                }
+
+                p,
+                a,
+                li,
+                td,
+                blockquote {
+                        mso-line-height-rule: exactly;
+                }
+
+                a[href^=tel],
+                a[href^=sms] {
+                        color: inherit;
+                        cursor: default;
+                        text-decoration: none;
+                }
+
+                p,
+                a,
+                li,
+                td,
+                body,
+                table,
+                blockquote {
+                        -ms-text-size-adjust: 100%;
+                        -webkit-text-size-adjust: 100%;
+                }
+
+                .ExternalClass,
+                .ExternalClass p,
+                .ExternalClass td,
+                .ExternalClass div,
+                .ExternalClass span,
+                .ExternalClass font {
+                        line-height: 100%;
+                }
+
+                a[x-apple-data-detectors] {
+                        color: inherit !important;
+                        text-decoration: none !important;
+                        font-size: inherit !important;
+                        font-family: inherit !important;
+                        font-weight: inherit !important;
+                        line-height: inherit !important;
+                }
+
+                .templateContainer {
+                        max-width: 600px !important;
+                }
+
+                a.mcnButton {
+                        display: block;
+                }
+
+                .mcnImage,
+                .mcnRetinaImage {
+                        vertical-align: bottom;
+                }
+
+                .mcnTextContent {
+                        word-break: break-word;
+                }
+
+                .mcnTextContent img {
+                        height: auto !important;
+                }
+
+                .mcnDividerBlock {
+                        table-layout: fixed !important;
+                }
+
+                /*
+        @tab Page
+        @section Heading 1
+        @style heading 1
+        */
+                h1 {
+                        /*@editable*/
+                        color: #222222;
+                        /*@editable*/
+                        font-family: 'Open Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
+                        /*@editable*/
+                        font-size: 40px;
+                        /*@editable*/
+                        font-style: normal;
+                        /*@editable*/
+                        font-weight: bold;
+                        /*@editable*/
+                        line-height: 150%;
+                        /*@editable*/
+                        letter-spacing: normal;
+                        /*@editable*/
+                        text-align: center;
+                }
+
+                /*
+        @tab Page
+        @section Heading 2
+        @style heading 2
+        */
+                h2 {
+                        /*@editable*/
+                        color: #222222;
+                        /*@editable*/
+                        font-family: Helvetica;
+                        /*@editable*/
+                        font-size: 34px;
+                        /*@editable*/
+                        font-style: normal;
+                        /*@editable*/
+                        font-weight: bold;
+                        /*@editable*/
+                        line-height: 150%;
+                        /*@editable*/
+                        letter-spacing: normal;
+                        /*@editable*/
+                        text-align: left;
+                }
+
+                /*
+        @tab Page
+        @section Heading 3
+        @style heading 3
+        */
+                h3 {
+                        /*@editable*/
+                        color: #444444;
+                        /*@editable*/
+                        font-family: Helvetica;
+                        /*@editable*/
+                        font-size: 22px;
+                        /*@editable*/
+                        font-style: normal;
+                        /*@editable*/
+                        font-weight: bold;
+                        /*@editable*/
+                        line-height: 150%;
+                        /*@editable*/
+                        letter-spacing: normal;
+                        /*@editable*/
+                        text-align: left;
+                }
+
+                /*
+        @tab Page
+        @section Heading 4
+        @style heading 4
+        */
+                h4 {
+                        /*@editable*/
+                        color: #949494;
+                        /*@editable*/
+                        font-family: Georgia;
+                        /*@editable*/
+                        font-size: 20px;
+                        /*@editable*/
+                        font-style: italic;
+                        /*@editable*/
+                        font-weight: normal;
+                        /*@editable*/
+                        line-height: 125%;
+                        /*@editable*/
+                        letter-spacing: normal;
+                        /*@editable*/
+                        text-align: left;
+                }
+
+                /*
+        @tab Header
+        @section Header Container Style
+        */
+                #templateHeader {
+                        /*@editable*/
+                        background-color: #f4f4f4;
+                        /*@editable*/
+                        background-image: none;
+                        /*@editable*/
+                        background-repeat: no-repeat;
+                        /*@editable*/
+                        background-position: center;
+                        /*@editable*/
+                        background-size: cover;
+                        /*@editable*/
+                        border-top: 0;
+                        /*@editable*/
+                        border-bottom: 0;
+                        /*@editable*/
+                        padding-top: 0px;
+                        /*@editable*/
+                        padding-bottom: 0px;
+                }
+
+                /*
+        @tab Header
+        @section Header Interior Style
+        */
+                .headerContainer {
+                        /*@editable*/
+                        background-color: #transparent;
+                        /*@editable*/
+                        background-image: none;
+                        /*@editable*/
+                        background-repeat: no-repeat;
+                        /*@editable*/
+                        background-position: center;
+                        /*@editable*/
+                        background-size: cover;
+                        /*@editable*/
+                        border-top: 0;
+                        /*@editable*/
+                        border-bottom: 0;
+                        /*@editable*/
+                        padding-top: 0;
+                        /*@editable*/
+                        padding-bottom: 0;
+                }
+
+                /*
+        @tab Header
+        @section Header Text
+        */
+                .headerContainer .mcnTextContent,
+                .headerContainer .mcnTextContent p {
+                        /*@editable*/
+                        color: #757575;
+                        /*@editable*/
+                        font-family: Helvetica;
+                        /*@editable*/
+                        font-size: 16px;
+                        /*@editable*/
+                        line-height: 150%;
+                        /*@editable*/
+                        text-align: left;
+                }
+
+                /*
+        @tab Header
+        @section Header Link
+        */
+                .headerContainer .mcnTextContent a,
+                .headerContainer .mcnTextContent p a {
+                        /*@editable*/
+                        color: #007C89;
+                        /*@editable*/
+                        font-weight: normal;
+                        /*@editable*/
+                        text-decoration: underline;
+                }
+
+                /*
+        @tab Body
+        @section Body Container Style
+        */
+                #templateBody {
+                        /*@editable*/
+                        background-color: #f4f4f4;
+                        /*@editable*/
+                        background-image: none;
+                        /*@editable*/
+                        background-repeat: no-repeat;
+                        /*@editable*/
+                        background-position: center;
+                        /*@editable*/
+                        background-size: cover;
+                        /*@editable*/
+                        border-top: 0;
+                        /*@editable*/
+                        border-bottom: 0;
+                        /*@editable*/
+                        padding-top: 0px;
+                        /*@editable*/
+                        padding-bottom: 20px;
+                }
+
+                /*
+        @tab Body
+        @section Body Interior Style
+        */
+                .bodyContainer {
+                        /*@editable*/
+                        background-color: #f4f4f4;
+                        /*@editable*/
+                        background-image: none;
+                        /*@editable*/
+                        background-repeat: no-repeat;
+                        /*@editable*/
+                        background-position: center;
+                        /*@editable*/
+                        background-size: cover;
+                        /*@editable*/
+                        border-top: 2px none #ff9933;
+                        /*@editable*/
+                        border-bottom: 2px none #ff9933;
+                        /*@editable*/
+                        padding-top: 10px;
+                        /*@editable*/
+                        padding-bottom: 10px;
+                }
+
+                /*
+        @tab Body
+        @section Body Text
+        */
+                .bodyContainer .mcnTextContent,
+                .bodyContainer .mcnTextContent p {
+                        /*@editable*/
+                        color: #757575;
+                        /*@editable*/
+                        font-family: Helvetica;
+                        /*@editable*/
+                        font-size: 16px;
+                        /*@editable*/
+                        line-height: 150%;
+                        /*@editable*/
+                        text-align: left;
+                }
+
+                /*
+        @tab Body
+        @section Body Link
+        */
+                .bodyContainer .mcnTextContent a,
+                .bodyContainer .mcnTextContent p a {
+                        /*@editable*/
+                        color: #222222;
+                        /*@editable*/
+                        font-weight: normal;
+                        /*@editable*/
+                        text-decoration: underline;
+                }
+
+                /*
+        @tab Footer
+        @section Footer Style
+        */
+                #templateFooter {
+                        /*@editable*/
+                        background-color: #f4f4f4;
+                        /*@editable*/
+                        background-image: none;
+                        /*@editable*/
+                        background-repeat: no-repeat;
+                        /*@editable*/
+                        background-position: center;
+                        /*@editable*/
+                        background-size: cover;
+                        /*@editable*/
+                        border-top: 0;
+                        /*@editable*/
+                        border-bottom: 0;
+                        /*@editable*/
+                        padding-top: 0px;
+                        /*@editable*/
+                        padding-bottom: 20px;
+                }
+
+                /*
+        @tab Footer
+        @section Footer Interior Style
+        */
+                .footerContainer {
+                        /*@editable*/
+                        background-color: #transparent;
+                        /*@editable*/
+                        background-image: none;
+                        /*@editable*/
+                        background-repeat: no-repeat;
+                        /*@editable*/
+                        background-position: center;
+                        /*@editable*/
+                        background-size: cover;
+                        /*@editable*/
+                        border-top: 0;
+                        /*@editable*/
+                        border-bottom: 0;
+                        /*@editable*/
+                        padding-top: 0;
+                        /*@editable*/
+                        padding-bottom: 0;
+                }
+
+                /*
+        @tab Footer
+        @section Footer Text
+        */
+                .footerContainer .mcnTextContent,
+                .footerContainer .mcnTextContent p {
+                        /*@editable*/
+                        color: #FFFFFF;
+                        /*@editable*/
+                        font-family: 'Helvetica Neue', Helvetica, Arial, Verdana, sans-serif;
+                        /*@editable*/
+                        font-size: 12px;
+                        /*@editable*/
+                        line-height: 150%;
+                        /*@editable*/
+                        text-align: center;
+                }
+
+                /*
+        @tab Footer
+        @section Footer Link
+        */
+                .footerContainer .mcnTextContent a,
+                .footerContainer .mcnTextContent p a {
+                        /*@editable*/
+                        color: #FFFFFF;
+                        /*@editable*/
+                        font-weight: normal;
+                        /*@editable*/
+                        text-decoration: underline;
+                }
+
+                @media only screen and (max-width: 480px) {
+
+                        body,
+                        table,
+                        td,
+                        p,
+                        a,
+                        li,
+                        blockquote {
+                                -webkit-text-size-adjust: none !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+                        body {
+                                width: 100% !important;
+                                min-width: 100% !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+                        .mcnRetinaImage {
+                                max-width: 100% !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+                        .mcnImage {
+                                width: 100% !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+
+                        .mcnCartContainer,
+                        .mcnCaptionTopContent,
+                        .mcnRecContentContainer,
+                        .mcnCaptionBottomContent,
+                        .mcnTextContentContainer,
+                        .mcnBoxedTextContentContainer,
+                        .mcnImageGroupContentContainer,
+                        .mcnCaptionLeftTextContentContainer,
+                        .mcnCaptionRightTextContentContainer,
+                        .mcnCaptionLeftImageContentContainer,
+                        .mcnCaptionRightImageContentContainer,
+                        .mcnImageCardLeftTextContentContainer,
+                        .mcnImageCardRightTextContentContainer,
+                        .mcnImageCardLeftImageContentContainer,
+                        .mcnImageCardRightImageContentContainer {
+                                max-width: 100% !important;
+                                width: 100% !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+                        .mcnBoxedTextContentContainer {
+                                min-width: 100% !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+                        .mcnImageGroupContent {
+                                padding: 9px !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+
+                        .mcnCaptionLeftContentOuter .mcnTextContent,
+                        .mcnCaptionRightContentOuter .mcnTextContent {
+                                padding-top: 9px !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+
+                        .mcnImageCardTopImageContent,
+                        .mcnCaptionBottomContent:last-child .mcnCaptionBottomImageContent,
+                        .mcnCaptionBlockInner .mcnCaptionTopContent:last-child .mcnTextContent {
+                                padding-top: 18px !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+                        .mcnImageCardBottomImageContent {
+                                padding-bottom: 9px !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+                        .mcnImageGroupBlockInner {
+                                padding-top: 0 !important;
+                                padding-bottom: 0 !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+                        .mcnImageGroupBlockOuter {
+                                padding-top: 9px !important;
+                                padding-bottom: 9px !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+
+                        .mcnTextContent,
+                        .mcnBoxedTextContentColumn {
+                                padding-right: 18px !important;
+                                padding-left: 18px !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+
+                        .mcnImageCardLeftImageContent,
+                        .mcnImageCardRightImageContent {
+                                padding-right: 18px !important;
+                                padding-bottom: 0 !important;
+                                padding-left: 18px !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+                        .mcpreview-image-uploader {
+                                display: none !important;
+                                width: 100% !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+
+                        /*
+        @tab Mobile Styles
+        @section Heading 1
+        @tip Make the first-level headings larger in size for better readability on small screens.
+        */
+                        h1 {
+                                /*@editable*/
+                                font-size: 30px !important;
+                                /*@editable*/
+                                line-height: 125% !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+
+                        /*
+        @tab Mobile Styles
+        @section Heading 2
+        @tip Make the second-level headings larger in size for better readability on small screens.
+        */
+                        h2 {
+                                /*@editable*/
+                                font-size: 26px !important;
+                                /*@editable*/
+                                line-height: 125% !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+
+                        /*
+        @tab Mobile Styles
+        @section Heading 3
+        @tip Make the third-level headings larger in size for better readability on small screens.
+        */
+                        h3 {
+                                /*@editable*/
+                                font-size: 20px !important;
+                                /*@editable*/
+                                line-height: 150% !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+
+                        /*
+        @tab Mobile Styles
+        @section Heading 4
+        @tip Make the fourth-level headings larger in size for better readability on small screens.
+        */
+                        h4 {
+                                /*@editable*/
+                                font-size: 18px !important;
+                                /*@editable*/
+                                line-height: 150% !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+
+                        /*
+        @tab Mobile Styles
+        @section Boxed Text
+        @tip Make the boxed text larger in size for better readability on small screens. We recommend a font size of at least 16px.
+        */
+                        .mcnBoxedTextContentContainer .mcnTextContent,
+                        .mcnBoxedTextContentContainer .mcnTextContent p {
+                                /*@editable*/
+                                font-size: 14px !important;
+                                /*@editable*/
+                                line-height: 150% !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+
+                        /*
+        @tab Mobile Styles
+        @section Header Text
+        @tip Make the header text larger in size for better readability on small screens.
+        */
+                        .headerContainer .mcnTextContent,
+                        .headerContainer .mcnTextContent p {
+                                /*@editable*/
+                                font-size: 16px !important;
+                                /*@editable*/
+                                line-height: 150% !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+
+                        /*
+        @tab Mobile Styles
+        @section Body Text
+        @tip Make the body text larger in size for better readability on small screens. We recommend a font size of at least 16px.
+        */
+                        .bodyContainer .mcnTextContent,
+                        .bodyContainer .mcnTextContent p {
+                                /*@editable*/
+                                font-size: 16px !important;
+                                /*@editable*/
+                                line-height: 150% !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+
+                        /*
+        @tab Mobile Styles
+        @section Footer Text
+        @tip Make the footer content text larger in size for better readability on small screens.
+        */
+                        .footerContainer .mcnTextContent,
+                        .footerContainer .mcnTextContent p {
+                                /*@editable*/
+                                font-size: 14px !important;
+                                /*@editable*/
+                                line-height: 150% !important;
+                        }
+
+                }
+                @media only screen and (max-width: 480px) {
+                        #meant-for {
+                                padding: 20px;
+                        }
+                }
+        </style>
+</head>
+
+<body>
+        <!--*|IF:MC_PREVIEW_TEXT|*-->
+        <!--[if !gte mso 9]><!----><span class="mcnPreviewText"
+                style="display:none; font-size:0px; line-height:0px; max-height:0px; max-width:0px; opacity:0; overflow:hidden; visibility:hidden; mso-hide:all;"></span>
+        <!--<![endif]-->
+        <!--*|END:IF|*-->
+        <center>
+                <table align="center" border="0" cellpadding="0" cellspacing="0" height="100%" width="100%" id="bodyTable">
+                        <tr>
+                                <td align="center" valign="top" id="bodyCell">
+                                        <!-- BEGIN TEMPLATE // -->
+                                        <table border="0" cellpadding="0" cellspacing="0" width="100%">
+                                                <tr>
+                                                        <td align="center" valign="top" id="templateHeader" data-template-container>
+                                                                <!--[if (gte mso 9)|(IE)]>
+                                    <table align="center" border="0" cellspacing="0" cellpadding="0" width="600" style="width:600px;">
+                                    <tr>
+                                    <td align="center" valign="top" width="600" style="width:600px;">
+                                    <![endif]-->
+                                                                <table align="center" border="0" cellpadding="0" cellspacing="0" width="100%"
+                                                                        class="templateContainer">
+                                                                        <tr>
+                                                                                <td valign="top" class="headerContainer"></td>
+                                                                        </tr>
+                                                                </table>
+                                                                <!--[if (gte mso 9)|(IE)]>
+                                    </td>
+                                    </tr>
+                                    </table>
+                                    <![endif]-->
+                                                        </td>
+                                                </tr>
+                                                <tr>
+                                                        <td align="center" valign="top" id="templateBody" data-template-container>
+                                                                <!--[if (gte mso 9)|(IE)]>
+                                    <table align="center" border="0" cellspacing="0" cellpadding="0" width="600" style="width:600px;">
+                                    <tr>
+                                    <td align="center" valign="top" width="600" style="width:600px;">
+                                    <![endif]-->
+                                                                <table align="center" border="0" cellpadding="0" cellspacing="0" width="100%"
+                                                                        class="templateContainer">
+                                                                        <tr>
+                                                                                <td valign="top" class="bodyContainer">
+                                                                                        <table border="0" cellpadding="0" cellspacing="0" width="100%"
+                                                                                                class="mcnCodeBlock">
+                                                                                                <tbody class="mcnTextBlockOuter">
+                                                                                                        <tr>
+                                                                                                                <td valign="top" class="mcnTextBlockInner">
+
+
+                                                                                                                        <div
+                                                                                                                                style="background-color:#fff; margin-left: 3%;  margin-top: 48px; margin-right: 3%; border: 1px solid #ddd;  border-radius: 6px;">
+                                                                                                                                <div style="padding-left: 15%; padding-right: 15%;">
+
+                                                                                                                                        <p
+                                                                                                                                                style="font-family:'Helvetica'; font-size: 16px; line-height: 26px; font-weight:700; text-align: center; padding-top: 24px; padding-bottom: 24px; padding-left: 8%; padding-right: 8%; ">
+                                                                                                                                                Please verify your email address for ${appname}
+                                                                                                                                                by clicking the button below.</p>
+
+                                                                                                                                        <div class="button-td button-td-primary"
+                                                                                                                                                style="border-radius: 6px; margin-bottom: 50px; display: block; text-align: center;">
+                                                                                                                                                <a class="button-a button-a-primary"
+                                                                                                                                                        href="${verificationLink}" target="_blank"
+                                                                                                                                                        style="background: #52B56E;font-size: 17px;line-height: 24px;font-weight: 700;font-family: 'Helvetica', sans-serif;text-decoration: none;padding: 9px 25px 9px 25px;color: #ffffff;display: block;border-radius: 6px;width: fit-content;margin: 0 auto;">Verify
+                                                                                                                                                        My Email</a>
+                                                                                                                                        </div>
+                                                                                                                                </div>
+                                                                                                                                <div
+                                                                                                                                        style="background-color:#fafafa; border-top: 1px solid #ddd; padding-left: 15%; padding-right: 15%; padding-bottom: 24px; padding-top: 24px">
+                                                                                                                                        <p
+                                                                                                                                                style="max-width: 600px !important; margin: auto; font-family: 'Hevetica', sans-serif; font-size: 14px; line-height: 23px; font-weight:400;  text-align: center; color: #808080;">
+                                                                                                                                                Alternatively, you can directly paste this link
+                                                                                                                                                in your browser <br>
+                                                                                                                                                <a style="font-family: 'Helvetica', sans-serif, sans-serif; text-align: center; word-break: break-all; font-weight: 400; font-size: 14px; line-height: 23px; color: #007aff !important;"
+                                                                                                                                                        target="_blank"
+                                                                                                                                                        href="${verificationLink}">${verificationLink}</a>
+                                                                                                                                        </p>
+                                                                                                                                </div>
+                                                                                                                        </div>
+
+
+
+
+                                                                                                                </td>
+                                                                                                        </tr>
+                                                                                                </tbody>
+                                                                                        </table>
+                                                                                </td>
+                                                                        </tr>
+                                                                </table>
+                                                                <!--[if (gte mso 9)|(IE)]>
+                                    </td>
+                                    </tr>
+                                    </table>
+                                    <![endif]-->
+                                                        </td>
+                                                </tr>
+                                                <tr>
+                                                        <td align="center" valign="top" id="templateFooter" data-template-container>
+                                                                <!--[if (gte mso 9)|(IE)]>
+                                    <table align="center" border="0" cellspacing="0" cellpadding="0" width="600" style="width:600px;">
+                                    <tr>
+                                    <td align="center" valign="top" width="600" style="width:600px;">
+                                    <![endif]-->
+                                                                <table align="center" border="0" cellpadding="0" cellspacing="0" width="100%"
+                                                                        class="templateContainer">
+                                                                        <tr>
+                                                                                <td valign="top" class="footerContainer">
+                                                                                        <table border="0" cellpadding="0" cellspacing="0" width="100%"
+                                                                                                class="mcnCodeBlock">
+                                                                                                <tbody class="mcnTextBlockOuter">
+                                                                                                        <tr>
+                                                                                                                <td valign="top" class="mcnTextBlockInner">
+
+
+                                                                                                                        <p
+                                                                                                                            id="meant-for"
+                                                                                                                                style="font-family: 'Helvetica', sans-serif; font-size: 16px; line-height: 26px; font-weight:400; text-align: center; color: #808080">
+                                                                                                                                This email is meant for <a
+                                                                                                                                        style="font-family: 'Helvetica', sans-serif; text-align: center; word-break: break-all; font-weight: 400; font-size: 16px; line-height: 26px; color: #808080 !important;"
+                                                                                                                                        target="_blank"
+                                                                                                                                        href="mailto:${toEmail}">${toEmail}</a>
+                                                                                                                        </p>
+                                                                                                                </td>
+                                                                                                        </tr>
+                                                                                                </tbody>
+                                                                                        </table>
+                                                                                </td>
+                                                                        </tr>
+                                                                </table>
+                                                                <!--[if (gte mso 9)|(IE)]>
+                                    </td>
+                                    </tr>
+                                    </table>
+                                    <![endif]-->
+                                                        </td>
+                                                </tr>
+                                        </table>
+                                        <!-- // END TEMPLATE -->
+                                </td>
+                        </tr>
+                </table>
+        </center>
+</body>
+
+</html>
+"""
+
+
+
+
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailverification/emaildelivery/services/smtp/index.html b/html/supertokens_python/recipe/emailverification/emaildelivery/services/smtp/index.html new file mode 100644 index 000000000..0bade6d5a --- /dev/null +++ b/html/supertokens_python/recipe/emailverification/emaildelivery/services/smtp/index.html @@ -0,0 +1,215 @@ + + + + + + +supertokens_python.recipe.emailverification.emaildelivery.services.smtp API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.emailverification.emaildelivery.services.smtp

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from typing import Any, Dict, Callable, Union
+
+from supertokens_python.ingredients.emaildelivery.services.smtp import Transporter
+from supertokens_python.ingredients.emaildelivery.types import (
+    EmailDeliveryInterface,
+    SMTPServiceInterface,
+    SMTPSettings,
+)
+from supertokens_python.recipe.emailverification.types import (
+    VerificationEmailTemplateVars,
+    SMTPOverrideInput,
+)
+
+from .service_implementation import ServiceImplementation
+
+
+class SMTPService(EmailDeliveryInterface[VerificationEmailTemplateVars]):
+    service_implementation: SMTPServiceInterface[VerificationEmailTemplateVars]
+
+    def __init__(
+        self,
+        smtp_settings: SMTPSettings,
+        override: Union[Callable[[SMTPOverrideInput], SMTPOverrideInput], None] = None,
+    ) -> None:
+        transporter = Transporter(smtp_settings)
+        oi = ServiceImplementation(transporter)
+        self.service_implementation = oi if override is None else override(oi)
+
+    async def send_email(
+        self,
+        template_vars: VerificationEmailTemplateVars,
+        user_context: Dict[str, Any],
+    ) -> None:
+        content = await self.service_implementation.get_content(
+            template_vars, user_context
+        )
+        await self.service_implementation.send_raw_email(content, user_context)
+
+
+
+

Sub-modules

+
+
supertokens_python.recipe.emailverification.emaildelivery.services.smtp.email_verify
+
+
+
+
supertokens_python.recipe.emailverification.emaildelivery.services.smtp.email_verify_email
+
+
+
+
supertokens_python.recipe.emailverification.emaildelivery.services.smtp.service_implementation
+
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class SMTPService +(smtp_settings: SMTPSettings, override: Optional[Callable[[SMTPServiceInterface[VerificationEmailTemplateVars]], SMTPServiceInterface[VerificationEmailTemplateVars]]] = None) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class SMTPService(EmailDeliveryInterface[VerificationEmailTemplateVars]):
+    service_implementation: SMTPServiceInterface[VerificationEmailTemplateVars]
+
+    def __init__(
+        self,
+        smtp_settings: SMTPSettings,
+        override: Union[Callable[[SMTPOverrideInput], SMTPOverrideInput], None] = None,
+    ) -> None:
+        transporter = Transporter(smtp_settings)
+        oi = ServiceImplementation(transporter)
+        self.service_implementation = oi if override is None else override(oi)
+
+    async def send_email(
+        self,
+        template_vars: VerificationEmailTemplateVars,
+        user_context: Dict[str, Any],
+    ) -> None:
+        content = await self.service_implementation.get_content(
+            template_vars, user_context
+        )
+        await self.service_implementation.send_raw_email(content, user_context)
+
+

Ancestors

+ +

Class variables

+
+
var service_implementationSMTPServiceInterface[VerificationEmailTemplateVars]
+
+
+
+
+

Methods

+
+
+async def send_email(self, template_vars: VerificationEmailTemplateVars, user_context: Dict[str, Any]) ‑> None +
+
+
+
+ +Expand source code + +
async def send_email(
+    self,
+    template_vars: VerificationEmailTemplateVars,
+    user_context: Dict[str, Any],
+) -> None:
+    content = await self.service_implementation.get_content(
+        template_vars, user_context
+    )
+    await self.service_implementation.send_raw_email(content, user_context)
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailverification/emaildelivery/services/smtp/service_implementation.html b/html/supertokens_python/recipe/emailverification/emaildelivery/services/smtp/service_implementation.html new file mode 100644 index 000000000..1c270e459 --- /dev/null +++ b/html/supertokens_python/recipe/emailverification/emaildelivery/services/smtp/service_implementation.html @@ -0,0 +1,175 @@ + + + + + + +supertokens_python.recipe.emailverification.emaildelivery.services.smtp.service_implementation API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.emailverification.emaildelivery.services.smtp.service_implementation

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from typing import Any, Dict
+
+from supertokens_python.ingredients.emaildelivery.types import (
+    EmailContent,
+    SMTPServiceInterface,
+)
+from supertokens_python.recipe.emailverification.emaildelivery.services.smtp.email_verify import (
+    get_email_verify_email_content,
+)
+from supertokens_python.recipe.emailverification.types import (
+    VerificationEmailTemplateVars,
+)
+
+
+class ServiceImplementation(SMTPServiceInterface[VerificationEmailTemplateVars]):
+    async def send_raw_email(
+        self, content: EmailContent, user_context: Dict[str, Any]
+    ) -> None:
+        await self.transporter.send_email(content, user_context)
+
+    async def get_content(
+        self, template_vars: VerificationEmailTemplateVars, user_context: Dict[str, Any]
+    ) -> EmailContent:
+        _ = user_context
+        return get_email_verify_email_content(template_vars)
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class ServiceImplementation +(transporter: Transporter) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class ServiceImplementation(SMTPServiceInterface[VerificationEmailTemplateVars]):
+    async def send_raw_email(
+        self, content: EmailContent, user_context: Dict[str, Any]
+    ) -> None:
+        await self.transporter.send_email(content, user_context)
+
+    async def get_content(
+        self, template_vars: VerificationEmailTemplateVars, user_context: Dict[str, Any]
+    ) -> EmailContent:
+        _ = user_context
+        return get_email_verify_email_content(template_vars)
+
+

Ancestors

+ +

Methods

+
+
+async def get_content(self, template_vars: VerificationEmailTemplateVars, user_context: Dict[str, Any]) ‑> EmailContent +
+
+
+
+ +Expand source code + +
async def get_content(
+    self, template_vars: VerificationEmailTemplateVars, user_context: Dict[str, Any]
+) -> EmailContent:
+    _ = user_context
+    return get_email_verify_email_content(template_vars)
+
+
+
+async def send_raw_email(self, content: EmailContent, user_context: Dict[str, Any]) ‑> None +
+
+
+
+ +Expand source code + +
async def send_raw_email(
+    self, content: EmailContent, user_context: Dict[str, Any]
+) -> None:
+    await self.transporter.send_email(content, user_context)
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailverification/ev_claim_validators.html b/html/supertokens_python/recipe/emailverification/ev_claim_validators.html new file mode 100644 index 000000000..2a04f93c7 --- /dev/null +++ b/html/supertokens_python/recipe/emailverification/ev_claim_validators.html @@ -0,0 +1,72 @@ + + + + + + +supertokens_python.recipe.emailverification.ev_claim_validators API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.emailverification.ev_claim_validators

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+
+
+
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailverification/exceptions.html b/html/supertokens_python/recipe/emailverification/exceptions.html new file mode 100644 index 000000000..26b6b5879 --- /dev/null +++ b/html/supertokens_python/recipe/emailverification/exceptions.html @@ -0,0 +1,140 @@ + + + + + + +supertokens_python.recipe.emailverification.exceptions API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.emailverification.exceptions

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from supertokens_python.exceptions import SuperTokensError
+
+
+class SuperTokensEmailVerificationError(SuperTokensError):
+    pass
+
+
+class EmailVerificationInvalidTokenError(SuperTokensEmailVerificationError):
+    pass
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class EmailVerificationInvalidTokenError +(*args, **kwargs) +
+
+

Common base class for all non-exit exceptions.

+
+ +Expand source code + +
class EmailVerificationInvalidTokenError(SuperTokensEmailVerificationError):
+    pass
+
+

Ancestors

+ +
+
+class SuperTokensEmailVerificationError +(*args, **kwargs) +
+
+

Common base class for all non-exit exceptions.

+
+ +Expand source code + +
class SuperTokensEmailVerificationError(SuperTokensError):
+    pass
+
+

Ancestors

+ +

Subclasses

+ +
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailverification/index.html b/html/supertokens_python/recipe/emailverification/index.html new file mode 100644 index 000000000..62d303262 --- /dev/null +++ b/html/supertokens_python/recipe/emailverification/index.html @@ -0,0 +1,210 @@ + + + + + + +supertokens_python.recipe.emailverification API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.emailverification

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Callable, Union, Optional
+
+from . import exceptions as ex
+from . import utils
+from .emaildelivery import services as emaildelivery_services
+from . import recipe
+from . import types
+from .interfaces import TypeGetEmailForUserIdFunction
+from .recipe import EmailVerificationRecipe
+from .types import EmailTemplateVars
+from ...ingredients.emaildelivery.types import EmailDeliveryConfig
+
+InputOverrideConfig = utils.OverrideConfig
+exception = ex
+SMTPService = emaildelivery_services.SMTPService
+EmailVerificationClaim = recipe.EmailVerificationClaim
+EmailDeliveryInterface = types.EmailDeliveryInterface
+
+
+if TYPE_CHECKING:
+    from supertokens_python.supertokens import AppInfo
+
+    from ...recipe_module import RecipeModule
+
+from .utils import MODE_TYPE, OverrideConfig
+
+
+def init(
+    mode: MODE_TYPE,
+    email_delivery: Union[EmailDeliveryConfig[EmailTemplateVars], None] = None,
+    get_email_for_user_id: Optional[TypeGetEmailForUserIdFunction] = None,
+    override: Union[OverrideConfig, None] = None,
+) -> Callable[[AppInfo], RecipeModule]:
+    return EmailVerificationRecipe.init(
+        mode,
+        email_delivery,
+        get_email_for_user_id,
+        override,
+    )
+
+
+
+

Sub-modules

+
+
supertokens_python.recipe.emailverification.api
+
+
+
+
supertokens_python.recipe.emailverification.asyncio
+
+
+
+
supertokens_python.recipe.emailverification.constants
+
+
+
+
supertokens_python.recipe.emailverification.emaildelivery
+
+
+
+
supertokens_python.recipe.emailverification.ev_claim_validators
+
+
+
+
supertokens_python.recipe.emailverification.exceptions
+
+
+
+
supertokens_python.recipe.emailverification.interfaces
+
+
+
+
supertokens_python.recipe.emailverification.recipe
+
+
+
+
supertokens_python.recipe.emailverification.recipe_implementation
+
+
+
+
supertokens_python.recipe.emailverification.syncio
+
+
+
+
supertokens_python.recipe.emailverification.types
+
+
+
+
supertokens_python.recipe.emailverification.utils
+
+
+
+
+
+
+
+
+

Functions

+
+
+def init(mode: MODE_TYPE, email_delivery: Union[EmailDeliveryConfig[EmailTemplateVars], None] = None, get_email_for_user_id: Optional[TypeGetEmailForUserIdFunction] = None, override: Union[OverrideConfig, None] = None) ‑> Callable[[AppInfo], RecipeModule] +
+
+
+
+ +Expand source code + +
def init(
+    mode: MODE_TYPE,
+    email_delivery: Union[EmailDeliveryConfig[EmailTemplateVars], None] = None,
+    get_email_for_user_id: Optional[TypeGetEmailForUserIdFunction] = None,
+    override: Union[OverrideConfig, None] = None,
+) -> Callable[[AppInfo], RecipeModule]:
+    return EmailVerificationRecipe.init(
+        mode,
+        email_delivery,
+        get_email_for_user_id,
+        override,
+    )
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailverification/interfaces.html b/html/supertokens_python/recipe/emailverification/interfaces.html new file mode 100644 index 000000000..bdd594d4e --- /dev/null +++ b/html/supertokens_python/recipe/emailverification/interfaces.html @@ -0,0 +1,1169 @@ + + + + + + +supertokens_python.recipe.emailverification.interfaces API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.emailverification.interfaces

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from abc import ABC, abstractmethod
+from typing import TYPE_CHECKING, Any, Awaitable, Callable, Dict, Optional, Union
+
+from supertokens_python.ingredients.emaildelivery import EmailDeliveryIngredient
+from supertokens_python.types import APIResponse, GeneralErrorResponse
+
+from ...supertokens import AppInfo
+from ..session.interfaces import SessionContainer
+
+if TYPE_CHECKING:
+    from supertokens_python.framework import BaseRequest, BaseResponse
+
+    from .types import User, VerificationEmailTemplateVars
+    from .utils import EmailVerificationConfig
+
+
+class CreateEmailVerificationTokenOkResult:
+    status = "OK"
+
+    def __init__(self, token: str):
+        self.token = token
+
+
+class CreateEmailVerificationTokenEmailAlreadyVerifiedError:
+    status = "EMAIL_ALREADY_VERIFIED_ERROR"
+
+
+class CreateEmailVerificationLinkEmailAlreadyVerifiedError:
+    status = "EMAIL_ALREADY_VERIFIED_ERROR"
+
+
+class CreateEmailVerificationLinkOkResult:
+    status = "OK"
+
+    def __init__(self, link: str):
+        self.link = link
+
+
+class SendEmailVerificationEmailAlreadyVerifiedError:
+    status = "EMAIL_ALREADY_VERIFIED_ERROR"
+
+
+class SendEmailVerificationEmailOkResult:
+    status = "OK"
+
+
+class VerifyEmailUsingTokenOkResult:
+    status = "OK"
+
+    def __init__(self, user: User):
+        self.user = user
+
+
+class VerifyEmailUsingTokenInvalidTokenError:
+    pass
+
+
+class RevokeEmailVerificationTokensOkResult:
+    pass
+
+
+class UnverifyEmailOkResult:
+    pass
+
+
+class RecipeInterface(ABC):
+    def __init__(self):
+        pass
+
+    @abstractmethod
+    async def create_email_verification_token(
+        self, user_id: str, email: str, tenant_id: str, user_context: Dict[str, Any]
+    ) -> Union[
+        CreateEmailVerificationTokenOkResult,
+        CreateEmailVerificationTokenEmailAlreadyVerifiedError,
+    ]:
+        pass
+
+    @abstractmethod
+    async def verify_email_using_token(
+        self, token: str, tenant_id: str, user_context: Dict[str, Any]
+    ) -> Union[VerifyEmailUsingTokenOkResult, VerifyEmailUsingTokenInvalidTokenError]:
+        pass
+
+    @abstractmethod
+    async def is_email_verified(
+        self, user_id: str, email: str, user_context: Dict[str, Any]
+    ) -> bool:
+        pass
+
+    @abstractmethod
+    async def revoke_email_verification_tokens(
+        self, user_id: str, email: str, tenant_id: str, user_context: Dict[str, Any]
+    ) -> RevokeEmailVerificationTokensOkResult:
+        pass
+
+    @abstractmethod
+    async def unverify_email(
+        self, user_id: str, email: str, user_context: Dict[str, Any]
+    ) -> UnverifyEmailOkResult:
+        pass
+
+
+class APIOptions:
+    def __init__(
+        self,
+        request: BaseRequest,
+        response: BaseResponse,
+        recipe_id: str,
+        config: EmailVerificationConfig,
+        recipe_implementation: RecipeInterface,
+        app_info: AppInfo,
+        email_delivery: EmailDeliveryIngredient[VerificationEmailTemplateVars],
+    ):
+        self.request = request
+        self.response = response
+        self.recipe_id = recipe_id
+        self.config = config
+        self.recipe_implementation = recipe_implementation
+        self.app_info = app_info
+        self.email_delivery = email_delivery
+
+
+class EmailVerifyPostOkResult(APIResponse):
+    def __init__(self, user: User):
+        self.user = user
+        self.status = "OK"
+
+    def to_json(self) -> Dict[str, Any]:
+        return {
+            "status": self.status,
+            "user": {"id": self.user.user_id, "email": self.user.email},
+        }
+
+
+class EmailVerifyPostInvalidTokenError(APIResponse):
+    def __init__(self):
+        self.status = "EMAIL_VERIFICATION_INVALID_TOKEN_ERROR"
+
+    def to_json(self) -> Dict[str, Any]:
+        return {"status": self.status}
+
+
+class IsEmailVerifiedGetOkResult(APIResponse):
+    def __init__(self, is_verified: bool):
+        self.status = "OK"
+        self.is_verified = is_verified
+
+    def to_json(self) -> Dict[str, Any]:
+        return {"status": self.status, "isVerified": self.is_verified}
+
+
+class GenerateEmailVerifyTokenPostOkResult(APIResponse):
+    def __init__(self):
+        self.status = "OK"
+
+    def to_json(self) -> Dict[str, Any]:
+        return {"status": self.status}
+
+
+class GenerateEmailVerifyTokenPostEmailAlreadyVerifiedError(APIResponse):
+    def __init__(self):
+        self.status = "EMAIL_ALREADY_VERIFIED_ERROR"
+
+    def to_json(self) -> Dict[str, Any]:
+        return {"status": self.status}
+
+
+class APIInterface(ABC):
+    def __init__(self):
+        self.disable_email_verify_post = False
+        self.disable_is_email_verified_get = False
+        self.disable_generate_email_verify_token_post = False
+
+    @abstractmethod
+    async def email_verify_post(
+        self,
+        token: str,
+        session: Optional[SessionContainer],
+        tenant_id: str,
+        api_options: APIOptions,
+        user_context: Dict[str, Any],
+    ) -> Union[
+        EmailVerifyPostOkResult, EmailVerifyPostInvalidTokenError, GeneralErrorResponse
+    ]:
+        pass
+
+    @abstractmethod
+    async def is_email_verified_get(
+        self,
+        session: SessionContainer,
+        api_options: APIOptions,
+        user_context: Dict[str, Any],
+    ) -> Union[IsEmailVerifiedGetOkResult, GeneralErrorResponse]:
+        pass
+
+    @abstractmethod
+    async def generate_email_verify_token_post(
+        self,
+        session: SessionContainer,
+        api_options: APIOptions,
+        user_context: Dict[str, Any],
+    ) -> Union[
+        GenerateEmailVerifyTokenPostOkResult,
+        GenerateEmailVerifyTokenPostEmailAlreadyVerifiedError,
+        GeneralErrorResponse,
+    ]:
+        pass
+
+
+class GetEmailForUserIdOkResult:
+    def __init__(self, email: str):
+        self.email = email
+
+
+class EmailDoesNotExistError(Exception):
+    pass
+
+
+class UnknownUserIdError(Exception):
+    pass
+
+
+TypeGetEmailForUserIdFunction = Callable[
+    [str, Dict[str, Any]],
+    Awaitable[
+        Union[GetEmailForUserIdOkResult, EmailDoesNotExistError, UnknownUserIdError]
+    ],
+]
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class APIInterface +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class APIInterface(ABC):
+    def __init__(self):
+        self.disable_email_verify_post = False
+        self.disable_is_email_verified_get = False
+        self.disable_generate_email_verify_token_post = False
+
+    @abstractmethod
+    async def email_verify_post(
+        self,
+        token: str,
+        session: Optional[SessionContainer],
+        tenant_id: str,
+        api_options: APIOptions,
+        user_context: Dict[str, Any],
+    ) -> Union[
+        EmailVerifyPostOkResult, EmailVerifyPostInvalidTokenError, GeneralErrorResponse
+    ]:
+        pass
+
+    @abstractmethod
+    async def is_email_verified_get(
+        self,
+        session: SessionContainer,
+        api_options: APIOptions,
+        user_context: Dict[str, Any],
+    ) -> Union[IsEmailVerifiedGetOkResult, GeneralErrorResponse]:
+        pass
+
+    @abstractmethod
+    async def generate_email_verify_token_post(
+        self,
+        session: SessionContainer,
+        api_options: APIOptions,
+        user_context: Dict[str, Any],
+    ) -> Union[
+        GenerateEmailVerifyTokenPostOkResult,
+        GenerateEmailVerifyTokenPostEmailAlreadyVerifiedError,
+        GeneralErrorResponse,
+    ]:
+        pass
+
+

Ancestors

+
    +
  • abc.ABC
  • +
+

Subclasses

+ +

Methods

+
+
+async def email_verify_post(self, token: str, session: Optional[SessionContainer], tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[EmailVerifyPostOkResultEmailVerifyPostInvalidTokenErrorGeneralErrorResponse] +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def email_verify_post(
+    self,
+    token: str,
+    session: Optional[SessionContainer],
+    tenant_id: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+) -> Union[
+    EmailVerifyPostOkResult, EmailVerifyPostInvalidTokenError, GeneralErrorResponse
+]:
+    pass
+
+
+
+async def generate_email_verify_token_post(self, session: SessionContainer, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[GenerateEmailVerifyTokenPostOkResultGenerateEmailVerifyTokenPostEmailAlreadyVerifiedErrorGeneralErrorResponse] +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def generate_email_verify_token_post(
+    self,
+    session: SessionContainer,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+) -> Union[
+    GenerateEmailVerifyTokenPostOkResult,
+    GenerateEmailVerifyTokenPostEmailAlreadyVerifiedError,
+    GeneralErrorResponse,
+]:
+    pass
+
+
+
+async def is_email_verified_get(self, session: SessionContainer, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[IsEmailVerifiedGetOkResultGeneralErrorResponse] +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def is_email_verified_get(
+    self,
+    session: SessionContainer,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+) -> Union[IsEmailVerifiedGetOkResult, GeneralErrorResponse]:
+    pass
+
+
+
+
+
+class APIOptions +(request: BaseRequest, response: BaseResponse, recipe_id: str, config: EmailVerificationConfig, recipe_implementation: RecipeInterface, app_info: AppInfo, email_delivery: EmailDeliveryIngredient[VerificationEmailTemplateVars]) +
+
+
+
+ +Expand source code + +
class APIOptions:
+    def __init__(
+        self,
+        request: BaseRequest,
+        response: BaseResponse,
+        recipe_id: str,
+        config: EmailVerificationConfig,
+        recipe_implementation: RecipeInterface,
+        app_info: AppInfo,
+        email_delivery: EmailDeliveryIngredient[VerificationEmailTemplateVars],
+    ):
+        self.request = request
+        self.response = response
+        self.recipe_id = recipe_id
+        self.config = config
+        self.recipe_implementation = recipe_implementation
+        self.app_info = app_info
+        self.email_delivery = email_delivery
+
+
+
+class CreateEmailVerificationLinkEmailAlreadyVerifiedError +
+
+
+
+ +Expand source code + +
class CreateEmailVerificationLinkEmailAlreadyVerifiedError:
+    status = "EMAIL_ALREADY_VERIFIED_ERROR"
+
+

Class variables

+
+
var status
+
+
+
+
+
+
+class CreateEmailVerificationLinkOkResult +(link: str) +
+
+
+
+ +Expand source code + +
class CreateEmailVerificationLinkOkResult:
+    status = "OK"
+
+    def __init__(self, link: str):
+        self.link = link
+
+

Class variables

+
+
var status
+
+
+
+
+
+
+class CreateEmailVerificationTokenEmailAlreadyVerifiedError +
+
+
+
+ +Expand source code + +
class CreateEmailVerificationTokenEmailAlreadyVerifiedError:
+    status = "EMAIL_ALREADY_VERIFIED_ERROR"
+
+

Class variables

+
+
var status
+
+
+
+
+
+
+class CreateEmailVerificationTokenOkResult +(token: str) +
+
+
+
+ +Expand source code + +
class CreateEmailVerificationTokenOkResult:
+    status = "OK"
+
+    def __init__(self, token: str):
+        self.token = token
+
+

Class variables

+
+
var status
+
+
+
+
+
+
+class EmailDoesNotExistError +(*args, **kwargs) +
+
+

Common base class for all non-exit exceptions.

+
+ +Expand source code + +
class EmailDoesNotExistError(Exception):
+    pass
+
+

Ancestors

+
    +
  • builtins.Exception
  • +
  • builtins.BaseException
  • +
+
+
+class EmailVerifyPostInvalidTokenError +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class EmailVerifyPostInvalidTokenError(APIResponse):
+    def __init__(self):
+        self.status = "EMAIL_VERIFICATION_INVALID_TOKEN_ERROR"
+
+    def to_json(self) -> Dict[str, Any]:
+        return {"status": self.status}
+
+

Ancestors

+ +

Methods

+
+
+def to_json(self) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
def to_json(self) -> Dict[str, Any]:
+    return {"status": self.status}
+
+
+
+
+
+class EmailVerifyPostOkResult +(user: User) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class EmailVerifyPostOkResult(APIResponse):
+    def __init__(self, user: User):
+        self.user = user
+        self.status = "OK"
+
+    def to_json(self) -> Dict[str, Any]:
+        return {
+            "status": self.status,
+            "user": {"id": self.user.user_id, "email": self.user.email},
+        }
+
+

Ancestors

+ +

Methods

+
+
+def to_json(self) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
def to_json(self) -> Dict[str, Any]:
+    return {
+        "status": self.status,
+        "user": {"id": self.user.user_id, "email": self.user.email},
+    }
+
+
+
+
+
+class GenerateEmailVerifyTokenPostEmailAlreadyVerifiedError +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class GenerateEmailVerifyTokenPostEmailAlreadyVerifiedError(APIResponse):
+    def __init__(self):
+        self.status = "EMAIL_ALREADY_VERIFIED_ERROR"
+
+    def to_json(self) -> Dict[str, Any]:
+        return {"status": self.status}
+
+

Ancestors

+ +

Methods

+
+
+def to_json(self) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
def to_json(self) -> Dict[str, Any]:
+    return {"status": self.status}
+
+
+
+
+
+class GenerateEmailVerifyTokenPostOkResult +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class GenerateEmailVerifyTokenPostOkResult(APIResponse):
+    def __init__(self):
+        self.status = "OK"
+
+    def to_json(self) -> Dict[str, Any]:
+        return {"status": self.status}
+
+

Ancestors

+ +

Methods

+
+
+def to_json(self) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
def to_json(self) -> Dict[str, Any]:
+    return {"status": self.status}
+
+
+
+
+
+class GetEmailForUserIdOkResult +(email: str) +
+
+
+
+ +Expand source code + +
class GetEmailForUserIdOkResult:
+    def __init__(self, email: str):
+        self.email = email
+
+
+
+class IsEmailVerifiedGetOkResult +(is_verified: bool) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class IsEmailVerifiedGetOkResult(APIResponse):
+    def __init__(self, is_verified: bool):
+        self.status = "OK"
+        self.is_verified = is_verified
+
+    def to_json(self) -> Dict[str, Any]:
+        return {"status": self.status, "isVerified": self.is_verified}
+
+

Ancestors

+ +

Methods

+
+
+def to_json(self) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
def to_json(self) -> Dict[str, Any]:
+    return {"status": self.status, "isVerified": self.is_verified}
+
+
+
+
+
+class RecipeInterface +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class RecipeInterface(ABC):
+    def __init__(self):
+        pass
+
+    @abstractmethod
+    async def create_email_verification_token(
+        self, user_id: str, email: str, tenant_id: str, user_context: Dict[str, Any]
+    ) -> Union[
+        CreateEmailVerificationTokenOkResult,
+        CreateEmailVerificationTokenEmailAlreadyVerifiedError,
+    ]:
+        pass
+
+    @abstractmethod
+    async def verify_email_using_token(
+        self, token: str, tenant_id: str, user_context: Dict[str, Any]
+    ) -> Union[VerifyEmailUsingTokenOkResult, VerifyEmailUsingTokenInvalidTokenError]:
+        pass
+
+    @abstractmethod
+    async def is_email_verified(
+        self, user_id: str, email: str, user_context: Dict[str, Any]
+    ) -> bool:
+        pass
+
+    @abstractmethod
+    async def revoke_email_verification_tokens(
+        self, user_id: str, email: str, tenant_id: str, user_context: Dict[str, Any]
+    ) -> RevokeEmailVerificationTokensOkResult:
+        pass
+
+    @abstractmethod
+    async def unverify_email(
+        self, user_id: str, email: str, user_context: Dict[str, Any]
+    ) -> UnverifyEmailOkResult:
+        pass
+
+

Ancestors

+
    +
  • abc.ABC
  • +
+

Subclasses

+ +

Methods

+
+
+async def create_email_verification_token(self, user_id: str, email: str, tenant_id: str, user_context: Dict[str, Any]) ‑> Union[CreateEmailVerificationTokenOkResultCreateEmailVerificationTokenEmailAlreadyVerifiedError] +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def create_email_verification_token(
+    self, user_id: str, email: str, tenant_id: str, user_context: Dict[str, Any]
+) -> Union[
+    CreateEmailVerificationTokenOkResult,
+    CreateEmailVerificationTokenEmailAlreadyVerifiedError,
+]:
+    pass
+
+
+
+async def is_email_verified(self, user_id: str, email: str, user_context: Dict[str, Any]) ‑> bool +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def is_email_verified(
+    self, user_id: str, email: str, user_context: Dict[str, Any]
+) -> bool:
+    pass
+
+
+
+async def revoke_email_verification_tokens(self, user_id: str, email: str, tenant_id: str, user_context: Dict[str, Any]) ‑> RevokeEmailVerificationTokensOkResult +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def revoke_email_verification_tokens(
+    self, user_id: str, email: str, tenant_id: str, user_context: Dict[str, Any]
+) -> RevokeEmailVerificationTokensOkResult:
+    pass
+
+
+
+async def unverify_email(self, user_id: str, email: str, user_context: Dict[str, Any]) ‑> UnverifyEmailOkResult +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def unverify_email(
+    self, user_id: str, email: str, user_context: Dict[str, Any]
+) -> UnverifyEmailOkResult:
+    pass
+
+
+
+async def verify_email_using_token(self, token: str, tenant_id: str, user_context: Dict[str, Any]) ‑> Union[VerifyEmailUsingTokenOkResultVerifyEmailUsingTokenInvalidTokenError] +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def verify_email_using_token(
+    self, token: str, tenant_id: str, user_context: Dict[str, Any]
+) -> Union[VerifyEmailUsingTokenOkResult, VerifyEmailUsingTokenInvalidTokenError]:
+    pass
+
+
+
+
+
+class RevokeEmailVerificationTokensOkResult +
+
+
+
+ +Expand source code + +
class RevokeEmailVerificationTokensOkResult:
+    pass
+
+
+
+class SendEmailVerificationEmailAlreadyVerifiedError +
+
+
+
+ +Expand source code + +
class SendEmailVerificationEmailAlreadyVerifiedError:
+    status = "EMAIL_ALREADY_VERIFIED_ERROR"
+
+

Class variables

+
+
var status
+
+
+
+
+
+
+class SendEmailVerificationEmailOkResult +
+
+
+
+ +Expand source code + +
class SendEmailVerificationEmailOkResult:
+    status = "OK"
+
+

Class variables

+
+
var status
+
+
+
+
+
+
+class UnknownUserIdError +(*args, **kwargs) +
+
+

Common base class for all non-exit exceptions.

+
+ +Expand source code + +
class UnknownUserIdError(Exception):
+    pass
+
+

Ancestors

+
    +
  • builtins.Exception
  • +
  • builtins.BaseException
  • +
+
+
+class UnverifyEmailOkResult +
+
+
+
+ +Expand source code + +
class UnverifyEmailOkResult:
+    pass
+
+
+
+class VerifyEmailUsingTokenInvalidTokenError +
+
+
+
+ +Expand source code + +
class VerifyEmailUsingTokenInvalidTokenError:
+    pass
+
+
+
+class VerifyEmailUsingTokenOkResult +(user: User) +
+
+
+
+ +Expand source code + +
class VerifyEmailUsingTokenOkResult:
+    status = "OK"
+
+    def __init__(self, user: User):
+        self.user = user
+
+

Class variables

+
+
var status
+
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailverification/recipe.html b/html/supertokens_python/recipe/emailverification/recipe.html new file mode 100644 index 000000000..38d7073ba --- /dev/null +++ b/html/supertokens_python/recipe/emailverification/recipe.html @@ -0,0 +1,1679 @@ + + + + + + +supertokens_python.recipe.emailverification.recipe API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.emailverification.recipe

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from os import environ
+from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union
+
+from supertokens_python.exceptions import SuperTokensError, raise_general_exception
+from supertokens_python.ingredients.emaildelivery import EmailDeliveryIngredient
+from supertokens_python.recipe.emailverification.exceptions import (
+    EmailVerificationInvalidTokenError,
+)
+from supertokens_python.recipe.emailverification.types import (
+    EmailTemplateVars,
+    EmailVerificationIngredients,
+    VerificationEmailTemplateVars,
+    VerificationEmailTemplateVarsUser,
+)
+from supertokens_python.recipe_module import APIHandled, RecipeModule
+
+from ...ingredients.emaildelivery.types import EmailDeliveryConfig
+from ...logger import log_debug_message
+from ...post_init_callbacks import PostSTInitCallbacks
+from ...types import MaybeAwaitable
+from ...utils import get_timestamp_ms
+from ..session import SessionRecipe
+from ..session.claim_base_classes.boolean_claim import (
+    BooleanClaim,
+    BooleanClaimValidators,
+)
+from ..session.exceptions import raise_unauthorised_exception
+from ..session.interfaces import (
+    ClaimValidationResult,
+    JSONObject,
+    SessionClaimValidator,
+    SessionContainer,
+)
+from .interfaces import (
+    APIInterface,
+    APIOptions,
+    CreateEmailVerificationTokenEmailAlreadyVerifiedError,
+    EmailDoesNotExistError,
+    EmailVerifyPostInvalidTokenError,
+    EmailVerifyPostOkResult,
+    GenerateEmailVerifyTokenPostEmailAlreadyVerifiedError,
+    GenerateEmailVerifyTokenPostOkResult,
+    GetEmailForUserIdOkResult,
+    IsEmailVerifiedGetOkResult,
+    TypeGetEmailForUserIdFunction,
+    UnknownUserIdError,
+    VerifyEmailUsingTokenOkResult,
+)
+from .recipe_implementation import RecipeImplementation
+
+if TYPE_CHECKING:
+    from supertokens_python.framework.request import BaseRequest
+    from supertokens_python.framework.response import BaseResponse
+    from supertokens_python.supertokens import AppInfo
+
+from supertokens_python.normalised_url_path import NormalisedURLPath
+from supertokens_python.querier import Querier
+from supertokens_python.recipe.emailverification.utils import get_email_verify_link
+
+from .api import handle_email_verify_api, handle_generate_email_verify_token_api
+from .constants import USER_EMAIL_VERIFY, USER_EMAIL_VERIFY_TOKEN
+from .exceptions import SuperTokensEmailVerificationError
+from .utils import MODE_TYPE, OverrideConfig, validate_and_normalise_user_input
+
+
+class EmailVerificationRecipe(RecipeModule):
+    recipe_id = "emailverification"
+    __instance = None
+    email_delivery: EmailDeliveryIngredient[VerificationEmailTemplateVars]
+
+    def __init__(
+        self,
+        recipe_id: str,
+        app_info: AppInfo,
+        ingredients: EmailVerificationIngredients,
+        mode: MODE_TYPE,
+        email_delivery: Union[EmailDeliveryConfig[EmailTemplateVars], None] = None,
+        get_email_for_user_id: Optional[TypeGetEmailForUserIdFunction] = None,
+        override: Union[OverrideConfig, None] = None,
+    ) -> None:
+        super().__init__(recipe_id, app_info)
+        self.config = validate_and_normalise_user_input(
+            app_info,
+            mode,
+            email_delivery,
+            get_email_for_user_id,
+            override,
+        )
+
+        recipe_implementation = RecipeImplementation(
+            Querier.get_instance(recipe_id), self.config
+        )
+        self.recipe_implementation = (
+            recipe_implementation
+            if self.config.override.functions is None
+            else self.config.override.functions(recipe_implementation)
+        )
+
+        api_implementation = APIImplementation()
+        self.api_implementation = (
+            api_implementation
+            if self.config.override.apis is None
+            else self.config.override.apis(api_implementation)
+        )
+
+        email_delivery_ingredient = ingredients.email_delivery
+        if email_delivery_ingredient is None:
+            self.email_delivery = EmailDeliveryIngredient(
+                self.config.get_email_delivery_config()
+            )
+        else:
+            self.email_delivery = email_delivery_ingredient
+
+        self.get_email_for_user_id_funcs_from_other_recipes: List[
+            TypeGetEmailForUserIdFunction
+        ] = []
+
+    def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
+        return isinstance(err, SuperTokensError) and isinstance(
+            err, SuperTokensEmailVerificationError
+        )
+
+    def get_apis_handled(self) -> List[APIHandled]:
+        return [
+            APIHandled(
+                NormalisedURLPath(USER_EMAIL_VERIFY_TOKEN),
+                "post",
+                USER_EMAIL_VERIFY_TOKEN,
+                self.api_implementation.disable_generate_email_verify_token_post,
+            ),
+            APIHandled(
+                NormalisedURLPath(USER_EMAIL_VERIFY),
+                "post",
+                USER_EMAIL_VERIFY,
+                self.api_implementation.disable_email_verify_post,
+            ),
+            APIHandled(
+                NormalisedURLPath(USER_EMAIL_VERIFY),
+                "get",
+                USER_EMAIL_VERIFY,
+                self.api_implementation.disable_is_email_verified_get,
+            ),
+        ]
+
+    async def handle_api_request(
+        self,
+        request_id: str,
+        tenant_id: str,
+        request: BaseRequest,
+        path: NormalisedURLPath,
+        method: str,
+        response: BaseResponse,
+        user_context: Dict[str, Any],
+    ) -> Union[BaseResponse, None]:
+        api_options = APIOptions(
+            request,
+            response,
+            self.recipe_id,
+            self.config,
+            self.recipe_implementation,
+            self.get_app_info(),
+            self.email_delivery,
+        )
+        if request_id == USER_EMAIL_VERIFY_TOKEN:
+            return await handle_generate_email_verify_token_api(
+                self.api_implementation, api_options, user_context
+            )
+        return await handle_email_verify_api(
+            self.api_implementation, tenant_id, api_options, user_context
+        )
+
+    async def handle_error(
+        self,
+        request: BaseRequest,
+        err: SuperTokensError,
+        response: BaseResponse,
+        user_context: Dict[str, Any],
+    ) -> BaseResponse:
+        if isinstance(err, EmailVerificationInvalidTokenError):
+            response.set_json_content(
+                {"status": "EMAIL_VERIFICATION_INVALID_TOKEN_ERROR"}
+            )
+            return response
+        response.set_json_content({"status": "EMAIL_ALREADY_VERIFIED_ERROR"})
+        return response
+
+    def get_all_cors_headers(self) -> List[str]:
+        return []
+
+    @staticmethod
+    def init(
+        mode: MODE_TYPE,
+        email_delivery: Union[EmailDeliveryConfig[EmailTemplateVars], None] = None,
+        get_email_for_user_id: Optional[TypeGetEmailForUserIdFunction] = None,
+        override: Union[OverrideConfig, None] = None,
+    ):
+        def func(app_info: AppInfo) -> EmailVerificationRecipe:
+            if EmailVerificationRecipe.__instance is None:
+                ingredients = EmailVerificationIngredients(email_delivery=None)
+                EmailVerificationRecipe.__instance = EmailVerificationRecipe(
+                    EmailVerificationRecipe.recipe_id,
+                    app_info,
+                    ingredients,
+                    mode,
+                    email_delivery,
+                    get_email_for_user_id,
+                    override,
+                )
+
+                def callback():
+                    SessionRecipe.get_instance().add_claim_from_other_recipe(
+                        EmailVerificationClaim
+                    )
+                    if mode == "REQUIRED":
+                        SessionRecipe.get_instance().add_claim_validator_from_other_recipe(
+                            EmailVerificationClaim.validators.is_verified()
+                        )
+
+                PostSTInitCallbacks.add_post_init_callback(callback)
+
+                return EmailVerificationRecipe.__instance
+            raise_general_exception(
+                "Emailverification recipe has already been initialised. Please check your code for bugs."
+            )
+
+        return func
+
+    @staticmethod
+    def get_instance() -> EmailVerificationRecipe:
+        if EmailVerificationRecipe.__instance is not None:
+            return EmailVerificationRecipe.__instance
+        raise_general_exception(
+            "Initialisation not done. Did you forget to call the SuperTokens.init function?"
+        )
+
+    @staticmethod
+    def get_instance_optional() -> Optional[EmailVerificationRecipe]:
+        return EmailVerificationRecipe.__instance
+
+    @staticmethod
+    def reset():
+        if ("SUPERTOKENS_ENV" not in environ) or (
+            environ["SUPERTOKENS_ENV"] != "testing"
+        ):
+            raise_general_exception("calling testing function in non testing env")
+        EmailVerificationRecipe.__instance = None
+
+    async def get_email_for_user_id(
+        self, user_id: str, user_context: Dict[str, Any]
+    ) -> Union[GetEmailForUserIdOkResult, EmailDoesNotExistError, UnknownUserIdError]:
+        if self.config.get_email_for_user_id is not None:
+            res = await self.config.get_email_for_user_id(user_id, user_context)
+            if not isinstance(res, UnknownUserIdError):
+                return res
+
+        for f in self.get_email_for_user_id_funcs_from_other_recipes:
+            res = await f(user_id, user_context)
+            if not isinstance(res, UnknownUserIdError):
+                return res
+
+        return UnknownUserIdError()
+
+    def add_get_email_for_user_id_func(self, f: TypeGetEmailForUserIdFunction):
+        self.get_email_for_user_id_funcs_from_other_recipes.append(f)
+
+
+class EmailVerificationClaimValidators(BooleanClaimValidators):
+    def __init__(self, claim: EmailVerificationClaimClass):
+        super().__init__(claim, None)
+
+    def is_verified(
+        self,
+        refetch_time_on_false_in_seconds: int = 10,
+        max_age_in_seconds: Optional[int] = None,
+        id_: Optional[str] = None,
+    ) -> SessionClaimValidator:
+
+        assert isinstance(self.claim, EmailVerificationClaimClass)
+        return IsVerifiedSCV(
+            (id_ or self.claim.key),
+            self.claim,
+            self,
+            refetch_time_on_false_in_seconds,
+            max_age_in_seconds,
+        )
+
+
+class EmailVerificationClaimClass(BooleanClaim):
+    def __init__(self):
+        async def fetch_value(
+            user_id: str, _tenant_id: str, user_context: Dict[str, Any]
+        ) -> bool:
+            recipe = EmailVerificationRecipe.get_instance()
+            email_info = await recipe.get_email_for_user_id(user_id, user_context)
+
+            if isinstance(email_info, GetEmailForUserIdOkResult):
+                return await recipe.recipe_implementation.is_email_verified(
+                    user_id, email_info.email, user_context
+                )
+            if isinstance(email_info, EmailDoesNotExistError):
+                # we consider people without email addresses as validated
+                return True
+            raise Exception("UNKNOWN_USER_ID")
+
+        super().__init__("st-ev", fetch_value, None)
+
+        self.validators = EmailVerificationClaimValidators(claim=self)
+
+
+EmailVerificationClaim = EmailVerificationClaimClass()
+
+
+class APIImplementation(APIInterface):
+    async def email_verify_post(
+        self,
+        token: str,
+        session: Optional[SessionContainer],
+        tenant_id: str,
+        api_options: APIOptions,
+        user_context: Dict[str, Any],
+    ) -> Union[EmailVerifyPostOkResult, EmailVerifyPostInvalidTokenError]:
+
+        response = await api_options.recipe_implementation.verify_email_using_token(
+            token, tenant_id, user_context
+        )
+        if isinstance(response, VerifyEmailUsingTokenOkResult):
+            if session is not None:
+                try:
+                    await session.fetch_and_set_claim(
+                        EmailVerificationClaim, user_context
+                    )
+                except Exception as e:
+                    # This should never happen since we have just set the status above
+                    if str(e) == "UNKNOWN_USER_ID":
+                        log_debug_message(
+                            "verifyEmailPOST: Returning UNAUTHORISED because the user id provided is unknown"
+                        )
+                        raise_unauthorised_exception("Unknown User ID provided")
+                    else:
+                        raise e
+
+            return EmailVerifyPostOkResult(response.user)
+        return EmailVerifyPostInvalidTokenError()
+
+    async def is_email_verified_get(
+        self,
+        session: SessionContainer,
+        api_options: APIOptions,
+        user_context: Dict[str, Any],
+    ) -> IsEmailVerifiedGetOkResult:
+        try:
+            await session.fetch_and_set_claim(EmailVerificationClaim, user_context)
+        except Exception as e:
+            if str(e) == "UNKNOWN_USER_ID":
+                log_debug_message(
+                    "isEmailVerifiedGET: Returning UNAUTHORISED because the user id provided is unknown"
+                )
+                raise_unauthorised_exception("Unknown User ID provided")
+            else:
+                raise e
+
+        is_verified = await session.get_claim_value(
+            EmailVerificationClaim, user_context
+        )
+
+        if is_verified is None:
+            raise Exception(
+                "Should never come here: EmailVerificationClaim failed to set value"
+            )
+
+        return IsEmailVerifiedGetOkResult(is_verified)
+
+    async def generate_email_verify_token_post(
+        self,
+        session: SessionContainer,
+        api_options: APIOptions,
+        user_context: Dict[str, Any],
+    ) -> Union[
+        GenerateEmailVerifyTokenPostOkResult,
+        GenerateEmailVerifyTokenPostEmailAlreadyVerifiedError,
+    ]:
+        user_id = session.get_user_id(user_context)
+        email_info = await EmailVerificationRecipe.get_instance().get_email_for_user_id(
+            user_id, user_context
+        )
+        tenant_id = session.get_tenant_id()
+
+        if isinstance(email_info, EmailDoesNotExistError):
+            log_debug_message(
+                "Email verification email not sent to user %s because it doesn't have an email address.",
+                user_id,
+            )
+            return GenerateEmailVerifyTokenPostEmailAlreadyVerifiedError()
+        if isinstance(email_info, GetEmailForUserIdOkResult):
+            response = (
+                await api_options.recipe_implementation.create_email_verification_token(
+                    user_id,
+                    email_info.email,
+                    tenant_id,
+                    user_context,
+                )
+            )
+
+            if isinstance(
+                response, CreateEmailVerificationTokenEmailAlreadyVerifiedError
+            ):
+                if await session.get_claim_value(EmailVerificationClaim) is not True:
+                    # this can happen if the email was "verified" in another browser
+                    # and this session is still outdated - and the user has not
+                    # called the get email verification API yet.
+                    await session.fetch_and_set_claim(
+                        EmailVerificationClaim, user_context
+                    )
+                log_debug_message(
+                    "Email verification email not sent to %s because it is already verified.",
+                    email_info.email,
+                )
+                return GenerateEmailVerifyTokenPostEmailAlreadyVerifiedError()
+
+            if await session.get_claim_value(EmailVerificationClaim) is not False:
+                # this can happen if the email was "unverified" in another browser
+                # and this session is still outdated - and the user has not
+                # called the get email verification API yet.
+                await session.fetch_and_set_claim(EmailVerificationClaim, user_context)
+
+            email_verify_link = get_email_verify_link(
+                api_options.app_info,
+                response.token,
+                tenant_id,
+                api_options.request,
+                user_context,
+            )
+
+            log_debug_message("Sending email verification email to %s", email_info)
+            email_verification_email_delivery_input = VerificationEmailTemplateVars(
+                user=VerificationEmailTemplateVarsUser(user_id, email_info.email),
+                email_verify_link=email_verify_link,
+                tenant_id=tenant_id,
+            )
+            await api_options.email_delivery.ingredient_interface_impl.send_email(
+                email_verification_email_delivery_input, user_context
+            )
+            return GenerateEmailVerifyTokenPostOkResult()
+
+        log_debug_message(
+            "generateEmailVerifyTokenPOST: Returning UNAUTHORISED because the user id provided is unknown"
+        )
+        raise_unauthorised_exception("Unknown User ID provided")
+
+
+class IsVerifiedSCV(SessionClaimValidator):
+    def __init__(
+        self,
+        id_: str,
+        claim: EmailVerificationClaimClass,
+        ev_claim_validators: EmailVerificationClaimValidators,
+        refetch_time_on_false_in_seconds: int,
+        max_age_in_seconds: Optional[int],
+    ):
+        super().__init__(id_)
+        self.claim: EmailVerificationClaimClass = claim
+        self.ev_claim_validators = ev_claim_validators
+        self.refetch_time_on_false_in_ms = refetch_time_on_false_in_seconds * 1000
+        self.max_age_in_sec = max_age_in_seconds
+
+    async def validate(
+        self, payload: JSONObject, user_context: Dict[str, Any]
+    ) -> ClaimValidationResult:
+        return await self.ev_claim_validators.has_value(
+            True, self.max_age_in_sec
+        ).validate(payload, user_context)
+
+    def should_refetch(
+        self, payload: JSONObject, user_context: Dict[str, Any]
+    ) -> MaybeAwaitable[bool]:
+        value = self.claim.get_value_from_payload(payload, user_context)
+        if value is None:
+            return True
+
+        current_time = get_timestamp_ms()
+        last_refetch_time = self.claim.get_last_refetch_time(payload, user_context)
+        assert last_refetch_time is not None
+
+        if self.max_age_in_sec is not None:
+            if last_refetch_time < current_time - self.max_age_in_sec * 1000:
+                return True
+
+        if value is False:
+            if last_refetch_time < current_time - self.refetch_time_on_false_in_ms:
+                return True
+
+        return False
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class APIImplementation +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class APIImplementation(APIInterface):
+    async def email_verify_post(
+        self,
+        token: str,
+        session: Optional[SessionContainer],
+        tenant_id: str,
+        api_options: APIOptions,
+        user_context: Dict[str, Any],
+    ) -> Union[EmailVerifyPostOkResult, EmailVerifyPostInvalidTokenError]:
+
+        response = await api_options.recipe_implementation.verify_email_using_token(
+            token, tenant_id, user_context
+        )
+        if isinstance(response, VerifyEmailUsingTokenOkResult):
+            if session is not None:
+                try:
+                    await session.fetch_and_set_claim(
+                        EmailVerificationClaim, user_context
+                    )
+                except Exception as e:
+                    # This should never happen since we have just set the status above
+                    if str(e) == "UNKNOWN_USER_ID":
+                        log_debug_message(
+                            "verifyEmailPOST: Returning UNAUTHORISED because the user id provided is unknown"
+                        )
+                        raise_unauthorised_exception("Unknown User ID provided")
+                    else:
+                        raise e
+
+            return EmailVerifyPostOkResult(response.user)
+        return EmailVerifyPostInvalidTokenError()
+
+    async def is_email_verified_get(
+        self,
+        session: SessionContainer,
+        api_options: APIOptions,
+        user_context: Dict[str, Any],
+    ) -> IsEmailVerifiedGetOkResult:
+        try:
+            await session.fetch_and_set_claim(EmailVerificationClaim, user_context)
+        except Exception as e:
+            if str(e) == "UNKNOWN_USER_ID":
+                log_debug_message(
+                    "isEmailVerifiedGET: Returning UNAUTHORISED because the user id provided is unknown"
+                )
+                raise_unauthorised_exception("Unknown User ID provided")
+            else:
+                raise e
+
+        is_verified = await session.get_claim_value(
+            EmailVerificationClaim, user_context
+        )
+
+        if is_verified is None:
+            raise Exception(
+                "Should never come here: EmailVerificationClaim failed to set value"
+            )
+
+        return IsEmailVerifiedGetOkResult(is_verified)
+
+    async def generate_email_verify_token_post(
+        self,
+        session: SessionContainer,
+        api_options: APIOptions,
+        user_context: Dict[str, Any],
+    ) -> Union[
+        GenerateEmailVerifyTokenPostOkResult,
+        GenerateEmailVerifyTokenPostEmailAlreadyVerifiedError,
+    ]:
+        user_id = session.get_user_id(user_context)
+        email_info = await EmailVerificationRecipe.get_instance().get_email_for_user_id(
+            user_id, user_context
+        )
+        tenant_id = session.get_tenant_id()
+
+        if isinstance(email_info, EmailDoesNotExistError):
+            log_debug_message(
+                "Email verification email not sent to user %s because it doesn't have an email address.",
+                user_id,
+            )
+            return GenerateEmailVerifyTokenPostEmailAlreadyVerifiedError()
+        if isinstance(email_info, GetEmailForUserIdOkResult):
+            response = (
+                await api_options.recipe_implementation.create_email_verification_token(
+                    user_id,
+                    email_info.email,
+                    tenant_id,
+                    user_context,
+                )
+            )
+
+            if isinstance(
+                response, CreateEmailVerificationTokenEmailAlreadyVerifiedError
+            ):
+                if await session.get_claim_value(EmailVerificationClaim) is not True:
+                    # this can happen if the email was "verified" in another browser
+                    # and this session is still outdated - and the user has not
+                    # called the get email verification API yet.
+                    await session.fetch_and_set_claim(
+                        EmailVerificationClaim, user_context
+                    )
+                log_debug_message(
+                    "Email verification email not sent to %s because it is already verified.",
+                    email_info.email,
+                )
+                return GenerateEmailVerifyTokenPostEmailAlreadyVerifiedError()
+
+            if await session.get_claim_value(EmailVerificationClaim) is not False:
+                # this can happen if the email was "unverified" in another browser
+                # and this session is still outdated - and the user has not
+                # called the get email verification API yet.
+                await session.fetch_and_set_claim(EmailVerificationClaim, user_context)
+
+            email_verify_link = get_email_verify_link(
+                api_options.app_info,
+                response.token,
+                tenant_id,
+                api_options.request,
+                user_context,
+            )
+
+            log_debug_message("Sending email verification email to %s", email_info)
+            email_verification_email_delivery_input = VerificationEmailTemplateVars(
+                user=VerificationEmailTemplateVarsUser(user_id, email_info.email),
+                email_verify_link=email_verify_link,
+                tenant_id=tenant_id,
+            )
+            await api_options.email_delivery.ingredient_interface_impl.send_email(
+                email_verification_email_delivery_input, user_context
+            )
+            return GenerateEmailVerifyTokenPostOkResult()
+
+        log_debug_message(
+            "generateEmailVerifyTokenPOST: Returning UNAUTHORISED because the user id provided is unknown"
+        )
+        raise_unauthorised_exception("Unknown User ID provided")
+
+

Ancestors

+ +

Methods

+
+
+async def email_verify_post(self, token: str, session: Optional[SessionContainer], tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[EmailVerifyPostOkResultEmailVerifyPostInvalidTokenError] +
+
+
+
+ +Expand source code + +
async def email_verify_post(
+    self,
+    token: str,
+    session: Optional[SessionContainer],
+    tenant_id: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+) -> Union[EmailVerifyPostOkResult, EmailVerifyPostInvalidTokenError]:
+
+    response = await api_options.recipe_implementation.verify_email_using_token(
+        token, tenant_id, user_context
+    )
+    if isinstance(response, VerifyEmailUsingTokenOkResult):
+        if session is not None:
+            try:
+                await session.fetch_and_set_claim(
+                    EmailVerificationClaim, user_context
+                )
+            except Exception as e:
+                # This should never happen since we have just set the status above
+                if str(e) == "UNKNOWN_USER_ID":
+                    log_debug_message(
+                        "verifyEmailPOST: Returning UNAUTHORISED because the user id provided is unknown"
+                    )
+                    raise_unauthorised_exception("Unknown User ID provided")
+                else:
+                    raise e
+
+        return EmailVerifyPostOkResult(response.user)
+    return EmailVerifyPostInvalidTokenError()
+
+
+
+async def generate_email_verify_token_post(self, session: SessionContainer, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[GenerateEmailVerifyTokenPostOkResultGenerateEmailVerifyTokenPostEmailAlreadyVerifiedError] +
+
+
+
+ +Expand source code + +
async def generate_email_verify_token_post(
+    self,
+    session: SessionContainer,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+) -> Union[
+    GenerateEmailVerifyTokenPostOkResult,
+    GenerateEmailVerifyTokenPostEmailAlreadyVerifiedError,
+]:
+    user_id = session.get_user_id(user_context)
+    email_info = await EmailVerificationRecipe.get_instance().get_email_for_user_id(
+        user_id, user_context
+    )
+    tenant_id = session.get_tenant_id()
+
+    if isinstance(email_info, EmailDoesNotExistError):
+        log_debug_message(
+            "Email verification email not sent to user %s because it doesn't have an email address.",
+            user_id,
+        )
+        return GenerateEmailVerifyTokenPostEmailAlreadyVerifiedError()
+    if isinstance(email_info, GetEmailForUserIdOkResult):
+        response = (
+            await api_options.recipe_implementation.create_email_verification_token(
+                user_id,
+                email_info.email,
+                tenant_id,
+                user_context,
+            )
+        )
+
+        if isinstance(
+            response, CreateEmailVerificationTokenEmailAlreadyVerifiedError
+        ):
+            if await session.get_claim_value(EmailVerificationClaim) is not True:
+                # this can happen if the email was "verified" in another browser
+                # and this session is still outdated - and the user has not
+                # called the get email verification API yet.
+                await session.fetch_and_set_claim(
+                    EmailVerificationClaim, user_context
+                )
+            log_debug_message(
+                "Email verification email not sent to %s because it is already verified.",
+                email_info.email,
+            )
+            return GenerateEmailVerifyTokenPostEmailAlreadyVerifiedError()
+
+        if await session.get_claim_value(EmailVerificationClaim) is not False:
+            # this can happen if the email was "unverified" in another browser
+            # and this session is still outdated - and the user has not
+            # called the get email verification API yet.
+            await session.fetch_and_set_claim(EmailVerificationClaim, user_context)
+
+        email_verify_link = get_email_verify_link(
+            api_options.app_info,
+            response.token,
+            tenant_id,
+            api_options.request,
+            user_context,
+        )
+
+        log_debug_message("Sending email verification email to %s", email_info)
+        email_verification_email_delivery_input = VerificationEmailTemplateVars(
+            user=VerificationEmailTemplateVarsUser(user_id, email_info.email),
+            email_verify_link=email_verify_link,
+            tenant_id=tenant_id,
+        )
+        await api_options.email_delivery.ingredient_interface_impl.send_email(
+            email_verification_email_delivery_input, user_context
+        )
+        return GenerateEmailVerifyTokenPostOkResult()
+
+    log_debug_message(
+        "generateEmailVerifyTokenPOST: Returning UNAUTHORISED because the user id provided is unknown"
+    )
+    raise_unauthorised_exception("Unknown User ID provided")
+
+
+
+async def is_email_verified_get(self, session: SessionContainer, api_options: APIOptions, user_context: Dict[str, Any]) ‑> IsEmailVerifiedGetOkResult +
+
+
+
+ +Expand source code + +
async def is_email_verified_get(
+    self,
+    session: SessionContainer,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+) -> IsEmailVerifiedGetOkResult:
+    try:
+        await session.fetch_and_set_claim(EmailVerificationClaim, user_context)
+    except Exception as e:
+        if str(e) == "UNKNOWN_USER_ID":
+            log_debug_message(
+                "isEmailVerifiedGET: Returning UNAUTHORISED because the user id provided is unknown"
+            )
+            raise_unauthorised_exception("Unknown User ID provided")
+        else:
+            raise e
+
+    is_verified = await session.get_claim_value(
+        EmailVerificationClaim, user_context
+    )
+
+    if is_verified is None:
+        raise Exception(
+            "Should never come here: EmailVerificationClaim failed to set value"
+        )
+
+    return IsEmailVerifiedGetOkResult(is_verified)
+
+
+
+
+
+class EmailVerificationClaimClass +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+

Args

+
+
key
+
The key to use when storing the claim in the payload.
+
fetch_value
+
a method that fetches the current value of this claim for the user. +A None return value signifies that we don't want to update the claim payload and or the claim value is +not present in the database. For example, this can happen with a second factor auth claim, where we +don't want to add the claim to the session automatically
+
+
+ +Expand source code + +
class EmailVerificationClaimClass(BooleanClaim):
+    def __init__(self):
+        async def fetch_value(
+            user_id: str, _tenant_id: str, user_context: Dict[str, Any]
+        ) -> bool:
+            recipe = EmailVerificationRecipe.get_instance()
+            email_info = await recipe.get_email_for_user_id(user_id, user_context)
+
+            if isinstance(email_info, GetEmailForUserIdOkResult):
+                return await recipe.recipe_implementation.is_email_verified(
+                    user_id, email_info.email, user_context
+                )
+            if isinstance(email_info, EmailDoesNotExistError):
+                # we consider people without email addresses as validated
+                return True
+            raise Exception("UNKNOWN_USER_ID")
+
+        super().__init__("st-ev", fetch_value, None)
+
+        self.validators = EmailVerificationClaimValidators(claim=self)
+
+

Ancestors

+ +

Inherited members

+ +
+
+class EmailVerificationClaimValidators +(claim: EmailVerificationClaimClass) +
+
+

Abstract base class for generic types.

+

A generic type is typically declared by inheriting from +this class parameterized with one or more type variables. +For example, a generic mapping type might be defined as::

+

class Mapping(Generic[KT, VT]): +def getitem(self, key: KT) -> VT: +… +# Etc.

+

This class can then be used as follows::

+

def lookup_name(mapping: Mapping[KT, VT], key: KT, default: VT) -> VT: +try: +return mapping[key] +except KeyError: +return default

+
+ +Expand source code + +
class EmailVerificationClaimValidators(BooleanClaimValidators):
+    def __init__(self, claim: EmailVerificationClaimClass):
+        super().__init__(claim, None)
+
+    def is_verified(
+        self,
+        refetch_time_on_false_in_seconds: int = 10,
+        max_age_in_seconds: Optional[int] = None,
+        id_: Optional[str] = None,
+    ) -> SessionClaimValidator:
+
+        assert isinstance(self.claim, EmailVerificationClaimClass)
+        return IsVerifiedSCV(
+            (id_ or self.claim.key),
+            self.claim,
+            self,
+            refetch_time_on_false_in_seconds,
+            max_age_in_seconds,
+        )
+
+

Ancestors

+ +

Methods

+
+
+def is_verified(self, refetch_time_on_false_in_seconds: int = 10, max_age_in_seconds: Optional[int] = None, id_: Optional[str] = None) ‑> SessionClaimValidator +
+
+
+
+ +Expand source code + +
def is_verified(
+    self,
+    refetch_time_on_false_in_seconds: int = 10,
+    max_age_in_seconds: Optional[int] = None,
+    id_: Optional[str] = None,
+) -> SessionClaimValidator:
+
+    assert isinstance(self.claim, EmailVerificationClaimClass)
+    return IsVerifiedSCV(
+        (id_ or self.claim.key),
+        self.claim,
+        self,
+        refetch_time_on_false_in_seconds,
+        max_age_in_seconds,
+    )
+
+
+
+
+
+class EmailVerificationRecipe +(recipe_id: str, app_info: AppInfo, ingredients: EmailVerificationIngredients, mode: MODE_TYPE, email_delivery: Union[EmailDeliveryConfig[EmailTemplateVars], None] = None, get_email_for_user_id: Optional[TypeGetEmailForUserIdFunction] = None, override: Union[OverrideConfig, None] = None) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class EmailVerificationRecipe(RecipeModule):
+    recipe_id = "emailverification"
+    __instance = None
+    email_delivery: EmailDeliveryIngredient[VerificationEmailTemplateVars]
+
+    def __init__(
+        self,
+        recipe_id: str,
+        app_info: AppInfo,
+        ingredients: EmailVerificationIngredients,
+        mode: MODE_TYPE,
+        email_delivery: Union[EmailDeliveryConfig[EmailTemplateVars], None] = None,
+        get_email_for_user_id: Optional[TypeGetEmailForUserIdFunction] = None,
+        override: Union[OverrideConfig, None] = None,
+    ) -> None:
+        super().__init__(recipe_id, app_info)
+        self.config = validate_and_normalise_user_input(
+            app_info,
+            mode,
+            email_delivery,
+            get_email_for_user_id,
+            override,
+        )
+
+        recipe_implementation = RecipeImplementation(
+            Querier.get_instance(recipe_id), self.config
+        )
+        self.recipe_implementation = (
+            recipe_implementation
+            if self.config.override.functions is None
+            else self.config.override.functions(recipe_implementation)
+        )
+
+        api_implementation = APIImplementation()
+        self.api_implementation = (
+            api_implementation
+            if self.config.override.apis is None
+            else self.config.override.apis(api_implementation)
+        )
+
+        email_delivery_ingredient = ingredients.email_delivery
+        if email_delivery_ingredient is None:
+            self.email_delivery = EmailDeliveryIngredient(
+                self.config.get_email_delivery_config()
+            )
+        else:
+            self.email_delivery = email_delivery_ingredient
+
+        self.get_email_for_user_id_funcs_from_other_recipes: List[
+            TypeGetEmailForUserIdFunction
+        ] = []
+
+    def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
+        return isinstance(err, SuperTokensError) and isinstance(
+            err, SuperTokensEmailVerificationError
+        )
+
+    def get_apis_handled(self) -> List[APIHandled]:
+        return [
+            APIHandled(
+                NormalisedURLPath(USER_EMAIL_VERIFY_TOKEN),
+                "post",
+                USER_EMAIL_VERIFY_TOKEN,
+                self.api_implementation.disable_generate_email_verify_token_post,
+            ),
+            APIHandled(
+                NormalisedURLPath(USER_EMAIL_VERIFY),
+                "post",
+                USER_EMAIL_VERIFY,
+                self.api_implementation.disable_email_verify_post,
+            ),
+            APIHandled(
+                NormalisedURLPath(USER_EMAIL_VERIFY),
+                "get",
+                USER_EMAIL_VERIFY,
+                self.api_implementation.disable_is_email_verified_get,
+            ),
+        ]
+
+    async def handle_api_request(
+        self,
+        request_id: str,
+        tenant_id: str,
+        request: BaseRequest,
+        path: NormalisedURLPath,
+        method: str,
+        response: BaseResponse,
+        user_context: Dict[str, Any],
+    ) -> Union[BaseResponse, None]:
+        api_options = APIOptions(
+            request,
+            response,
+            self.recipe_id,
+            self.config,
+            self.recipe_implementation,
+            self.get_app_info(),
+            self.email_delivery,
+        )
+        if request_id == USER_EMAIL_VERIFY_TOKEN:
+            return await handle_generate_email_verify_token_api(
+                self.api_implementation, api_options, user_context
+            )
+        return await handle_email_verify_api(
+            self.api_implementation, tenant_id, api_options, user_context
+        )
+
+    async def handle_error(
+        self,
+        request: BaseRequest,
+        err: SuperTokensError,
+        response: BaseResponse,
+        user_context: Dict[str, Any],
+    ) -> BaseResponse:
+        if isinstance(err, EmailVerificationInvalidTokenError):
+            response.set_json_content(
+                {"status": "EMAIL_VERIFICATION_INVALID_TOKEN_ERROR"}
+            )
+            return response
+        response.set_json_content({"status": "EMAIL_ALREADY_VERIFIED_ERROR"})
+        return response
+
+    def get_all_cors_headers(self) -> List[str]:
+        return []
+
+    @staticmethod
+    def init(
+        mode: MODE_TYPE,
+        email_delivery: Union[EmailDeliveryConfig[EmailTemplateVars], None] = None,
+        get_email_for_user_id: Optional[TypeGetEmailForUserIdFunction] = None,
+        override: Union[OverrideConfig, None] = None,
+    ):
+        def func(app_info: AppInfo) -> EmailVerificationRecipe:
+            if EmailVerificationRecipe.__instance is None:
+                ingredients = EmailVerificationIngredients(email_delivery=None)
+                EmailVerificationRecipe.__instance = EmailVerificationRecipe(
+                    EmailVerificationRecipe.recipe_id,
+                    app_info,
+                    ingredients,
+                    mode,
+                    email_delivery,
+                    get_email_for_user_id,
+                    override,
+                )
+
+                def callback():
+                    SessionRecipe.get_instance().add_claim_from_other_recipe(
+                        EmailVerificationClaim
+                    )
+                    if mode == "REQUIRED":
+                        SessionRecipe.get_instance().add_claim_validator_from_other_recipe(
+                            EmailVerificationClaim.validators.is_verified()
+                        )
+
+                PostSTInitCallbacks.add_post_init_callback(callback)
+
+                return EmailVerificationRecipe.__instance
+            raise_general_exception(
+                "Emailverification recipe has already been initialised. Please check your code for bugs."
+            )
+
+        return func
+
+    @staticmethod
+    def get_instance() -> EmailVerificationRecipe:
+        if EmailVerificationRecipe.__instance is not None:
+            return EmailVerificationRecipe.__instance
+        raise_general_exception(
+            "Initialisation not done. Did you forget to call the SuperTokens.init function?"
+        )
+
+    @staticmethod
+    def get_instance_optional() -> Optional[EmailVerificationRecipe]:
+        return EmailVerificationRecipe.__instance
+
+    @staticmethod
+    def reset():
+        if ("SUPERTOKENS_ENV" not in environ) or (
+            environ["SUPERTOKENS_ENV"] != "testing"
+        ):
+            raise_general_exception("calling testing function in non testing env")
+        EmailVerificationRecipe.__instance = None
+
+    async def get_email_for_user_id(
+        self, user_id: str, user_context: Dict[str, Any]
+    ) -> Union[GetEmailForUserIdOkResult, EmailDoesNotExistError, UnknownUserIdError]:
+        if self.config.get_email_for_user_id is not None:
+            res = await self.config.get_email_for_user_id(user_id, user_context)
+            if not isinstance(res, UnknownUserIdError):
+                return res
+
+        for f in self.get_email_for_user_id_funcs_from_other_recipes:
+            res = await f(user_id, user_context)
+            if not isinstance(res, UnknownUserIdError):
+                return res
+
+        return UnknownUserIdError()
+
+    def add_get_email_for_user_id_func(self, f: TypeGetEmailForUserIdFunction):
+        self.get_email_for_user_id_funcs_from_other_recipes.append(f)
+
+

Ancestors

+ +

Class variables

+
+
var email_deliveryEmailDeliveryIngredient[VerificationEmailTemplateVars]
+
+
+
+
var recipe_id
+
+
+
+
+

Static methods

+
+
+def get_instance() ‑> EmailVerificationRecipe +
+
+
+
+ +Expand source code + +
@staticmethod
+def get_instance() -> EmailVerificationRecipe:
+    if EmailVerificationRecipe.__instance is not None:
+        return EmailVerificationRecipe.__instance
+    raise_general_exception(
+        "Initialisation not done. Did you forget to call the SuperTokens.init function?"
+    )
+
+
+
+def get_instance_optional() ‑> Optional[EmailVerificationRecipe] +
+
+
+
+ +Expand source code + +
@staticmethod
+def get_instance_optional() -> Optional[EmailVerificationRecipe]:
+    return EmailVerificationRecipe.__instance
+
+
+
+def init(mode: MODE_TYPE, email_delivery: Union[EmailDeliveryConfig[EmailTemplateVars], None] = None, get_email_for_user_id: Optional[TypeGetEmailForUserIdFunction] = None, override: Union[OverrideConfig, None] = None) +
+
+
+
+ +Expand source code + +
@staticmethod
+def init(
+    mode: MODE_TYPE,
+    email_delivery: Union[EmailDeliveryConfig[EmailTemplateVars], None] = None,
+    get_email_for_user_id: Optional[TypeGetEmailForUserIdFunction] = None,
+    override: Union[OverrideConfig, None] = None,
+):
+    def func(app_info: AppInfo) -> EmailVerificationRecipe:
+        if EmailVerificationRecipe.__instance is None:
+            ingredients = EmailVerificationIngredients(email_delivery=None)
+            EmailVerificationRecipe.__instance = EmailVerificationRecipe(
+                EmailVerificationRecipe.recipe_id,
+                app_info,
+                ingredients,
+                mode,
+                email_delivery,
+                get_email_for_user_id,
+                override,
+            )
+
+            def callback():
+                SessionRecipe.get_instance().add_claim_from_other_recipe(
+                    EmailVerificationClaim
+                )
+                if mode == "REQUIRED":
+                    SessionRecipe.get_instance().add_claim_validator_from_other_recipe(
+                        EmailVerificationClaim.validators.is_verified()
+                    )
+
+            PostSTInitCallbacks.add_post_init_callback(callback)
+
+            return EmailVerificationRecipe.__instance
+        raise_general_exception(
+            "Emailverification recipe has already been initialised. Please check your code for bugs."
+        )
+
+    return func
+
+
+
+def reset() +
+
+
+
+ +Expand source code + +
@staticmethod
+def reset():
+    if ("SUPERTOKENS_ENV" not in environ) or (
+        environ["SUPERTOKENS_ENV"] != "testing"
+    ):
+        raise_general_exception("calling testing function in non testing env")
+    EmailVerificationRecipe.__instance = None
+
+
+
+

Methods

+
+
+def add_get_email_for_user_id_func(self, f: TypeGetEmailForUserIdFunction) +
+
+
+
+ +Expand source code + +
def add_get_email_for_user_id_func(self, f: TypeGetEmailForUserIdFunction):
+    self.get_email_for_user_id_funcs_from_other_recipes.append(f)
+
+
+
+def get_all_cors_headers(self) ‑> List[str] +
+
+
+
+ +Expand source code + +
def get_all_cors_headers(self) -> List[str]:
+    return []
+
+
+
+def get_apis_handled(self) ‑> List[APIHandled] +
+
+
+
+ +Expand source code + +
def get_apis_handled(self) -> List[APIHandled]:
+    return [
+        APIHandled(
+            NormalisedURLPath(USER_EMAIL_VERIFY_TOKEN),
+            "post",
+            USER_EMAIL_VERIFY_TOKEN,
+            self.api_implementation.disable_generate_email_verify_token_post,
+        ),
+        APIHandled(
+            NormalisedURLPath(USER_EMAIL_VERIFY),
+            "post",
+            USER_EMAIL_VERIFY,
+            self.api_implementation.disable_email_verify_post,
+        ),
+        APIHandled(
+            NormalisedURLPath(USER_EMAIL_VERIFY),
+            "get",
+            USER_EMAIL_VERIFY,
+            self.api_implementation.disable_is_email_verified_get,
+        ),
+    ]
+
+
+
+async def get_email_for_user_id(self, user_id: str, user_context: Dict[str, Any]) ‑> Union[UnknownUserIdErrorGetEmailForUserIdOkResultEmailDoesNotExistError] +
+
+
+
+ +Expand source code + +
async def get_email_for_user_id(
+    self, user_id: str, user_context: Dict[str, Any]
+) -> Union[GetEmailForUserIdOkResult, EmailDoesNotExistError, UnknownUserIdError]:
+    if self.config.get_email_for_user_id is not None:
+        res = await self.config.get_email_for_user_id(user_id, user_context)
+        if not isinstance(res, UnknownUserIdError):
+            return res
+
+    for f in self.get_email_for_user_id_funcs_from_other_recipes:
+        res = await f(user_id, user_context)
+        if not isinstance(res, UnknownUserIdError):
+            return res
+
+    return UnknownUserIdError()
+
+
+
+async def handle_api_request(self, request_id: str, tenant_id: str, request: BaseRequest, path: NormalisedURLPath, method: str, response: BaseResponse, user_context: Dict[str, Any]) ‑> Union[BaseResponse, None] +
+
+
+
+ +Expand source code + +
async def handle_api_request(
+    self,
+    request_id: str,
+    tenant_id: str,
+    request: BaseRequest,
+    path: NormalisedURLPath,
+    method: str,
+    response: BaseResponse,
+    user_context: Dict[str, Any],
+) -> Union[BaseResponse, None]:
+    api_options = APIOptions(
+        request,
+        response,
+        self.recipe_id,
+        self.config,
+        self.recipe_implementation,
+        self.get_app_info(),
+        self.email_delivery,
+    )
+    if request_id == USER_EMAIL_VERIFY_TOKEN:
+        return await handle_generate_email_verify_token_api(
+            self.api_implementation, api_options, user_context
+        )
+    return await handle_email_verify_api(
+        self.api_implementation, tenant_id, api_options, user_context
+    )
+
+
+
+async def handle_error(self, request: BaseRequest, err: SuperTokensError, response: BaseResponse, user_context: Dict[str, Any]) ‑> BaseResponse +
+
+
+
+ +Expand source code + +
async def handle_error(
+    self,
+    request: BaseRequest,
+    err: SuperTokensError,
+    response: BaseResponse,
+    user_context: Dict[str, Any],
+) -> BaseResponse:
+    if isinstance(err, EmailVerificationInvalidTokenError):
+        response.set_json_content(
+            {"status": "EMAIL_VERIFICATION_INVALID_TOKEN_ERROR"}
+        )
+        return response
+    response.set_json_content({"status": "EMAIL_ALREADY_VERIFIED_ERROR"})
+    return response
+
+
+
+def is_error_from_this_recipe_based_on_instance(self, err: Exception) ‑> bool +
+
+
+
+ +Expand source code + +
def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
+    return isinstance(err, SuperTokensError) and isinstance(
+        err, SuperTokensEmailVerificationError
+    )
+
+
+
+
+
+class IsVerifiedSCV +(id_: str, claim: EmailVerificationClaimClass, ev_claim_validators: EmailVerificationClaimValidators, refetch_time_on_false_in_seconds: int, max_age_in_seconds: Optional[int]) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class IsVerifiedSCV(SessionClaimValidator):
+    def __init__(
+        self,
+        id_: str,
+        claim: EmailVerificationClaimClass,
+        ev_claim_validators: EmailVerificationClaimValidators,
+        refetch_time_on_false_in_seconds: int,
+        max_age_in_seconds: Optional[int],
+    ):
+        super().__init__(id_)
+        self.claim: EmailVerificationClaimClass = claim
+        self.ev_claim_validators = ev_claim_validators
+        self.refetch_time_on_false_in_ms = refetch_time_on_false_in_seconds * 1000
+        self.max_age_in_sec = max_age_in_seconds
+
+    async def validate(
+        self, payload: JSONObject, user_context: Dict[str, Any]
+    ) -> ClaimValidationResult:
+        return await self.ev_claim_validators.has_value(
+            True, self.max_age_in_sec
+        ).validate(payload, user_context)
+
+    def should_refetch(
+        self, payload: JSONObject, user_context: Dict[str, Any]
+    ) -> MaybeAwaitable[bool]:
+        value = self.claim.get_value_from_payload(payload, user_context)
+        if value is None:
+            return True
+
+        current_time = get_timestamp_ms()
+        last_refetch_time = self.claim.get_last_refetch_time(payload, user_context)
+        assert last_refetch_time is not None
+
+        if self.max_age_in_sec is not None:
+            if last_refetch_time < current_time - self.max_age_in_sec * 1000:
+                return True
+
+        if value is False:
+            if last_refetch_time < current_time - self.refetch_time_on_false_in_ms:
+                return True
+
+        return False
+
+

Ancestors

+ +

Methods

+
+
+def should_refetch(self, payload: JSONObject, user_context: Dict[str, Any]) ‑> Union[Awaitable[bool], bool] +
+
+
+
+ +Expand source code + +
def should_refetch(
+    self, payload: JSONObject, user_context: Dict[str, Any]
+) -> MaybeAwaitable[bool]:
+    value = self.claim.get_value_from_payload(payload, user_context)
+    if value is None:
+        return True
+
+    current_time = get_timestamp_ms()
+    last_refetch_time = self.claim.get_last_refetch_time(payload, user_context)
+    assert last_refetch_time is not None
+
+    if self.max_age_in_sec is not None:
+        if last_refetch_time < current_time - self.max_age_in_sec * 1000:
+            return True
+
+    if value is False:
+        if last_refetch_time < current_time - self.refetch_time_on_false_in_ms:
+            return True
+
+    return False
+
+
+
+async def validate(self, payload: JSONObject, user_context: Dict[str, Any]) ‑> ClaimValidationResult +
+
+
+
+ +Expand source code + +
async def validate(
+    self, payload: JSONObject, user_context: Dict[str, Any]
+) -> ClaimValidationResult:
+    return await self.ev_claim_validators.has_value(
+        True, self.max_age_in_sec
+    ).validate(payload, user_context)
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailverification/recipe_implementation.html b/html/supertokens_python/recipe/emailverification/recipe_implementation.html new file mode 100644 index 000000000..b5aba3c97 --- /dev/null +++ b/html/supertokens_python/recipe/emailverification/recipe_implementation.html @@ -0,0 +1,372 @@ + + + + + + +supertokens_python.recipe.emailverification.recipe_implementation API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.emailverification.recipe_implementation

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Any, Dict, Union
+
+from supertokens_python.normalised_url_path import NormalisedURLPath
+
+from .interfaces import (
+    CreateEmailVerificationTokenEmailAlreadyVerifiedError,
+    CreateEmailVerificationTokenOkResult,
+    RecipeInterface,
+    RevokeEmailVerificationTokensOkResult,
+    UnverifyEmailOkResult,
+    VerifyEmailUsingTokenInvalidTokenError,
+    VerifyEmailUsingTokenOkResult,
+)
+from .types import User
+
+if TYPE_CHECKING:
+    from supertokens_python.querier import Querier
+
+    from .utils import EmailVerificationConfig
+
+
+class RecipeImplementation(RecipeInterface):
+    def __init__(self, querier: Querier, config: EmailVerificationConfig):
+        super().__init__()
+        self.querier = querier
+        self.config = config
+
+    async def create_email_verification_token(
+        self, user_id: str, email: str, tenant_id: str, user_context: Dict[str, Any]
+    ) -> Union[
+        CreateEmailVerificationTokenOkResult,
+        CreateEmailVerificationTokenEmailAlreadyVerifiedError,
+    ]:
+        data = {"userId": user_id, "email": email}
+        response = await self.querier.send_post_request(
+            NormalisedURLPath(f"{tenant_id}/recipe/user/email/verify/token"),
+            data,
+            user_context,
+        )
+        if "status" in response and response["status"] == "OK":
+            return CreateEmailVerificationTokenOkResult(response["token"])
+        return CreateEmailVerificationTokenEmailAlreadyVerifiedError()
+
+    async def verify_email_using_token(
+        self, token: str, tenant_id: str, user_context: Dict[str, Any]
+    ) -> Union[VerifyEmailUsingTokenOkResult, VerifyEmailUsingTokenInvalidTokenError]:
+        data = {"method": "token", "token": token}
+        response = await self.querier.send_post_request(
+            NormalisedURLPath(f"{tenant_id}/recipe/user/email/verify"),
+            data,
+            user_context,
+        )
+        if "status" in response and response["status"] == "OK":
+            return VerifyEmailUsingTokenOkResult(
+                User(response["userId"], response["email"])
+            )
+        return VerifyEmailUsingTokenInvalidTokenError()
+
+    async def is_email_verified(
+        self, user_id: str, email: str, user_context: Dict[str, Any]
+    ) -> bool:
+        params = {"userId": user_id, "email": email}
+        response = await self.querier.send_get_request(
+            NormalisedURLPath("/recipe/user/email/verify"), params, user_context
+        )
+        return response["isVerified"]
+
+    async def revoke_email_verification_tokens(
+        self, user_id: str, email: str, tenant_id: str, user_context: Dict[str, Any]
+    ) -> RevokeEmailVerificationTokensOkResult:
+        data = {"userId": user_id, "email": email}
+        await self.querier.send_post_request(
+            NormalisedURLPath(f"{tenant_id}/recipe/user/email/verify/token/remove"),
+            data,
+            user_context,
+        )
+        return RevokeEmailVerificationTokensOkResult()
+
+    async def unverify_email(
+        self, user_id: str, email: str, user_context: Dict[str, Any]
+    ) -> UnverifyEmailOkResult:
+        data = {"userId": user_id, "email": email}
+        await self.querier.send_post_request(
+            NormalisedURLPath("/recipe/user/email/verify/remove"), data, user_context
+        )
+        return UnverifyEmailOkResult()
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class RecipeImplementation +(querier: Querier, config: EmailVerificationConfig) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class RecipeImplementation(RecipeInterface):
+    def __init__(self, querier: Querier, config: EmailVerificationConfig):
+        super().__init__()
+        self.querier = querier
+        self.config = config
+
+    async def create_email_verification_token(
+        self, user_id: str, email: str, tenant_id: str, user_context: Dict[str, Any]
+    ) -> Union[
+        CreateEmailVerificationTokenOkResult,
+        CreateEmailVerificationTokenEmailAlreadyVerifiedError,
+    ]:
+        data = {"userId": user_id, "email": email}
+        response = await self.querier.send_post_request(
+            NormalisedURLPath(f"{tenant_id}/recipe/user/email/verify/token"),
+            data,
+            user_context,
+        )
+        if "status" in response and response["status"] == "OK":
+            return CreateEmailVerificationTokenOkResult(response["token"])
+        return CreateEmailVerificationTokenEmailAlreadyVerifiedError()
+
+    async def verify_email_using_token(
+        self, token: str, tenant_id: str, user_context: Dict[str, Any]
+    ) -> Union[VerifyEmailUsingTokenOkResult, VerifyEmailUsingTokenInvalidTokenError]:
+        data = {"method": "token", "token": token}
+        response = await self.querier.send_post_request(
+            NormalisedURLPath(f"{tenant_id}/recipe/user/email/verify"),
+            data,
+            user_context,
+        )
+        if "status" in response and response["status"] == "OK":
+            return VerifyEmailUsingTokenOkResult(
+                User(response["userId"], response["email"])
+            )
+        return VerifyEmailUsingTokenInvalidTokenError()
+
+    async def is_email_verified(
+        self, user_id: str, email: str, user_context: Dict[str, Any]
+    ) -> bool:
+        params = {"userId": user_id, "email": email}
+        response = await self.querier.send_get_request(
+            NormalisedURLPath("/recipe/user/email/verify"), params, user_context
+        )
+        return response["isVerified"]
+
+    async def revoke_email_verification_tokens(
+        self, user_id: str, email: str, tenant_id: str, user_context: Dict[str, Any]
+    ) -> RevokeEmailVerificationTokensOkResult:
+        data = {"userId": user_id, "email": email}
+        await self.querier.send_post_request(
+            NormalisedURLPath(f"{tenant_id}/recipe/user/email/verify/token/remove"),
+            data,
+            user_context,
+        )
+        return RevokeEmailVerificationTokensOkResult()
+
+    async def unverify_email(
+        self, user_id: str, email: str, user_context: Dict[str, Any]
+    ) -> UnverifyEmailOkResult:
+        data = {"userId": user_id, "email": email}
+        await self.querier.send_post_request(
+            NormalisedURLPath("/recipe/user/email/verify/remove"), data, user_context
+        )
+        return UnverifyEmailOkResult()
+
+

Ancestors

+ +

Methods

+
+
+async def create_email_verification_token(self, user_id: str, email: str, tenant_id: str, user_context: Dict[str, Any]) ‑> Union[CreateEmailVerificationTokenOkResultCreateEmailVerificationTokenEmailAlreadyVerifiedError] +
+
+
+
+ +Expand source code + +
async def create_email_verification_token(
+    self, user_id: str, email: str, tenant_id: str, user_context: Dict[str, Any]
+) -> Union[
+    CreateEmailVerificationTokenOkResult,
+    CreateEmailVerificationTokenEmailAlreadyVerifiedError,
+]:
+    data = {"userId": user_id, "email": email}
+    response = await self.querier.send_post_request(
+        NormalisedURLPath(f"{tenant_id}/recipe/user/email/verify/token"),
+        data,
+        user_context,
+    )
+    if "status" in response and response["status"] == "OK":
+        return CreateEmailVerificationTokenOkResult(response["token"])
+    return CreateEmailVerificationTokenEmailAlreadyVerifiedError()
+
+
+
+async def is_email_verified(self, user_id: str, email: str, user_context: Dict[str, Any]) ‑> bool +
+
+
+
+ +Expand source code + +
async def is_email_verified(
+    self, user_id: str, email: str, user_context: Dict[str, Any]
+) -> bool:
+    params = {"userId": user_id, "email": email}
+    response = await self.querier.send_get_request(
+        NormalisedURLPath("/recipe/user/email/verify"), params, user_context
+    )
+    return response["isVerified"]
+
+
+
+async def revoke_email_verification_tokens(self, user_id: str, email: str, tenant_id: str, user_context: Dict[str, Any]) ‑> RevokeEmailVerificationTokensOkResult +
+
+
+
+ +Expand source code + +
async def revoke_email_verification_tokens(
+    self, user_id: str, email: str, tenant_id: str, user_context: Dict[str, Any]
+) -> RevokeEmailVerificationTokensOkResult:
+    data = {"userId": user_id, "email": email}
+    await self.querier.send_post_request(
+        NormalisedURLPath(f"{tenant_id}/recipe/user/email/verify/token/remove"),
+        data,
+        user_context,
+    )
+    return RevokeEmailVerificationTokensOkResult()
+
+
+
+async def unverify_email(self, user_id: str, email: str, user_context: Dict[str, Any]) ‑> UnverifyEmailOkResult +
+
+
+
+ +Expand source code + +
async def unverify_email(
+    self, user_id: str, email: str, user_context: Dict[str, Any]
+) -> UnverifyEmailOkResult:
+    data = {"userId": user_id, "email": email}
+    await self.querier.send_post_request(
+        NormalisedURLPath("/recipe/user/email/verify/remove"), data, user_context
+    )
+    return UnverifyEmailOkResult()
+
+
+
+async def verify_email_using_token(self, token: str, tenant_id: str, user_context: Dict[str, Any]) ‑> Union[VerifyEmailUsingTokenOkResultVerifyEmailUsingTokenInvalidTokenError] +
+
+
+
+ +Expand source code + +
async def verify_email_using_token(
+    self, token: str, tenant_id: str, user_context: Dict[str, Any]
+) -> Union[VerifyEmailUsingTokenOkResult, VerifyEmailUsingTokenInvalidTokenError]:
+    data = {"method": "token", "token": token}
+    response = await self.querier.send_post_request(
+        NormalisedURLPath(f"{tenant_id}/recipe/user/email/verify"),
+        data,
+        user_context,
+    )
+    if "status" in response and response["status"] == "OK":
+        return VerifyEmailUsingTokenOkResult(
+            User(response["userId"], response["email"])
+        )
+    return VerifyEmailUsingTokenInvalidTokenError()
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailverification/syncio/index.html b/html/supertokens_python/recipe/emailverification/syncio/index.html new file mode 100644 index 000000000..fa4c97e09 --- /dev/null +++ b/html/supertokens_python/recipe/emailverification/syncio/index.html @@ -0,0 +1,357 @@ + + + + + + +supertokens_python.recipe.emailverification.syncio API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.emailverification.syncio

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from typing import Any, Dict, Optional, Union
+
+from supertokens_python.async_to_sync_wrapper import sync
+from supertokens_python.recipe.emailverification.types import EmailTemplateVars
+
+
+def create_email_verification_token(
+    tenant_id: str,
+    user_id: str,
+    email: Optional[str] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+):
+    from supertokens_python.recipe.emailverification.asyncio import (
+        create_email_verification_token,
+    )
+
+    return sync(
+        create_email_verification_token(tenant_id, user_id, email, user_context)
+    )
+
+
+def verify_email_using_token(
+    tenant_id: str,
+    token: str,
+    user_context: Union[None, Dict[str, Any]] = None,
+):
+    from supertokens_python.recipe.emailverification.asyncio import (
+        verify_email_using_token,
+    )
+
+    return sync(verify_email_using_token(tenant_id, token, user_context))
+
+
+def is_email_verified(
+    user_id: str,
+    email: Optional[str] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+):
+    from supertokens_python.recipe.emailverification.asyncio import is_email_verified
+
+    return sync(is_email_verified(user_id, email, user_context))
+
+
+def revoke_email_verification_tokens(
+    tenant_id: str,
+    user_id: str,
+    email: Optional[str] = None,
+    user_context: Optional[Dict[str, Any]] = None,
+):
+    from supertokens_python.recipe.emailverification.asyncio import (
+        revoke_email_verification_tokens,
+    )
+
+    return sync(
+        revoke_email_verification_tokens(tenant_id, user_id, email, user_context)
+    )
+
+
+def unverify_email(
+    user_id: str,
+    email: Optional[str] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+):
+    from supertokens_python.recipe.emailverification.asyncio import unverify_email
+
+    return sync(unverify_email(user_id, email, user_context))
+
+
+def send_email(
+    input_: EmailTemplateVars,
+    user_context: Union[None, Dict[str, Any]] = None,
+):
+    from supertokens_python.recipe.emailverification.asyncio import send_email
+
+    return sync(send_email(input_, user_context))
+
+
+def create_email_verification_link(
+    tenant_id: str,
+    user_id: str,
+    email: Optional[str],
+    user_context: Optional[Dict[str, Any]] = None,
+):
+    from supertokens_python.recipe.emailverification.asyncio import (
+        create_email_verification_link,
+    )
+
+    return sync(create_email_verification_link(tenant_id, user_id, email, user_context))
+
+
+def send_email_verification_email(
+    tenant_id: str,
+    user_id: str,
+    email: Optional[str],
+    user_context: Optional[Dict[str, Any]] = None,
+):
+    from supertokens_python.recipe.emailverification.asyncio import (
+        send_email_verification_email,
+    )
+
+    return sync(send_email_verification_email(tenant_id, user_id, email, user_context))
+
+
+
+
+
+
+
+

Functions

+
+ +
+
+
+ +Expand source code + +
def create_email_verification_link(
+    tenant_id: str,
+    user_id: str,
+    email: Optional[str],
+    user_context: Optional[Dict[str, Any]] = None,
+):
+    from supertokens_python.recipe.emailverification.asyncio import (
+        create_email_verification_link,
+    )
+
+    return sync(create_email_verification_link(tenant_id, user_id, email, user_context))
+
+
+
+def create_email_verification_token(tenant_id: str, user_id: str, email: Optional[str] = None, user_context: Optional[Dict[str, Any]] = None) +
+
+
+
+ +Expand source code + +
def create_email_verification_token(
+    tenant_id: str,
+    user_id: str,
+    email: Optional[str] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+):
+    from supertokens_python.recipe.emailverification.asyncio import (
+        create_email_verification_token,
+    )
+
+    return sync(
+        create_email_verification_token(tenant_id, user_id, email, user_context)
+    )
+
+
+
+def is_email_verified(user_id: str, email: Optional[str] = None, user_context: Optional[Dict[str, Any]] = None) +
+
+
+
+ +Expand source code + +
def is_email_verified(
+    user_id: str,
+    email: Optional[str] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+):
+    from supertokens_python.recipe.emailverification.asyncio import is_email_verified
+
+    return sync(is_email_verified(user_id, email, user_context))
+
+
+
+def revoke_email_verification_tokens(tenant_id: str, user_id: str, email: Optional[str] = None, user_context: Optional[Dict[str, Any]] = None) +
+
+
+
+ +Expand source code + +
def revoke_email_verification_tokens(
+    tenant_id: str,
+    user_id: str,
+    email: Optional[str] = None,
+    user_context: Optional[Dict[str, Any]] = None,
+):
+    from supertokens_python.recipe.emailverification.asyncio import (
+        revoke_email_verification_tokens,
+    )
+
+    return sync(
+        revoke_email_verification_tokens(tenant_id, user_id, email, user_context)
+    )
+
+
+
+def send_email(input_: VerificationEmailTemplateVars, user_context: Optional[Dict[str, Any]] = None) +
+
+
+
+ +Expand source code + +
def send_email(
+    input_: EmailTemplateVars,
+    user_context: Union[None, Dict[str, Any]] = None,
+):
+    from supertokens_python.recipe.emailverification.asyncio import send_email
+
+    return sync(send_email(input_, user_context))
+
+
+
+def send_email_verification_email(tenant_id: str, user_id: str, email: Optional[str], user_context: Optional[Dict[str, Any]] = None) +
+
+
+
+ +Expand source code + +
def send_email_verification_email(
+    tenant_id: str,
+    user_id: str,
+    email: Optional[str],
+    user_context: Optional[Dict[str, Any]] = None,
+):
+    from supertokens_python.recipe.emailverification.asyncio import (
+        send_email_verification_email,
+    )
+
+    return sync(send_email_verification_email(tenant_id, user_id, email, user_context))
+
+
+
+def unverify_email(user_id: str, email: Optional[str] = None, user_context: Optional[Dict[str, Any]] = None) +
+
+
+
+ +Expand source code + +
def unverify_email(
+    user_id: str,
+    email: Optional[str] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+):
+    from supertokens_python.recipe.emailverification.asyncio import unverify_email
+
+    return sync(unverify_email(user_id, email, user_context))
+
+
+
+def verify_email_using_token(tenant_id: str, token: str, user_context: Optional[Dict[str, Any]] = None) +
+
+
+
+ +Expand source code + +
def verify_email_using_token(
+    tenant_id: str,
+    token: str,
+    user_context: Union[None, Dict[str, Any]] = None,
+):
+    from supertokens_python.recipe.emailverification.asyncio import (
+        verify_email_using_token,
+    )
+
+    return sync(verify_email_using_token(tenant_id, token, user_context))
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailverification/types.html b/html/supertokens_python/recipe/emailverification/types.html new file mode 100644 index 000000000..b605b650a --- /dev/null +++ b/html/supertokens_python/recipe/emailverification/types.html @@ -0,0 +1,236 @@ + + + + + + +supertokens_python.recipe.emailverification.types API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.emailverification.types

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from typing import Union
+
+from supertokens_python.ingredients.emaildelivery import EmailDeliveryIngredient
+from supertokens_python.ingredients.emaildelivery.types import (
+    SMTPServiceInterface,
+    EmailDeliveryInterface,
+)
+
+
+class User:
+    def __init__(self, user_id: str, email: str):
+        self.user_id = user_id
+        self.email = email
+
+
+class VerificationEmailTemplateVarsUser:
+    def __init__(self, user_id: str, email: str):
+        self.id = user_id
+        self.email = email
+
+
+class VerificationEmailTemplateVars:
+    def __init__(
+        self,
+        user: VerificationEmailTemplateVarsUser,
+        email_verify_link: str,
+        tenant_id: str,
+    ) -> None:
+        self.user = user
+        self.email_verify_link = email_verify_link
+        self.tenant_id = tenant_id
+
+
+# Export:
+EmailTemplateVars = VerificationEmailTemplateVars
+
+SMTPOverrideInput = SMTPServiceInterface[EmailTemplateVars]
+
+EmailDeliveryOverrideInput = EmailDeliveryInterface[EmailTemplateVars]
+
+
+class EmailVerificationIngredients:
+    def __init__(
+        self,
+        email_delivery: Union[EmailDeliveryIngredient[EmailTemplateVars], None] = None,
+    ):
+        self.email_delivery = email_delivery
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class EmailVerificationIngredients +(email_delivery: Union[EmailDeliveryIngredient[VerificationEmailTemplateVars], None] = None) +
+
+
+
+ +Expand source code + +
class EmailVerificationIngredients:
+    def __init__(
+        self,
+        email_delivery: Union[EmailDeliveryIngredient[EmailTemplateVars], None] = None,
+    ):
+        self.email_delivery = email_delivery
+
+
+
+class User +(user_id: str, email: str) +
+
+
+
+ +Expand source code + +
class User:
+    def __init__(self, user_id: str, email: str):
+        self.user_id = user_id
+        self.email = email
+
+
+
+class VerificationEmailTemplateVars +(user: VerificationEmailTemplateVarsUser, email_verify_link: str, tenant_id: str) +
+
+
+
+ +Expand source code + +
class VerificationEmailTemplateVars:
+    def __init__(
+        self,
+        user: VerificationEmailTemplateVarsUser,
+        email_verify_link: str,
+        tenant_id: str,
+    ) -> None:
+        self.user = user
+        self.email_verify_link = email_verify_link
+        self.tenant_id = tenant_id
+
+
+
+class EmailTemplateVars +(user: VerificationEmailTemplateVarsUser, email_verify_link: str, tenant_id: str) +
+
+
+
+ +Expand source code + +
class VerificationEmailTemplateVars:
+    def __init__(
+        self,
+        user: VerificationEmailTemplateVarsUser,
+        email_verify_link: str,
+        tenant_id: str,
+    ) -> None:
+        self.user = user
+        self.email_verify_link = email_verify_link
+        self.tenant_id = tenant_id
+
+
+
+class VerificationEmailTemplateVarsUser +(user_id: str, email: str) +
+
+
+
+ +Expand source code + +
class VerificationEmailTemplateVarsUser:
+    def __init__(self, user_id: str, email: str):
+        self.id = user_id
+        self.email = email
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/emailverification/utils.html b/html/supertokens_python/recipe/emailverification/utils.html new file mode 100644 index 000000000..d51278ddf --- /dev/null +++ b/html/supertokens_python/recipe/emailverification/utils.html @@ -0,0 +1,322 @@ + + + + + + +supertokens_python.recipe.emailverification.utils API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.emailverification.utils

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Any, Dict, Optional
+from supertokens_python.framework import BaseRequest
+
+from supertokens_python.ingredients.emaildelivery.types import (
+    EmailDeliveryConfig,
+    EmailDeliveryConfigWithService,
+)
+from supertokens_python.recipe.emailverification.emaildelivery.services.backward_compatibility import (
+    BackwardCompatibilityService,
+)
+from typing_extensions import Literal
+
+if TYPE_CHECKING:
+    from typing import Callable, Union
+
+    from supertokens_python.supertokens import AppInfo
+
+    from .interfaces import APIInterface, RecipeInterface, TypeGetEmailForUserIdFunction
+    from .types import EmailTemplateVars, VerificationEmailTemplateVars
+
+
+class OverrideConfig:
+    def __init__(
+        self,
+        functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None,
+        apis: Union[Callable[[APIInterface], APIInterface], None] = None,
+    ):
+        self.functions = functions
+        self.apis = apis
+
+
+MODE_TYPE = Literal["REQUIRED", "OPTIONAL"]
+
+
+class EmailVerificationConfig:
+    def __init__(
+        self,
+        mode: MODE_TYPE,
+        get_email_delivery_config: Callable[
+            [], EmailDeliveryConfigWithService[VerificationEmailTemplateVars]
+        ],
+        get_email_for_user_id: Optional[TypeGetEmailForUserIdFunction],
+        override: OverrideConfig,
+    ):
+        self.mode = mode
+        self.override = override
+        self.get_email_delivery_config = get_email_delivery_config
+        self.get_email_for_user_id = get_email_for_user_id
+
+
+def validate_and_normalise_user_input(
+    app_info: AppInfo,
+    mode: MODE_TYPE,
+    email_delivery: Union[EmailDeliveryConfig[EmailTemplateVars], None] = None,
+    get_email_for_user_id: Optional[TypeGetEmailForUserIdFunction] = None,
+    override: Union[OverrideConfig, None] = None,
+) -> EmailVerificationConfig:
+    if mode not in ["REQUIRED", "OPTIONAL"]:
+        raise ValueError(
+            "Email Verification recipe mode must be one of 'REQUIRED' or 'OPTIONAL'"
+        )
+
+    def get_email_delivery_config() -> (
+        EmailDeliveryConfigWithService[VerificationEmailTemplateVars]
+    ):
+        email_service = email_delivery.service if email_delivery is not None else None
+        if email_service is None:
+            email_service = BackwardCompatibilityService(app_info)
+
+        if email_delivery is not None and email_delivery.override is not None:
+            override = email_delivery.override
+        else:
+            override = None
+        return EmailDeliveryConfigWithService(email_service, override=override)
+
+    if override is not None and not isinstance(override, OverrideConfig):  # type: ignore
+        raise ValueError("override must be of type OverrideConfig or None")
+
+    if override is None:
+        override = OverrideConfig()
+
+    return EmailVerificationConfig(
+        mode,
+        get_email_delivery_config,
+        get_email_for_user_id,
+        override,
+    )
+
+
+def get_email_verify_link(
+    app_info: AppInfo,
+    token: str,
+    tenant_id: str,
+    request: Optional[BaseRequest],
+    user_context: Dict[str, Any],
+) -> str:
+    return (
+        app_info.get_origin(request, user_context).get_as_string_dangerous()
+        + app_info.website_base_path.get_as_string_dangerous()
+        + "/verify-email"
+        + "?token="
+        + token
+        + "&tenantId="
+        + tenant_id
+    )
+
+
+
+
+
+
+
+

Functions

+
+ +
+
+
+ +Expand source code + +
def get_email_verify_link(
+    app_info: AppInfo,
+    token: str,
+    tenant_id: str,
+    request: Optional[BaseRequest],
+    user_context: Dict[str, Any],
+) -> str:
+    return (
+        app_info.get_origin(request, user_context).get_as_string_dangerous()
+        + app_info.website_base_path.get_as_string_dangerous()
+        + "/verify-email"
+        + "?token="
+        + token
+        + "&tenantId="
+        + tenant_id
+    )
+
+
+
+def validate_and_normalise_user_input(app_info: AppInfo, mode: MODE_TYPE, email_delivery: Union[EmailDeliveryConfig[EmailTemplateVars], None] = None, get_email_for_user_id: Optional[TypeGetEmailForUserIdFunction] = None, override: Union[OverrideConfig, None] = None) ‑> EmailVerificationConfig +
+
+
+
+ +Expand source code + +
def validate_and_normalise_user_input(
+    app_info: AppInfo,
+    mode: MODE_TYPE,
+    email_delivery: Union[EmailDeliveryConfig[EmailTemplateVars], None] = None,
+    get_email_for_user_id: Optional[TypeGetEmailForUserIdFunction] = None,
+    override: Union[OverrideConfig, None] = None,
+) -> EmailVerificationConfig:
+    if mode not in ["REQUIRED", "OPTIONAL"]:
+        raise ValueError(
+            "Email Verification recipe mode must be one of 'REQUIRED' or 'OPTIONAL'"
+        )
+
+    def get_email_delivery_config() -> (
+        EmailDeliveryConfigWithService[VerificationEmailTemplateVars]
+    ):
+        email_service = email_delivery.service if email_delivery is not None else None
+        if email_service is None:
+            email_service = BackwardCompatibilityService(app_info)
+
+        if email_delivery is not None and email_delivery.override is not None:
+            override = email_delivery.override
+        else:
+            override = None
+        return EmailDeliveryConfigWithService(email_service, override=override)
+
+    if override is not None and not isinstance(override, OverrideConfig):  # type: ignore
+        raise ValueError("override must be of type OverrideConfig or None")
+
+    if override is None:
+        override = OverrideConfig()
+
+    return EmailVerificationConfig(
+        mode,
+        get_email_delivery_config,
+        get_email_for_user_id,
+        override,
+    )
+
+
+
+
+
+

Classes

+
+
+class EmailVerificationConfig +(mode: MODE_TYPE, get_email_delivery_config: Callable[[], EmailDeliveryConfigWithService[VerificationEmailTemplateVars]], get_email_for_user_id: Optional[TypeGetEmailForUserIdFunction], override: OverrideConfig) +
+
+
+
+ +Expand source code + +
class EmailVerificationConfig:
+    def __init__(
+        self,
+        mode: MODE_TYPE,
+        get_email_delivery_config: Callable[
+            [], EmailDeliveryConfigWithService[VerificationEmailTemplateVars]
+        ],
+        get_email_for_user_id: Optional[TypeGetEmailForUserIdFunction],
+        override: OverrideConfig,
+    ):
+        self.mode = mode
+        self.override = override
+        self.get_email_delivery_config = get_email_delivery_config
+        self.get_email_for_user_id = get_email_for_user_id
+
+
+
+class OverrideConfig +(functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None, apis: Union[Callable[[APIInterface], APIInterface], None] = None) +
+
+
+
+ +Expand source code + +
class OverrideConfig:
+    def __init__(
+        self,
+        functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None,
+        apis: Union[Callable[[APIInterface], APIInterface], None] = None,
+    ):
+        self.functions = functions
+        self.apis = apis
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/index.html b/html/supertokens_python/recipe/index.html new file mode 100644 index 000000000..969701a6a --- /dev/null +++ b/html/supertokens_python/recipe/index.html @@ -0,0 +1,115 @@ + + + + + + +supertokens_python.recipe API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe

+
+
+
+
+

Sub-modules

+
+
supertokens_python.recipe.dashboard
+
+
+
+
supertokens_python.recipe.emailpassword
+
+
+
+
supertokens_python.recipe.emailverification
+
+
+
+
supertokens_python.recipe.jwt
+
+
+
+
supertokens_python.recipe.multitenancy
+
+
+
+
supertokens_python.recipe.openid
+
+
+
+
supertokens_python.recipe.passwordless
+
+
+
+
supertokens_python.recipe.session
+
+
+
+
supertokens_python.recipe.thirdparty
+
+
+
+
supertokens_python.recipe.usermetadata
+
+
+
+
supertokens_python.recipe.userroles
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/jwt/api/implementation.html b/html/supertokens_python/recipe/jwt/api/implementation.html new file mode 100644 index 000000000..854bab900 --- /dev/null +++ b/html/supertokens_python/recipe/jwt/api/implementation.html @@ -0,0 +1,157 @@ + + + + + + +supertokens_python.recipe.jwt.api.implementation API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.jwt.api.implementation

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from typing import Any, Dict
+
+from supertokens_python.recipe.jwt.interfaces import (
+    APIInterface,
+    APIOptions,
+    JWKSGetResponse,
+)
+
+
+class APIImplementation(APIInterface):
+    async def jwks_get(
+        self, api_options: APIOptions, user_context: Dict[str, Any]
+    ) -> JWKSGetResponse:
+        response = await api_options.recipe_implementation.get_jwks(user_context)
+
+        if response.validity_in_secs is not None:
+            api_options.response.set_header(
+                "Cache-Control", f"max-age={response.validity_in_secs}, must-revalidate"
+            )
+
+        return JWKSGetResponse(response.keys)
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class APIImplementation +
+
+
+
+ +Expand source code + +
class APIImplementation(APIInterface):
+    async def jwks_get(
+        self, api_options: APIOptions, user_context: Dict[str, Any]
+    ) -> JWKSGetResponse:
+        response = await api_options.recipe_implementation.get_jwks(user_context)
+
+        if response.validity_in_secs is not None:
+            api_options.response.set_header(
+                "Cache-Control", f"max-age={response.validity_in_secs}, must-revalidate"
+            )
+
+        return JWKSGetResponse(response.keys)
+
+

Ancestors

+ +

Methods

+
+
+async def jwks_get(self, api_options: APIOptions, user_context: Dict[str, Any]) ‑> JWKSGetResponse +
+
+
+
+ +Expand source code + +
async def jwks_get(
+    self, api_options: APIOptions, user_context: Dict[str, Any]
+) -> JWKSGetResponse:
+    response = await api_options.recipe_implementation.get_jwks(user_context)
+
+    if response.validity_in_secs is not None:
+        api_options.response.set_header(
+            "Cache-Control", f"max-age={response.validity_in_secs}, must-revalidate"
+        )
+
+    return JWKSGetResponse(response.keys)
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/jwt/api/index.html b/html/supertokens_python/recipe/jwt/api/index.html new file mode 100644 index 000000000..6bbcaa708 --- /dev/null +++ b/html/supertokens_python/recipe/jwt/api/index.html @@ -0,0 +1,70 @@ + + + + + + +supertokens_python.recipe.jwt.api API documentation + + + + + + + + + + + +
+ + +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/jwt/api/jwks_get.html b/html/supertokens_python/recipe/jwt/api/jwks_get.html new file mode 100644 index 000000000..0cde8f98a --- /dev/null +++ b/html/supertokens_python/recipe/jwt/api/jwks_get.html @@ -0,0 +1,126 @@ + + + + + + +supertokens_python.recipe.jwt.api.jwks_get API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.jwt.api.jwks_get

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+from typing import Any, Dict
+from supertokens_python.recipe.jwt.interfaces import APIInterface, APIOptions
+from supertokens_python.utils import send_200_response
+
+from ..interfaces import JWKSGetResponse
+
+
+async def jwks_get(
+    api_implementation: APIInterface,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+):
+    if api_implementation.disable_jwks_get:
+        return None
+
+    result = await api_implementation.jwks_get(api_options, user_context)
+
+    if isinstance(result, JWKSGetResponse):
+        api_options.response.set_header("Access-Control-Allow-Origin", "*")
+
+    return send_200_response(result.to_json(), api_options.response)
+
+
+
+
+
+
+
+

Functions

+
+
+async def jwks_get(api_implementation: APIInterface, api_options: APIOptions, user_context: Dict[str, Any]) +
+
+
+
+ +Expand source code + +
async def jwks_get(
+    api_implementation: APIInterface,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+):
+    if api_implementation.disable_jwks_get:
+        return None
+
+    result = await api_implementation.jwks_get(api_options, user_context)
+
+    if isinstance(result, JWKSGetResponse):
+        api_options.response.set_header("Access-Control-Allow-Origin", "*")
+
+    return send_200_response(result.to_json(), api_options.response)
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/jwt/asyncio/index.html b/html/supertokens_python/recipe/jwt/asyncio/index.html new file mode 100644 index 000000000..c4cfb3c50 --- /dev/null +++ b/html/supertokens_python/recipe/jwt/asyncio/index.html @@ -0,0 +1,150 @@ + + + + + + +supertokens_python.recipe.jwt.asyncio API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.jwt.asyncio

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from typing import Any, Dict, Union, Optional
+
+from supertokens_python.recipe.jwt.interfaces import (
+    CreateJwtOkResult,
+    CreateJwtResultUnsupportedAlgorithm,
+    GetJWKSResult,
+)
+from supertokens_python.recipe.jwt.recipe import JWTRecipe
+
+
+async def create_jwt(
+    payload: Optional[Dict[str, Any]] = None,
+    validity_seconds: Optional[int] = None,
+    use_static_signing_key: Optional[bool] = None,
+    user_context: Optional[Dict[str, Any]] = None,
+) -> Union[CreateJwtOkResult, CreateJwtResultUnsupportedAlgorithm]:
+    if user_context is None:
+        user_context = {}
+    if payload is None:
+        payload = {}
+
+    return await JWTRecipe.get_instance().recipe_implementation.create_jwt(
+        payload, validity_seconds, use_static_signing_key, user_context
+    )
+
+
+async def get_jwks(user_context: Optional[Dict[str, Any]] = None) -> GetJWKSResult:
+    if user_context is None:
+        user_context = {}
+    return await JWTRecipe.get_instance().recipe_implementation.get_jwks(user_context)
+
+
+
+
+
+
+
+

Functions

+
+
+async def create_jwt(payload: Optional[Dict[str, Any]] = None, validity_seconds: Optional[int] = None, use_static_signing_key: Optional[bool] = None, user_context: Optional[Dict[str, Any]] = None) ‑> Union[CreateJwtOkResultCreateJwtResultUnsupportedAlgorithm] +
+
+
+
+ +Expand source code + +
async def create_jwt(
+    payload: Optional[Dict[str, Any]] = None,
+    validity_seconds: Optional[int] = None,
+    use_static_signing_key: Optional[bool] = None,
+    user_context: Optional[Dict[str, Any]] = None,
+) -> Union[CreateJwtOkResult, CreateJwtResultUnsupportedAlgorithm]:
+    if user_context is None:
+        user_context = {}
+    if payload is None:
+        payload = {}
+
+    return await JWTRecipe.get_instance().recipe_implementation.create_jwt(
+        payload, validity_seconds, use_static_signing_key, user_context
+    )
+
+
+
+async def get_jwks(user_context: Optional[Dict[str, Any]] = None) ‑> GetJWKSResult +
+
+
+
+ +Expand source code + +
async def get_jwks(user_context: Optional[Dict[str, Any]] = None) -> GetJWKSResult:
+    if user_context is None:
+        user_context = {}
+    return await JWTRecipe.get_instance().recipe_implementation.get_jwks(user_context)
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/jwt/constants.html b/html/supertokens_python/recipe/jwt/constants.html new file mode 100644 index 000000000..e6d5d47ca --- /dev/null +++ b/html/supertokens_python/recipe/jwt/constants.html @@ -0,0 +1,73 @@ + + + + + + +supertokens_python.recipe.jwt.constants API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.jwt.constants

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+GET_JWKS_API = "/jwt/jwks.json"
+
+
+
+
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/jwt/exceptions.html b/html/supertokens_python/recipe/jwt/exceptions.html new file mode 100644 index 000000000..2376b6833 --- /dev/null +++ b/html/supertokens_python/recipe/jwt/exceptions.html @@ -0,0 +1,108 @@ + + + + + + +supertokens_python.recipe.jwt.exceptions API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.jwt.exceptions

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from supertokens_python.exceptions import SuperTokensError
+
+
+class SuperTokensJWTError(SuperTokensError):
+    pass
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class SuperTokensJWTError +(*args, **kwargs) +
+
+

Common base class for all non-exit exceptions.

+
+ +Expand source code + +
class SuperTokensJWTError(SuperTokensError):
+    pass
+
+

Ancestors

+ +
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/jwt/index.html b/html/supertokens_python/recipe/jwt/index.html new file mode 100644 index 000000000..8601620d4 --- /dev/null +++ b/html/supertokens_python/recipe/jwt/index.html @@ -0,0 +1,165 @@ + + + + + + +supertokens_python.recipe.jwt API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.jwt

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Callable, Union
+
+from .recipe import JWTRecipe
+from .utils import OverrideConfig
+
+if TYPE_CHECKING:
+    from supertokens_python.supertokens import AppInfo
+
+    from ...recipe_module import RecipeModule
+
+
+def init(
+    jwt_validity_seconds: Union[int, None] = None,
+    override: Union[OverrideConfig, None] = None,
+) -> Callable[[AppInfo], RecipeModule]:
+    return JWTRecipe.init(jwt_validity_seconds, override)
+
+
+
+

Sub-modules

+
+
supertokens_python.recipe.jwt.api
+
+
+
+
supertokens_python.recipe.jwt.asyncio
+
+
+
+
supertokens_python.recipe.jwt.constants
+
+
+
+
supertokens_python.recipe.jwt.exceptions
+
+
+
+
supertokens_python.recipe.jwt.interfaces
+
+
+
+
supertokens_python.recipe.jwt.recipe
+
+
+
+
supertokens_python.recipe.jwt.recipe_implementation
+
+
+
+
supertokens_python.recipe.jwt.syncio
+
+
+
+
supertokens_python.recipe.jwt.utils
+
+
+
+
+
+
+
+
+

Functions

+
+
+def init(jwt_validity_seconds: Union[int, None] = None, override: Union[OverrideConfig, None] = None) ‑> Callable[[AppInfo], RecipeModule] +
+
+
+
+ +Expand source code + +
def init(
+    jwt_validity_seconds: Union[int, None] = None,
+    override: Union[OverrideConfig, None] = None,
+) -> Callable[[AppInfo], RecipeModule]:
+    return JWTRecipe.init(jwt_validity_seconds, override)
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/jwt/interfaces.html b/html/supertokens_python/recipe/jwt/interfaces.html new file mode 100644 index 000000000..d883348e7 --- /dev/null +++ b/html/supertokens_python/recipe/jwt/interfaces.html @@ -0,0 +1,484 @@ + + + + + + +supertokens_python.recipe.jwt.interfaces API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.jwt.interfaces

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from abc import ABC, abstractmethod
+from typing import Any, Dict, List, Union, Optional
+
+from supertokens_python.framework import BaseRequest, BaseResponse
+from supertokens_python.types import APIResponse, GeneralErrorResponse
+
+from .utils import JWTConfig
+
+
+class JsonWebKey:
+    def __init__(self, kty: str, kid: str, n: str, e: str, alg: str, use: str):
+        self.kty = kty
+        self.kid = kid
+        self.n = n
+        self.e = e
+        self.alg = alg
+        self.use = use
+
+
+class CreateJwtOkResult:
+    def __init__(self, jwt: str):
+        self.jwt = jwt
+
+
+class CreateJwtResultUnsupportedAlgorithm:
+    pass
+
+
+class GetJWKSResult:
+    def __init__(self, keys: List[JsonWebKey], validity_in_secs: Optional[int]):
+        self.keys = keys
+        self.validity_in_secs = validity_in_secs
+
+
+class RecipeInterface(ABC):
+    def __init__(self):
+        pass
+
+    @abstractmethod
+    async def create_jwt(
+        self,
+        payload: Dict[str, Any],
+        validity_seconds: Optional[int],
+        use_static_signing_key: Optional[bool],
+        user_context: Dict[str, Any],
+    ) -> Union[CreateJwtOkResult, CreateJwtResultUnsupportedAlgorithm]:
+        pass
+
+    @abstractmethod
+    async def get_jwks(self, user_context: Dict[str, Any]) -> GetJWKSResult:
+        pass
+
+
+class APIOptions:
+    def __init__(
+        self,
+        request: BaseRequest,
+        response: BaseResponse,
+        recipe_id: str,
+        config: JWTConfig,
+        recipe_implementation: RecipeInterface,
+    ):
+        self.request = request
+        self.response = response
+        self.recipe_id = recipe_id
+        self.config = config
+        self.recipe_implementation = recipe_implementation
+
+
+class JWKSGetResponse(APIResponse):
+    def __init__(self, keys: List[JsonWebKey]):
+        self.keys = keys
+
+    def to_json(self) -> Dict[str, Any]:
+        keys: List[Dict[str, Any]] = []
+        for key in self.keys:
+            keys.append(
+                {
+                    "kty": key.kty,
+                    "kid": key.kid,
+                    "n": key.n,
+                    "e": key.e,
+                    "alg": key.alg,
+                    "use": key.use,
+                }
+            )
+
+        return {"keys": keys}
+
+
+class APIInterface:
+    def __init__(self):
+        self.disable_jwks_get = False
+
+    @abstractmethod
+    async def jwks_get(
+        self, api_options: APIOptions, user_context: Dict[str, Any]
+    ) -> Union[JWKSGetResponse, GeneralErrorResponse]:
+        pass
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class APIInterface +
+
+
+
+ +Expand source code + +
class APIInterface:
+    def __init__(self):
+        self.disable_jwks_get = False
+
+    @abstractmethod
+    async def jwks_get(
+        self, api_options: APIOptions, user_context: Dict[str, Any]
+    ) -> Union[JWKSGetResponse, GeneralErrorResponse]:
+        pass
+
+

Subclasses

+ +

Methods

+
+
+async def jwks_get(self, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[JWKSGetResponseGeneralErrorResponse] +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def jwks_get(
+    self, api_options: APIOptions, user_context: Dict[str, Any]
+) -> Union[JWKSGetResponse, GeneralErrorResponse]:
+    pass
+
+
+
+
+
+class APIOptions +(request: BaseRequest, response: BaseResponse, recipe_id: str, config: JWTConfig, recipe_implementation: RecipeInterface) +
+
+
+
+ +Expand source code + +
class APIOptions:
+    def __init__(
+        self,
+        request: BaseRequest,
+        response: BaseResponse,
+        recipe_id: str,
+        config: JWTConfig,
+        recipe_implementation: RecipeInterface,
+    ):
+        self.request = request
+        self.response = response
+        self.recipe_id = recipe_id
+        self.config = config
+        self.recipe_implementation = recipe_implementation
+
+
+
+class CreateJwtOkResult +(jwt: str) +
+
+
+
+ +Expand source code + +
class CreateJwtOkResult:
+    def __init__(self, jwt: str):
+        self.jwt = jwt
+
+
+
+class CreateJwtResultUnsupportedAlgorithm +
+
+
+
+ +Expand source code + +
class CreateJwtResultUnsupportedAlgorithm:
+    pass
+
+
+
+class GetJWKSResult +(keys: List[JsonWebKey], validity_in_secs: Optional[int]) +
+
+
+
+ +Expand source code + +
class GetJWKSResult:
+    def __init__(self, keys: List[JsonWebKey], validity_in_secs: Optional[int]):
+        self.keys = keys
+        self.validity_in_secs = validity_in_secs
+
+
+
+class JWKSGetResponse +(keys: List[JsonWebKey]) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class JWKSGetResponse(APIResponse):
+    def __init__(self, keys: List[JsonWebKey]):
+        self.keys = keys
+
+    def to_json(self) -> Dict[str, Any]:
+        keys: List[Dict[str, Any]] = []
+        for key in self.keys:
+            keys.append(
+                {
+                    "kty": key.kty,
+                    "kid": key.kid,
+                    "n": key.n,
+                    "e": key.e,
+                    "alg": key.alg,
+                    "use": key.use,
+                }
+            )
+
+        return {"keys": keys}
+
+

Ancestors

+ +

Methods

+
+
+def to_json(self) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
def to_json(self) -> Dict[str, Any]:
+    keys: List[Dict[str, Any]] = []
+    for key in self.keys:
+        keys.append(
+            {
+                "kty": key.kty,
+                "kid": key.kid,
+                "n": key.n,
+                "e": key.e,
+                "alg": key.alg,
+                "use": key.use,
+            }
+        )
+
+    return {"keys": keys}
+
+
+
+
+
+class JsonWebKey +(kty: str, kid: str, n: str, e: str, alg: str, use: str) +
+
+
+
+ +Expand source code + +
class JsonWebKey:
+    def __init__(self, kty: str, kid: str, n: str, e: str, alg: str, use: str):
+        self.kty = kty
+        self.kid = kid
+        self.n = n
+        self.e = e
+        self.alg = alg
+        self.use = use
+
+
+
+class RecipeInterface +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class RecipeInterface(ABC):
+    def __init__(self):
+        pass
+
+    @abstractmethod
+    async def create_jwt(
+        self,
+        payload: Dict[str, Any],
+        validity_seconds: Optional[int],
+        use_static_signing_key: Optional[bool],
+        user_context: Dict[str, Any],
+    ) -> Union[CreateJwtOkResult, CreateJwtResultUnsupportedAlgorithm]:
+        pass
+
+    @abstractmethod
+    async def get_jwks(self, user_context: Dict[str, Any]) -> GetJWKSResult:
+        pass
+
+

Ancestors

+
    +
  • abc.ABC
  • +
+

Subclasses

+ +

Methods

+
+
+async def create_jwt(self, payload: Dict[str, Any], validity_seconds: Optional[int], use_static_signing_key: Optional[bool], user_context: Dict[str, Any]) ‑> Union[CreateJwtOkResultCreateJwtResultUnsupportedAlgorithm] +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def create_jwt(
+    self,
+    payload: Dict[str, Any],
+    validity_seconds: Optional[int],
+    use_static_signing_key: Optional[bool],
+    user_context: Dict[str, Any],
+) -> Union[CreateJwtOkResult, CreateJwtResultUnsupportedAlgorithm]:
+    pass
+
+
+
+async def get_jwks(self, user_context: Dict[str, Any]) ‑> GetJWKSResult +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def get_jwks(self, user_context: Dict[str, Any]) -> GetJWKSResult:
+    pass
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/jwt/recipe.html b/html/supertokens_python/recipe/jwt/recipe.html new file mode 100644 index 000000000..ffa3b64a6 --- /dev/null +++ b/html/supertokens_python/recipe/jwt/recipe.html @@ -0,0 +1,543 @@ + + + + + + +supertokens_python.recipe.jwt.recipe API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.jwt.recipe

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from os import environ
+from typing import TYPE_CHECKING, List, Union, Optional, Dict, Any
+
+from supertokens_python.querier import Querier
+from supertokens_python.recipe.jwt.api.implementation import APIImplementation
+from supertokens_python.recipe.jwt.api.jwks_get import jwks_get
+from supertokens_python.recipe.jwt.constants import GET_JWKS_API
+from supertokens_python.recipe.jwt.exceptions import SuperTokensJWTError
+from supertokens_python.recipe.jwt.interfaces import APIOptions
+from supertokens_python.recipe.jwt.recipe_implementation import RecipeImplementation
+from supertokens_python.recipe.jwt.utils import (
+    OverrideConfig,
+    validate_and_normalise_user_input,
+)
+
+if TYPE_CHECKING:
+    from supertokens_python.framework.request import BaseRequest
+    from supertokens_python.framework.response import BaseResponse
+    from supertokens_python.supertokens import AppInfo
+
+from supertokens_python.exceptions import SuperTokensError, raise_general_exception
+from supertokens_python.normalised_url_path import NormalisedURLPath
+from supertokens_python.recipe_module import APIHandled, RecipeModule
+
+
+class JWTRecipe(RecipeModule):
+    recipe_id = "jwt"
+    __instance = None
+
+    def __init__(
+        self,
+        recipe_id: str,
+        app_info: AppInfo,
+        jwt_validity_seconds: Union[int, None] = None,
+        override: Union[OverrideConfig, None] = None,
+    ):
+        super().__init__(recipe_id, app_info)
+        self.config = validate_and_normalise_user_input(jwt_validity_seconds, override)
+
+        recipe_implementation = RecipeImplementation(
+            Querier.get_instance(recipe_id), self.config, app_info
+        )
+        self.recipe_implementation = (
+            recipe_implementation
+            if self.config.override.functions is None
+            else self.config.override.functions(recipe_implementation)
+        )
+        api_implementation = APIImplementation()
+        self.api_implementation = (
+            api_implementation
+            if self.config.override.apis is None
+            else self.config.override.apis(api_implementation)
+        )
+
+    def get_apis_handled(self) -> List[APIHandled]:
+        return [
+            APIHandled(
+                method="get",
+                path_without_api_base_path=NormalisedURLPath(GET_JWKS_API),
+                request_id=GET_JWKS_API,
+                disabled=self.api_implementation.disable_jwks_get,
+            )
+        ]
+
+    async def handle_api_request(
+        self,
+        request_id: str,
+        tenant_id: Optional[str],
+        request: BaseRequest,
+        path: NormalisedURLPath,
+        method: str,
+        response: BaseResponse,
+        user_context: Dict[str, Any],
+    ):
+        options = APIOptions(
+            request,
+            response,
+            self.get_recipe_id(),
+            self.config,
+            self.recipe_implementation,
+        )
+
+        if request_id == GET_JWKS_API:
+            return await jwks_get(self.api_implementation, options, user_context)
+
+        return None
+
+    async def handle_error(
+        self,
+        request: BaseRequest,
+        err: SuperTokensError,
+        response: BaseResponse,
+        user_context: Dict[str, Any],
+    ):
+        raise err
+
+    def get_all_cors_headers(self) -> List[str]:
+        return []
+
+    def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
+        return isinstance(err, SuperTokensError) and isinstance(
+            err, SuperTokensJWTError
+        )
+
+    @staticmethod
+    def init(
+        jwt_validity_seconds: Union[int, None] = None,
+        override: Union[OverrideConfig, None] = None,
+    ):
+        def func(app_info: AppInfo):
+            if JWTRecipe.__instance is None:
+                JWTRecipe.__instance = JWTRecipe(
+                    JWTRecipe.recipe_id, app_info, jwt_validity_seconds, override
+                )
+                return JWTRecipe.__instance
+            raise_general_exception(
+                "JWT recipe has already been initialised. Please check "
+                "your code for bugs."
+            )
+
+        return func
+
+    @staticmethod
+    def get_instance() -> JWTRecipe:
+        if JWTRecipe.__instance is not None:
+            return JWTRecipe.__instance
+        raise_general_exception(
+            "Initialisation not done. Did you forget to call the SuperTokens.init function?"
+        )
+
+    @staticmethod
+    def reset():
+        if ("SUPERTOKENS_ENV" not in environ) or (
+            environ["SUPERTOKENS_ENV"] != "testing"
+        ):
+            raise_general_exception("calling testing function in non testing env")
+        JWTRecipe.__instance = None
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class JWTRecipe +(recipe_id: str, app_info: AppInfo, jwt_validity_seconds: Union[int, None] = None, override: Union[OverrideConfig, None] = None) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class JWTRecipe(RecipeModule):
+    recipe_id = "jwt"
+    __instance = None
+
+    def __init__(
+        self,
+        recipe_id: str,
+        app_info: AppInfo,
+        jwt_validity_seconds: Union[int, None] = None,
+        override: Union[OverrideConfig, None] = None,
+    ):
+        super().__init__(recipe_id, app_info)
+        self.config = validate_and_normalise_user_input(jwt_validity_seconds, override)
+
+        recipe_implementation = RecipeImplementation(
+            Querier.get_instance(recipe_id), self.config, app_info
+        )
+        self.recipe_implementation = (
+            recipe_implementation
+            if self.config.override.functions is None
+            else self.config.override.functions(recipe_implementation)
+        )
+        api_implementation = APIImplementation()
+        self.api_implementation = (
+            api_implementation
+            if self.config.override.apis is None
+            else self.config.override.apis(api_implementation)
+        )
+
+    def get_apis_handled(self) -> List[APIHandled]:
+        return [
+            APIHandled(
+                method="get",
+                path_without_api_base_path=NormalisedURLPath(GET_JWKS_API),
+                request_id=GET_JWKS_API,
+                disabled=self.api_implementation.disable_jwks_get,
+            )
+        ]
+
+    async def handle_api_request(
+        self,
+        request_id: str,
+        tenant_id: Optional[str],
+        request: BaseRequest,
+        path: NormalisedURLPath,
+        method: str,
+        response: BaseResponse,
+        user_context: Dict[str, Any],
+    ):
+        options = APIOptions(
+            request,
+            response,
+            self.get_recipe_id(),
+            self.config,
+            self.recipe_implementation,
+        )
+
+        if request_id == GET_JWKS_API:
+            return await jwks_get(self.api_implementation, options, user_context)
+
+        return None
+
+    async def handle_error(
+        self,
+        request: BaseRequest,
+        err: SuperTokensError,
+        response: BaseResponse,
+        user_context: Dict[str, Any],
+    ):
+        raise err
+
+    def get_all_cors_headers(self) -> List[str]:
+        return []
+
+    def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
+        return isinstance(err, SuperTokensError) and isinstance(
+            err, SuperTokensJWTError
+        )
+
+    @staticmethod
+    def init(
+        jwt_validity_seconds: Union[int, None] = None,
+        override: Union[OverrideConfig, None] = None,
+    ):
+        def func(app_info: AppInfo):
+            if JWTRecipe.__instance is None:
+                JWTRecipe.__instance = JWTRecipe(
+                    JWTRecipe.recipe_id, app_info, jwt_validity_seconds, override
+                )
+                return JWTRecipe.__instance
+            raise_general_exception(
+                "JWT recipe has already been initialised. Please check "
+                "your code for bugs."
+            )
+
+        return func
+
+    @staticmethod
+    def get_instance() -> JWTRecipe:
+        if JWTRecipe.__instance is not None:
+            return JWTRecipe.__instance
+        raise_general_exception(
+            "Initialisation not done. Did you forget to call the SuperTokens.init function?"
+        )
+
+    @staticmethod
+    def reset():
+        if ("SUPERTOKENS_ENV" not in environ) or (
+            environ["SUPERTOKENS_ENV"] != "testing"
+        ):
+            raise_general_exception("calling testing function in non testing env")
+        JWTRecipe.__instance = None
+
+

Ancestors

+ +

Class variables

+
+
var get_tenant_id : Optional[Callable[[str, Dict[str, Any]], Awaitable[str]]]
+
+
+
+
var recipe_id
+
+
+
+
+

Static methods

+
+
+def get_instance() ‑> JWTRecipe +
+
+
+
+ +Expand source code + +
@staticmethod
+def get_instance() -> JWTRecipe:
+    if JWTRecipe.__instance is not None:
+        return JWTRecipe.__instance
+    raise_general_exception(
+        "Initialisation not done. Did you forget to call the SuperTokens.init function?"
+    )
+
+
+
+def init(jwt_validity_seconds: Union[int, None] = None, override: Union[OverrideConfig, None] = None) +
+
+
+
+ +Expand source code + +
@staticmethod
+def init(
+    jwt_validity_seconds: Union[int, None] = None,
+    override: Union[OverrideConfig, None] = None,
+):
+    def func(app_info: AppInfo):
+        if JWTRecipe.__instance is None:
+            JWTRecipe.__instance = JWTRecipe(
+                JWTRecipe.recipe_id, app_info, jwt_validity_seconds, override
+            )
+            return JWTRecipe.__instance
+        raise_general_exception(
+            "JWT recipe has already been initialised. Please check "
+            "your code for bugs."
+        )
+
+    return func
+
+
+
+def reset() +
+
+
+
+ +Expand source code + +
@staticmethod
+def reset():
+    if ("SUPERTOKENS_ENV" not in environ) or (
+        environ["SUPERTOKENS_ENV"] != "testing"
+    ):
+        raise_general_exception("calling testing function in non testing env")
+    JWTRecipe.__instance = None
+
+
+
+

Methods

+
+
+def get_all_cors_headers(self) ‑> List[str] +
+
+
+
+ +Expand source code + +
def get_all_cors_headers(self) -> List[str]:
+    return []
+
+
+
+def get_apis_handled(self) ‑> List[APIHandled] +
+
+
+
+ +Expand source code + +
def get_apis_handled(self) -> List[APIHandled]:
+    return [
+        APIHandled(
+            method="get",
+            path_without_api_base_path=NormalisedURLPath(GET_JWKS_API),
+            request_id=GET_JWKS_API,
+            disabled=self.api_implementation.disable_jwks_get,
+        )
+    ]
+
+
+
+async def handle_api_request(self, request_id: str, tenant_id: Optional[str], request: BaseRequest, path: NormalisedURLPath, method: str, response: BaseResponse, user_context: Dict[str, Any]) +
+
+
+
+ +Expand source code + +
async def handle_api_request(
+    self,
+    request_id: str,
+    tenant_id: Optional[str],
+    request: BaseRequest,
+    path: NormalisedURLPath,
+    method: str,
+    response: BaseResponse,
+    user_context: Dict[str, Any],
+):
+    options = APIOptions(
+        request,
+        response,
+        self.get_recipe_id(),
+        self.config,
+        self.recipe_implementation,
+    )
+
+    if request_id == GET_JWKS_API:
+        return await jwks_get(self.api_implementation, options, user_context)
+
+    return None
+
+
+
+async def handle_error(self, request: BaseRequest, err: SuperTokensError, response: BaseResponse, user_context: Dict[str, Any]) +
+
+
+
+ +Expand source code + +
async def handle_error(
+    self,
+    request: BaseRequest,
+    err: SuperTokensError,
+    response: BaseResponse,
+    user_context: Dict[str, Any],
+):
+    raise err
+
+
+
+def is_error_from_this_recipe_based_on_instance(self, err: Exception) ‑> bool +
+
+
+
+ +Expand source code + +
def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
+    return isinstance(err, SuperTokensError) and isinstance(
+        err, SuperTokensJWTError
+    )
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/jwt/recipe_implementation.html b/html/supertokens_python/recipe/jwt/recipe_implementation.html new file mode 100644 index 000000000..891b90ba4 --- /dev/null +++ b/html/supertokens_python/recipe/jwt/recipe_implementation.html @@ -0,0 +1,332 @@ + + + + + + +supertokens_python.recipe.jwt.recipe_implementation API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.jwt.recipe_implementation

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Any, Dict, List, Union, Optional
+
+from supertokens_python.normalised_url_path import NormalisedURLPath
+from supertokens_python.querier import Querier
+import re
+
+if TYPE_CHECKING:
+    from .utils import JWTConfig
+    from supertokens_python.supertokens import AppInfo
+
+from supertokens_python.recipe.jwt.interfaces import (
+    CreateJwtOkResult,
+    CreateJwtResultUnsupportedAlgorithm,
+    GetJWKSResult,
+    RecipeInterface,
+)
+
+from .interfaces import JsonWebKey
+
+
+# This corresponds to the dynamicSigningKeyOverlapMS in the core
+DEFAULT_JWKS_MAX_AGE = 60
+
+
+class RecipeImplementation(RecipeInterface):
+    def __init__(self, querier: Querier, config: JWTConfig, app_info: AppInfo):
+        super().__init__()
+        self.querier = querier
+        self.config = config
+        self.app_info = app_info
+
+    async def create_jwt(
+        self,
+        payload: Dict[str, Any],
+        validity_seconds: Optional[int],
+        use_static_signing_key: Optional[bool],
+        user_context: Dict[str, Any],
+    ) -> Union[CreateJwtOkResult, CreateJwtResultUnsupportedAlgorithm]:
+        if validity_seconds is None:
+            validity_seconds = self.config.jwt_validity_seconds
+
+        data = {
+            "payload": payload,
+            "validity": validity_seconds,
+            "useStaticSigningKey": use_static_signing_key is not False,
+            "algorithm": "RS256",
+            "jwksDomain": self.app_info.api_domain.get_as_string_dangerous(),
+        }
+        response = await self.querier.send_post_request(
+            NormalisedURLPath("/recipe/jwt"),
+            data,
+            user_context=user_context,
+        )
+
+        if response["status"] == "OK":
+            return CreateJwtOkResult(response["jwt"])
+        return CreateJwtResultUnsupportedAlgorithm()
+
+    async def get_jwks(self, user_context: Dict[str, Any]) -> GetJWKSResult:
+        response = await self.querier.send_get_request(
+            NormalisedURLPath("/.well-known/jwks.json"),
+            {},
+            user_context=user_context,
+        )
+
+        validity_in_secs = DEFAULT_JWKS_MAX_AGE
+        cache_control = response["_headers"].get("Cache-Control")
+
+        if cache_control is not None:
+            pattern = r",?\s*max-age=(\d+)(?:,|$)"
+            max_age_header = re.match(pattern, cache_control)
+            if max_age_header is not None:
+                try:
+                    validity_in_secs = int(max_age_header.group(1))
+                except Exception:
+                    validity_in_secs = DEFAULT_JWKS_MAX_AGE
+
+        keys: List[JsonWebKey] = []
+        for key in response["keys"]:
+            keys.append(
+                JsonWebKey(
+                    key["kty"], key["kid"], key["n"], key["e"], key["alg"], key["use"]
+                )
+            )
+
+        return GetJWKSResult(keys, validity_in_secs)
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class RecipeImplementation +(querier: Querier, config: JWTConfig, app_info: AppInfo) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class RecipeImplementation(RecipeInterface):
+    def __init__(self, querier: Querier, config: JWTConfig, app_info: AppInfo):
+        super().__init__()
+        self.querier = querier
+        self.config = config
+        self.app_info = app_info
+
+    async def create_jwt(
+        self,
+        payload: Dict[str, Any],
+        validity_seconds: Optional[int],
+        use_static_signing_key: Optional[bool],
+        user_context: Dict[str, Any],
+    ) -> Union[CreateJwtOkResult, CreateJwtResultUnsupportedAlgorithm]:
+        if validity_seconds is None:
+            validity_seconds = self.config.jwt_validity_seconds
+
+        data = {
+            "payload": payload,
+            "validity": validity_seconds,
+            "useStaticSigningKey": use_static_signing_key is not False,
+            "algorithm": "RS256",
+            "jwksDomain": self.app_info.api_domain.get_as_string_dangerous(),
+        }
+        response = await self.querier.send_post_request(
+            NormalisedURLPath("/recipe/jwt"),
+            data,
+            user_context=user_context,
+        )
+
+        if response["status"] == "OK":
+            return CreateJwtOkResult(response["jwt"])
+        return CreateJwtResultUnsupportedAlgorithm()
+
+    async def get_jwks(self, user_context: Dict[str, Any]) -> GetJWKSResult:
+        response = await self.querier.send_get_request(
+            NormalisedURLPath("/.well-known/jwks.json"),
+            {},
+            user_context=user_context,
+        )
+
+        validity_in_secs = DEFAULT_JWKS_MAX_AGE
+        cache_control = response["_headers"].get("Cache-Control")
+
+        if cache_control is not None:
+            pattern = r",?\s*max-age=(\d+)(?:,|$)"
+            max_age_header = re.match(pattern, cache_control)
+            if max_age_header is not None:
+                try:
+                    validity_in_secs = int(max_age_header.group(1))
+                except Exception:
+                    validity_in_secs = DEFAULT_JWKS_MAX_AGE
+
+        keys: List[JsonWebKey] = []
+        for key in response["keys"]:
+            keys.append(
+                JsonWebKey(
+                    key["kty"], key["kid"], key["n"], key["e"], key["alg"], key["use"]
+                )
+            )
+
+        return GetJWKSResult(keys, validity_in_secs)
+
+

Ancestors

+ +

Methods

+
+
+async def create_jwt(self, payload: Dict[str, Any], validity_seconds: Optional[int], use_static_signing_key: Optional[bool], user_context: Dict[str, Any]) ‑> Union[CreateJwtOkResultCreateJwtResultUnsupportedAlgorithm] +
+
+
+
+ +Expand source code + +
async def create_jwt(
+    self,
+    payload: Dict[str, Any],
+    validity_seconds: Optional[int],
+    use_static_signing_key: Optional[bool],
+    user_context: Dict[str, Any],
+) -> Union[CreateJwtOkResult, CreateJwtResultUnsupportedAlgorithm]:
+    if validity_seconds is None:
+        validity_seconds = self.config.jwt_validity_seconds
+
+    data = {
+        "payload": payload,
+        "validity": validity_seconds,
+        "useStaticSigningKey": use_static_signing_key is not False,
+        "algorithm": "RS256",
+        "jwksDomain": self.app_info.api_domain.get_as_string_dangerous(),
+    }
+    response = await self.querier.send_post_request(
+        NormalisedURLPath("/recipe/jwt"),
+        data,
+        user_context=user_context,
+    )
+
+    if response["status"] == "OK":
+        return CreateJwtOkResult(response["jwt"])
+    return CreateJwtResultUnsupportedAlgorithm()
+
+
+
+async def get_jwks(self, user_context: Dict[str, Any]) ‑> GetJWKSResult +
+
+
+
+ +Expand source code + +
async def get_jwks(self, user_context: Dict[str, Any]) -> GetJWKSResult:
+    response = await self.querier.send_get_request(
+        NormalisedURLPath("/.well-known/jwks.json"),
+        {},
+        user_context=user_context,
+    )
+
+    validity_in_secs = DEFAULT_JWKS_MAX_AGE
+    cache_control = response["_headers"].get("Cache-Control")
+
+    if cache_control is not None:
+        pattern = r",?\s*max-age=(\d+)(?:,|$)"
+        max_age_header = re.match(pattern, cache_control)
+        if max_age_header is not None:
+            try:
+                validity_in_secs = int(max_age_header.group(1))
+            except Exception:
+                validity_in_secs = DEFAULT_JWKS_MAX_AGE
+
+    keys: List[JsonWebKey] = []
+    for key in response["keys"]:
+        keys.append(
+            JsonWebKey(
+                key["kty"], key["kid"], key["n"], key["e"], key["alg"], key["use"]
+            )
+        )
+
+    return GetJWKSResult(keys, validity_in_secs)
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/jwt/syncio/index.html b/html/supertokens_python/recipe/jwt/syncio/index.html new file mode 100644 index 000000000..d759d6af4 --- /dev/null +++ b/html/supertokens_python/recipe/jwt/syncio/index.html @@ -0,0 +1,141 @@ + + + + + + +supertokens_python.recipe.jwt.syncio API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.jwt.syncio

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from typing import Any, Dict, Union, Optional
+
+from supertokens_python.async_to_sync_wrapper import sync
+from supertokens_python.recipe.jwt import asyncio
+from supertokens_python.recipe.jwt.interfaces import (
+    CreateJwtOkResult,
+    CreateJwtResultUnsupportedAlgorithm,
+    GetJWKSResult,
+)
+
+
+def create_jwt(
+    payload: Optional[Dict[str, Any]] = None,
+    validity_seconds: Optional[int] = None,
+    use_static_signing_key: Optional[bool] = None,
+    user_context: Optional[Dict[str, Any]] = None,
+) -> Union[CreateJwtOkResult, CreateJwtResultUnsupportedAlgorithm]:
+    return sync(
+        asyncio.create_jwt(
+            payload, validity_seconds, use_static_signing_key, user_context
+        )
+    )
+
+
+def get_jwks(user_context: Optional[Dict[str, Any]] = None) -> GetJWKSResult:
+    return sync(asyncio.get_jwks(user_context))
+
+
+
+
+
+
+
+

Functions

+
+
+def create_jwt(payload: Optional[Dict[str, Any]] = None, validity_seconds: Optional[int] = None, use_static_signing_key: Optional[bool] = None, user_context: Optional[Dict[str, Any]] = None) ‑> Union[CreateJwtOkResultCreateJwtResultUnsupportedAlgorithm] +
+
+
+
+ +Expand source code + +
def create_jwt(
+    payload: Optional[Dict[str, Any]] = None,
+    validity_seconds: Optional[int] = None,
+    use_static_signing_key: Optional[bool] = None,
+    user_context: Optional[Dict[str, Any]] = None,
+) -> Union[CreateJwtOkResult, CreateJwtResultUnsupportedAlgorithm]:
+    return sync(
+        asyncio.create_jwt(
+            payload, validity_seconds, use_static_signing_key, user_context
+        )
+    )
+
+
+
+def get_jwks(user_context: Optional[Dict[str, Any]] = None) ‑> GetJWKSResult +
+
+
+
+ +Expand source code + +
def get_jwks(user_context: Optional[Dict[str, Any]] = None) -> GetJWKSResult:
+    return sync(asyncio.get_jwks(user_context))
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/jwt/utils.html b/html/supertokens_python/recipe/jwt/utils.html new file mode 100644 index 000000000..5620fd9ce --- /dev/null +++ b/html/supertokens_python/recipe/jwt/utils.html @@ -0,0 +1,195 @@ + + + + + + +supertokens_python.recipe.jwt.utils API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.jwt.utils

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Callable, Union
+
+if TYPE_CHECKING:
+    from .interfaces import APIInterface, RecipeInterface
+
+
+class OverrideConfig:
+    def __init__(
+        self,
+        functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None,
+        apis: Union[Callable[[APIInterface], APIInterface], None] = None,
+    ):
+        self.functions = functions
+        self.apis = apis
+
+
+class JWTConfig:
+    def __init__(self, override: OverrideConfig, jwt_validity_seconds: int):
+        self.override = override
+        self.jwt_validity_seconds = jwt_validity_seconds
+
+
+def validate_and_normalise_user_input(
+    jwt_validity_seconds: Union[int, None] = None,
+    override: Union[OverrideConfig, None] = None,
+):
+    if jwt_validity_seconds is not None and not isinstance(jwt_validity_seconds, int):  # type: ignore
+        raise ValueError("jwt_validity_seconds must be an integer or None")
+
+    if override is not None and not isinstance(override, OverrideConfig):  # type: ignore
+        raise ValueError("override must be an instance of OverrideConfig or None")
+
+    if override is None:
+        override = OverrideConfig()
+    if jwt_validity_seconds is None:
+        jwt_validity_seconds = 3153600000
+
+    return JWTConfig(override, jwt_validity_seconds)
+
+
+
+
+
+
+
+

Functions

+
+
+def validate_and_normalise_user_input(jwt_validity_seconds: Union[int, None] = None, override: Union[OverrideConfig, None] = None) +
+
+
+
+ +Expand source code + +
def validate_and_normalise_user_input(
+    jwt_validity_seconds: Union[int, None] = None,
+    override: Union[OverrideConfig, None] = None,
+):
+    if jwt_validity_seconds is not None and not isinstance(jwt_validity_seconds, int):  # type: ignore
+        raise ValueError("jwt_validity_seconds must be an integer or None")
+
+    if override is not None and not isinstance(override, OverrideConfig):  # type: ignore
+        raise ValueError("override must be an instance of OverrideConfig or None")
+
+    if override is None:
+        override = OverrideConfig()
+    if jwt_validity_seconds is None:
+        jwt_validity_seconds = 3153600000
+
+    return JWTConfig(override, jwt_validity_seconds)
+
+
+
+
+
+

Classes

+
+
+class JWTConfig +(override: OverrideConfig, jwt_validity_seconds: int) +
+
+
+
+ +Expand source code + +
class JWTConfig:
+    def __init__(self, override: OverrideConfig, jwt_validity_seconds: int):
+        self.override = override
+        self.jwt_validity_seconds = jwt_validity_seconds
+
+
+
+class OverrideConfig +(functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None, apis: Union[Callable[[APIInterface], APIInterface], None] = None) +
+
+
+
+ +Expand source code + +
class OverrideConfig:
+    def __init__(
+        self,
+        functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None,
+        apis: Union[Callable[[APIInterface], APIInterface], None] = None,
+    ):
+        self.functions = functions
+        self.apis = apis
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/multitenancy/api/implementation.html b/html/supertokens_python/recipe/multitenancy/api/implementation.html new file mode 100644 index 000000000..99f3112d4 --- /dev/null +++ b/html/supertokens_python/recipe/multitenancy/api/implementation.html @@ -0,0 +1,316 @@ + + + + + + +supertokens_python.recipe.multitenancy.api.implementation API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.multitenancy.api.implementation

+
+
+
+ +Expand source code + +
# Copyright (c) 2023, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from typing import Any, Dict, Optional, Union, List
+from ..constants import DEFAULT_TENANT_ID
+
+from supertokens_python.recipe.multitenancy.interfaces import (
+    APIOptions,
+    LoginMethodsGetOkResult,
+    LoginMethodEmailPassword,
+    LoginMethodPasswordless,
+    LoginMethodThirdParty,
+)
+from supertokens_python.types import GeneralErrorResponse
+
+from ..interfaces import APIInterface, ThirdPartyProvider
+
+
+class APIImplementation(APIInterface):
+    async def login_methods_get(
+        self,
+        tenant_id: str,
+        client_type: Optional[str],
+        api_options: APIOptions,
+        user_context: Dict[str, Any],
+    ) -> Union[LoginMethodsGetOkResult, GeneralErrorResponse]:
+        from supertokens_python.recipe.thirdparty.providers.config_utils import (
+            merge_providers_from_core_and_static,
+            find_and_create_provider_instance,
+        )
+
+        from supertokens_python.recipe.thirdparty.exceptions import (
+            ClientTypeNotFoundError,
+        )
+
+        tenant_config = await api_options.recipe_implementation.get_tenant(
+            tenant_id, user_context
+        )
+
+        if tenant_config is None:
+            raise Exception("Tenant not found")
+
+        provider_inputs_from_static = api_options.static_third_party_providers
+        provider_configs_from_core = tenant_config.third_party.providers
+
+        merged_providers = merge_providers_from_core_and_static(
+            provider_configs_from_core,
+            provider_inputs_from_static,
+            tenant_id == DEFAULT_TENANT_ID,
+        )
+
+        final_provider_list: List[ThirdPartyProvider] = []
+
+        for provider_input in merged_providers:
+            try:
+                provider_instance = await find_and_create_provider_instance(
+                    merged_providers,
+                    provider_input.config.third_party_id,
+                    client_type,
+                    user_context,
+                )
+
+                if provider_instance is None:
+                    raise Exception("Should never come here")
+
+            except ClientTypeNotFoundError:
+                continue
+            final_provider_list.append(
+                ThirdPartyProvider(provider_instance.id, provider_instance.config.name)
+            )
+
+        return LoginMethodsGetOkResult(
+            email_password=LoginMethodEmailPassword(
+                tenant_config.emailpassword.enabled
+            ),
+            passwordless=LoginMethodPasswordless(tenant_config.passwordless.enabled),
+            third_party=LoginMethodThirdParty(
+                tenant_config.third_party.enabled, final_provider_list
+            ),
+        )
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class APIImplementation +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class APIImplementation(APIInterface):
+    async def login_methods_get(
+        self,
+        tenant_id: str,
+        client_type: Optional[str],
+        api_options: APIOptions,
+        user_context: Dict[str, Any],
+    ) -> Union[LoginMethodsGetOkResult, GeneralErrorResponse]:
+        from supertokens_python.recipe.thirdparty.providers.config_utils import (
+            merge_providers_from_core_and_static,
+            find_and_create_provider_instance,
+        )
+
+        from supertokens_python.recipe.thirdparty.exceptions import (
+            ClientTypeNotFoundError,
+        )
+
+        tenant_config = await api_options.recipe_implementation.get_tenant(
+            tenant_id, user_context
+        )
+
+        if tenant_config is None:
+            raise Exception("Tenant not found")
+
+        provider_inputs_from_static = api_options.static_third_party_providers
+        provider_configs_from_core = tenant_config.third_party.providers
+
+        merged_providers = merge_providers_from_core_and_static(
+            provider_configs_from_core,
+            provider_inputs_from_static,
+            tenant_id == DEFAULT_TENANT_ID,
+        )
+
+        final_provider_list: List[ThirdPartyProvider] = []
+
+        for provider_input in merged_providers:
+            try:
+                provider_instance = await find_and_create_provider_instance(
+                    merged_providers,
+                    provider_input.config.third_party_id,
+                    client_type,
+                    user_context,
+                )
+
+                if provider_instance is None:
+                    raise Exception("Should never come here")
+
+            except ClientTypeNotFoundError:
+                continue
+            final_provider_list.append(
+                ThirdPartyProvider(provider_instance.id, provider_instance.config.name)
+            )
+
+        return LoginMethodsGetOkResult(
+            email_password=LoginMethodEmailPassword(
+                tenant_config.emailpassword.enabled
+            ),
+            passwordless=LoginMethodPasswordless(tenant_config.passwordless.enabled),
+            third_party=LoginMethodThirdParty(
+                tenant_config.third_party.enabled, final_provider_list
+            ),
+        )
+
+

Ancestors

+ +

Methods

+
+
+async def login_methods_get(self, tenant_id: str, client_type: Optional[str], api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[LoginMethodsGetOkResultGeneralErrorResponse] +
+
+
+
+ +Expand source code + +
async def login_methods_get(
+    self,
+    tenant_id: str,
+    client_type: Optional[str],
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+) -> Union[LoginMethodsGetOkResult, GeneralErrorResponse]:
+    from supertokens_python.recipe.thirdparty.providers.config_utils import (
+        merge_providers_from_core_and_static,
+        find_and_create_provider_instance,
+    )
+
+    from supertokens_python.recipe.thirdparty.exceptions import (
+        ClientTypeNotFoundError,
+    )
+
+    tenant_config = await api_options.recipe_implementation.get_tenant(
+        tenant_id, user_context
+    )
+
+    if tenant_config is None:
+        raise Exception("Tenant not found")
+
+    provider_inputs_from_static = api_options.static_third_party_providers
+    provider_configs_from_core = tenant_config.third_party.providers
+
+    merged_providers = merge_providers_from_core_and_static(
+        provider_configs_from_core,
+        provider_inputs_from_static,
+        tenant_id == DEFAULT_TENANT_ID,
+    )
+
+    final_provider_list: List[ThirdPartyProvider] = []
+
+    for provider_input in merged_providers:
+        try:
+            provider_instance = await find_and_create_provider_instance(
+                merged_providers,
+                provider_input.config.third_party_id,
+                client_type,
+                user_context,
+            )
+
+            if provider_instance is None:
+                raise Exception("Should never come here")
+
+        except ClientTypeNotFoundError:
+            continue
+        final_provider_list.append(
+            ThirdPartyProvider(provider_instance.id, provider_instance.config.name)
+        )
+
+    return LoginMethodsGetOkResult(
+        email_password=LoginMethodEmailPassword(
+            tenant_config.emailpassword.enabled
+        ),
+        passwordless=LoginMethodPasswordless(tenant_config.passwordless.enabled),
+        third_party=LoginMethodThirdParty(
+            tenant_config.third_party.enabled, final_provider_list
+        ),
+    )
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/multitenancy/api/index.html b/html/supertokens_python/recipe/multitenancy/api/index.html new file mode 100644 index 000000000..b75fca94d --- /dev/null +++ b/html/supertokens_python/recipe/multitenancy/api/index.html @@ -0,0 +1,89 @@ + + + + + + +supertokens_python.recipe.multitenancy.api API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.multitenancy.api

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from .login_methods import handle_login_methods_api  # type: ignore
+
+
+
+

Sub-modules

+
+
supertokens_python.recipe.multitenancy.api.implementation
+
+
+
+
supertokens_python.recipe.multitenancy.api.login_methods
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/multitenancy/api/login_methods.html b/html/supertokens_python/recipe/multitenancy/api/login_methods.html new file mode 100644 index 000000000..5c631e14f --- /dev/null +++ b/html/supertokens_python/recipe/multitenancy/api/login_methods.html @@ -0,0 +1,132 @@ + + + + + + +supertokens_python.recipe.multitenancy.api.login_methods API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.multitenancy.api.login_methods

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+from typing import Any, Dict
+
+from supertokens_python.recipe.multitenancy.interfaces import (
+    APIInterface,
+    APIOptions,
+)
+from supertokens_python.utils import send_200_response
+
+
+async def handle_login_methods_api(
+    api_implementation: APIInterface,
+    tenant_id: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+):
+    if api_implementation.disable_login_methods_get:
+        return None
+
+    client_type = api_options.request.get_query_param("clientType")
+
+    result = await api_implementation.login_methods_get(
+        tenant_id, client_type, api_options, user_context
+    )
+
+    return send_200_response(result.to_json(), api_options.response)
+
+
+
+
+
+
+
+

Functions

+
+
+async def handle_login_methods_api(api_implementation: APIInterface, tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) +
+
+
+
+ +Expand source code + +
async def handle_login_methods_api(
+    api_implementation: APIInterface,
+    tenant_id: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+):
+    if api_implementation.disable_login_methods_get:
+        return None
+
+    client_type = api_options.request.get_query_param("clientType")
+
+    result = await api_implementation.login_methods_get(
+        tenant_id, client_type, api_options, user_context
+    )
+
+    return send_200_response(result.to_json(), api_options.response)
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/multitenancy/asyncio/index.html b/html/supertokens_python/recipe/multitenancy/asyncio/index.html new file mode 100644 index 000000000..ae58c9239 --- /dev/null +++ b/html/supertokens_python/recipe/multitenancy/asyncio/index.html @@ -0,0 +1,404 @@ + + + + + + +supertokens_python.recipe.multitenancy.asyncio API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.multitenancy.asyncio

+
+
+
+ +Expand source code + +
#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from typing import Any, Dict, Union, Optional, TYPE_CHECKING
+
+from ..interfaces import (
+    TenantConfig,
+    CreateOrUpdateTenantOkResult,
+    DeleteTenantOkResult,
+    GetTenantOkResult,
+    ListAllTenantsOkResult,
+    CreateOrUpdateThirdPartyConfigOkResult,
+    DeleteThirdPartyConfigOkResult,
+    AssociateUserToTenantOkResult,
+    AssociateUserToTenantUnknownUserIdError,
+    AssociateUserToTenantEmailAlreadyExistsError,
+    AssociateUserToTenantPhoneNumberAlreadyExistsError,
+    AssociateUserToTenantThirdPartyUserAlreadyExistsError,
+    DisassociateUserFromTenantOkResult,
+)
+from ..recipe import MultitenancyRecipe
+
+if TYPE_CHECKING:
+    from ..interfaces import ProviderConfig
+
+
+async def create_or_update_tenant(
+    tenant_id: str,
+    config: Optional[TenantConfig],
+    user_context: Optional[Dict[str, Any]] = None,
+) -> CreateOrUpdateTenantOkResult:
+    if user_context is None:
+        user_context = {}
+    recipe = MultitenancyRecipe.get_instance()
+
+    return await recipe.recipe_implementation.create_or_update_tenant(
+        tenant_id, config, user_context
+    )
+
+
+async def delete_tenant(
+    tenant_id: str, user_context: Optional[Dict[str, Any]] = None
+) -> DeleteTenantOkResult:
+    if user_context is None:
+        user_context = {}
+    recipe = MultitenancyRecipe.get_instance()
+
+    return await recipe.recipe_implementation.delete_tenant(tenant_id, user_context)
+
+
+async def get_tenant(
+    tenant_id: str, user_context: Optional[Dict[str, Any]] = None
+) -> Optional[GetTenantOkResult]:
+    if user_context is None:
+        user_context = {}
+    recipe = MultitenancyRecipe.get_instance()
+
+    return await recipe.recipe_implementation.get_tenant(tenant_id, user_context)
+
+
+async def list_all_tenants(
+    user_context: Optional[Dict[str, Any]] = None
+) -> ListAllTenantsOkResult:
+    if user_context is None:
+        user_context = {}
+
+    recipe = MultitenancyRecipe.get_instance()
+
+    return await recipe.recipe_implementation.list_all_tenants(user_context)
+
+
+async def create_or_update_third_party_config(
+    tenant_id: str,
+    config: ProviderConfig,
+    skip_validation: Optional[bool] = None,
+    user_context: Optional[Dict[str, Any]] = None,
+) -> CreateOrUpdateThirdPartyConfigOkResult:
+    if user_context is None:
+        user_context = {}
+
+    recipe = MultitenancyRecipe.get_instance()
+
+    return await recipe.recipe_implementation.create_or_update_third_party_config(
+        tenant_id, config, skip_validation, user_context
+    )
+
+
+async def delete_third_party_config(
+    tenant_id: str,
+    third_party_id: str,
+    user_context: Optional[Dict[str, Any]] = None,
+) -> DeleteThirdPartyConfigOkResult:
+    if user_context is None:
+        user_context = {}
+
+    recipe = MultitenancyRecipe.get_instance()
+
+    return await recipe.recipe_implementation.delete_third_party_config(
+        tenant_id, third_party_id, user_context
+    )
+
+
+async def associate_user_to_tenant(
+    tenant_id: str,
+    user_id: str,
+    user_context: Optional[Dict[str, Any]] = None,
+) -> Union[
+    AssociateUserToTenantOkResult,
+    AssociateUserToTenantUnknownUserIdError,
+    AssociateUserToTenantEmailAlreadyExistsError,
+    AssociateUserToTenantPhoneNumberAlreadyExistsError,
+    AssociateUserToTenantThirdPartyUserAlreadyExistsError,
+]:
+    if user_context is None:
+        user_context = {}
+
+    recipe = MultitenancyRecipe.get_instance()
+
+    return await recipe.recipe_implementation.associate_user_to_tenant(
+        tenant_id, user_id, user_context
+    )
+
+
+async def dissociate_user_from_tenant(
+    tenant_id: str,
+    user_id: str,
+    user_context: Optional[Dict[str, Any]] = None,
+) -> DisassociateUserFromTenantOkResult:
+    if user_context is None:
+        user_context = {}
+
+    recipe = MultitenancyRecipe.get_instance()
+
+    return await recipe.recipe_implementation.dissociate_user_from_tenant(
+        tenant_id, user_id, user_context
+    )
+
+
+
+
+
+
+
+

Functions

+
+
+async def associate_user_to_tenant(tenant_id: str, user_id: str, user_context: Optional[Dict[str, Any]] = None) ‑> Union[AssociateUserToTenantOkResultAssociateUserToTenantUnknownUserIdErrorAssociateUserToTenantEmailAlreadyExistsErrorAssociateUserToTenantPhoneNumberAlreadyExistsErrorAssociateUserToTenantThirdPartyUserAlreadyExistsError] +
+
+
+
+ +Expand source code + +
async def associate_user_to_tenant(
+    tenant_id: str,
+    user_id: str,
+    user_context: Optional[Dict[str, Any]] = None,
+) -> Union[
+    AssociateUserToTenantOkResult,
+    AssociateUserToTenantUnknownUserIdError,
+    AssociateUserToTenantEmailAlreadyExistsError,
+    AssociateUserToTenantPhoneNumberAlreadyExistsError,
+    AssociateUserToTenantThirdPartyUserAlreadyExistsError,
+]:
+    if user_context is None:
+        user_context = {}
+
+    recipe = MultitenancyRecipe.get_instance()
+
+    return await recipe.recipe_implementation.associate_user_to_tenant(
+        tenant_id, user_id, user_context
+    )
+
+
+
+async def create_or_update_tenant(tenant_id: str, config: Optional[TenantConfig], user_context: Optional[Dict[str, Any]] = None) ‑> CreateOrUpdateTenantOkResult +
+
+
+
+ +Expand source code + +
async def create_or_update_tenant(
+    tenant_id: str,
+    config: Optional[TenantConfig],
+    user_context: Optional[Dict[str, Any]] = None,
+) -> CreateOrUpdateTenantOkResult:
+    if user_context is None:
+        user_context = {}
+    recipe = MultitenancyRecipe.get_instance()
+
+    return await recipe.recipe_implementation.create_or_update_tenant(
+        tenant_id, config, user_context
+    )
+
+
+
+async def create_or_update_third_party_config(tenant_id: str, config: ProviderConfig, skip_validation: Optional[bool] = None, user_context: Optional[Dict[str, Any]] = None) ‑> CreateOrUpdateThirdPartyConfigOkResult +
+
+
+
+ +Expand source code + +
async def create_or_update_third_party_config(
+    tenant_id: str,
+    config: ProviderConfig,
+    skip_validation: Optional[bool] = None,
+    user_context: Optional[Dict[str, Any]] = None,
+) -> CreateOrUpdateThirdPartyConfigOkResult:
+    if user_context is None:
+        user_context = {}
+
+    recipe = MultitenancyRecipe.get_instance()
+
+    return await recipe.recipe_implementation.create_or_update_third_party_config(
+        tenant_id, config, skip_validation, user_context
+    )
+
+
+
+async def delete_tenant(tenant_id: str, user_context: Optional[Dict[str, Any]] = None) ‑> DeleteTenantOkResult +
+
+
+
+ +Expand source code + +
async def delete_tenant(
+    tenant_id: str, user_context: Optional[Dict[str, Any]] = None
+) -> DeleteTenantOkResult:
+    if user_context is None:
+        user_context = {}
+    recipe = MultitenancyRecipe.get_instance()
+
+    return await recipe.recipe_implementation.delete_tenant(tenant_id, user_context)
+
+
+
+async def delete_third_party_config(tenant_id: str, third_party_id: str, user_context: Optional[Dict[str, Any]] = None) ‑> DeleteThirdPartyConfigOkResult +
+
+
+
+ +Expand source code + +
async def delete_third_party_config(
+    tenant_id: str,
+    third_party_id: str,
+    user_context: Optional[Dict[str, Any]] = None,
+) -> DeleteThirdPartyConfigOkResult:
+    if user_context is None:
+        user_context = {}
+
+    recipe = MultitenancyRecipe.get_instance()
+
+    return await recipe.recipe_implementation.delete_third_party_config(
+        tenant_id, third_party_id, user_context
+    )
+
+
+
+async def dissociate_user_from_tenant(tenant_id: str, user_id: str, user_context: Optional[Dict[str, Any]] = None) ‑> DisassociateUserFromTenantOkResult +
+
+
+
+ +Expand source code + +
async def dissociate_user_from_tenant(
+    tenant_id: str,
+    user_id: str,
+    user_context: Optional[Dict[str, Any]] = None,
+) -> DisassociateUserFromTenantOkResult:
+    if user_context is None:
+        user_context = {}
+
+    recipe = MultitenancyRecipe.get_instance()
+
+    return await recipe.recipe_implementation.dissociate_user_from_tenant(
+        tenant_id, user_id, user_context
+    )
+
+
+
+async def get_tenant(tenant_id: str, user_context: Optional[Dict[str, Any]] = None) ‑> Optional[GetTenantOkResult] +
+
+
+
+ +Expand source code + +
async def get_tenant(
+    tenant_id: str, user_context: Optional[Dict[str, Any]] = None
+) -> Optional[GetTenantOkResult]:
+    if user_context is None:
+        user_context = {}
+    recipe = MultitenancyRecipe.get_instance()
+
+    return await recipe.recipe_implementation.get_tenant(tenant_id, user_context)
+
+
+
+async def list_all_tenants(user_context: Optional[Dict[str, Any]] = None) ‑> ListAllTenantsOkResult +
+
+
+
+ +Expand source code + +
async def list_all_tenants(
+    user_context: Optional[Dict[str, Any]] = None
+) -> ListAllTenantsOkResult:
+    if user_context is None:
+        user_context = {}
+
+    recipe = MultitenancyRecipe.get_instance()
+
+    return await recipe.recipe_implementation.list_all_tenants(user_context)
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/multitenancy/constants.html b/html/supertokens_python/recipe/multitenancy/constants.html new file mode 100644 index 000000000..80375303f --- /dev/null +++ b/html/supertokens_python/recipe/multitenancy/constants.html @@ -0,0 +1,73 @@ + + + + + + +supertokens_python.recipe.multitenancy.constants API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.multitenancy.constants

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+LOGIN_METHODS = "/loginmethods"
+DEFAULT_TENANT_ID = "public"
+
+
+
+
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/multitenancy/exceptions.html b/html/supertokens_python/recipe/multitenancy/exceptions.html new file mode 100644 index 000000000..029f9ca74 --- /dev/null +++ b/html/supertokens_python/recipe/multitenancy/exceptions.html @@ -0,0 +1,108 @@ + + + + + + +supertokens_python.recipe.multitenancy.exceptions API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.multitenancy.exceptions

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from supertokens_python.exceptions import SuperTokensError
+
+
+class MultitenancyError(SuperTokensError):
+    pass
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class MultitenancyError +(*args, **kwargs) +
+
+

Common base class for all non-exit exceptions.

+
+ +Expand source code + +
class MultitenancyError(SuperTokensError):
+    pass
+
+

Ancestors

+ +
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/multitenancy/index.html b/html/supertokens_python/recipe/multitenancy/index.html new file mode 100644 index 000000000..f7ed66737 --- /dev/null +++ b/html/supertokens_python/recipe/multitenancy/index.html @@ -0,0 +1,180 @@ + + + + + + +supertokens_python.recipe.multitenancy API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.multitenancy

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Callable, Union
+
+from . import exceptions as ex
+from . import recipe
+
+AllowedDomainsClaim = recipe.AllowedDomainsClaim
+exceptions = ex
+
+if TYPE_CHECKING:
+    from supertokens_python.supertokens import AppInfo
+
+    from ...recipe_module import RecipeModule
+    from .interfaces import TypeGetAllowedDomainsForTenantId
+    from .utils import InputOverrideConfig
+
+
+def init(
+    get_allowed_domains_for_tenant_id: Union[
+        TypeGetAllowedDomainsForTenantId, None
+    ] = None,
+    override: Union[InputOverrideConfig, None] = None,
+) -> Callable[[AppInfo], RecipeModule]:
+    return recipe.MultitenancyRecipe.init(
+        get_allowed_domains_for_tenant_id,
+        override,
+    )
+
+
+
+

Sub-modules

+
+
supertokens_python.recipe.multitenancy.api
+
+
+
+
supertokens_python.recipe.multitenancy.asyncio
+
+
+
+
supertokens_python.recipe.multitenancy.constants
+
+
+
+
supertokens_python.recipe.multitenancy.exceptions
+
+
+
+
supertokens_python.recipe.multitenancy.interfaces
+
+
+
+
supertokens_python.recipe.multitenancy.recipe
+
+
+
+
supertokens_python.recipe.multitenancy.recipe_implementation
+
+
+
+
supertokens_python.recipe.multitenancy.syncio
+
+
+
+
supertokens_python.recipe.multitenancy.utils
+
+
+
+
+
+
+
+
+

Functions

+
+
+def init(get_allowed_domains_for_tenant_id: Union[TypeGetAllowedDomainsForTenantId, None] = None, override: Union[InputOverrideConfig, None] = None) ‑> Callable[[AppInfo], RecipeModule] +
+
+
+
+ +Expand source code + +
def init(
+    get_allowed_domains_for_tenant_id: Union[
+        TypeGetAllowedDomainsForTenantId, None
+    ] = None,
+    override: Union[InputOverrideConfig, None] = None,
+) -> Callable[[AppInfo], RecipeModule]:
+    return recipe.MultitenancyRecipe.init(
+        get_allowed_domains_for_tenant_id,
+        override,
+    )
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/multitenancy/interfaces.html b/html/supertokens_python/recipe/multitenancy/interfaces.html new file mode 100644 index 000000000..b57c7b58f --- /dev/null +++ b/html/supertokens_python/recipe/multitenancy/interfaces.html @@ -0,0 +1,1705 @@ + + + + + + +supertokens_python.recipe.multitenancy.interfaces API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.multitenancy.interfaces

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from abc import ABC, abstractmethod
+from typing import TYPE_CHECKING, Any, Dict, Union, Callable, Awaitable, Optional, List
+
+from supertokens_python.types import APIResponse, GeneralErrorResponse
+
+if TYPE_CHECKING:
+    from supertokens_python.framework import BaseRequest, BaseResponse
+    from supertokens_python.recipe.thirdparty.provider import (
+        ProviderConfig,
+        ProviderInput,
+    )
+    from .utils import MultitenancyConfig
+
+
+class TenantConfig:
+    def __init__(
+        self,
+        email_password_enabled: Union[bool, None] = None,
+        passwordless_enabled: Union[bool, None] = None,
+        third_party_enabled: Union[bool, None] = None,
+        core_config: Union[Dict[str, Any], None] = None,
+    ):
+        self.email_password_enabled = email_password_enabled
+        self.passwordless_enabled = passwordless_enabled
+        self.third_party_enabled = third_party_enabled
+        self.core_config = core_config
+
+    def to_json(self) -> Dict[str, Any]:
+        res: Dict[str, Any] = {}
+        if self.email_password_enabled is not None:
+            res["emailPasswordEnabled"] = self.email_password_enabled
+        if self.passwordless_enabled is not None:
+            res["passwordlessEnabled"] = self.passwordless_enabled
+        if self.third_party_enabled is not None:
+            res["thirdPartyEnabled"] = self.third_party_enabled
+        if self.core_config is not None:
+            res["coreConfig"] = self.core_config
+        return res
+
+
+class CreateOrUpdateTenantOkResult:
+    status = "OK"
+
+    def __init__(self, created_new: bool):
+        self.created_new = created_new
+
+
+class DeleteTenantOkResult:
+    status = "OK"
+
+    def __init__(self, did_exist: bool):
+        self.did_exist = did_exist
+
+
+class EmailPasswordConfig:
+    def __init__(self, enabled: bool):
+        self.enabled = enabled
+
+    def to_json(self):
+        return {"enabled": self.enabled}
+
+
+class PasswordlessConfig:
+    def __init__(self, enabled: bool):
+        self.enabled = enabled
+
+    def to_json(self):
+        return {"enabled": self.enabled}
+
+
+class ThirdPartyConfig:
+    def __init__(self, enabled: bool, providers: List[ProviderConfig]):
+        self.enabled = enabled
+        self.providers = providers
+
+    def to_json(self):
+        return {
+            "enabled": self.enabled,
+            "providers": [provider.to_json() for provider in self.providers],
+        }
+
+
+class TenantConfigResponse:
+    def __init__(
+        self,
+        emailpassword: EmailPasswordConfig,
+        passwordless: PasswordlessConfig,
+        third_party: ThirdPartyConfig,
+        core_config: Dict[str, Any],
+    ):
+        self.emailpassword = emailpassword
+        self.passwordless = passwordless
+        self.third_party = third_party
+        self.core_config = core_config
+
+
+class GetTenantOkResult(TenantConfigResponse):
+    status = "OK"
+
+
+class ListAllTenantsItem(TenantConfigResponse):
+    def __init__(
+        self,
+        tenant_id: str,
+        emailpassword: EmailPasswordConfig,
+        passwordless: PasswordlessConfig,
+        third_party: ThirdPartyConfig,
+        core_config: Dict[str, Any],
+    ):
+        super().__init__(emailpassword, passwordless, third_party, core_config)
+        self.tenant_id = tenant_id
+
+    def to_json(self):
+        res = {
+            "tenantId": self.tenant_id,
+            "emailPassword": self.emailpassword.to_json(),
+            "passwordless": self.passwordless.to_json(),
+            "thirdParty": self.third_party.to_json(),
+            "coreConfig": self.core_config,
+        }
+
+        return res
+
+
+class ListAllTenantsOkResult:
+    status = "OK"
+
+    def __init__(self, tenants: List[ListAllTenantsItem]):
+        self.tenants = tenants
+
+
+class CreateOrUpdateThirdPartyConfigOkResult:
+    status = "OK"
+
+    def __init__(self, created_new: bool):
+        self.created_new = created_new
+
+
+class DeleteThirdPartyConfigOkResult:
+    status = "OK"
+
+    def __init__(self, did_config_exist: bool):
+        self.did_config_exist = did_config_exist
+
+
+class AssociateUserToTenantOkResult:
+    status = "OK"
+
+    def __init__(self, was_already_associated: bool):
+        self.was_already_associated = was_already_associated
+
+
+class AssociateUserToTenantUnknownUserIdError:
+    status = "UNKNOWN_USER_ID_ERROR"
+
+
+class AssociateUserToTenantEmailAlreadyExistsError:
+    status = "EMAIL_ALREADY_EXISTS_ERROR"
+
+
+class AssociateUserToTenantPhoneNumberAlreadyExistsError:
+    status = "PHONE_NUMBER_ALREADY_EXISTS_ERROR"
+
+
+class AssociateUserToTenantThirdPartyUserAlreadyExistsError:
+    status = "THIRD_PARTY_USER_ALREADY_EXISTS_ERROR"
+
+
+class DisassociateUserFromTenantOkResult:
+    status = "OK"
+
+    def __init__(self, was_associated: bool):
+        self.was_associated = was_associated
+
+
+class RecipeInterface(ABC):
+    def __init__(self):
+        pass
+
+    @abstractmethod
+    async def get_tenant_id(
+        self, tenant_id_from_frontend: str, user_context: Dict[str, Any]
+    ) -> str:
+        pass
+
+    @abstractmethod
+    async def create_or_update_tenant(
+        self,
+        tenant_id: str,
+        config: Optional[TenantConfig],
+        user_context: Dict[str, Any],
+    ) -> CreateOrUpdateTenantOkResult:
+        pass
+
+    @abstractmethod
+    async def delete_tenant(
+        self, tenant_id: str, user_context: Dict[str, Any]
+    ) -> DeleteTenantOkResult:
+        pass
+
+    @abstractmethod
+    async def get_tenant(
+        self, tenant_id: str, user_context: Dict[str, Any]
+    ) -> Optional[GetTenantOkResult]:
+        pass
+
+    @abstractmethod
+    async def list_all_tenants(
+        self, user_context: Dict[str, Any]
+    ) -> ListAllTenantsOkResult:
+        pass
+
+    # third party provider management
+    @abstractmethod
+    async def create_or_update_third_party_config(
+        self,
+        tenant_id: str,
+        config: ProviderConfig,
+        skip_validation: Optional[bool],
+        user_context: Dict[str, Any],
+    ) -> CreateOrUpdateThirdPartyConfigOkResult:
+        pass
+
+    @abstractmethod
+    async def delete_third_party_config(
+        self,
+        tenant_id: str,
+        third_party_id: str,
+        user_context: Dict[str, Any],
+    ) -> DeleteThirdPartyConfigOkResult:
+        pass
+
+    # user tenant association
+    @abstractmethod
+    async def associate_user_to_tenant(
+        self,
+        tenant_id: str,
+        user_id: str,
+        user_context: Dict[str, Any],
+    ) -> Union[
+        AssociateUserToTenantOkResult,
+        AssociateUserToTenantUnknownUserIdError,
+        AssociateUserToTenantEmailAlreadyExistsError,
+        AssociateUserToTenantPhoneNumberAlreadyExistsError,
+        AssociateUserToTenantThirdPartyUserAlreadyExistsError,
+    ]:
+        pass
+
+    @abstractmethod
+    async def dissociate_user_from_tenant(
+        self,
+        tenant_id: str,
+        user_id: str,
+        user_context: Dict[str, Any],
+    ) -> DisassociateUserFromTenantOkResult:
+        pass
+
+
+class APIOptions:
+    def __init__(
+        self,
+        request: BaseRequest,
+        response: BaseResponse,
+        recipe_id: str,
+        config: MultitenancyConfig,
+        recipe_implementation: RecipeInterface,
+        static_third_party_providers: List[ProviderInput],
+    ):
+        self.request = request
+        self.response = response
+        self.recipe_id = recipe_id
+        self.config = config
+        self.recipe_implementation = recipe_implementation
+        self.static_third_party_providers = static_third_party_providers
+
+
+class ThirdPartyProvider:
+    def __init__(
+        self, id: str, name: Optional[str]
+    ):  # pylint: disable=redefined-builtin
+        self.id = id
+        self.name = name
+
+    def to_json(self) -> Dict[str, Any]:
+        return {
+            "id": self.id,
+            "name": self.name,
+        }
+
+
+class LoginMethodEmailPassword:
+    def __init__(self, enabled: bool):
+        self.enabled = enabled
+
+    def to_json(self) -> Dict[str, Any]:
+        return {
+            "enabled": self.enabled,
+        }
+
+
+class LoginMethodPasswordless:
+    def __init__(self, enabled: bool):
+        self.enabled = enabled
+
+    def to_json(self) -> Dict[str, Any]:
+        return {
+            "enabled": self.enabled,
+        }
+
+
+class LoginMethodThirdParty:
+    def __init__(self, enabled: bool, providers: List[ThirdPartyProvider]):
+        self.enabled = enabled
+        self.providers = providers
+
+    def to_json(self) -> Dict[str, Any]:
+        return {
+            "enabled": self.enabled,
+            "providers": [provider.to_json() for provider in self.providers],
+        }
+
+
+class LoginMethodsGetOkResult(APIResponse):
+    def __init__(
+        self,
+        email_password: LoginMethodEmailPassword,
+        passwordless: LoginMethodPasswordless,
+        third_party: LoginMethodThirdParty,
+    ):
+        self.status = "OK"
+        self.email_password = email_password
+        self.passwordless = passwordless
+        self.third_party = third_party
+
+    def to_json(self) -> Dict[str, Any]:
+        return {
+            "status": self.status,
+            "emailPassword": self.email_password.to_json(),
+            "passwordless": self.passwordless.to_json(),
+            "thirdParty": self.third_party.to_json(),
+        }
+
+
+class APIInterface(ABC):
+    def __init__(self):
+        self.disable_login_methods_get = False
+
+    @abstractmethod
+    async def login_methods_get(
+        self,
+        tenant_id: str,
+        client_type: Optional[str],
+        api_options: APIOptions,
+        user_context: Dict[str, Any],
+    ) -> Union[LoginMethodsGetOkResult, GeneralErrorResponse]:
+        pass
+
+
+TypeGetAllowedDomainsForTenantId = Callable[
+    [str, Dict[str, Any]], Awaitable[Optional[List[str]]]
+]
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class APIInterface +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class APIInterface(ABC):
+    def __init__(self):
+        self.disable_login_methods_get = False
+
+    @abstractmethod
+    async def login_methods_get(
+        self,
+        tenant_id: str,
+        client_type: Optional[str],
+        api_options: APIOptions,
+        user_context: Dict[str, Any],
+    ) -> Union[LoginMethodsGetOkResult, GeneralErrorResponse]:
+        pass
+
+

Ancestors

+
    +
  • abc.ABC
  • +
+

Subclasses

+ +

Methods

+
+
+async def login_methods_get(self, tenant_id: str, client_type: Optional[str], api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[LoginMethodsGetOkResultGeneralErrorResponse] +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def login_methods_get(
+    self,
+    tenant_id: str,
+    client_type: Optional[str],
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+) -> Union[LoginMethodsGetOkResult, GeneralErrorResponse]:
+    pass
+
+
+
+
+
+class APIOptions +(request: BaseRequest, response: BaseResponse, recipe_id: str, config: MultitenancyConfig, recipe_implementation: RecipeInterface, static_third_party_providers: List[ProviderInput]) +
+
+
+
+ +Expand source code + +
class APIOptions:
+    def __init__(
+        self,
+        request: BaseRequest,
+        response: BaseResponse,
+        recipe_id: str,
+        config: MultitenancyConfig,
+        recipe_implementation: RecipeInterface,
+        static_third_party_providers: List[ProviderInput],
+    ):
+        self.request = request
+        self.response = response
+        self.recipe_id = recipe_id
+        self.config = config
+        self.recipe_implementation = recipe_implementation
+        self.static_third_party_providers = static_third_party_providers
+
+
+
+class AssociateUserToTenantEmailAlreadyExistsError +
+
+
+
+ +Expand source code + +
class AssociateUserToTenantEmailAlreadyExistsError:
+    status = "EMAIL_ALREADY_EXISTS_ERROR"
+
+

Class variables

+
+
var status
+
+
+
+
+
+
+class AssociateUserToTenantOkResult +(was_already_associated: bool) +
+
+
+
+ +Expand source code + +
class AssociateUserToTenantOkResult:
+    status = "OK"
+
+    def __init__(self, was_already_associated: bool):
+        self.was_already_associated = was_already_associated
+
+

Class variables

+
+
var status
+
+
+
+
+
+
+class AssociateUserToTenantPhoneNumberAlreadyExistsError +
+
+
+
+ +Expand source code + +
class AssociateUserToTenantPhoneNumberAlreadyExistsError:
+    status = "PHONE_NUMBER_ALREADY_EXISTS_ERROR"
+
+

Class variables

+
+
var status
+
+
+
+
+
+
+class AssociateUserToTenantThirdPartyUserAlreadyExistsError +
+
+
+
+ +Expand source code + +
class AssociateUserToTenantThirdPartyUserAlreadyExistsError:
+    status = "THIRD_PARTY_USER_ALREADY_EXISTS_ERROR"
+
+

Class variables

+
+
var status
+
+
+
+
+
+
+class AssociateUserToTenantUnknownUserIdError +
+
+
+
+ +Expand source code + +
class AssociateUserToTenantUnknownUserIdError:
+    status = "UNKNOWN_USER_ID_ERROR"
+
+

Class variables

+
+
var status
+
+
+
+
+
+
+class CreateOrUpdateTenantOkResult +(created_new: bool) +
+
+
+
+ +Expand source code + +
class CreateOrUpdateTenantOkResult:
+    status = "OK"
+
+    def __init__(self, created_new: bool):
+        self.created_new = created_new
+
+

Class variables

+
+
var status
+
+
+
+
+
+
+class CreateOrUpdateThirdPartyConfigOkResult +(created_new: bool) +
+
+
+
+ +Expand source code + +
class CreateOrUpdateThirdPartyConfigOkResult:
+    status = "OK"
+
+    def __init__(self, created_new: bool):
+        self.created_new = created_new
+
+

Class variables

+
+
var status
+
+
+
+
+
+
+class DeleteTenantOkResult +(did_exist: bool) +
+
+
+
+ +Expand source code + +
class DeleteTenantOkResult:
+    status = "OK"
+
+    def __init__(self, did_exist: bool):
+        self.did_exist = did_exist
+
+

Class variables

+
+
var status
+
+
+
+
+
+
+class DeleteThirdPartyConfigOkResult +(did_config_exist: bool) +
+
+
+
+ +Expand source code + +
class DeleteThirdPartyConfigOkResult:
+    status = "OK"
+
+    def __init__(self, did_config_exist: bool):
+        self.did_config_exist = did_config_exist
+
+

Class variables

+
+
var status
+
+
+
+
+
+
+class DisassociateUserFromTenantOkResult +(was_associated: bool) +
+
+
+
+ +Expand source code + +
class DisassociateUserFromTenantOkResult:
+    status = "OK"
+
+    def __init__(self, was_associated: bool):
+        self.was_associated = was_associated
+
+

Class variables

+
+
var status
+
+
+
+
+
+
+class EmailPasswordConfig +(enabled: bool) +
+
+
+
+ +Expand source code + +
class EmailPasswordConfig:
+    def __init__(self, enabled: bool):
+        self.enabled = enabled
+
+    def to_json(self):
+        return {"enabled": self.enabled}
+
+

Methods

+
+
+def to_json(self) +
+
+
+
+ +Expand source code + +
def to_json(self):
+    return {"enabled": self.enabled}
+
+
+
+
+
+class GetTenantOkResult +(emailpassword: EmailPasswordConfig, passwordless: PasswordlessConfig, third_party: ThirdPartyConfig, core_config: Dict[str, Any]) +
+
+
+
+ +Expand source code + +
class GetTenantOkResult(TenantConfigResponse):
+    status = "OK"
+
+

Ancestors

+ +

Class variables

+
+
var status
+
+
+
+
+
+
+class ListAllTenantsItem +(tenant_id: str, emailpassword: EmailPasswordConfig, passwordless: PasswordlessConfig, third_party: ThirdPartyConfig, core_config: Dict[str, Any]) +
+
+
+
+ +Expand source code + +
class ListAllTenantsItem(TenantConfigResponse):
+    def __init__(
+        self,
+        tenant_id: str,
+        emailpassword: EmailPasswordConfig,
+        passwordless: PasswordlessConfig,
+        third_party: ThirdPartyConfig,
+        core_config: Dict[str, Any],
+    ):
+        super().__init__(emailpassword, passwordless, third_party, core_config)
+        self.tenant_id = tenant_id
+
+    def to_json(self):
+        res = {
+            "tenantId": self.tenant_id,
+            "emailPassword": self.emailpassword.to_json(),
+            "passwordless": self.passwordless.to_json(),
+            "thirdParty": self.third_party.to_json(),
+            "coreConfig": self.core_config,
+        }
+
+        return res
+
+

Ancestors

+ +

Methods

+
+
+def to_json(self) +
+
+
+
+ +Expand source code + +
def to_json(self):
+    res = {
+        "tenantId": self.tenant_id,
+        "emailPassword": self.emailpassword.to_json(),
+        "passwordless": self.passwordless.to_json(),
+        "thirdParty": self.third_party.to_json(),
+        "coreConfig": self.core_config,
+    }
+
+    return res
+
+
+
+
+
+class ListAllTenantsOkResult +(tenants: List[ListAllTenantsItem]) +
+
+
+
+ +Expand source code + +
class ListAllTenantsOkResult:
+    status = "OK"
+
+    def __init__(self, tenants: List[ListAllTenantsItem]):
+        self.tenants = tenants
+
+

Class variables

+
+
var status
+
+
+
+
+
+
+class LoginMethodEmailPassword +(enabled: bool) +
+
+
+
+ +Expand source code + +
class LoginMethodEmailPassword:
+    def __init__(self, enabled: bool):
+        self.enabled = enabled
+
+    def to_json(self) -> Dict[str, Any]:
+        return {
+            "enabled": self.enabled,
+        }
+
+

Methods

+
+
+def to_json(self) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
def to_json(self) -> Dict[str, Any]:
+    return {
+        "enabled": self.enabled,
+    }
+
+
+
+
+
+class LoginMethodPasswordless +(enabled: bool) +
+
+
+
+ +Expand source code + +
class LoginMethodPasswordless:
+    def __init__(self, enabled: bool):
+        self.enabled = enabled
+
+    def to_json(self) -> Dict[str, Any]:
+        return {
+            "enabled": self.enabled,
+        }
+
+

Methods

+
+
+def to_json(self) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
def to_json(self) -> Dict[str, Any]:
+    return {
+        "enabled": self.enabled,
+    }
+
+
+
+
+
+class LoginMethodThirdParty +(enabled: bool, providers: List[ThirdPartyProvider]) +
+
+
+
+ +Expand source code + +
class LoginMethodThirdParty:
+    def __init__(self, enabled: bool, providers: List[ThirdPartyProvider]):
+        self.enabled = enabled
+        self.providers = providers
+
+    def to_json(self) -> Dict[str, Any]:
+        return {
+            "enabled": self.enabled,
+            "providers": [provider.to_json() for provider in self.providers],
+        }
+
+

Methods

+
+
+def to_json(self) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
def to_json(self) -> Dict[str, Any]:
+    return {
+        "enabled": self.enabled,
+        "providers": [provider.to_json() for provider in self.providers],
+    }
+
+
+
+
+
+class LoginMethodsGetOkResult +(email_password: LoginMethodEmailPassword, passwordless: LoginMethodPasswordless, third_party: LoginMethodThirdParty) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class LoginMethodsGetOkResult(APIResponse):
+    def __init__(
+        self,
+        email_password: LoginMethodEmailPassword,
+        passwordless: LoginMethodPasswordless,
+        third_party: LoginMethodThirdParty,
+    ):
+        self.status = "OK"
+        self.email_password = email_password
+        self.passwordless = passwordless
+        self.third_party = third_party
+
+    def to_json(self) -> Dict[str, Any]:
+        return {
+            "status": self.status,
+            "emailPassword": self.email_password.to_json(),
+            "passwordless": self.passwordless.to_json(),
+            "thirdParty": self.third_party.to_json(),
+        }
+
+

Ancestors

+ +

Methods

+
+
+def to_json(self) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
def to_json(self) -> Dict[str, Any]:
+    return {
+        "status": self.status,
+        "emailPassword": self.email_password.to_json(),
+        "passwordless": self.passwordless.to_json(),
+        "thirdParty": self.third_party.to_json(),
+    }
+
+
+
+
+
+class PasswordlessConfig +(enabled: bool) +
+
+
+
+ +Expand source code + +
class PasswordlessConfig:
+    def __init__(self, enabled: bool):
+        self.enabled = enabled
+
+    def to_json(self):
+        return {"enabled": self.enabled}
+
+

Methods

+
+
+def to_json(self) +
+
+
+
+ +Expand source code + +
def to_json(self):
+    return {"enabled": self.enabled}
+
+
+
+
+
+class RecipeInterface +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class RecipeInterface(ABC):
+    def __init__(self):
+        pass
+
+    @abstractmethod
+    async def get_tenant_id(
+        self, tenant_id_from_frontend: str, user_context: Dict[str, Any]
+    ) -> str:
+        pass
+
+    @abstractmethod
+    async def create_or_update_tenant(
+        self,
+        tenant_id: str,
+        config: Optional[TenantConfig],
+        user_context: Dict[str, Any],
+    ) -> CreateOrUpdateTenantOkResult:
+        pass
+
+    @abstractmethod
+    async def delete_tenant(
+        self, tenant_id: str, user_context: Dict[str, Any]
+    ) -> DeleteTenantOkResult:
+        pass
+
+    @abstractmethod
+    async def get_tenant(
+        self, tenant_id: str, user_context: Dict[str, Any]
+    ) -> Optional[GetTenantOkResult]:
+        pass
+
+    @abstractmethod
+    async def list_all_tenants(
+        self, user_context: Dict[str, Any]
+    ) -> ListAllTenantsOkResult:
+        pass
+
+    # third party provider management
+    @abstractmethod
+    async def create_or_update_third_party_config(
+        self,
+        tenant_id: str,
+        config: ProviderConfig,
+        skip_validation: Optional[bool],
+        user_context: Dict[str, Any],
+    ) -> CreateOrUpdateThirdPartyConfigOkResult:
+        pass
+
+    @abstractmethod
+    async def delete_third_party_config(
+        self,
+        tenant_id: str,
+        third_party_id: str,
+        user_context: Dict[str, Any],
+    ) -> DeleteThirdPartyConfigOkResult:
+        pass
+
+    # user tenant association
+    @abstractmethod
+    async def associate_user_to_tenant(
+        self,
+        tenant_id: str,
+        user_id: str,
+        user_context: Dict[str, Any],
+    ) -> Union[
+        AssociateUserToTenantOkResult,
+        AssociateUserToTenantUnknownUserIdError,
+        AssociateUserToTenantEmailAlreadyExistsError,
+        AssociateUserToTenantPhoneNumberAlreadyExistsError,
+        AssociateUserToTenantThirdPartyUserAlreadyExistsError,
+    ]:
+        pass
+
+    @abstractmethod
+    async def dissociate_user_from_tenant(
+        self,
+        tenant_id: str,
+        user_id: str,
+        user_context: Dict[str, Any],
+    ) -> DisassociateUserFromTenantOkResult:
+        pass
+
+

Ancestors

+
    +
  • abc.ABC
  • +
+

Subclasses

+ +

Methods

+
+
+async def associate_user_to_tenant(self, tenant_id: str, user_id: str, user_context: Dict[str, Any]) ‑> Union[AssociateUserToTenantOkResultAssociateUserToTenantUnknownUserIdErrorAssociateUserToTenantEmailAlreadyExistsErrorAssociateUserToTenantPhoneNumberAlreadyExistsErrorAssociateUserToTenantThirdPartyUserAlreadyExistsError] +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def associate_user_to_tenant(
+    self,
+    tenant_id: str,
+    user_id: str,
+    user_context: Dict[str, Any],
+) -> Union[
+    AssociateUserToTenantOkResult,
+    AssociateUserToTenantUnknownUserIdError,
+    AssociateUserToTenantEmailAlreadyExistsError,
+    AssociateUserToTenantPhoneNumberAlreadyExistsError,
+    AssociateUserToTenantThirdPartyUserAlreadyExistsError,
+]:
+    pass
+
+
+
+async def create_or_update_tenant(self, tenant_id: str, config: Optional[TenantConfig], user_context: Dict[str, Any]) ‑> CreateOrUpdateTenantOkResult +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def create_or_update_tenant(
+    self,
+    tenant_id: str,
+    config: Optional[TenantConfig],
+    user_context: Dict[str, Any],
+) -> CreateOrUpdateTenantOkResult:
+    pass
+
+
+
+async def create_or_update_third_party_config(self, tenant_id: str, config: ProviderConfig, skip_validation: Optional[bool], user_context: Dict[str, Any]) ‑> CreateOrUpdateThirdPartyConfigOkResult +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def create_or_update_third_party_config(
+    self,
+    tenant_id: str,
+    config: ProviderConfig,
+    skip_validation: Optional[bool],
+    user_context: Dict[str, Any],
+) -> CreateOrUpdateThirdPartyConfigOkResult:
+    pass
+
+
+
+async def delete_tenant(self, tenant_id: str, user_context: Dict[str, Any]) ‑> DeleteTenantOkResult +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def delete_tenant(
+    self, tenant_id: str, user_context: Dict[str, Any]
+) -> DeleteTenantOkResult:
+    pass
+
+
+
+async def delete_third_party_config(self, tenant_id: str, third_party_id: str, user_context: Dict[str, Any]) ‑> DeleteThirdPartyConfigOkResult +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def delete_third_party_config(
+    self,
+    tenant_id: str,
+    third_party_id: str,
+    user_context: Dict[str, Any],
+) -> DeleteThirdPartyConfigOkResult:
+    pass
+
+
+
+async def dissociate_user_from_tenant(self, tenant_id: str, user_id: str, user_context: Dict[str, Any]) ‑> DisassociateUserFromTenantOkResult +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def dissociate_user_from_tenant(
+    self,
+    tenant_id: str,
+    user_id: str,
+    user_context: Dict[str, Any],
+) -> DisassociateUserFromTenantOkResult:
+    pass
+
+
+
+async def get_tenant(self, tenant_id: str, user_context: Dict[str, Any]) ‑> Optional[GetTenantOkResult] +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def get_tenant(
+    self, tenant_id: str, user_context: Dict[str, Any]
+) -> Optional[GetTenantOkResult]:
+    pass
+
+
+
+async def get_tenant_id(self, tenant_id_from_frontend: str, user_context: Dict[str, Any]) ‑> str +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def get_tenant_id(
+    self, tenant_id_from_frontend: str, user_context: Dict[str, Any]
+) -> str:
+    pass
+
+
+
+async def list_all_tenants(self, user_context: Dict[str, Any]) ‑> ListAllTenantsOkResult +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def list_all_tenants(
+    self, user_context: Dict[str, Any]
+) -> ListAllTenantsOkResult:
+    pass
+
+
+
+
+
+class TenantConfig +(email_password_enabled: Union[bool, None] = None, passwordless_enabled: Union[bool, None] = None, third_party_enabled: Union[bool, None] = None, core_config: Union[Dict[str, Any], None] = None) +
+
+
+
+ +Expand source code + +
class TenantConfig:
+    def __init__(
+        self,
+        email_password_enabled: Union[bool, None] = None,
+        passwordless_enabled: Union[bool, None] = None,
+        third_party_enabled: Union[bool, None] = None,
+        core_config: Union[Dict[str, Any], None] = None,
+    ):
+        self.email_password_enabled = email_password_enabled
+        self.passwordless_enabled = passwordless_enabled
+        self.third_party_enabled = third_party_enabled
+        self.core_config = core_config
+
+    def to_json(self) -> Dict[str, Any]:
+        res: Dict[str, Any] = {}
+        if self.email_password_enabled is not None:
+            res["emailPasswordEnabled"] = self.email_password_enabled
+        if self.passwordless_enabled is not None:
+            res["passwordlessEnabled"] = self.passwordless_enabled
+        if self.third_party_enabled is not None:
+            res["thirdPartyEnabled"] = self.third_party_enabled
+        if self.core_config is not None:
+            res["coreConfig"] = self.core_config
+        return res
+
+

Methods

+
+
+def to_json(self) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
def to_json(self) -> Dict[str, Any]:
+    res: Dict[str, Any] = {}
+    if self.email_password_enabled is not None:
+        res["emailPasswordEnabled"] = self.email_password_enabled
+    if self.passwordless_enabled is not None:
+        res["passwordlessEnabled"] = self.passwordless_enabled
+    if self.third_party_enabled is not None:
+        res["thirdPartyEnabled"] = self.third_party_enabled
+    if self.core_config is not None:
+        res["coreConfig"] = self.core_config
+    return res
+
+
+
+
+
+class TenantConfigResponse +(emailpassword: EmailPasswordConfig, passwordless: PasswordlessConfig, third_party: ThirdPartyConfig, core_config: Dict[str, Any]) +
+
+
+
+ +Expand source code + +
class TenantConfigResponse:
+    def __init__(
+        self,
+        emailpassword: EmailPasswordConfig,
+        passwordless: PasswordlessConfig,
+        third_party: ThirdPartyConfig,
+        core_config: Dict[str, Any],
+    ):
+        self.emailpassword = emailpassword
+        self.passwordless = passwordless
+        self.third_party = third_party
+        self.core_config = core_config
+
+

Subclasses

+ +
+
+class ThirdPartyConfig +(enabled: bool, providers: List[ProviderConfig]) +
+
+
+
+ +Expand source code + +
class ThirdPartyConfig:
+    def __init__(self, enabled: bool, providers: List[ProviderConfig]):
+        self.enabled = enabled
+        self.providers = providers
+
+    def to_json(self):
+        return {
+            "enabled": self.enabled,
+            "providers": [provider.to_json() for provider in self.providers],
+        }
+
+

Methods

+
+
+def to_json(self) +
+
+
+
+ +Expand source code + +
def to_json(self):
+    return {
+        "enabled": self.enabled,
+        "providers": [provider.to_json() for provider in self.providers],
+    }
+
+
+
+
+
+class ThirdPartyProvider +(id: str, name: Optional[str]) +
+
+
+
+ +Expand source code + +
class ThirdPartyProvider:
+    def __init__(
+        self, id: str, name: Optional[str]
+    ):  # pylint: disable=redefined-builtin
+        self.id = id
+        self.name = name
+
+    def to_json(self) -> Dict[str, Any]:
+        return {
+            "id": self.id,
+            "name": self.name,
+        }
+
+

Methods

+
+
+def to_json(self) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
def to_json(self) -> Dict[str, Any]:
+    return {
+        "id": self.id,
+        "name": self.name,
+    }
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/multitenancy/recipe.html b/html/supertokens_python/recipe/multitenancy/recipe.html new file mode 100644 index 000000000..81f991bda --- /dev/null +++ b/html/supertokens_python/recipe/multitenancy/recipe.html @@ -0,0 +1,743 @@ + + + + + + +supertokens_python.recipe.multitenancy.recipe API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.multitenancy.recipe

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from os import environ
+from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union
+
+from supertokens_python.exceptions import SuperTokensError, raise_general_exception
+from supertokens_python.recipe.session.claim_base_classes.primitive_array_claim import (
+    PrimitiveArrayClaim,
+)
+from supertokens_python.recipe_module import APIHandled, RecipeModule
+
+from ...post_init_callbacks import PostSTInitCallbacks
+
+from .interfaces import (
+    APIOptions,
+    TypeGetAllowedDomainsForTenantId,
+)
+
+from .recipe_implementation import RecipeImplementation
+
+if TYPE_CHECKING:
+    from supertokens_python.framework.request import BaseRequest
+    from supertokens_python.framework.response import BaseResponse
+    from supertokens_python.supertokens import AppInfo
+    from supertokens_python.recipe.thirdparty.provider import ProviderInput
+
+from supertokens_python.normalised_url_path import NormalisedURLPath
+from supertokens_python.querier import Querier
+from supertokens_python.recipe.multitenancy.api.implementation import APIImplementation
+
+
+from .api import handle_login_methods_api
+from .constants import LOGIN_METHODS
+from .exceptions import MultitenancyError
+from .utils import (
+    InputOverrideConfig,
+    validate_and_normalise_user_input,
+)
+
+
+class MultitenancyRecipe(RecipeModule):
+    recipe_id = "multitenancy"
+    __instance = None
+
+    def __init__(
+        self,
+        recipe_id: str,
+        app_info: AppInfo,
+        get_allowed_domains_for_tenant_id: Optional[
+            TypeGetAllowedDomainsForTenantId
+        ] = None,
+        override: Union[InputOverrideConfig, None] = None,
+    ) -> None:
+        super().__init__(recipe_id, app_info)
+        self.config = validate_and_normalise_user_input(
+            get_allowed_domains_for_tenant_id,
+            override,
+        )
+
+        recipe_implementation = RecipeImplementation(
+            Querier.get_instance(recipe_id), self.config
+        )
+        self.recipe_implementation = (
+            recipe_implementation
+            if self.config.override.functions is None
+            else self.config.override.functions(recipe_implementation)
+        )
+
+        api_implementation = APIImplementation()
+        self.api_implementation = (
+            api_implementation
+            if self.config.override.apis is None
+            else self.config.override.apis(api_implementation)
+        )
+
+        self.static_third_party_providers: List[ProviderInput] = []
+        self.get_allowed_domains_for_tenant_id = (
+            self.config.get_allowed_domains_for_tenant_id
+        )
+
+        RecipeModule.get_tenant_id = recipe_implementation.get_tenant_id
+
+    def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
+        return isinstance(err, MultitenancyError)
+
+    def get_apis_handled(self) -> List[APIHandled]:
+        return [
+            APIHandled(
+                NormalisedURLPath(LOGIN_METHODS),
+                "get",
+                LOGIN_METHODS,
+                self.api_implementation.disable_login_methods_get,
+            ),
+        ]
+
+    async def handle_api_request(
+        self,
+        request_id: str,
+        tenant_id: str,
+        request: BaseRequest,
+        path: NormalisedURLPath,
+        method: str,
+        response: BaseResponse,
+        user_context: Dict[str, Any],
+    ) -> Union[BaseResponse, None]:
+        api_options = APIOptions(
+            request,
+            response,
+            self.recipe_id,
+            self.config,
+            self.recipe_implementation,
+            self.static_third_party_providers,
+        )
+        return await handle_login_methods_api(
+            self.api_implementation,
+            tenant_id,
+            api_options,
+            user_context,
+        )
+
+    async def handle_error(
+        self,
+        request: BaseRequest,
+        err: SuperTokensError,
+        response: BaseResponse,
+        user_context: Dict[str, Any],
+    ) -> BaseResponse:
+        raise err
+
+    def get_all_cors_headers(self) -> List[str]:
+        return []
+
+    @staticmethod
+    def init(
+        get_allowed_domains_for_tenant_id: Union[
+            TypeGetAllowedDomainsForTenantId, None
+        ] = None,
+        override: Union[InputOverrideConfig, None] = None,
+    ):
+        def func(app_info: AppInfo):
+            if MultitenancyRecipe.__instance is None:
+                MultitenancyRecipe.__instance = MultitenancyRecipe(
+                    MultitenancyRecipe.recipe_id,
+                    app_info,
+                    get_allowed_domains_for_tenant_id,
+                    override,
+                )
+
+                def callback():
+                    try:
+                        from supertokens_python.recipe.session import SessionRecipe
+
+                        SessionRecipe.get_instance().add_claim_from_other_recipe(
+                            AllowedDomainsClaim
+                        )
+                    except Exception:
+                        # Skip adding claims if session recipe is not initilised
+                        pass
+
+                PostSTInitCallbacks.add_post_init_callback(callback)
+
+                return MultitenancyRecipe.__instance
+            raise_general_exception(
+                "Multitenancy recipe has already been initialised. Please check your code for bugs."
+            )
+
+        return func
+
+    @staticmethod
+    def get_instance() -> MultitenancyRecipe:
+        if MultitenancyRecipe.__instance is not None:
+            return MultitenancyRecipe.__instance
+        raise_general_exception(
+            "Initialisation not done. Did you forget to call the SuperTokens.init function?"
+        )
+
+    @staticmethod
+    def get_instance_optional() -> Optional[MultitenancyRecipe]:
+        return MultitenancyRecipe.__instance
+
+    @staticmethod
+    def reset():
+        if ("SUPERTOKENS_ENV" not in environ) or (
+            environ["SUPERTOKENS_ENV"] != "testing"
+        ):
+            raise_general_exception("calling testing function in non testing env")
+        MultitenancyRecipe.__instance = None
+
+
+class AllowedDomainsClaimClass(PrimitiveArrayClaim[List[str]]):
+    def __init__(self):
+        default_max_age_in_sec = 60 * 60
+
+        async def fetch_value(
+            _: str, tenant_id: str, user_context: Dict[str, Any]
+        ) -> Optional[List[str]]:
+            recipe = MultitenancyRecipe.get_instance()
+
+            if recipe.get_allowed_domains_for_tenant_id is None:
+                # User did not provide a function to get allowed domains, but is using a validator. So we don't allow any domains by default
+                return None
+
+            return await recipe.get_allowed_domains_for_tenant_id(
+                tenant_id, user_context
+            )
+
+        super().__init__("st-t-dmns", fetch_value, default_max_age_in_sec)
+
+
+AllowedDomainsClaim = AllowedDomainsClaimClass()
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class AllowedDomainsClaimClass +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+

Args

+
+
key
+
The key to use when storing the claim in the payload.
+
fetch_value
+
a method that fetches the current value of this claim for the user. +A None return value signifies that we don't want to update the claim payload and or the claim value is +not present in the database. For example, this can happen with a second factor auth claim, where we +don't want to add the claim to the session automatically
+
+
+ +Expand source code + +
class AllowedDomainsClaimClass(PrimitiveArrayClaim[List[str]]):
+    def __init__(self):
+        default_max_age_in_sec = 60 * 60
+
+        async def fetch_value(
+            _: str, tenant_id: str, user_context: Dict[str, Any]
+        ) -> Optional[List[str]]:
+            recipe = MultitenancyRecipe.get_instance()
+
+            if recipe.get_allowed_domains_for_tenant_id is None:
+                # User did not provide a function to get allowed domains, but is using a validator. So we don't allow any domains by default
+                return None
+
+            return await recipe.get_allowed_domains_for_tenant_id(
+                tenant_id, user_context
+            )
+
+        super().__init__("st-t-dmns", fetch_value, default_max_age_in_sec)
+
+

Ancestors

+ +

Inherited members

+ +
+
+class MultitenancyRecipe +(recipe_id: str, app_info: AppInfo, get_allowed_domains_for_tenant_id: Optional[TypeGetAllowedDomainsForTenantId] = None, override: Union[InputOverrideConfig, None] = None) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class MultitenancyRecipe(RecipeModule):
+    recipe_id = "multitenancy"
+    __instance = None
+
+    def __init__(
+        self,
+        recipe_id: str,
+        app_info: AppInfo,
+        get_allowed_domains_for_tenant_id: Optional[
+            TypeGetAllowedDomainsForTenantId
+        ] = None,
+        override: Union[InputOverrideConfig, None] = None,
+    ) -> None:
+        super().__init__(recipe_id, app_info)
+        self.config = validate_and_normalise_user_input(
+            get_allowed_domains_for_tenant_id,
+            override,
+        )
+
+        recipe_implementation = RecipeImplementation(
+            Querier.get_instance(recipe_id), self.config
+        )
+        self.recipe_implementation = (
+            recipe_implementation
+            if self.config.override.functions is None
+            else self.config.override.functions(recipe_implementation)
+        )
+
+        api_implementation = APIImplementation()
+        self.api_implementation = (
+            api_implementation
+            if self.config.override.apis is None
+            else self.config.override.apis(api_implementation)
+        )
+
+        self.static_third_party_providers: List[ProviderInput] = []
+        self.get_allowed_domains_for_tenant_id = (
+            self.config.get_allowed_domains_for_tenant_id
+        )
+
+        RecipeModule.get_tenant_id = recipe_implementation.get_tenant_id
+
+    def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
+        return isinstance(err, MultitenancyError)
+
+    def get_apis_handled(self) -> List[APIHandled]:
+        return [
+            APIHandled(
+                NormalisedURLPath(LOGIN_METHODS),
+                "get",
+                LOGIN_METHODS,
+                self.api_implementation.disable_login_methods_get,
+            ),
+        ]
+
+    async def handle_api_request(
+        self,
+        request_id: str,
+        tenant_id: str,
+        request: BaseRequest,
+        path: NormalisedURLPath,
+        method: str,
+        response: BaseResponse,
+        user_context: Dict[str, Any],
+    ) -> Union[BaseResponse, None]:
+        api_options = APIOptions(
+            request,
+            response,
+            self.recipe_id,
+            self.config,
+            self.recipe_implementation,
+            self.static_third_party_providers,
+        )
+        return await handle_login_methods_api(
+            self.api_implementation,
+            tenant_id,
+            api_options,
+            user_context,
+        )
+
+    async def handle_error(
+        self,
+        request: BaseRequest,
+        err: SuperTokensError,
+        response: BaseResponse,
+        user_context: Dict[str, Any],
+    ) -> BaseResponse:
+        raise err
+
+    def get_all_cors_headers(self) -> List[str]:
+        return []
+
+    @staticmethod
+    def init(
+        get_allowed_domains_for_tenant_id: Union[
+            TypeGetAllowedDomainsForTenantId, None
+        ] = None,
+        override: Union[InputOverrideConfig, None] = None,
+    ):
+        def func(app_info: AppInfo):
+            if MultitenancyRecipe.__instance is None:
+                MultitenancyRecipe.__instance = MultitenancyRecipe(
+                    MultitenancyRecipe.recipe_id,
+                    app_info,
+                    get_allowed_domains_for_tenant_id,
+                    override,
+                )
+
+                def callback():
+                    try:
+                        from supertokens_python.recipe.session import SessionRecipe
+
+                        SessionRecipe.get_instance().add_claim_from_other_recipe(
+                            AllowedDomainsClaim
+                        )
+                    except Exception:
+                        # Skip adding claims if session recipe is not initilised
+                        pass
+
+                PostSTInitCallbacks.add_post_init_callback(callback)
+
+                return MultitenancyRecipe.__instance
+            raise_general_exception(
+                "Multitenancy recipe has already been initialised. Please check your code for bugs."
+            )
+
+        return func
+
+    @staticmethod
+    def get_instance() -> MultitenancyRecipe:
+        if MultitenancyRecipe.__instance is not None:
+            return MultitenancyRecipe.__instance
+        raise_general_exception(
+            "Initialisation not done. Did you forget to call the SuperTokens.init function?"
+        )
+
+    @staticmethod
+    def get_instance_optional() -> Optional[MultitenancyRecipe]:
+        return MultitenancyRecipe.__instance
+
+    @staticmethod
+    def reset():
+        if ("SUPERTOKENS_ENV" not in environ) or (
+            environ["SUPERTOKENS_ENV"] != "testing"
+        ):
+            raise_general_exception("calling testing function in non testing env")
+        MultitenancyRecipe.__instance = None
+
+

Ancestors

+ +

Class variables

+
+
var get_tenant_id : Optional[Callable[[str, Dict[str, Any]], Awaitable[str]]]
+
+
+
+
var recipe_id
+
+
+
+
+

Static methods

+
+
+def get_instance() ‑> MultitenancyRecipe +
+
+
+
+ +Expand source code + +
@staticmethod
+def get_instance() -> MultitenancyRecipe:
+    if MultitenancyRecipe.__instance is not None:
+        return MultitenancyRecipe.__instance
+    raise_general_exception(
+        "Initialisation not done. Did you forget to call the SuperTokens.init function?"
+    )
+
+
+
+def get_instance_optional() ‑> Optional[MultitenancyRecipe] +
+
+
+
+ +Expand source code + +
@staticmethod
+def get_instance_optional() -> Optional[MultitenancyRecipe]:
+    return MultitenancyRecipe.__instance
+
+
+
+def init(get_allowed_domains_for_tenant_id: Union[TypeGetAllowedDomainsForTenantId, None] = None, override: Union[InputOverrideConfig, None] = None) +
+
+
+
+ +Expand source code + +
@staticmethod
+def init(
+    get_allowed_domains_for_tenant_id: Union[
+        TypeGetAllowedDomainsForTenantId, None
+    ] = None,
+    override: Union[InputOverrideConfig, None] = None,
+):
+    def func(app_info: AppInfo):
+        if MultitenancyRecipe.__instance is None:
+            MultitenancyRecipe.__instance = MultitenancyRecipe(
+                MultitenancyRecipe.recipe_id,
+                app_info,
+                get_allowed_domains_for_tenant_id,
+                override,
+            )
+
+            def callback():
+                try:
+                    from supertokens_python.recipe.session import SessionRecipe
+
+                    SessionRecipe.get_instance().add_claim_from_other_recipe(
+                        AllowedDomainsClaim
+                    )
+                except Exception:
+                    # Skip adding claims if session recipe is not initilised
+                    pass
+
+            PostSTInitCallbacks.add_post_init_callback(callback)
+
+            return MultitenancyRecipe.__instance
+        raise_general_exception(
+            "Multitenancy recipe has already been initialised. Please check your code for bugs."
+        )
+
+    return func
+
+
+
+def reset() +
+
+
+
+ +Expand source code + +
@staticmethod
+def reset():
+    if ("SUPERTOKENS_ENV" not in environ) or (
+        environ["SUPERTOKENS_ENV"] != "testing"
+    ):
+        raise_general_exception("calling testing function in non testing env")
+    MultitenancyRecipe.__instance = None
+
+
+
+

Methods

+
+
+def get_all_cors_headers(self) ‑> List[str] +
+
+
+
+ +Expand source code + +
def get_all_cors_headers(self) -> List[str]:
+    return []
+
+
+
+def get_apis_handled(self) ‑> List[APIHandled] +
+
+
+
+ +Expand source code + +
def get_apis_handled(self) -> List[APIHandled]:
+    return [
+        APIHandled(
+            NormalisedURLPath(LOGIN_METHODS),
+            "get",
+            LOGIN_METHODS,
+            self.api_implementation.disable_login_methods_get,
+        ),
+    ]
+
+
+
+async def handle_api_request(self, request_id: str, tenant_id: str, request: BaseRequest, path: NormalisedURLPath, method: str, response: BaseResponse, user_context: Dict[str, Any]) ‑> Union[BaseResponse, None] +
+
+
+
+ +Expand source code + +
async def handle_api_request(
+    self,
+    request_id: str,
+    tenant_id: str,
+    request: BaseRequest,
+    path: NormalisedURLPath,
+    method: str,
+    response: BaseResponse,
+    user_context: Dict[str, Any],
+) -> Union[BaseResponse, None]:
+    api_options = APIOptions(
+        request,
+        response,
+        self.recipe_id,
+        self.config,
+        self.recipe_implementation,
+        self.static_third_party_providers,
+    )
+    return await handle_login_methods_api(
+        self.api_implementation,
+        tenant_id,
+        api_options,
+        user_context,
+    )
+
+
+
+async def handle_error(self, request: BaseRequest, err: SuperTokensError, response: BaseResponse, user_context: Dict[str, Any]) ‑> BaseResponse +
+
+
+
+ +Expand source code + +
async def handle_error(
+    self,
+    request: BaseRequest,
+    err: SuperTokensError,
+    response: BaseResponse,
+    user_context: Dict[str, Any],
+) -> BaseResponse:
+    raise err
+
+
+
+def is_error_from_this_recipe_based_on_instance(self, err: Exception) ‑> bool +
+
+
+
+ +Expand source code + +
def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
+    return isinstance(err, MultitenancyError)
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/multitenancy/recipe_implementation.html b/html/supertokens_python/recipe/multitenancy/recipe_implementation.html new file mode 100644 index 000000000..c292ec5c7 --- /dev/null +++ b/html/supertokens_python/recipe/multitenancy/recipe_implementation.html @@ -0,0 +1,960 @@ + + + + + + +supertokens_python.recipe.multitenancy.recipe_implementation API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.multitenancy.recipe_implementation

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Optional, Dict, Any, Union, List
+from supertokens_python.recipe.multitenancy.interfaces import (
+    AssociateUserToTenantOkResult,
+    AssociateUserToTenantUnknownUserIdError,
+    AssociateUserToTenantEmailAlreadyExistsError,
+    AssociateUserToTenantPhoneNumberAlreadyExistsError,
+    AssociateUserToTenantThirdPartyUserAlreadyExistsError,
+    DisassociateUserFromTenantOkResult,
+)
+
+from .interfaces import (
+    RecipeInterface,
+    TenantConfig,
+    CreateOrUpdateTenantOkResult,
+    DeleteTenantOkResult,
+    TenantConfigResponse,
+    GetTenantOkResult,
+    EmailPasswordConfig,
+    PasswordlessConfig,
+    ThirdPartyConfig,
+    ListAllTenantsOkResult,
+    CreateOrUpdateThirdPartyConfigOkResult,
+    DeleteThirdPartyConfigOkResult,
+    ListAllTenantsItem,
+)
+
+if TYPE_CHECKING:
+    from supertokens_python.querier import Querier
+    from supertokens_python.recipe.thirdparty.provider import ProviderConfig
+    from .utils import MultitenancyConfig
+
+from supertokens_python.querier import NormalisedURLPath
+from .constants import DEFAULT_TENANT_ID
+
+
+def parse_tenant_config(tenant: Dict[str, Any]) -> TenantConfigResponse:
+    from supertokens_python.recipe.thirdparty.provider import (
+        UserInfoMap,
+        UserFields,
+        ProviderClientConfig,
+        ProviderConfig,
+    )
+
+    providers: List[ProviderConfig] = []
+    for p in tenant["thirdParty"]["providers"]:
+        user_info_map: Optional[UserInfoMap] = None
+        if "userInfoMap" in p:
+            map_from_payload = p["userInfoMap"].get("fromIdTokenPayload", {})
+            map_from_api = p["userInfoMap"].get("fromUserInfoAPI", {})
+            user_info_map = UserInfoMap(
+                UserFields(
+                    map_from_payload.get("userId"),
+                    map_from_payload.get("email"),
+                    map_from_payload.get("emailVerified"),
+                ),
+                UserFields(
+                    map_from_api.get("userId"),
+                    map_from_api.get("email"),
+                    map_from_api.get("emailVerified"),
+                ),
+            )
+
+        providers.append(
+            ProviderConfig(
+                third_party_id=p["thirdPartyId"],
+                name=p.get("name"),
+                clients=[
+                    ProviderClientConfig(
+                        client_id=c["clientId"],
+                        client_secret=c.get("clientSecret"),
+                        client_type=c.get("clientType"),
+                        scope=c.get("scope"),
+                        force_pkce=c.get("forcePKCE", False),
+                        additional_config=c.get("additionalConfig"),
+                    )
+                    for c in p["clients"]
+                ],
+                authorization_endpoint=p.get("authorizationEndpoint"),
+                authorization_endpoint_query_params=p.get(
+                    "authorizationEndpointQueryParams"
+                ),
+                token_endpoint=p.get("tokenEndpoint"),
+                token_endpoint_body_params=p.get("tokenEndpointBodyParams"),
+                user_info_endpoint=p.get("userInfoEndpoint"),
+                user_info_endpoint_query_params=p.get("userInfoEndpointQueryParams"),
+                user_info_endpoint_headers=p.get("userInfoEndpointHeaders"),
+                jwks_uri=p.get("jwksURI"),
+                oidc_discovery_endpoint=p.get("oidcDiscoveryEndpoint"),
+                user_info_map=user_info_map,
+                require_email=p.get("requireEmail", True),
+                validate_id_token_payload=None,
+                generate_fake_email=None,
+                validate_access_token=None,
+            )
+        )
+
+    return TenantConfigResponse(
+        emailpassword=EmailPasswordConfig(tenant["emailPassword"]["enabled"]),
+        passwordless=PasswordlessConfig(tenant["passwordless"]["enabled"]),
+        third_party=ThirdPartyConfig(
+            tenant["thirdParty"]["enabled"],
+            providers,
+        ),
+        core_config=tenant["coreConfig"],
+    )
+
+
+class RecipeImplementation(RecipeInterface):
+    def __init__(self, querier: Querier, config: MultitenancyConfig):
+        super().__init__()
+        self.querier = querier
+        self.config = config
+
+    async def get_tenant_id(
+        self, tenant_id_from_frontend: str, user_context: Dict[str, Any]
+    ) -> str:
+        return tenant_id_from_frontend
+
+    async def create_or_update_tenant(
+        self,
+        tenant_id: str,
+        config: Optional[TenantConfig],
+        user_context: Dict[str, Any],
+    ) -> CreateOrUpdateTenantOkResult:
+        response = await self.querier.send_put_request(
+            NormalisedURLPath("/recipe/multitenancy/tenant"),
+            {
+                "tenantId": tenant_id,
+                **(config.to_json() if config is not None else {}),
+            },
+            user_context=user_context,
+        )
+        return CreateOrUpdateTenantOkResult(
+            created_new=response["createdNew"],
+        )
+
+    async def delete_tenant(
+        self, tenant_id: str, user_context: Dict[str, Any]
+    ) -> DeleteTenantOkResult:
+        response = await self.querier.send_post_request(
+            NormalisedURLPath("/recipe/multitenancy/tenant/remove"),
+            {"tenantId": tenant_id},
+            user_context=user_context,
+        )
+        return DeleteTenantOkResult(
+            did_exist=response["didExist"],
+        )
+
+    async def get_tenant(
+        self, tenant_id: Optional[str], user_context: Dict[str, Any]
+    ) -> Optional[GetTenantOkResult]:
+        res = await self.querier.send_get_request(
+            NormalisedURLPath(
+                f"{tenant_id or DEFAULT_TENANT_ID}/recipe/multitenancy/tenant"
+            ),
+            None,
+            user_context=user_context,
+        )
+
+        if res["status"] == "TENANT_NOT_FOUND_ERROR":
+            return None
+
+        tenant_config = parse_tenant_config(res)
+
+        return GetTenantOkResult(
+            emailpassword=tenant_config.emailpassword,
+            passwordless=tenant_config.passwordless,
+            third_party=tenant_config.third_party,
+            core_config=tenant_config.core_config,
+        )
+
+    async def list_all_tenants(
+        self, user_context: Dict[str, Any]
+    ) -> ListAllTenantsOkResult:
+        response = await self.querier.send_get_request(
+            NormalisedURLPath("/recipe/multitenancy/tenant/list"),
+            {},
+            user_context=user_context,
+        )
+
+        tenant_items: List[ListAllTenantsItem] = []
+
+        for tenant in response["tenants"]:
+            config = parse_tenant_config(tenant)
+            item = ListAllTenantsItem(
+                tenant["tenantId"],
+                config.emailpassword,
+                config.passwordless,
+                config.third_party,
+                config.core_config,
+            )
+            tenant_items.append(item)
+
+        return ListAllTenantsOkResult(
+            tenants=tenant_items,
+        )
+
+    async def create_or_update_third_party_config(
+        self,
+        tenant_id: Optional[str],
+        config: ProviderConfig,
+        skip_validation: Optional[bool],
+        user_context: Dict[str, Any],
+    ) -> CreateOrUpdateThirdPartyConfigOkResult:
+        response = await self.querier.send_put_request(
+            NormalisedURLPath(
+                f"{tenant_id or DEFAULT_TENANT_ID}/recipe/multitenancy/config/thirdparty"
+            ),
+            {
+                "config": config.to_json(),
+                "skipValidation": skip_validation is True,
+            },
+            user_context=user_context,
+        )
+
+        return CreateOrUpdateThirdPartyConfigOkResult(
+            created_new=response["createdNew"],
+        )
+
+    async def delete_third_party_config(
+        self,
+        tenant_id: Optional[str],
+        third_party_id: str,
+        user_context: Dict[str, Any],
+    ) -> DeleteThirdPartyConfigOkResult:
+        response = await self.querier.send_post_request(
+            NormalisedURLPath(
+                f"{tenant_id or DEFAULT_TENANT_ID}/recipe/multitenancy/config/thirdparty/remove"
+            ),
+            {
+                "thirdPartyId": third_party_id,
+            },
+            user_context=user_context,
+        )
+
+        return DeleteThirdPartyConfigOkResult(
+            did_config_exist=response["didConfigExist"],
+        )
+
+    async def associate_user_to_tenant(
+        self, tenant_id: Optional[str], user_id: str, user_context: Dict[str, Any]
+    ) -> Union[
+        AssociateUserToTenantOkResult,
+        AssociateUserToTenantUnknownUserIdError,
+        AssociateUserToTenantEmailAlreadyExistsError,
+        AssociateUserToTenantPhoneNumberAlreadyExistsError,
+        AssociateUserToTenantThirdPartyUserAlreadyExistsError,
+    ]:
+        response = await self.querier.send_post_request(
+            NormalisedURLPath(
+                f"{tenant_id or DEFAULT_TENANT_ID}/recipe/multitenancy/tenant/user"
+            ),
+            {
+                "userId": user_id,
+            },
+            user_context=user_context,
+        )
+
+        if response["status"] == "OK":
+            return AssociateUserToTenantOkResult(
+                was_already_associated=response["wasAlreadyAssociated"],
+            )
+        if response["status"] == AssociateUserToTenantUnknownUserIdError.status:
+            return AssociateUserToTenantUnknownUserIdError()
+        if response["status"] == AssociateUserToTenantEmailAlreadyExistsError.status:
+            return AssociateUserToTenantEmailAlreadyExistsError()
+        if (
+            response["status"]
+            == AssociateUserToTenantPhoneNumberAlreadyExistsError.status
+        ):
+            return AssociateUserToTenantPhoneNumberAlreadyExistsError()
+        if (
+            response["status"]
+            == AssociateUserToTenantThirdPartyUserAlreadyExistsError.status
+        ):
+            return AssociateUserToTenantThirdPartyUserAlreadyExistsError()
+
+        raise Exception("Should never come here")
+
+    async def dissociate_user_from_tenant(
+        self, tenant_id: Optional[str], user_id: str, user_context: Dict[str, Any]
+    ) -> DisassociateUserFromTenantOkResult:
+        response = await self.querier.send_post_request(
+            NormalisedURLPath(
+                f"{tenant_id or DEFAULT_TENANT_ID}/recipe/multitenancy/tenant/user/remove"
+            ),
+            {
+                "userId": user_id,
+            },
+            user_context=user_context,
+        )
+
+        return DisassociateUserFromTenantOkResult(
+            was_associated=response["wasAssociated"],
+        )
+
+
+
+
+
+
+
+

Functions

+
+
+def parse_tenant_config(tenant: Dict[str, Any]) ‑> TenantConfigResponse +
+
+
+
+ +Expand source code + +
def parse_tenant_config(tenant: Dict[str, Any]) -> TenantConfigResponse:
+    from supertokens_python.recipe.thirdparty.provider import (
+        UserInfoMap,
+        UserFields,
+        ProviderClientConfig,
+        ProviderConfig,
+    )
+
+    providers: List[ProviderConfig] = []
+    for p in tenant["thirdParty"]["providers"]:
+        user_info_map: Optional[UserInfoMap] = None
+        if "userInfoMap" in p:
+            map_from_payload = p["userInfoMap"].get("fromIdTokenPayload", {})
+            map_from_api = p["userInfoMap"].get("fromUserInfoAPI", {})
+            user_info_map = UserInfoMap(
+                UserFields(
+                    map_from_payload.get("userId"),
+                    map_from_payload.get("email"),
+                    map_from_payload.get("emailVerified"),
+                ),
+                UserFields(
+                    map_from_api.get("userId"),
+                    map_from_api.get("email"),
+                    map_from_api.get("emailVerified"),
+                ),
+            )
+
+        providers.append(
+            ProviderConfig(
+                third_party_id=p["thirdPartyId"],
+                name=p.get("name"),
+                clients=[
+                    ProviderClientConfig(
+                        client_id=c["clientId"],
+                        client_secret=c.get("clientSecret"),
+                        client_type=c.get("clientType"),
+                        scope=c.get("scope"),
+                        force_pkce=c.get("forcePKCE", False),
+                        additional_config=c.get("additionalConfig"),
+                    )
+                    for c in p["clients"]
+                ],
+                authorization_endpoint=p.get("authorizationEndpoint"),
+                authorization_endpoint_query_params=p.get(
+                    "authorizationEndpointQueryParams"
+                ),
+                token_endpoint=p.get("tokenEndpoint"),
+                token_endpoint_body_params=p.get("tokenEndpointBodyParams"),
+                user_info_endpoint=p.get("userInfoEndpoint"),
+                user_info_endpoint_query_params=p.get("userInfoEndpointQueryParams"),
+                user_info_endpoint_headers=p.get("userInfoEndpointHeaders"),
+                jwks_uri=p.get("jwksURI"),
+                oidc_discovery_endpoint=p.get("oidcDiscoveryEndpoint"),
+                user_info_map=user_info_map,
+                require_email=p.get("requireEmail", True),
+                validate_id_token_payload=None,
+                generate_fake_email=None,
+                validate_access_token=None,
+            )
+        )
+
+    return TenantConfigResponse(
+        emailpassword=EmailPasswordConfig(tenant["emailPassword"]["enabled"]),
+        passwordless=PasswordlessConfig(tenant["passwordless"]["enabled"]),
+        third_party=ThirdPartyConfig(
+            tenant["thirdParty"]["enabled"],
+            providers,
+        ),
+        core_config=tenant["coreConfig"],
+    )
+
+
+
+
+
+

Classes

+
+
+class RecipeImplementation +(querier: Querier, config: MultitenancyConfig) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class RecipeImplementation(RecipeInterface):
+    def __init__(self, querier: Querier, config: MultitenancyConfig):
+        super().__init__()
+        self.querier = querier
+        self.config = config
+
+    async def get_tenant_id(
+        self, tenant_id_from_frontend: str, user_context: Dict[str, Any]
+    ) -> str:
+        return tenant_id_from_frontend
+
+    async def create_or_update_tenant(
+        self,
+        tenant_id: str,
+        config: Optional[TenantConfig],
+        user_context: Dict[str, Any],
+    ) -> CreateOrUpdateTenantOkResult:
+        response = await self.querier.send_put_request(
+            NormalisedURLPath("/recipe/multitenancy/tenant"),
+            {
+                "tenantId": tenant_id,
+                **(config.to_json() if config is not None else {}),
+            },
+            user_context=user_context,
+        )
+        return CreateOrUpdateTenantOkResult(
+            created_new=response["createdNew"],
+        )
+
+    async def delete_tenant(
+        self, tenant_id: str, user_context: Dict[str, Any]
+    ) -> DeleteTenantOkResult:
+        response = await self.querier.send_post_request(
+            NormalisedURLPath("/recipe/multitenancy/tenant/remove"),
+            {"tenantId": tenant_id},
+            user_context=user_context,
+        )
+        return DeleteTenantOkResult(
+            did_exist=response["didExist"],
+        )
+
+    async def get_tenant(
+        self, tenant_id: Optional[str], user_context: Dict[str, Any]
+    ) -> Optional[GetTenantOkResult]:
+        res = await self.querier.send_get_request(
+            NormalisedURLPath(
+                f"{tenant_id or DEFAULT_TENANT_ID}/recipe/multitenancy/tenant"
+            ),
+            None,
+            user_context=user_context,
+        )
+
+        if res["status"] == "TENANT_NOT_FOUND_ERROR":
+            return None
+
+        tenant_config = parse_tenant_config(res)
+
+        return GetTenantOkResult(
+            emailpassword=tenant_config.emailpassword,
+            passwordless=tenant_config.passwordless,
+            third_party=tenant_config.third_party,
+            core_config=tenant_config.core_config,
+        )
+
+    async def list_all_tenants(
+        self, user_context: Dict[str, Any]
+    ) -> ListAllTenantsOkResult:
+        response = await self.querier.send_get_request(
+            NormalisedURLPath("/recipe/multitenancy/tenant/list"),
+            {},
+            user_context=user_context,
+        )
+
+        tenant_items: List[ListAllTenantsItem] = []
+
+        for tenant in response["tenants"]:
+            config = parse_tenant_config(tenant)
+            item = ListAllTenantsItem(
+                tenant["tenantId"],
+                config.emailpassword,
+                config.passwordless,
+                config.third_party,
+                config.core_config,
+            )
+            tenant_items.append(item)
+
+        return ListAllTenantsOkResult(
+            tenants=tenant_items,
+        )
+
+    async def create_or_update_third_party_config(
+        self,
+        tenant_id: Optional[str],
+        config: ProviderConfig,
+        skip_validation: Optional[bool],
+        user_context: Dict[str, Any],
+    ) -> CreateOrUpdateThirdPartyConfigOkResult:
+        response = await self.querier.send_put_request(
+            NormalisedURLPath(
+                f"{tenant_id or DEFAULT_TENANT_ID}/recipe/multitenancy/config/thirdparty"
+            ),
+            {
+                "config": config.to_json(),
+                "skipValidation": skip_validation is True,
+            },
+            user_context=user_context,
+        )
+
+        return CreateOrUpdateThirdPartyConfigOkResult(
+            created_new=response["createdNew"],
+        )
+
+    async def delete_third_party_config(
+        self,
+        tenant_id: Optional[str],
+        third_party_id: str,
+        user_context: Dict[str, Any],
+    ) -> DeleteThirdPartyConfigOkResult:
+        response = await self.querier.send_post_request(
+            NormalisedURLPath(
+                f"{tenant_id or DEFAULT_TENANT_ID}/recipe/multitenancy/config/thirdparty/remove"
+            ),
+            {
+                "thirdPartyId": third_party_id,
+            },
+            user_context=user_context,
+        )
+
+        return DeleteThirdPartyConfigOkResult(
+            did_config_exist=response["didConfigExist"],
+        )
+
+    async def associate_user_to_tenant(
+        self, tenant_id: Optional[str], user_id: str, user_context: Dict[str, Any]
+    ) -> Union[
+        AssociateUserToTenantOkResult,
+        AssociateUserToTenantUnknownUserIdError,
+        AssociateUserToTenantEmailAlreadyExistsError,
+        AssociateUserToTenantPhoneNumberAlreadyExistsError,
+        AssociateUserToTenantThirdPartyUserAlreadyExistsError,
+    ]:
+        response = await self.querier.send_post_request(
+            NormalisedURLPath(
+                f"{tenant_id or DEFAULT_TENANT_ID}/recipe/multitenancy/tenant/user"
+            ),
+            {
+                "userId": user_id,
+            },
+            user_context=user_context,
+        )
+
+        if response["status"] == "OK":
+            return AssociateUserToTenantOkResult(
+                was_already_associated=response["wasAlreadyAssociated"],
+            )
+        if response["status"] == AssociateUserToTenantUnknownUserIdError.status:
+            return AssociateUserToTenantUnknownUserIdError()
+        if response["status"] == AssociateUserToTenantEmailAlreadyExistsError.status:
+            return AssociateUserToTenantEmailAlreadyExistsError()
+        if (
+            response["status"]
+            == AssociateUserToTenantPhoneNumberAlreadyExistsError.status
+        ):
+            return AssociateUserToTenantPhoneNumberAlreadyExistsError()
+        if (
+            response["status"]
+            == AssociateUserToTenantThirdPartyUserAlreadyExistsError.status
+        ):
+            return AssociateUserToTenantThirdPartyUserAlreadyExistsError()
+
+        raise Exception("Should never come here")
+
+    async def dissociate_user_from_tenant(
+        self, tenant_id: Optional[str], user_id: str, user_context: Dict[str, Any]
+    ) -> DisassociateUserFromTenantOkResult:
+        response = await self.querier.send_post_request(
+            NormalisedURLPath(
+                f"{tenant_id or DEFAULT_TENANT_ID}/recipe/multitenancy/tenant/user/remove"
+            ),
+            {
+                "userId": user_id,
+            },
+            user_context=user_context,
+        )
+
+        return DisassociateUserFromTenantOkResult(
+            was_associated=response["wasAssociated"],
+        )
+
+

Ancestors

+ +

Methods

+
+
+async def associate_user_to_tenant(self, tenant_id: Optional[str], user_id: str, user_context: Dict[str, Any]) ‑> Union[AssociateUserToTenantOkResultAssociateUserToTenantUnknownUserIdErrorAssociateUserToTenantEmailAlreadyExistsErrorAssociateUserToTenantPhoneNumberAlreadyExistsErrorAssociateUserToTenantThirdPartyUserAlreadyExistsError] +
+
+
+
+ +Expand source code + +
async def associate_user_to_tenant(
+    self, tenant_id: Optional[str], user_id: str, user_context: Dict[str, Any]
+) -> Union[
+    AssociateUserToTenantOkResult,
+    AssociateUserToTenantUnknownUserIdError,
+    AssociateUserToTenantEmailAlreadyExistsError,
+    AssociateUserToTenantPhoneNumberAlreadyExistsError,
+    AssociateUserToTenantThirdPartyUserAlreadyExistsError,
+]:
+    response = await self.querier.send_post_request(
+        NormalisedURLPath(
+            f"{tenant_id or DEFAULT_TENANT_ID}/recipe/multitenancy/tenant/user"
+        ),
+        {
+            "userId": user_id,
+        },
+        user_context=user_context,
+    )
+
+    if response["status"] == "OK":
+        return AssociateUserToTenantOkResult(
+            was_already_associated=response["wasAlreadyAssociated"],
+        )
+    if response["status"] == AssociateUserToTenantUnknownUserIdError.status:
+        return AssociateUserToTenantUnknownUserIdError()
+    if response["status"] == AssociateUserToTenantEmailAlreadyExistsError.status:
+        return AssociateUserToTenantEmailAlreadyExistsError()
+    if (
+        response["status"]
+        == AssociateUserToTenantPhoneNumberAlreadyExistsError.status
+    ):
+        return AssociateUserToTenantPhoneNumberAlreadyExistsError()
+    if (
+        response["status"]
+        == AssociateUserToTenantThirdPartyUserAlreadyExistsError.status
+    ):
+        return AssociateUserToTenantThirdPartyUserAlreadyExistsError()
+
+    raise Exception("Should never come here")
+
+
+
+async def create_or_update_tenant(self, tenant_id: str, config: Optional[TenantConfig], user_context: Dict[str, Any]) ‑> CreateOrUpdateTenantOkResult +
+
+
+
+ +Expand source code + +
async def create_or_update_tenant(
+    self,
+    tenant_id: str,
+    config: Optional[TenantConfig],
+    user_context: Dict[str, Any],
+) -> CreateOrUpdateTenantOkResult:
+    response = await self.querier.send_put_request(
+        NormalisedURLPath("/recipe/multitenancy/tenant"),
+        {
+            "tenantId": tenant_id,
+            **(config.to_json() if config is not None else {}),
+        },
+        user_context=user_context,
+    )
+    return CreateOrUpdateTenantOkResult(
+        created_new=response["createdNew"],
+    )
+
+
+
+async def create_or_update_third_party_config(self, tenant_id: Optional[str], config: ProviderConfig, skip_validation: Optional[bool], user_context: Dict[str, Any]) ‑> CreateOrUpdateThirdPartyConfigOkResult +
+
+
+
+ +Expand source code + +
async def create_or_update_third_party_config(
+    self,
+    tenant_id: Optional[str],
+    config: ProviderConfig,
+    skip_validation: Optional[bool],
+    user_context: Dict[str, Any],
+) -> CreateOrUpdateThirdPartyConfigOkResult:
+    response = await self.querier.send_put_request(
+        NormalisedURLPath(
+            f"{tenant_id or DEFAULT_TENANT_ID}/recipe/multitenancy/config/thirdparty"
+        ),
+        {
+            "config": config.to_json(),
+            "skipValidation": skip_validation is True,
+        },
+        user_context=user_context,
+    )
+
+    return CreateOrUpdateThirdPartyConfigOkResult(
+        created_new=response["createdNew"],
+    )
+
+
+
+async def delete_tenant(self, tenant_id: str, user_context: Dict[str, Any]) ‑> DeleteTenantOkResult +
+
+
+
+ +Expand source code + +
async def delete_tenant(
+    self, tenant_id: str, user_context: Dict[str, Any]
+) -> DeleteTenantOkResult:
+    response = await self.querier.send_post_request(
+        NormalisedURLPath("/recipe/multitenancy/tenant/remove"),
+        {"tenantId": tenant_id},
+        user_context=user_context,
+    )
+    return DeleteTenantOkResult(
+        did_exist=response["didExist"],
+    )
+
+
+
+async def delete_third_party_config(self, tenant_id: Optional[str], third_party_id: str, user_context: Dict[str, Any]) ‑> DeleteThirdPartyConfigOkResult +
+
+
+
+ +Expand source code + +
async def delete_third_party_config(
+    self,
+    tenant_id: Optional[str],
+    third_party_id: str,
+    user_context: Dict[str, Any],
+) -> DeleteThirdPartyConfigOkResult:
+    response = await self.querier.send_post_request(
+        NormalisedURLPath(
+            f"{tenant_id or DEFAULT_TENANT_ID}/recipe/multitenancy/config/thirdparty/remove"
+        ),
+        {
+            "thirdPartyId": third_party_id,
+        },
+        user_context=user_context,
+    )
+
+    return DeleteThirdPartyConfigOkResult(
+        did_config_exist=response["didConfigExist"],
+    )
+
+
+
+async def dissociate_user_from_tenant(self, tenant_id: Optional[str], user_id: str, user_context: Dict[str, Any]) ‑> DisassociateUserFromTenantOkResult +
+
+
+
+ +Expand source code + +
async def dissociate_user_from_tenant(
+    self, tenant_id: Optional[str], user_id: str, user_context: Dict[str, Any]
+) -> DisassociateUserFromTenantOkResult:
+    response = await self.querier.send_post_request(
+        NormalisedURLPath(
+            f"{tenant_id or DEFAULT_TENANT_ID}/recipe/multitenancy/tenant/user/remove"
+        ),
+        {
+            "userId": user_id,
+        },
+        user_context=user_context,
+    )
+
+    return DisassociateUserFromTenantOkResult(
+        was_associated=response["wasAssociated"],
+    )
+
+
+
+async def get_tenant(self, tenant_id: Optional[str], user_context: Dict[str, Any]) ‑> Optional[GetTenantOkResult] +
+
+
+
+ +Expand source code + +
async def get_tenant(
+    self, tenant_id: Optional[str], user_context: Dict[str, Any]
+) -> Optional[GetTenantOkResult]:
+    res = await self.querier.send_get_request(
+        NormalisedURLPath(
+            f"{tenant_id or DEFAULT_TENANT_ID}/recipe/multitenancy/tenant"
+        ),
+        None,
+        user_context=user_context,
+    )
+
+    if res["status"] == "TENANT_NOT_FOUND_ERROR":
+        return None
+
+    tenant_config = parse_tenant_config(res)
+
+    return GetTenantOkResult(
+        emailpassword=tenant_config.emailpassword,
+        passwordless=tenant_config.passwordless,
+        third_party=tenant_config.third_party,
+        core_config=tenant_config.core_config,
+    )
+
+
+
+async def get_tenant_id(self, tenant_id_from_frontend: str, user_context: Dict[str, Any]) ‑> str +
+
+
+
+ +Expand source code + +
async def get_tenant_id(
+    self, tenant_id_from_frontend: str, user_context: Dict[str, Any]
+) -> str:
+    return tenant_id_from_frontend
+
+
+
+async def list_all_tenants(self, user_context: Dict[str, Any]) ‑> ListAllTenantsOkResult +
+
+
+
+ +Expand source code + +
async def list_all_tenants(
+    self, user_context: Dict[str, Any]
+) -> ListAllTenantsOkResult:
+    response = await self.querier.send_get_request(
+        NormalisedURLPath("/recipe/multitenancy/tenant/list"),
+        {},
+        user_context=user_context,
+    )
+
+    tenant_items: List[ListAllTenantsItem] = []
+
+    for tenant in response["tenants"]:
+        config = parse_tenant_config(tenant)
+        item = ListAllTenantsItem(
+            tenant["tenantId"],
+            config.emailpassword,
+            config.passwordless,
+            config.third_party,
+            config.core_config,
+        )
+        tenant_items.append(item)
+
+    return ListAllTenantsOkResult(
+        tenants=tenant_items,
+    )
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/multitenancy/syncio/index.html b/html/supertokens_python/recipe/multitenancy/syncio/index.html new file mode 100644 index 000000000..045db6e69 --- /dev/null +++ b/html/supertokens_python/recipe/multitenancy/syncio/index.html @@ -0,0 +1,367 @@ + + + + + + +supertokens_python.recipe.multitenancy.syncio API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.multitenancy.syncio

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+from typing import Any, Dict, Optional, TYPE_CHECKING
+
+from supertokens_python.async_to_sync_wrapper import sync
+
+if TYPE_CHECKING:
+    from ..interfaces import TenantConfig, ProviderConfig
+
+
+def create_or_update_tenant(
+    tenant_id: str,
+    config: Optional[TenantConfig],
+    user_context: Optional[Dict[str, Any]] = None,
+):
+    if user_context is None:
+        user_context = {}
+
+    from supertokens_python.recipe.multitenancy.asyncio import create_or_update_tenant
+
+    return sync(create_or_update_tenant(tenant_id, config, user_context))
+
+
+def delete_tenant(tenant_id: str, user_context: Optional[Dict[str, Any]] = None):
+    if user_context is None:
+        user_context = {}
+
+    from supertokens_python.recipe.multitenancy.asyncio import delete_tenant
+
+    return sync(delete_tenant(tenant_id, user_context))
+
+
+def get_tenant(tenant_id: str, user_context: Optional[Dict[str, Any]] = None):
+    if user_context is None:
+        user_context = {}
+
+    from supertokens_python.recipe.multitenancy.asyncio import get_tenant
+
+    return sync(get_tenant(tenant_id, user_context))
+
+
+def list_all_tenants(user_context: Optional[Dict[str, Any]] = None):
+    if user_context is None:
+        user_context = {}
+
+    from supertokens_python.recipe.multitenancy.asyncio import list_all_tenants
+
+    return sync(list_all_tenants(user_context))
+
+
+def create_or_update_third_party_config(
+    tenant_id: str,
+    config: ProviderConfig,
+    skip_validation: Optional[bool] = None,
+    user_context: Optional[Dict[str, Any]] = None,
+):
+    if user_context is None:
+        user_context = {}
+
+    from supertokens_python.recipe.multitenancy.asyncio import (
+        create_or_update_third_party_config,
+    )
+
+    return sync(
+        create_or_update_third_party_config(
+            tenant_id, config, skip_validation, user_context
+        )
+    )
+
+
+def delete_third_party_config(
+    tenant_id: str,
+    third_party_id: str,
+    user_context: Optional[Dict[str, Any]] = None,
+):
+    if user_context is None:
+        user_context = {}
+
+    from supertokens_python.recipe.multitenancy.asyncio import delete_third_party_config
+
+    return sync(delete_third_party_config(tenant_id, third_party_id, user_context))
+
+
+def associate_user_to_tenant(
+    tenant_id: str,
+    user_id: str,
+    user_context: Optional[Dict[str, Any]] = None,
+):
+    if user_context is None:
+        user_context = {}
+
+    from supertokens_python.recipe.multitenancy.asyncio import associate_user_to_tenant
+
+    return sync(associate_user_to_tenant(tenant_id, user_id, user_context))
+
+
+def dissociate_user_from_tenant(
+    tenant_id: str,
+    user_id: str,
+    user_context: Optional[Dict[str, Any]] = None,
+):
+    if user_context is None:
+        user_context = {}
+
+    from supertokens_python.recipe.multitenancy.asyncio import (
+        dissociate_user_from_tenant,
+    )
+
+    return sync(dissociate_user_from_tenant(tenant_id, user_id, user_context))
+
+
+
+
+
+
+
+

Functions

+
+
+def associate_user_to_tenant(tenant_id: str, user_id: str, user_context: Optional[Dict[str, Any]] = None) +
+
+
+
+ +Expand source code + +
def associate_user_to_tenant(
+    tenant_id: str,
+    user_id: str,
+    user_context: Optional[Dict[str, Any]] = None,
+):
+    if user_context is None:
+        user_context = {}
+
+    from supertokens_python.recipe.multitenancy.asyncio import associate_user_to_tenant
+
+    return sync(associate_user_to_tenant(tenant_id, user_id, user_context))
+
+
+
+def create_or_update_tenant(tenant_id: str, config: Optional[TenantConfig], user_context: Optional[Dict[str, Any]] = None) +
+
+
+
+ +Expand source code + +
def create_or_update_tenant(
+    tenant_id: str,
+    config: Optional[TenantConfig],
+    user_context: Optional[Dict[str, Any]] = None,
+):
+    if user_context is None:
+        user_context = {}
+
+    from supertokens_python.recipe.multitenancy.asyncio import create_or_update_tenant
+
+    return sync(create_or_update_tenant(tenant_id, config, user_context))
+
+
+
+def create_or_update_third_party_config(tenant_id: str, config: ProviderConfig, skip_validation: Optional[bool] = None, user_context: Optional[Dict[str, Any]] = None) +
+
+
+
+ +Expand source code + +
def create_or_update_third_party_config(
+    tenant_id: str,
+    config: ProviderConfig,
+    skip_validation: Optional[bool] = None,
+    user_context: Optional[Dict[str, Any]] = None,
+):
+    if user_context is None:
+        user_context = {}
+
+    from supertokens_python.recipe.multitenancy.asyncio import (
+        create_or_update_third_party_config,
+    )
+
+    return sync(
+        create_or_update_third_party_config(
+            tenant_id, config, skip_validation, user_context
+        )
+    )
+
+
+
+def delete_tenant(tenant_id: str, user_context: Optional[Dict[str, Any]] = None) +
+
+
+
+ +Expand source code + +
def delete_tenant(tenant_id: str, user_context: Optional[Dict[str, Any]] = None):
+    if user_context is None:
+        user_context = {}
+
+    from supertokens_python.recipe.multitenancy.asyncio import delete_tenant
+
+    return sync(delete_tenant(tenant_id, user_context))
+
+
+
+def delete_third_party_config(tenant_id: str, third_party_id: str, user_context: Optional[Dict[str, Any]] = None) +
+
+
+
+ +Expand source code + +
def delete_third_party_config(
+    tenant_id: str,
+    third_party_id: str,
+    user_context: Optional[Dict[str, Any]] = None,
+):
+    if user_context is None:
+        user_context = {}
+
+    from supertokens_python.recipe.multitenancy.asyncio import delete_third_party_config
+
+    return sync(delete_third_party_config(tenant_id, third_party_id, user_context))
+
+
+
+def dissociate_user_from_tenant(tenant_id: str, user_id: str, user_context: Optional[Dict[str, Any]] = None) +
+
+
+
+ +Expand source code + +
def dissociate_user_from_tenant(
+    tenant_id: str,
+    user_id: str,
+    user_context: Optional[Dict[str, Any]] = None,
+):
+    if user_context is None:
+        user_context = {}
+
+    from supertokens_python.recipe.multitenancy.asyncio import (
+        dissociate_user_from_tenant,
+    )
+
+    return sync(dissociate_user_from_tenant(tenant_id, user_id, user_context))
+
+
+
+def get_tenant(tenant_id: str, user_context: Optional[Dict[str, Any]] = None) +
+
+
+
+ +Expand source code + +
def get_tenant(tenant_id: str, user_context: Optional[Dict[str, Any]] = None):
+    if user_context is None:
+        user_context = {}
+
+    from supertokens_python.recipe.multitenancy.asyncio import get_tenant
+
+    return sync(get_tenant(tenant_id, user_context))
+
+
+
+def list_all_tenants(user_context: Optional[Dict[str, Any]] = None) +
+
+
+
+ +Expand source code + +
def list_all_tenants(user_context: Optional[Dict[str, Any]] = None):
+    if user_context is None:
+        user_context = {}
+
+    from supertokens_python.recipe.multitenancy.asyncio import list_all_tenants
+
+    return sync(list_all_tenants(user_context))
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/multitenancy/utils.html b/html/supertokens_python/recipe/multitenancy/utils.html new file mode 100644 index 000000000..1da5d4229 --- /dev/null +++ b/html/supertokens_python/recipe/multitenancy/utils.html @@ -0,0 +1,360 @@ + + + + + + +supertokens_python.recipe.multitenancy.utils API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.multitenancy.utils

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Awaitable, Optional, Callable
+from supertokens_python.exceptions import SuperTokensError
+from supertokens_python.framework import BaseRequest, BaseResponse
+from supertokens_python.utils import (
+    resolve,
+)
+
+if TYPE_CHECKING:
+    from typing import Union
+    from .interfaces import (
+        TypeGetAllowedDomainsForTenantId,
+        RecipeInterface,
+        APIInterface,
+    )
+
+
+class ErrorHandlers:
+    def __init__(
+        self,
+        on_tenant_does_not_exist: Callable[
+            [SuperTokensError, BaseRequest, BaseResponse],
+            Union[BaseResponse, Awaitable[BaseResponse]],
+        ],
+        on_recipe_disabled_for_tenant: Callable[
+            [SuperTokensError, BaseRequest, BaseResponse],
+            Union[BaseResponse, Awaitable[BaseResponse]],
+        ],
+    ):
+        self.__on_tenant_does_not_exist = on_tenant_does_not_exist
+        self.__on_recipe_disabled_for_tenant = on_recipe_disabled_for_tenant
+
+    async def on_tenant_does_not_exist(
+        self,
+        err: SuperTokensError,
+        request: BaseRequest,
+        response: BaseResponse,
+    ) -> BaseResponse:
+        return await resolve(self.__on_tenant_does_not_exist(err, request, response))
+
+    async def on_recipe_disabled_for_tenant(
+        self, err: SuperTokensError, request: BaseRequest, response: BaseResponse
+    ) -> BaseResponse:
+        return await resolve(
+            self.__on_recipe_disabled_for_tenant(err, request, response)
+        )
+
+
+class InputOverrideConfig:
+    def __init__(
+        self,
+        functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None,
+        apis: Union[Callable[[APIInterface], APIInterface], None] = None,
+    ):
+        self.functions = functions
+        self.apis = apis
+
+
+class OverrideConfig:
+    def __init__(
+        self,
+        functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None,
+        apis: Union[Callable[[APIInterface], APIInterface], None] = None,
+    ):
+        self.functions = functions
+        self.apis = apis
+
+
+class MultitenancyConfig:
+    def __init__(
+        self,
+        get_allowed_domains_for_tenant_id: Optional[TypeGetAllowedDomainsForTenantId],
+        override: OverrideConfig,
+    ):
+        self.get_allowed_domains_for_tenant_id = get_allowed_domains_for_tenant_id
+        self.override = override
+
+
+def validate_and_normalise_user_input(
+    get_allowed_domains_for_tenant_id: Optional[TypeGetAllowedDomainsForTenantId],
+    override: Union[InputOverrideConfig, None] = None,
+) -> MultitenancyConfig:
+    if override is not None and not isinstance(override, OverrideConfig):  # type: ignore
+        raise ValueError("override must be of type OverrideConfig or None")
+
+    if override is None:
+        override = InputOverrideConfig()
+
+    return MultitenancyConfig(
+        get_allowed_domains_for_tenant_id,
+        OverrideConfig(override.functions, override.apis),
+    )
+
+
+
+
+
+
+
+

Functions

+
+
+def validate_and_normalise_user_input(get_allowed_domains_for_tenant_id: Optional[TypeGetAllowedDomainsForTenantId], override: Union[InputOverrideConfig, None] = None) ‑> MultitenancyConfig +
+
+
+
+ +Expand source code + +
def validate_and_normalise_user_input(
+    get_allowed_domains_for_tenant_id: Optional[TypeGetAllowedDomainsForTenantId],
+    override: Union[InputOverrideConfig, None] = None,
+) -> MultitenancyConfig:
+    if override is not None and not isinstance(override, OverrideConfig):  # type: ignore
+        raise ValueError("override must be of type OverrideConfig or None")
+
+    if override is None:
+        override = InputOverrideConfig()
+
+    return MultitenancyConfig(
+        get_allowed_domains_for_tenant_id,
+        OverrideConfig(override.functions, override.apis),
+    )
+
+
+
+
+
+

Classes

+
+
+class ErrorHandlers +(on_tenant_does_not_exist: Callable[[SuperTokensError, BaseRequest, BaseResponse], Union[BaseResponse, Awaitable[BaseResponse]]], on_recipe_disabled_for_tenant: Callable[[SuperTokensError, BaseRequest, BaseResponse], Union[BaseResponse, Awaitable[BaseResponse]]]) +
+
+
+
+ +Expand source code + +
class ErrorHandlers:
+    def __init__(
+        self,
+        on_tenant_does_not_exist: Callable[
+            [SuperTokensError, BaseRequest, BaseResponse],
+            Union[BaseResponse, Awaitable[BaseResponse]],
+        ],
+        on_recipe_disabled_for_tenant: Callable[
+            [SuperTokensError, BaseRequest, BaseResponse],
+            Union[BaseResponse, Awaitable[BaseResponse]],
+        ],
+    ):
+        self.__on_tenant_does_not_exist = on_tenant_does_not_exist
+        self.__on_recipe_disabled_for_tenant = on_recipe_disabled_for_tenant
+
+    async def on_tenant_does_not_exist(
+        self,
+        err: SuperTokensError,
+        request: BaseRequest,
+        response: BaseResponse,
+    ) -> BaseResponse:
+        return await resolve(self.__on_tenant_does_not_exist(err, request, response))
+
+    async def on_recipe_disabled_for_tenant(
+        self, err: SuperTokensError, request: BaseRequest, response: BaseResponse
+    ) -> BaseResponse:
+        return await resolve(
+            self.__on_recipe_disabled_for_tenant(err, request, response)
+        )
+
+

Methods

+
+
+async def on_recipe_disabled_for_tenant(self, err: SuperTokensError, request: BaseRequest, response: BaseResponse) ‑> BaseResponse +
+
+
+
+ +Expand source code + +
async def on_recipe_disabled_for_tenant(
+    self, err: SuperTokensError, request: BaseRequest, response: BaseResponse
+) -> BaseResponse:
+    return await resolve(
+        self.__on_recipe_disabled_for_tenant(err, request, response)
+    )
+
+
+
+async def on_tenant_does_not_exist(self, err: SuperTokensError, request: BaseRequest, response: BaseResponse) ‑> BaseResponse +
+
+
+
+ +Expand source code + +
async def on_tenant_does_not_exist(
+    self,
+    err: SuperTokensError,
+    request: BaseRequest,
+    response: BaseResponse,
+) -> BaseResponse:
+    return await resolve(self.__on_tenant_does_not_exist(err, request, response))
+
+
+
+
+
+class InputOverrideConfig +(functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None, apis: Union[Callable[[APIInterface], APIInterface], None] = None) +
+
+
+
+ +Expand source code + +
class InputOverrideConfig:
+    def __init__(
+        self,
+        functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None,
+        apis: Union[Callable[[APIInterface], APIInterface], None] = None,
+    ):
+        self.functions = functions
+        self.apis = apis
+
+
+
+class MultitenancyConfig +(get_allowed_domains_for_tenant_id: Optional[TypeGetAllowedDomainsForTenantId], override: OverrideConfig) +
+
+
+
+ +Expand source code + +
class MultitenancyConfig:
+    def __init__(
+        self,
+        get_allowed_domains_for_tenant_id: Optional[TypeGetAllowedDomainsForTenantId],
+        override: OverrideConfig,
+    ):
+        self.get_allowed_domains_for_tenant_id = get_allowed_domains_for_tenant_id
+        self.override = override
+
+
+
+class OverrideConfig +(functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None, apis: Union[Callable[[APIInterface], APIInterface], None] = None) +
+
+
+
+ +Expand source code + +
class OverrideConfig:
+    def __init__(
+        self,
+        functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None,
+        apis: Union[Callable[[APIInterface], APIInterface], None] = None,
+    ):
+        self.functions = functions
+        self.apis = apis
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/openid/api/implementation.html b/html/supertokens_python/recipe/openid/api/implementation.html new file mode 100644 index 000000000..511b2ca8e --- /dev/null +++ b/html/supertokens_python/recipe/openid/api/implementation.html @@ -0,0 +1,157 @@ + + + + + + +supertokens_python.recipe.openid.api.implementation API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.openid.api.implementation

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from typing import Any, Dict
+
+from supertokens_python.recipe.openid.interfaces import (
+    APIInterface,
+    APIOptions,
+    OpenIdDiscoveryConfigurationGetResponse,
+)
+
+
+class APIImplementation(APIInterface):
+    async def open_id_discovery_configuration_get(
+        self, api_options: APIOptions, user_context: Dict[str, Any]
+    ) -> OpenIdDiscoveryConfigurationGetResponse:
+        response = (
+            await api_options.recipe_implementation.get_open_id_discovery_configuration(
+                user_context
+            )
+        )
+        return OpenIdDiscoveryConfigurationGetResponse(
+            response.issuer, response.jwks_uri
+        )
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class APIImplementation +
+
+
+
+ +Expand source code + +
class APIImplementation(APIInterface):
+    async def open_id_discovery_configuration_get(
+        self, api_options: APIOptions, user_context: Dict[str, Any]
+    ) -> OpenIdDiscoveryConfigurationGetResponse:
+        response = (
+            await api_options.recipe_implementation.get_open_id_discovery_configuration(
+                user_context
+            )
+        )
+        return OpenIdDiscoveryConfigurationGetResponse(
+            response.issuer, response.jwks_uri
+        )
+
+

Ancestors

+ +

Methods

+
+
+async def open_id_discovery_configuration_get(self, api_options: APIOptions, user_context: Dict[str, Any]) ‑> OpenIdDiscoveryConfigurationGetResponse +
+
+
+
+ +Expand source code + +
async def open_id_discovery_configuration_get(
+    self, api_options: APIOptions, user_context: Dict[str, Any]
+) -> OpenIdDiscoveryConfigurationGetResponse:
+    response = (
+        await api_options.recipe_implementation.get_open_id_discovery_configuration(
+            user_context
+        )
+    )
+    return OpenIdDiscoveryConfigurationGetResponse(
+        response.issuer, response.jwks_uri
+    )
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/openid/api/index.html b/html/supertokens_python/recipe/openid/api/index.html new file mode 100644 index 000000000..a874415f4 --- /dev/null +++ b/html/supertokens_python/recipe/openid/api/index.html @@ -0,0 +1,70 @@ + + + + + + +supertokens_python.recipe.openid.api API documentation + + + + + + + + + + + +
+ + +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/openid/api/open_id_discovery_configuration_get.html b/html/supertokens_python/recipe/openid/api/open_id_discovery_configuration_get.html new file mode 100644 index 000000000..c855f23a3 --- /dev/null +++ b/html/supertokens_python/recipe/openid/api/open_id_discovery_configuration_get.html @@ -0,0 +1,128 @@ + + + + + + +supertokens_python.recipe.openid.api.open_id_discovery_configuration_get API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.openid.api.open_id_discovery_configuration_get

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+from typing import Any, Dict
+from supertokens_python.recipe.openid.interfaces import APIInterface, APIOptions
+from supertokens_python.utils import send_200_response
+
+from ..interfaces import OpenIdDiscoveryConfigurationGetResponse
+
+
+async def open_id_discovery_configuration_get(
+    api_implementation: APIInterface,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+):
+    if api_implementation.disable_open_id_discovery_configuration_get:
+        return None
+
+    result = await api_implementation.open_id_discovery_configuration_get(
+        api_options, user_context
+    )
+
+    if isinstance(result, OpenIdDiscoveryConfigurationGetResponse):
+        api_options.response.set_header("Access-Control-Allow-Origin", "*")
+    return send_200_response(result.to_json(), api_options.response)
+
+
+
+
+
+
+
+

Functions

+
+
+async def open_id_discovery_configuration_get(api_implementation: APIInterface, api_options: APIOptions, user_context: Dict[str, Any]) +
+
+
+
+ +Expand source code + +
async def open_id_discovery_configuration_get(
+    api_implementation: APIInterface,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+):
+    if api_implementation.disable_open_id_discovery_configuration_get:
+        return None
+
+    result = await api_implementation.open_id_discovery_configuration_get(
+        api_options, user_context
+    )
+
+    if isinstance(result, OpenIdDiscoveryConfigurationGetResponse):
+        api_options.response.set_header("Access-Control-Allow-Origin", "*")
+    return send_200_response(result.to_json(), api_options.response)
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/openid/asyncio/index.html b/html/supertokens_python/recipe/openid/asyncio/index.html new file mode 100644 index 000000000..5e595dfd0 --- /dev/null +++ b/html/supertokens_python/recipe/openid/asyncio/index.html @@ -0,0 +1,188 @@ + + + + + + +supertokens_python.recipe.openid.asyncio API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.openid.asyncio

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from typing import Any, Dict, Union, Optional
+
+from supertokens_python.recipe.openid.interfaces import (
+    GetOpenIdDiscoveryConfigurationResult,
+)
+from supertokens_python.recipe.openid.recipe import OpenIdRecipe
+
+from ...jwt.interfaces import (
+    CreateJwtOkResult,
+    CreateJwtResultUnsupportedAlgorithm,
+    GetJWKSResult,
+)
+
+
+async def create_jwt(
+    payload: Optional[Dict[str, Any]] = None,
+    validity_seconds: Optional[int] = None,
+    use_static_signing_key: Optional[bool] = None,
+    user_context: Optional[Dict[str, Any]] = None,
+) -> Union[CreateJwtOkResult, CreateJwtResultUnsupportedAlgorithm]:
+    if user_context is None:
+        user_context = {}
+    if payload is None:
+        payload = {}
+
+    return await OpenIdRecipe.get_instance().recipe_implementation.create_jwt(
+        payload, validity_seconds, use_static_signing_key, user_context
+    )
+
+
+async def get_jwks(user_context: Optional[Dict[str, Any]] = None) -> GetJWKSResult:
+    if user_context is None:
+        user_context = {}
+    return await OpenIdRecipe.get_instance().recipe_implementation.get_jwks(
+        user_context
+    )
+
+
+async def get_open_id_discovery_configuration(
+    user_context: Optional[Dict[str, Any]] = None
+) -> GetOpenIdDiscoveryConfigurationResult:
+    if user_context is None:
+        user_context = {}
+    return await OpenIdRecipe.get_instance().recipe_implementation.get_open_id_discovery_configuration(
+        user_context
+    )
+
+
+
+
+
+
+
+

Functions

+
+
+async def create_jwt(payload: Optional[Dict[str, Any]] = None, validity_seconds: Optional[int] = None, use_static_signing_key: Optional[bool] = None, user_context: Optional[Dict[str, Any]] = None) ‑> Union[CreateJwtOkResultCreateJwtResultUnsupportedAlgorithm] +
+
+
+
+ +Expand source code + +
async def create_jwt(
+    payload: Optional[Dict[str, Any]] = None,
+    validity_seconds: Optional[int] = None,
+    use_static_signing_key: Optional[bool] = None,
+    user_context: Optional[Dict[str, Any]] = None,
+) -> Union[CreateJwtOkResult, CreateJwtResultUnsupportedAlgorithm]:
+    if user_context is None:
+        user_context = {}
+    if payload is None:
+        payload = {}
+
+    return await OpenIdRecipe.get_instance().recipe_implementation.create_jwt(
+        payload, validity_seconds, use_static_signing_key, user_context
+    )
+
+
+
+async def get_jwks(user_context: Optional[Dict[str, Any]] = None) ‑> GetJWKSResult +
+
+
+
+ +Expand source code + +
async def get_jwks(user_context: Optional[Dict[str, Any]] = None) -> GetJWKSResult:
+    if user_context is None:
+        user_context = {}
+    return await OpenIdRecipe.get_instance().recipe_implementation.get_jwks(
+        user_context
+    )
+
+
+
+async def get_open_id_discovery_configuration(user_context: Optional[Dict[str, Any]] = None) ‑> GetOpenIdDiscoveryConfigurationResult +
+
+
+
+ +Expand source code + +
async def get_open_id_discovery_configuration(
+    user_context: Optional[Dict[str, Any]] = None
+) -> GetOpenIdDiscoveryConfigurationResult:
+    if user_context is None:
+        user_context = {}
+    return await OpenIdRecipe.get_instance().recipe_implementation.get_open_id_discovery_configuration(
+        user_context
+    )
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/openid/constants.html b/html/supertokens_python/recipe/openid/constants.html new file mode 100644 index 000000000..215fdab42 --- /dev/null +++ b/html/supertokens_python/recipe/openid/constants.html @@ -0,0 +1,73 @@ + + + + + + +supertokens_python.recipe.openid.constants API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.openid.constants

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+GET_DISCOVERY_CONFIG_URL = "/.well-known/openid-configuration"
+
+
+
+
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/openid/exceptions.html b/html/supertokens_python/recipe/openid/exceptions.html new file mode 100644 index 000000000..fd7db557f --- /dev/null +++ b/html/supertokens_python/recipe/openid/exceptions.html @@ -0,0 +1,108 @@ + + + + + + +supertokens_python.recipe.openid.exceptions API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.openid.exceptions

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from supertokens_python.exceptions import SuperTokensError
+
+
+class SuperTokensOpenIdError(SuperTokensError):
+    pass
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class SuperTokensOpenIdError +(*args, **kwargs) +
+
+

Common base class for all non-exit exceptions.

+
+ +Expand source code + +
class SuperTokensOpenIdError(SuperTokensError):
+    pass
+
+

Ancestors

+ +
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/openid/index.html b/html/supertokens_python/recipe/openid/index.html new file mode 100644 index 000000000..84a318a3f --- /dev/null +++ b/html/supertokens_python/recipe/openid/index.html @@ -0,0 +1,167 @@ + + + + + + +supertokens_python.recipe.openid API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.openid

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Callable, Union
+
+from .recipe import OpenIdRecipe
+from .utils import InputOverrideConfig
+
+if TYPE_CHECKING:
+    from supertokens_python.supertokens import AppInfo
+
+    from ...recipe_module import RecipeModule
+
+
+def init(
+    jwt_validity_seconds: Union[int, None] = None,
+    issuer: Union[str, None] = None,
+    override: Union[InputOverrideConfig, None] = None,
+) -> Callable[[AppInfo], RecipeModule]:
+    return OpenIdRecipe.init(jwt_validity_seconds, issuer, override)
+
+
+
+

Sub-modules

+
+
supertokens_python.recipe.openid.api
+
+
+
+
supertokens_python.recipe.openid.asyncio
+
+
+
+
supertokens_python.recipe.openid.constants
+
+
+
+
supertokens_python.recipe.openid.exceptions
+
+
+
+
supertokens_python.recipe.openid.interfaces
+
+
+
+
supertokens_python.recipe.openid.recipe
+
+
+
+
supertokens_python.recipe.openid.recipe_implementation
+
+
+
+
supertokens_python.recipe.openid.syncio
+
+
+
+
supertokens_python.recipe.openid.utils
+
+
+
+
+
+
+
+
+

Functions

+
+
+def init(jwt_validity_seconds: Union[int, None] = None, issuer: Union[str, None] = None, override: Union[InputOverrideConfig, None] = None) ‑> Callable[[AppInfo], RecipeModule] +
+
+
+
+ +Expand source code + +
def init(
+    jwt_validity_seconds: Union[int, None] = None,
+    issuer: Union[str, None] = None,
+    override: Union[InputOverrideConfig, None] = None,
+) -> Callable[[AppInfo], RecipeModule]:
+    return OpenIdRecipe.init(jwt_validity_seconds, issuer, override)
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/openid/interfaces.html b/html/supertokens_python/recipe/openid/interfaces.html new file mode 100644 index 000000000..dfa934768 --- /dev/null +++ b/html/supertokens_python/recipe/openid/interfaces.html @@ -0,0 +1,417 @@ + + + + + + +supertokens_python.recipe.openid.interfaces API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.openid.interfaces

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from abc import ABC, abstractmethod
+from typing import Any, Dict, Union, Optional
+
+from supertokens_python.framework import BaseRequest, BaseResponse
+from supertokens_python.recipe.jwt.interfaces import (
+    CreateJwtOkResult,
+    CreateJwtResultUnsupportedAlgorithm,
+    GetJWKSResult,
+)
+from supertokens_python.types import APIResponse, GeneralErrorResponse
+
+from .utils import OpenIdConfig
+
+
+class GetOpenIdDiscoveryConfigurationResult:
+    def __init__(self, issuer: str, jwks_uri: str):
+        self.issuer = issuer
+        self.jwks_uri = jwks_uri
+
+
+class RecipeInterface(ABC):
+    def __init__(self):
+        pass
+
+    @abstractmethod
+    async def create_jwt(
+        self,
+        payload: Dict[str, Any],
+        validity_seconds: Optional[int],
+        use_static_signing_key: Optional[bool],
+        user_context: Dict[str, Any],
+    ) -> Union[CreateJwtOkResult, CreateJwtResultUnsupportedAlgorithm]:
+        pass
+
+    @abstractmethod
+    async def get_jwks(self, user_context: Dict[str, Any]) -> GetJWKSResult:
+        pass
+
+    @abstractmethod
+    async def get_open_id_discovery_configuration(
+        self, user_context: Dict[str, Any]
+    ) -> GetOpenIdDiscoveryConfigurationResult:
+        pass
+
+
+class APIOptions:
+    def __init__(
+        self,
+        request: BaseRequest,
+        response: BaseResponse,
+        recipe_id: str,
+        config: OpenIdConfig,
+        recipe_implementation: RecipeInterface,
+    ):
+        self.request = request
+        self.response = response
+        self.recipe_id = recipe_id
+        self.config = config
+        self.recipe_implementation = recipe_implementation
+
+
+class OpenIdDiscoveryConfigurationGetResponse(APIResponse):
+    status: str = "OK"
+
+    def __init__(self, issuer: str, jwks_uri: str):
+        self.issuer = issuer
+        self.jwks_uri = jwks_uri
+
+    def to_json(self):
+        return {"status": self.status, "issuer": self.issuer, "jwks_uri": self.jwks_uri}
+
+
+class APIInterface:
+    def __init__(self):
+        self.disable_open_id_discovery_configuration_get = False
+
+    @abstractmethod
+    async def open_id_discovery_configuration_get(
+        self, api_options: APIOptions, user_context: Dict[str, Any]
+    ) -> Union[OpenIdDiscoveryConfigurationGetResponse, GeneralErrorResponse]:
+        pass
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class APIInterface +
+
+
+
+ +Expand source code + +
class APIInterface:
+    def __init__(self):
+        self.disable_open_id_discovery_configuration_get = False
+
+    @abstractmethod
+    async def open_id_discovery_configuration_get(
+        self, api_options: APIOptions, user_context: Dict[str, Any]
+    ) -> Union[OpenIdDiscoveryConfigurationGetResponse, GeneralErrorResponse]:
+        pass
+
+

Subclasses

+ +

Methods

+
+
+async def open_id_discovery_configuration_get(self, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[OpenIdDiscoveryConfigurationGetResponseGeneralErrorResponse] +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def open_id_discovery_configuration_get(
+    self, api_options: APIOptions, user_context: Dict[str, Any]
+) -> Union[OpenIdDiscoveryConfigurationGetResponse, GeneralErrorResponse]:
+    pass
+
+
+
+
+
+class APIOptions +(request: BaseRequest, response: BaseResponse, recipe_id: str, config: OpenIdConfig, recipe_implementation: RecipeInterface) +
+
+
+
+ +Expand source code + +
class APIOptions:
+    def __init__(
+        self,
+        request: BaseRequest,
+        response: BaseResponse,
+        recipe_id: str,
+        config: OpenIdConfig,
+        recipe_implementation: RecipeInterface,
+    ):
+        self.request = request
+        self.response = response
+        self.recipe_id = recipe_id
+        self.config = config
+        self.recipe_implementation = recipe_implementation
+
+
+
+class GetOpenIdDiscoveryConfigurationResult +(issuer: str, jwks_uri: str) +
+
+
+
+ +Expand source code + +
class GetOpenIdDiscoveryConfigurationResult:
+    def __init__(self, issuer: str, jwks_uri: str):
+        self.issuer = issuer
+        self.jwks_uri = jwks_uri
+
+
+
+class OpenIdDiscoveryConfigurationGetResponse +(issuer: str, jwks_uri: str) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class OpenIdDiscoveryConfigurationGetResponse(APIResponse):
+    status: str = "OK"
+
+    def __init__(self, issuer: str, jwks_uri: str):
+        self.issuer = issuer
+        self.jwks_uri = jwks_uri
+
+    def to_json(self):
+        return {"status": self.status, "issuer": self.issuer, "jwks_uri": self.jwks_uri}
+
+

Ancestors

+ +

Class variables

+
+
var status : str
+
+
+
+
+

Methods

+
+
+def to_json(self) +
+
+
+
+ +Expand source code + +
def to_json(self):
+    return {"status": self.status, "issuer": self.issuer, "jwks_uri": self.jwks_uri}
+
+
+
+
+
+class RecipeInterface +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class RecipeInterface(ABC):
+    def __init__(self):
+        pass
+
+    @abstractmethod
+    async def create_jwt(
+        self,
+        payload: Dict[str, Any],
+        validity_seconds: Optional[int],
+        use_static_signing_key: Optional[bool],
+        user_context: Dict[str, Any],
+    ) -> Union[CreateJwtOkResult, CreateJwtResultUnsupportedAlgorithm]:
+        pass
+
+    @abstractmethod
+    async def get_jwks(self, user_context: Dict[str, Any]) -> GetJWKSResult:
+        pass
+
+    @abstractmethod
+    async def get_open_id_discovery_configuration(
+        self, user_context: Dict[str, Any]
+    ) -> GetOpenIdDiscoveryConfigurationResult:
+        pass
+
+

Ancestors

+
    +
  • abc.ABC
  • +
+

Subclasses

+ +

Methods

+
+
+async def create_jwt(self, payload: Dict[str, Any], validity_seconds: Optional[int], use_static_signing_key: Optional[bool], user_context: Dict[str, Any]) ‑> Union[CreateJwtOkResultCreateJwtResultUnsupportedAlgorithm] +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def create_jwt(
+    self,
+    payload: Dict[str, Any],
+    validity_seconds: Optional[int],
+    use_static_signing_key: Optional[bool],
+    user_context: Dict[str, Any],
+) -> Union[CreateJwtOkResult, CreateJwtResultUnsupportedAlgorithm]:
+    pass
+
+
+
+async def get_jwks(self, user_context: Dict[str, Any]) ‑> GetJWKSResult +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def get_jwks(self, user_context: Dict[str, Any]) -> GetJWKSResult:
+    pass
+
+
+
+async def get_open_id_discovery_configuration(self, user_context: Dict[str, Any]) ‑> GetOpenIdDiscoveryConfigurationResult +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def get_open_id_discovery_configuration(
+    self, user_context: Dict[str, Any]
+) -> GetOpenIdDiscoveryConfigurationResult:
+    pass
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/openid/recipe.html b/html/supertokens_python/recipe/openid/recipe.html new file mode 100644 index 000000000..f75bfc1a7 --- /dev/null +++ b/html/supertokens_python/recipe/openid/recipe.html @@ -0,0 +1,595 @@ + + + + + + +supertokens_python.recipe.openid.recipe API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.openid.recipe

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from os import environ
+from typing import TYPE_CHECKING, List, Union, Any, Dict
+
+from supertokens_python.querier import Querier
+
+from .api.implementation import APIImplementation
+from .api.open_id_discovery_configuration_get import open_id_discovery_configuration_get
+from .constants import GET_DISCOVERY_CONFIG_URL
+from .exceptions import SuperTokensOpenIdError
+from .interfaces import APIOptions
+from .recipe_implementation import RecipeImplementation
+from .utils import InputOverrideConfig, validate_and_normalise_user_input
+
+if TYPE_CHECKING:
+    from supertokens_python.framework.request import BaseRequest
+    from supertokens_python.framework.response import BaseResponse
+    from supertokens_python.supertokens import AppInfo
+
+from supertokens_python.exceptions import SuperTokensError, raise_general_exception
+from supertokens_python.normalised_url_path import NormalisedURLPath
+from supertokens_python.recipe_module import APIHandled, RecipeModule
+
+
+class OpenIdRecipe(RecipeModule):
+    recipe_id = "openid"
+    __instance = None
+
+    def __init__(
+        self,
+        recipe_id: str,
+        app_info: AppInfo,
+        jwt_validity_seconds: Union[int, None] = None,
+        issuer: Union[str, None] = None,
+        override: Union[InputOverrideConfig, None] = None,
+    ):
+        from supertokens_python.recipe.jwt import JWTRecipe
+
+        super().__init__(recipe_id, app_info)
+        self.config = validate_and_normalise_user_input(app_info, issuer, override)
+        jwt_feature = None
+        if override is not None:
+            jwt_feature = override.jwt_feature
+        self.jwt_recipe = JWTRecipe(
+            recipe_id, app_info, jwt_validity_seconds, jwt_feature
+        )
+
+        recipe_implementation = RecipeImplementation(
+            Querier.get_instance(recipe_id),
+            self.config,
+            app_info,
+            self.jwt_recipe.recipe_implementation,
+        )
+        self.recipe_implementation = (
+            recipe_implementation
+            if self.config.override.functions is None
+            else self.config.override.functions(recipe_implementation)
+        )
+        api_implementation = APIImplementation()
+        self.api_implementation = (
+            api_implementation
+            if self.config.override.apis is None
+            else self.config.override.apis(api_implementation)
+        )
+
+    def get_apis_handled(self) -> List[APIHandled]:
+        return [
+            APIHandled(
+                method="get",
+                path_without_api_base_path=NormalisedURLPath(GET_DISCOVERY_CONFIG_URL),
+                request_id=GET_DISCOVERY_CONFIG_URL,
+                disabled=self.api_implementation.disable_open_id_discovery_configuration_get,
+            )
+        ] + self.jwt_recipe.get_apis_handled()
+
+    async def handle_api_request(
+        self,
+        request_id: str,
+        tenant_id: str,
+        request: BaseRequest,
+        path: NormalisedURLPath,
+        method: str,
+        response: BaseResponse,
+        user_context: Dict[str, Any],
+    ):
+        options = APIOptions(
+            request,
+            response,
+            self.get_recipe_id(),
+            self.config,
+            self.recipe_implementation,
+        )
+
+        if request_id == GET_DISCOVERY_CONFIG_URL:
+            return await open_id_discovery_configuration_get(
+                self.api_implementation, options, user_context
+            )
+        return await self.jwt_recipe.handle_api_request(
+            request_id, tenant_id, request, path, method, response, user_context
+        )
+
+    async def handle_error(
+        self,
+        request: BaseRequest,
+        err: SuperTokensError,
+        response: BaseResponse,
+        user_context: Dict[str, Any],
+    ):
+        if isinstance(err, SuperTokensOpenIdError):
+            raise err
+        return await self.jwt_recipe.handle_error(request, err, response, user_context)
+
+    def get_all_cors_headers(self) -> List[str]:
+        return self.jwt_recipe.get_all_cors_headers()
+
+    def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
+        return isinstance(err, SuperTokensError) and (
+            isinstance(err, SuperTokensOpenIdError)
+            or self.jwt_recipe.is_error_from_this_recipe_based_on_instance(err)
+        )
+
+    @staticmethod
+    def init(
+        jwt_validity_seconds: Union[int, None] = None,
+        issuer: Union[str, None] = None,
+        override: Union[InputOverrideConfig, None] = None,
+    ):
+        def func(app_info: AppInfo):
+            if OpenIdRecipe.__instance is None:
+                OpenIdRecipe.__instance = OpenIdRecipe(
+                    OpenIdRecipe.recipe_id,
+                    app_info,
+                    jwt_validity_seconds,
+                    issuer,
+                    override,
+                )
+                return OpenIdRecipe.__instance
+            raise_general_exception(
+                "OpenId recipe has already been initialised. Please check your code for bugs."
+            )
+
+        return func
+
+    @staticmethod
+    def get_instance() -> OpenIdRecipe:
+        if OpenIdRecipe.__instance is not None:
+            return OpenIdRecipe.__instance
+        raise_general_exception(
+            "Initialisation not done. Did you forget to call the SuperTokens.init function?"
+        )
+
+    @staticmethod
+    def reset():
+        if ("SUPERTOKENS_ENV" not in environ) or (
+            environ["SUPERTOKENS_ENV"] != "testing"
+        ):
+            raise_general_exception("calling testing function in non testing env")
+        OpenIdRecipe.__instance = None
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class OpenIdRecipe +(recipe_id: str, app_info: AppInfo, jwt_validity_seconds: Union[int, None] = None, issuer: Union[str, None] = None, override: Union[InputOverrideConfig, None] = None) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class OpenIdRecipe(RecipeModule):
+    recipe_id = "openid"
+    __instance = None
+
+    def __init__(
+        self,
+        recipe_id: str,
+        app_info: AppInfo,
+        jwt_validity_seconds: Union[int, None] = None,
+        issuer: Union[str, None] = None,
+        override: Union[InputOverrideConfig, None] = None,
+    ):
+        from supertokens_python.recipe.jwt import JWTRecipe
+
+        super().__init__(recipe_id, app_info)
+        self.config = validate_and_normalise_user_input(app_info, issuer, override)
+        jwt_feature = None
+        if override is not None:
+            jwt_feature = override.jwt_feature
+        self.jwt_recipe = JWTRecipe(
+            recipe_id, app_info, jwt_validity_seconds, jwt_feature
+        )
+
+        recipe_implementation = RecipeImplementation(
+            Querier.get_instance(recipe_id),
+            self.config,
+            app_info,
+            self.jwt_recipe.recipe_implementation,
+        )
+        self.recipe_implementation = (
+            recipe_implementation
+            if self.config.override.functions is None
+            else self.config.override.functions(recipe_implementation)
+        )
+        api_implementation = APIImplementation()
+        self.api_implementation = (
+            api_implementation
+            if self.config.override.apis is None
+            else self.config.override.apis(api_implementation)
+        )
+
+    def get_apis_handled(self) -> List[APIHandled]:
+        return [
+            APIHandled(
+                method="get",
+                path_without_api_base_path=NormalisedURLPath(GET_DISCOVERY_CONFIG_URL),
+                request_id=GET_DISCOVERY_CONFIG_URL,
+                disabled=self.api_implementation.disable_open_id_discovery_configuration_get,
+            )
+        ] + self.jwt_recipe.get_apis_handled()
+
+    async def handle_api_request(
+        self,
+        request_id: str,
+        tenant_id: str,
+        request: BaseRequest,
+        path: NormalisedURLPath,
+        method: str,
+        response: BaseResponse,
+        user_context: Dict[str, Any],
+    ):
+        options = APIOptions(
+            request,
+            response,
+            self.get_recipe_id(),
+            self.config,
+            self.recipe_implementation,
+        )
+
+        if request_id == GET_DISCOVERY_CONFIG_URL:
+            return await open_id_discovery_configuration_get(
+                self.api_implementation, options, user_context
+            )
+        return await self.jwt_recipe.handle_api_request(
+            request_id, tenant_id, request, path, method, response, user_context
+        )
+
+    async def handle_error(
+        self,
+        request: BaseRequest,
+        err: SuperTokensError,
+        response: BaseResponse,
+        user_context: Dict[str, Any],
+    ):
+        if isinstance(err, SuperTokensOpenIdError):
+            raise err
+        return await self.jwt_recipe.handle_error(request, err, response, user_context)
+
+    def get_all_cors_headers(self) -> List[str]:
+        return self.jwt_recipe.get_all_cors_headers()
+
+    def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
+        return isinstance(err, SuperTokensError) and (
+            isinstance(err, SuperTokensOpenIdError)
+            or self.jwt_recipe.is_error_from_this_recipe_based_on_instance(err)
+        )
+
+    @staticmethod
+    def init(
+        jwt_validity_seconds: Union[int, None] = None,
+        issuer: Union[str, None] = None,
+        override: Union[InputOverrideConfig, None] = None,
+    ):
+        def func(app_info: AppInfo):
+            if OpenIdRecipe.__instance is None:
+                OpenIdRecipe.__instance = OpenIdRecipe(
+                    OpenIdRecipe.recipe_id,
+                    app_info,
+                    jwt_validity_seconds,
+                    issuer,
+                    override,
+                )
+                return OpenIdRecipe.__instance
+            raise_general_exception(
+                "OpenId recipe has already been initialised. Please check your code for bugs."
+            )
+
+        return func
+
+    @staticmethod
+    def get_instance() -> OpenIdRecipe:
+        if OpenIdRecipe.__instance is not None:
+            return OpenIdRecipe.__instance
+        raise_general_exception(
+            "Initialisation not done. Did you forget to call the SuperTokens.init function?"
+        )
+
+    @staticmethod
+    def reset():
+        if ("SUPERTOKENS_ENV" not in environ) or (
+            environ["SUPERTOKENS_ENV"] != "testing"
+        ):
+            raise_general_exception("calling testing function in non testing env")
+        OpenIdRecipe.__instance = None
+
+

Ancestors

+ +

Class variables

+
+
var get_tenant_id : Optional[Callable[[str, Dict[str, Any]], Awaitable[str]]]
+
+
+
+
var recipe_id
+
+
+
+
+

Static methods

+
+
+def get_instance() ‑> OpenIdRecipe +
+
+
+
+ +Expand source code + +
@staticmethod
+def get_instance() -> OpenIdRecipe:
+    if OpenIdRecipe.__instance is not None:
+        return OpenIdRecipe.__instance
+    raise_general_exception(
+        "Initialisation not done. Did you forget to call the SuperTokens.init function?"
+    )
+
+
+
+def init(jwt_validity_seconds: Union[int, None] = None, issuer: Union[str, None] = None, override: Union[InputOverrideConfig, None] = None) +
+
+
+
+ +Expand source code + +
@staticmethod
+def init(
+    jwt_validity_seconds: Union[int, None] = None,
+    issuer: Union[str, None] = None,
+    override: Union[InputOverrideConfig, None] = None,
+):
+    def func(app_info: AppInfo):
+        if OpenIdRecipe.__instance is None:
+            OpenIdRecipe.__instance = OpenIdRecipe(
+                OpenIdRecipe.recipe_id,
+                app_info,
+                jwt_validity_seconds,
+                issuer,
+                override,
+            )
+            return OpenIdRecipe.__instance
+        raise_general_exception(
+            "OpenId recipe has already been initialised. Please check your code for bugs."
+        )
+
+    return func
+
+
+
+def reset() +
+
+
+
+ +Expand source code + +
@staticmethod
+def reset():
+    if ("SUPERTOKENS_ENV" not in environ) or (
+        environ["SUPERTOKENS_ENV"] != "testing"
+    ):
+        raise_general_exception("calling testing function in non testing env")
+    OpenIdRecipe.__instance = None
+
+
+
+

Methods

+
+
+def get_all_cors_headers(self) ‑> List[str] +
+
+
+
+ +Expand source code + +
def get_all_cors_headers(self) -> List[str]:
+    return self.jwt_recipe.get_all_cors_headers()
+
+
+
+def get_apis_handled(self) ‑> List[APIHandled] +
+
+
+
+ +Expand source code + +
def get_apis_handled(self) -> List[APIHandled]:
+    return [
+        APIHandled(
+            method="get",
+            path_without_api_base_path=NormalisedURLPath(GET_DISCOVERY_CONFIG_URL),
+            request_id=GET_DISCOVERY_CONFIG_URL,
+            disabled=self.api_implementation.disable_open_id_discovery_configuration_get,
+        )
+    ] + self.jwt_recipe.get_apis_handled()
+
+
+
+async def handle_api_request(self, request_id: str, tenant_id: str, request: BaseRequest, path: NormalisedURLPath, method: str, response: BaseResponse, user_context: Dict[str, Any]) +
+
+
+
+ +Expand source code + +
async def handle_api_request(
+    self,
+    request_id: str,
+    tenant_id: str,
+    request: BaseRequest,
+    path: NormalisedURLPath,
+    method: str,
+    response: BaseResponse,
+    user_context: Dict[str, Any],
+):
+    options = APIOptions(
+        request,
+        response,
+        self.get_recipe_id(),
+        self.config,
+        self.recipe_implementation,
+    )
+
+    if request_id == GET_DISCOVERY_CONFIG_URL:
+        return await open_id_discovery_configuration_get(
+            self.api_implementation, options, user_context
+        )
+    return await self.jwt_recipe.handle_api_request(
+        request_id, tenant_id, request, path, method, response, user_context
+    )
+
+
+
+async def handle_error(self, request: BaseRequest, err: SuperTokensError, response: BaseResponse, user_context: Dict[str, Any]) +
+
+
+
+ +Expand source code + +
async def handle_error(
+    self,
+    request: BaseRequest,
+    err: SuperTokensError,
+    response: BaseResponse,
+    user_context: Dict[str, Any],
+):
+    if isinstance(err, SuperTokensOpenIdError):
+        raise err
+    return await self.jwt_recipe.handle_error(request, err, response, user_context)
+
+
+
+def is_error_from_this_recipe_based_on_instance(self, err: Exception) ‑> bool +
+
+
+
+ +Expand source code + +
def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
+    return isinstance(err, SuperTokensError) and (
+        isinstance(err, SuperTokensOpenIdError)
+        or self.jwt_recipe.is_error_from_this_recipe_based_on_instance(err)
+    )
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/openid/recipe_implementation.html b/html/supertokens_python/recipe/openid/recipe_implementation.html new file mode 100644 index 000000000..614da5d80 --- /dev/null +++ b/html/supertokens_python/recipe/openid/recipe_implementation.html @@ -0,0 +1,295 @@ + + + + + + +supertokens_python.recipe.openid.recipe_implementation API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.openid.recipe_implementation

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Any, Dict, Union, Optional
+
+from supertokens_python.querier import Querier
+
+if TYPE_CHECKING:
+    from .utils import OpenIdConfig
+    from .interfaces import CreateJwtOkResult, CreateJwtResultUnsupportedAlgorithm
+    from supertokens_python.supertokens import AppInfo
+
+from supertokens_python.normalised_url_path import NormalisedURLPath
+from supertokens_python.recipe.jwt.constants import GET_JWKS_API
+from supertokens_python.recipe.jwt.interfaces import (
+    RecipeInterface as JWTRecipeInterface,
+)
+
+from .interfaces import (
+    GetJWKSResult,
+    GetOpenIdDiscoveryConfigurationResult,
+    RecipeInterface,
+)
+
+
+class RecipeImplementation(RecipeInterface):
+    async def get_open_id_discovery_configuration(
+        self, user_context: Dict[str, Any]
+    ) -> GetOpenIdDiscoveryConfigurationResult:
+        issuer = (
+            self.config.issuer_domain.get_as_string_dangerous()
+            + self.config.issuer_path.get_as_string_dangerous()
+        )
+
+        jwks_uri = (
+            self.config.issuer_domain.get_as_string_dangerous()
+            + self.config.issuer_path.append(
+                NormalisedURLPath(GET_JWKS_API)
+            ).get_as_string_dangerous()
+        )
+
+        return GetOpenIdDiscoveryConfigurationResult(issuer, jwks_uri)
+
+    def __init__(
+        self,
+        querier: Querier,
+        config: OpenIdConfig,
+        app_info: AppInfo,
+        jwt_recipe_implementation: JWTRecipeInterface,
+    ):
+        super().__init__()
+        self.querier = querier
+        self.config = config
+        self.app_info = app_info
+        self.jwt_recipe_implementation = jwt_recipe_implementation
+
+    async def create_jwt(
+        self,
+        payload: Dict[str, Any],
+        validity_seconds: Optional[int],
+        use_static_signing_key: Optional[bool],
+        user_context: Dict[str, Any],
+    ) -> Union[CreateJwtOkResult, CreateJwtResultUnsupportedAlgorithm]:
+        issuer = (
+            self.config.issuer_domain.get_as_string_dangerous()
+            + self.config.issuer_path.get_as_string_dangerous()
+        )
+        payload = {"iss": issuer, **payload}
+        return await self.jwt_recipe_implementation.create_jwt(
+            payload, validity_seconds, use_static_signing_key, user_context
+        )
+
+    async def get_jwks(self, user_context: Dict[str, Any]) -> GetJWKSResult:
+        return await self.jwt_recipe_implementation.get_jwks(user_context)
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class RecipeImplementation +(querier: Querier, config: OpenIdConfig, app_info: AppInfo, jwt_recipe_implementation: JWTRecipeInterface) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class RecipeImplementation(RecipeInterface):
+    async def get_open_id_discovery_configuration(
+        self, user_context: Dict[str, Any]
+    ) -> GetOpenIdDiscoveryConfigurationResult:
+        issuer = (
+            self.config.issuer_domain.get_as_string_dangerous()
+            + self.config.issuer_path.get_as_string_dangerous()
+        )
+
+        jwks_uri = (
+            self.config.issuer_domain.get_as_string_dangerous()
+            + self.config.issuer_path.append(
+                NormalisedURLPath(GET_JWKS_API)
+            ).get_as_string_dangerous()
+        )
+
+        return GetOpenIdDiscoveryConfigurationResult(issuer, jwks_uri)
+
+    def __init__(
+        self,
+        querier: Querier,
+        config: OpenIdConfig,
+        app_info: AppInfo,
+        jwt_recipe_implementation: JWTRecipeInterface,
+    ):
+        super().__init__()
+        self.querier = querier
+        self.config = config
+        self.app_info = app_info
+        self.jwt_recipe_implementation = jwt_recipe_implementation
+
+    async def create_jwt(
+        self,
+        payload: Dict[str, Any],
+        validity_seconds: Optional[int],
+        use_static_signing_key: Optional[bool],
+        user_context: Dict[str, Any],
+    ) -> Union[CreateJwtOkResult, CreateJwtResultUnsupportedAlgorithm]:
+        issuer = (
+            self.config.issuer_domain.get_as_string_dangerous()
+            + self.config.issuer_path.get_as_string_dangerous()
+        )
+        payload = {"iss": issuer, **payload}
+        return await self.jwt_recipe_implementation.create_jwt(
+            payload, validity_seconds, use_static_signing_key, user_context
+        )
+
+    async def get_jwks(self, user_context: Dict[str, Any]) -> GetJWKSResult:
+        return await self.jwt_recipe_implementation.get_jwks(user_context)
+
+

Ancestors

+ +

Methods

+
+
+async def create_jwt(self, payload: Dict[str, Any], validity_seconds: Optional[int], use_static_signing_key: Optional[bool], user_context: Dict[str, Any]) ‑> Union[CreateJwtOkResult, CreateJwtResultUnsupportedAlgorithm] +
+
+
+
+ +Expand source code + +
async def create_jwt(
+    self,
+    payload: Dict[str, Any],
+    validity_seconds: Optional[int],
+    use_static_signing_key: Optional[bool],
+    user_context: Dict[str, Any],
+) -> Union[CreateJwtOkResult, CreateJwtResultUnsupportedAlgorithm]:
+    issuer = (
+        self.config.issuer_domain.get_as_string_dangerous()
+        + self.config.issuer_path.get_as_string_dangerous()
+    )
+    payload = {"iss": issuer, **payload}
+    return await self.jwt_recipe_implementation.create_jwt(
+        payload, validity_seconds, use_static_signing_key, user_context
+    )
+
+
+
+async def get_jwks(self, user_context: Dict[str, Any]) ‑> GetJWKSResult +
+
+
+
+ +Expand source code + +
async def get_jwks(self, user_context: Dict[str, Any]) -> GetJWKSResult:
+    return await self.jwt_recipe_implementation.get_jwks(user_context)
+
+
+
+async def get_open_id_discovery_configuration(self, user_context: Dict[str, Any]) ‑> GetOpenIdDiscoveryConfigurationResult +
+
+
+
+ +Expand source code + +
async def get_open_id_discovery_configuration(
+    self, user_context: Dict[str, Any]
+) -> GetOpenIdDiscoveryConfigurationResult:
+    issuer = (
+        self.config.issuer_domain.get_as_string_dangerous()
+        + self.config.issuer_path.get_as_string_dangerous()
+    )
+
+    jwks_uri = (
+        self.config.issuer_domain.get_as_string_dangerous()
+        + self.config.issuer_path.append(
+            NormalisedURLPath(GET_JWKS_API)
+        ).get_as_string_dangerous()
+    )
+
+    return GetOpenIdDiscoveryConfigurationResult(issuer, jwks_uri)
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/openid/syncio/index.html b/html/supertokens_python/recipe/openid/syncio/index.html new file mode 100644 index 000000000..d35d2a0da --- /dev/null +++ b/html/supertokens_python/recipe/openid/syncio/index.html @@ -0,0 +1,167 @@ + + + + + + +supertokens_python.recipe.openid.syncio API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.openid.syncio

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from typing import Any, Dict, Union, Optional
+
+from supertokens_python.async_to_sync_wrapper import sync
+from supertokens_python.recipe.openid import asyncio
+from supertokens_python.recipe.openid.interfaces import (
+    GetOpenIdDiscoveryConfigurationResult,
+)
+
+from ...jwt.interfaces import (
+    CreateJwtOkResult,
+    CreateJwtResultUnsupportedAlgorithm,
+    GetJWKSResult,
+)
+
+
+def create_jwt(
+    payload: Optional[Dict[str, Any]] = None,
+    validity_seconds: Optional[int] = None,
+    use_static_signing_key: Optional[bool] = None,
+    user_context: Optional[Dict[str, Any]] = None,
+) -> Union[CreateJwtOkResult, CreateJwtResultUnsupportedAlgorithm]:
+    return sync(
+        asyncio.create_jwt(
+            payload, validity_seconds, use_static_signing_key, user_context
+        )
+    )
+
+
+def get_jwks(user_context: Optional[Dict[str, Any]] = None) -> GetJWKSResult:
+    return sync(asyncio.get_jwks(user_context))
+
+
+def get_open_id_discovery_configuration(
+    user_context: Optional[Dict[str, Any]] = None
+) -> GetOpenIdDiscoveryConfigurationResult:
+    return sync(asyncio.get_open_id_discovery_configuration(user_context))
+
+
+
+
+
+
+
+

Functions

+
+
+def create_jwt(payload: Optional[Dict[str, Any]] = None, validity_seconds: Optional[int] = None, use_static_signing_key: Optional[bool] = None, user_context: Optional[Dict[str, Any]] = None) ‑> Union[CreateJwtOkResultCreateJwtResultUnsupportedAlgorithm] +
+
+
+
+ +Expand source code + +
def create_jwt(
+    payload: Optional[Dict[str, Any]] = None,
+    validity_seconds: Optional[int] = None,
+    use_static_signing_key: Optional[bool] = None,
+    user_context: Optional[Dict[str, Any]] = None,
+) -> Union[CreateJwtOkResult, CreateJwtResultUnsupportedAlgorithm]:
+    return sync(
+        asyncio.create_jwt(
+            payload, validity_seconds, use_static_signing_key, user_context
+        )
+    )
+
+
+
+def get_jwks(user_context: Optional[Dict[str, Any]] = None) ‑> GetJWKSResult +
+
+
+
+ +Expand source code + +
def get_jwks(user_context: Optional[Dict[str, Any]] = None) -> GetJWKSResult:
+    return sync(asyncio.get_jwks(user_context))
+
+
+
+def get_open_id_discovery_configuration(user_context: Optional[Dict[str, Any]] = None) ‑> GetOpenIdDiscoveryConfigurationResult +
+
+
+
+ +Expand source code + +
def get_open_id_discovery_configuration(
+    user_context: Optional[Dict[str, Any]] = None
+) -> GetOpenIdDiscoveryConfigurationResult:
+    return sync(asyncio.get_open_id_discovery_configuration(user_context))
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/openid/utils.html b/html/supertokens_python/recipe/openid/utils.html new file mode 100644 index 000000000..186a3f426 --- /dev/null +++ b/html/supertokens_python/recipe/openid/utils.html @@ -0,0 +1,273 @@ + + + + + + +supertokens_python.recipe.openid.utils API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.openid.utils

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Callable, Union
+
+if TYPE_CHECKING:
+    from .interfaces import RecipeInterface, APIInterface
+    from supertokens_python import AppInfo
+    from supertokens_python.recipe.jwt import OverrideConfig as JWTOverrideConfig
+
+from supertokens_python.normalised_url_domain import NormalisedURLDomain
+from supertokens_python.normalised_url_path import NormalisedURLPath
+
+
+class InputOverrideConfig:
+    def __init__(
+        self,
+        functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None,
+        apis: Union[Callable[[APIInterface], APIInterface], None] = None,
+        jwt_feature: Union[JWTOverrideConfig, None] = None,
+    ):
+        self.functions = functions
+        self.apis = apis
+        self.jwt_feature = jwt_feature
+
+
+class OverrideConfig:
+    def __init__(
+        self,
+        functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None,
+        apis: Union[Callable[[APIInterface], APIInterface], None] = None,
+    ):
+        self.functions = functions
+        self.apis = apis
+
+
+class OpenIdConfig:
+    def __init__(
+        self,
+        override: OverrideConfig,
+        issuer_domain: NormalisedURLDomain,
+        issuer_path: NormalisedURLPath,
+    ):
+        self.override = override
+        self.issuer_domain = issuer_domain
+        self.issuer_path = issuer_path
+
+
+def validate_and_normalise_user_input(
+    app_info: AppInfo,
+    issuer: Union[str, None] = None,
+    override: Union[InputOverrideConfig, None] = None,
+):
+    if issuer is None:
+        issuer_domain = app_info.api_domain
+        issuer_path = app_info.api_base_path
+    else:
+        issuer_domain = NormalisedURLDomain(issuer)
+        issuer_path = NormalisedURLPath(issuer)
+
+    if not issuer_path.equals(app_info.api_base_path):
+        raise Exception(
+            "The path of the issuer URL must be equal to the apiBasePath. The default value is /auth"
+        )
+
+    if override is not None and not isinstance(override, InputOverrideConfig):  # type: ignore
+        raise ValueError("override must be an instance of InputOverrideConfig or None")
+
+    if override is None:
+        override = InputOverrideConfig()
+
+    return OpenIdConfig(
+        OverrideConfig(functions=override.functions, apis=override.apis),
+        issuer_domain,
+        issuer_path,
+    )
+
+
+
+
+
+
+
+

Functions

+
+
+def validate_and_normalise_user_input(app_info: AppInfo, issuer: Union[str, None] = None, override: Union[InputOverrideConfig, None] = None) +
+
+
+
+ +Expand source code + +
def validate_and_normalise_user_input(
+    app_info: AppInfo,
+    issuer: Union[str, None] = None,
+    override: Union[InputOverrideConfig, None] = None,
+):
+    if issuer is None:
+        issuer_domain = app_info.api_domain
+        issuer_path = app_info.api_base_path
+    else:
+        issuer_domain = NormalisedURLDomain(issuer)
+        issuer_path = NormalisedURLPath(issuer)
+
+    if not issuer_path.equals(app_info.api_base_path):
+        raise Exception(
+            "The path of the issuer URL must be equal to the apiBasePath. The default value is /auth"
+        )
+
+    if override is not None and not isinstance(override, InputOverrideConfig):  # type: ignore
+        raise ValueError("override must be an instance of InputOverrideConfig or None")
+
+    if override is None:
+        override = InputOverrideConfig()
+
+    return OpenIdConfig(
+        OverrideConfig(functions=override.functions, apis=override.apis),
+        issuer_domain,
+        issuer_path,
+    )
+
+
+
+
+
+

Classes

+
+
+class InputOverrideConfig +(functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None, apis: Union[Callable[[APIInterface], APIInterface], None] = None, jwt_feature: Union[JWTOverrideConfig, None] = None) +
+
+
+
+ +Expand source code + +
class InputOverrideConfig:
+    def __init__(
+        self,
+        functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None,
+        apis: Union[Callable[[APIInterface], APIInterface], None] = None,
+        jwt_feature: Union[JWTOverrideConfig, None] = None,
+    ):
+        self.functions = functions
+        self.apis = apis
+        self.jwt_feature = jwt_feature
+
+
+
+class OpenIdConfig +(override: OverrideConfig, issuer_domain: NormalisedURLDomain, issuer_path: NormalisedURLPath) +
+
+
+
+ +Expand source code + +
class OpenIdConfig:
+    def __init__(
+        self,
+        override: OverrideConfig,
+        issuer_domain: NormalisedURLDomain,
+        issuer_path: NormalisedURLPath,
+    ):
+        self.override = override
+        self.issuer_domain = issuer_domain
+        self.issuer_path = issuer_path
+
+
+
+class OverrideConfig +(functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None, apis: Union[Callable[[APIInterface], APIInterface], None] = None) +
+
+
+
+ +Expand source code + +
class OverrideConfig:
+    def __init__(
+        self,
+        functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None,
+        apis: Union[Callable[[APIInterface], APIInterface], None] = None,
+    ):
+        self.functions = functions
+        self.apis = apis
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/passwordless/api/consume_code.html b/html/supertokens_python/recipe/passwordless/api/consume_code.html new file mode 100644 index 000000000..1592367ee --- /dev/null +++ b/html/supertokens_python/recipe/passwordless/api/consume_code.html @@ -0,0 +1,194 @@ + + + + + + +supertokens_python.recipe.passwordless.api.consume_code API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.passwordless.api.consume_code

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from typing import Any, Dict
+from supertokens_python.exceptions import raise_bad_input_exception
+from supertokens_python.recipe.passwordless.interfaces import APIInterface, APIOptions
+from supertokens_python.utils import send_200_response
+
+
+async def consume_code(
+    api_implementation: APIInterface,
+    tenant_id: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+):
+    if api_implementation.disable_consume_code_post:
+        return None
+
+    body = await api_options.request.json()
+
+    if body is None:
+        raise_bad_input_exception("Please provide a JSON body")
+
+    user_input_code = None
+    device_id = None
+    link_code = None
+
+    if "preAuthSessionId" not in body:
+        raise_bad_input_exception("Please provide preAuthSessionId")
+
+    if "deviceId" in body or "userInputCode" in body:
+        if "linkCode" in body:
+            raise_bad_input_exception(
+                "Please provide one of (linkCode) or (deviceId+userInputCode) and not both"
+            )
+        if "deviceId" not in body or "userInputCode" not in body:
+            raise_bad_input_exception("Please provide both deviceId and userInputCode")
+        device_id = body["deviceId"]
+        user_input_code = body["userInputCode"]
+    elif "linkCode" in body:
+        link_code = body["linkCode"]
+    else:
+        raise_bad_input_exception(
+            "Please provide one of (linkCode) or (deviceId+userInputCode) and not both"
+        )
+
+    pre_auth_session_id = body["preAuthSessionId"]
+
+    result = await api_implementation.consume_code_post(
+        pre_auth_session_id,
+        user_input_code,
+        device_id,
+        link_code,
+        tenant_id,
+        api_options,
+        user_context,
+    )
+    return send_200_response(result.to_json(), api_options.response)
+
+
+
+
+
+
+
+

Functions

+
+
+async def consume_code(api_implementation: APIInterface, tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) +
+
+
+
+ +Expand source code + +
async def consume_code(
+    api_implementation: APIInterface,
+    tenant_id: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+):
+    if api_implementation.disable_consume_code_post:
+        return None
+
+    body = await api_options.request.json()
+
+    if body is None:
+        raise_bad_input_exception("Please provide a JSON body")
+
+    user_input_code = None
+    device_id = None
+    link_code = None
+
+    if "preAuthSessionId" not in body:
+        raise_bad_input_exception("Please provide preAuthSessionId")
+
+    if "deviceId" in body or "userInputCode" in body:
+        if "linkCode" in body:
+            raise_bad_input_exception(
+                "Please provide one of (linkCode) or (deviceId+userInputCode) and not both"
+            )
+        if "deviceId" not in body or "userInputCode" not in body:
+            raise_bad_input_exception("Please provide both deviceId and userInputCode")
+        device_id = body["deviceId"]
+        user_input_code = body["userInputCode"]
+    elif "linkCode" in body:
+        link_code = body["linkCode"]
+    else:
+        raise_bad_input_exception(
+            "Please provide one of (linkCode) or (deviceId+userInputCode) and not both"
+        )
+
+    pre_auth_session_id = body["preAuthSessionId"]
+
+    result = await api_implementation.consume_code_post(
+        pre_auth_session_id,
+        user_input_code,
+        device_id,
+        link_code,
+        tenant_id,
+        api_options,
+        user_context,
+    )
+    return send_200_response(result.to_json(), api_options.response)
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/passwordless/api/create_code.html b/html/supertokens_python/recipe/passwordless/api/create_code.html new file mode 100644 index 000000000..23b8bff04 --- /dev/null +++ b/html/supertokens_python/recipe/passwordless/api/create_code.html @@ -0,0 +1,287 @@ + + + + + + +supertokens_python.recipe.passwordless.api.create_code API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.passwordless.api.create_code

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from typing import Union, Any, Dict
+
+import phonenumbers  # type: ignore
+from phonenumbers import format_number, parse  # type: ignore
+from supertokens_python.exceptions import raise_bad_input_exception
+from supertokens_python.recipe.passwordless.interfaces import APIInterface, APIOptions
+from supertokens_python.recipe.passwordless.utils import (
+    ContactEmailOnlyConfig,
+    ContactEmailOrPhoneConfig,
+    ContactPhoneOnlyConfig,
+)
+from supertokens_python.types import GeneralErrorResponse
+from supertokens_python.utils import send_200_response
+
+
+async def create_code(
+    api_implementation: APIInterface,
+    tenant_id: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+):
+    if api_implementation.disable_create_code_post:
+        return None
+
+    body = await api_options.request.json()
+
+    if body is None:
+        raise_bad_input_exception("Please provide a JSON body")
+
+    email: Union[str, None] = None
+    phone_number: Union[str, None] = None
+
+    if ("email" in body and "phoneNumber" in body) or (
+        "email" not in body and "phoneNumber" not in body
+    ):
+        raise_bad_input_exception("Please provide exactly one of email or phoneNumber")
+
+    if "email" not in body and isinstance(
+        api_options.config.contact_config, ContactEmailOnlyConfig
+    ):
+        raise_bad_input_exception(
+            'Please provide an email since you have set the contactMethod to "EMAIL"'
+        )
+
+    if "phoneNumber" not in body and isinstance(
+        api_options.config.contact_config, ContactPhoneOnlyConfig
+    ):
+        raise_bad_input_exception(
+            'Please provide a phoneNumber since you have set the contactMethod to "PHONE"'
+        )
+
+    if "email" in body:
+        email = body["email"]
+    if "phoneNumber" in body:
+        phone_number = body["phoneNumber"]
+
+    if email is not None and (
+        isinstance(
+            api_options.config.contact_config,
+            (ContactEmailOnlyConfig, ContactEmailOrPhoneConfig),
+        )
+    ):
+        email = email.strip()
+        validation_error = (
+            await api_options.config.contact_config.validate_email_address(
+                email, tenant_id
+            )
+        )
+        if validation_error is not None:
+            api_options.response.set_json_content(
+                GeneralErrorResponse(validation_error).to_json()
+            )
+            return api_options.response
+
+    if phone_number is not None and (
+        isinstance(
+            api_options.config.contact_config,
+            (ContactEmailOrPhoneConfig, ContactPhoneOnlyConfig),
+        )
+    ):
+        validation_error = (
+            await api_options.config.contact_config.validate_phone_number(
+                phone_number, tenant_id
+            )
+        )
+        if validation_error is not None:
+            api_options.response.set_json_content(
+                GeneralErrorResponse(validation_error).to_json()
+            )
+            return api_options.response
+        try:
+            phone_number_formatted: str = format_number(
+                parse(phone_number, None), phonenumbers.PhoneNumberFormat.E164
+            )  # type: ignore
+            phone_number = phone_number_formatted
+        except Exception:
+            phone_number = phone_number.strip()
+
+    result = await api_implementation.create_code_post(
+        email=email,
+        phone_number=phone_number,
+        tenant_id=tenant_id,
+        api_options=api_options,
+        user_context=user_context,
+    )
+    return send_200_response(result.to_json(), api_options.response)
+
+
+
+
+
+
+
+

Functions

+
+
+async def create_code(api_implementation: APIInterface, tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) +
+
+
+
+ +Expand source code + +
async def create_code(
+    api_implementation: APIInterface,
+    tenant_id: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+):
+    if api_implementation.disable_create_code_post:
+        return None
+
+    body = await api_options.request.json()
+
+    if body is None:
+        raise_bad_input_exception("Please provide a JSON body")
+
+    email: Union[str, None] = None
+    phone_number: Union[str, None] = None
+
+    if ("email" in body and "phoneNumber" in body) or (
+        "email" not in body and "phoneNumber" not in body
+    ):
+        raise_bad_input_exception("Please provide exactly one of email or phoneNumber")
+
+    if "email" not in body and isinstance(
+        api_options.config.contact_config, ContactEmailOnlyConfig
+    ):
+        raise_bad_input_exception(
+            'Please provide an email since you have set the contactMethod to "EMAIL"'
+        )
+
+    if "phoneNumber" not in body and isinstance(
+        api_options.config.contact_config, ContactPhoneOnlyConfig
+    ):
+        raise_bad_input_exception(
+            'Please provide a phoneNumber since you have set the contactMethod to "PHONE"'
+        )
+
+    if "email" in body:
+        email = body["email"]
+    if "phoneNumber" in body:
+        phone_number = body["phoneNumber"]
+
+    if email is not None and (
+        isinstance(
+            api_options.config.contact_config,
+            (ContactEmailOnlyConfig, ContactEmailOrPhoneConfig),
+        )
+    ):
+        email = email.strip()
+        validation_error = (
+            await api_options.config.contact_config.validate_email_address(
+                email, tenant_id
+            )
+        )
+        if validation_error is not None:
+            api_options.response.set_json_content(
+                GeneralErrorResponse(validation_error).to_json()
+            )
+            return api_options.response
+
+    if phone_number is not None and (
+        isinstance(
+            api_options.config.contact_config,
+            (ContactEmailOrPhoneConfig, ContactPhoneOnlyConfig),
+        )
+    ):
+        validation_error = (
+            await api_options.config.contact_config.validate_phone_number(
+                phone_number, tenant_id
+            )
+        )
+        if validation_error is not None:
+            api_options.response.set_json_content(
+                GeneralErrorResponse(validation_error).to_json()
+            )
+            return api_options.response
+        try:
+            phone_number_formatted: str = format_number(
+                parse(phone_number, None), phonenumbers.PhoneNumberFormat.E164
+            )  # type: ignore
+            phone_number = phone_number_formatted
+        except Exception:
+            phone_number = phone_number.strip()
+
+    result = await api_implementation.create_code_post(
+        email=email,
+        phone_number=phone_number,
+        tenant_id=tenant_id,
+        api_options=api_options,
+        user_context=user_context,
+    )
+    return send_200_response(result.to_json(), api_options.response)
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/passwordless/api/email_exists.html b/html/supertokens_python/recipe/passwordless/api/email_exists.html new file mode 100644 index 000000000..86bdda666 --- /dev/null +++ b/html/supertokens_python/recipe/passwordless/api/email_exists.html @@ -0,0 +1,131 @@ + + + + + + +supertokens_python.recipe.passwordless.api.email_exists API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.passwordless.api.email_exists

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+from typing import Any, Dict
+from supertokens_python.exceptions import raise_bad_input_exception
+from supertokens_python.recipe.passwordless.interfaces import APIInterface, APIOptions
+from supertokens_python.utils import send_200_response
+
+
+async def email_exists(
+    api_implementation: APIInterface,
+    tenant_id: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+):
+    if api_implementation.disable_email_exists_get:
+        return None
+
+    email = api_options.request.get_query_param("email")
+    if email is None:
+        raise_bad_input_exception("Please provide the email as a GET param")
+
+    result = await api_implementation.email_exists_get(
+        email, tenant_id, api_options, user_context
+    )
+    return send_200_response(result.to_json(), api_options.response)
+
+
+
+
+
+
+
+

Functions

+
+
+async def email_exists(api_implementation: APIInterface, tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) +
+
+
+
+ +Expand source code + +
async def email_exists(
+    api_implementation: APIInterface,
+    tenant_id: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+):
+    if api_implementation.disable_email_exists_get:
+        return None
+
+    email = api_options.request.get_query_param("email")
+    if email is None:
+        raise_bad_input_exception("Please provide the email as a GET param")
+
+    result = await api_implementation.email_exists_get(
+        email, tenant_id, api_options, user_context
+    )
+    return send_200_response(result.to_json(), api_options.response)
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/passwordless/api/implementation.html b/html/supertokens_python/recipe/passwordless/api/implementation.html new file mode 100644 index 000000000..3d24bf691 --- /dev/null +++ b/html/supertokens_python/recipe/passwordless/api/implementation.html @@ -0,0 +1,1056 @@ + + + + + + +supertokens_python.recipe.passwordless.api.implementation API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.passwordless.api.implementation

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from typing import Any, Dict, Union
+
+from supertokens_python.logger import log_debug_message
+from supertokens_python.recipe.passwordless.interfaces import (
+    APIInterface,
+    APIOptions,
+    ConsumeCodeExpiredUserInputCodeError,
+    ConsumeCodeIncorrectUserInputCodeError,
+    ConsumeCodePostExpiredUserInputCodeError,
+    ConsumeCodePostIncorrectUserInputCodeError,
+    ConsumeCodePostOkResult,
+    ConsumeCodePostRestartFlowError,
+    ConsumeCodeRestartFlowError,
+    CreateCodePostOkResult,
+    CreateNewCodeForDeviceOkResult,
+    CreateNewCodeForDeviceUserInputCodeAlreadyUsedError,
+    EmailExistsGetOkResult,
+    PasswordlessLoginEmailTemplateVars,
+    PhoneNumberExistsGetOkResult,
+    ResendCodePostOkResult,
+    ResendCodePostRestartFlowError,
+)
+from supertokens_python.recipe.passwordless.types import (
+    PasswordlessLoginSMSTemplateVars,
+)
+from supertokens_python.recipe.passwordless.utils import (
+    ContactEmailOnlyConfig,
+    ContactEmailOrPhoneConfig,
+    ContactPhoneOnlyConfig,
+)
+from supertokens_python.recipe.session.asyncio import create_new_session
+from supertokens_python.types import GeneralErrorResponse
+from ...emailverification import EmailVerificationRecipe
+from ...emailverification.interfaces import CreateEmailVerificationTokenOkResult
+
+
+class APIImplementation(APIInterface):
+    async def create_code_post(
+        self,
+        email: Union[str, None],
+        phone_number: Union[str, None],
+        tenant_id: str,
+        api_options: APIOptions,
+        user_context: Dict[str, Any],
+    ) -> Union[CreateCodePostOkResult, GeneralErrorResponse]:
+        user_input_code = None
+        if api_options.config.get_custom_user_input_code is not None:
+            user_input_code = await api_options.config.get_custom_user_input_code(
+                tenant_id, user_context
+            )
+        response = await api_options.recipe_implementation.create_code(
+            email, phone_number, user_input_code, tenant_id, user_context
+        )
+        magic_link = None
+        user_input_code = None
+        flow_type = api_options.config.flow_type
+        if flow_type in ("MAGIC_LINK", "USER_INPUT_CODE_AND_MAGIC_LINK"):
+            magic_link = (
+                api_options.app_info.get_origin(
+                    api_options.request, user_context
+                ).get_as_string_dangerous()
+                + api_options.app_info.website_base_path.get_as_string_dangerous()
+                + "/verify"
+                + "?preAuthSessionId="
+                + response.pre_auth_session_id
+                + "&tenantId="
+                + tenant_id
+                + "#"
+                + response.link_code
+            )
+        if flow_type in ("USER_INPUT_CODE", "USER_INPUT_CODE_AND_MAGIC_LINK"):
+            user_input_code = response.user_input_code
+
+        if isinstance(api_options.config.contact_config, ContactEmailOnlyConfig) or (
+            isinstance(api_options.config.contact_config, ContactEmailOrPhoneConfig)
+            and email is not None
+        ):
+            if email is None:
+                raise Exception("Should never come here")
+
+            log_debug_message("Sending passwordless login email to %s", email)
+            passwordless_email_delivery_input = PasswordlessLoginEmailTemplateVars(
+                email=email,
+                user_input_code=user_input_code,
+                url_with_link_code=magic_link,
+                code_life_time=response.code_life_time,
+                pre_auth_session_id=response.pre_auth_session_id,
+                tenant_id=tenant_id,
+            )
+            await api_options.email_delivery.ingredient_interface_impl.send_email(
+                passwordless_email_delivery_input, user_context
+            )
+        elif isinstance(
+            api_options.config.contact_config,
+            (ContactEmailOrPhoneConfig, ContactPhoneOnlyConfig),
+        ):
+            if phone_number is None:
+                raise Exception("Should never come here")
+            log_debug_message("Sending passwordless login SMS to %s", phone_number)
+            sms_input = PasswordlessLoginSMSTemplateVars(
+                phone_number=phone_number,
+                user_input_code=user_input_code,
+                url_with_link_code=magic_link,
+                code_life_time=response.code_life_time,
+                pre_auth_session_id=response.pre_auth_session_id,
+                tenant_id=tenant_id,
+            )
+            await api_options.sms_delivery.ingredient_interface_impl.send_sms(
+                sms_input, user_context
+            )
+
+        return CreateCodePostOkResult(
+            response.device_id, response.pre_auth_session_id, flow_type
+        )
+
+    async def resend_code_post(
+        self,
+        device_id: str,
+        pre_auth_session_id: str,
+        tenant_id: str,
+        api_options: APIOptions,
+        user_context: Dict[str, Any],
+    ) -> Union[
+        ResendCodePostOkResult, ResendCodePostRestartFlowError, GeneralErrorResponse
+    ]:
+        device_info = await api_options.recipe_implementation.list_codes_by_device_id(
+            device_id=device_id, tenant_id=tenant_id, user_context=user_context
+        )
+        if device_info is None:
+            return ResendCodePostRestartFlowError()
+        if (
+            isinstance(api_options.config.contact_config, ContactEmailOnlyConfig)
+            and device_info.email is None
+        ) or (
+            isinstance(api_options.config.contact_config, ContactPhoneOnlyConfig)
+            and device_info.phone_number is None
+        ):
+            return ResendCodePostRestartFlowError()
+        number_of_tries_to_create_new_code = 0
+        while True:
+            number_of_tries_to_create_new_code += 1
+            user_input_code = None
+            if api_options.config.get_custom_user_input_code is not None:
+                user_input_code = await api_options.config.get_custom_user_input_code(
+                    tenant_id, user_context
+                )
+            response = (
+                await api_options.recipe_implementation.create_new_code_for_device(
+                    device_id=device_id,
+                    user_input_code=user_input_code,
+                    tenant_id=tenant_id,
+                    user_context=user_context,
+                )
+            )
+            if isinstance(
+                response, CreateNewCodeForDeviceUserInputCodeAlreadyUsedError
+            ):
+                if number_of_tries_to_create_new_code >= 3:
+                    return GeneralErrorResponse(
+                        "Failed to generate a one time code. Please try again"
+                    )
+                continue
+
+            if isinstance(response, CreateNewCodeForDeviceOkResult):
+                magic_link = None
+                user_input_code = None
+                flow_type = api_options.config.flow_type
+                if flow_type in ("MAGIC_LINK", "USER_INPUT_CODE_AND_MAGIC_LINK"):
+                    magic_link = (
+                        api_options.app_info.get_origin(
+                            api_options.request, user_context
+                        ).get_as_string_dangerous()
+                        + api_options.app_info.website_base_path.get_as_string_dangerous()
+                        + "/verify"
+                        + "?preAuthSessionId="
+                        + response.pre_auth_session_id
+                        + "&tenantId="
+                        + tenant_id
+                        + "#"
+                        + response.link_code
+                    )
+                if flow_type in ("USER_INPUT_CODE", "USER_INPUT_CODE_AND_MAGIC_LINK"):
+                    user_input_code = response.user_input_code
+
+                if isinstance(
+                    api_options.config.contact_config, ContactEmailOnlyConfig
+                ) or (
+                    isinstance(
+                        api_options.config.contact_config, ContactEmailOrPhoneConfig
+                    )
+                    and device_info.email is not None
+                ):
+                    if device_info.email is None:
+                        raise Exception("Should never come here")
+
+                    log_debug_message(
+                        "Sending passwordless login email to %s", device_info.email
+                    )
+                    passwordless_email_delivery_input = (
+                        PasswordlessLoginEmailTemplateVars(
+                            email=device_info.email,
+                            user_input_code=user_input_code,
+                            url_with_link_code=magic_link,
+                            code_life_time=response.code_life_time,
+                            pre_auth_session_id=response.pre_auth_session_id,
+                            tenant_id=tenant_id,
+                        )
+                    )
+                    await api_options.email_delivery.ingredient_interface_impl.send_email(
+                        passwordless_email_delivery_input, user_context
+                    )
+                elif isinstance(
+                    api_options.config.contact_config,
+                    (ContactEmailOrPhoneConfig, ContactPhoneOnlyConfig),
+                ):
+                    if device_info.phone_number is None:
+                        raise Exception("Should never come here")
+                    log_debug_message(
+                        "Sending passwordless login SMS to %s", device_info.phone_number
+                    )
+                    sms_input = PasswordlessLoginSMSTemplateVars(
+                        phone_number=device_info.phone_number,
+                        user_input_code=user_input_code,
+                        url_with_link_code=magic_link,
+                        code_life_time=response.code_life_time,
+                        pre_auth_session_id=response.pre_auth_session_id,
+                        tenant_id=tenant_id,
+                    )
+                    await api_options.sms_delivery.ingredient_interface_impl.send_sms(
+                        sms_input, user_context
+                    )
+                return ResendCodePostOkResult()
+            return ResendCodePostRestartFlowError()
+
+    async def consume_code_post(
+        self,
+        pre_auth_session_id: str,
+        user_input_code: Union[str, None],
+        device_id: Union[str, None],
+        link_code: Union[str, None],
+        tenant_id: str,
+        api_options: APIOptions,
+        user_context: Dict[str, Any],
+    ) -> Union[
+        ConsumeCodePostOkResult,
+        ConsumeCodePostRestartFlowError,
+        ConsumeCodePostIncorrectUserInputCodeError,
+        ConsumeCodePostExpiredUserInputCodeError,
+        GeneralErrorResponse,
+    ]:
+        response = await api_options.recipe_implementation.consume_code(
+            pre_auth_session_id=pre_auth_session_id,
+            user_input_code=user_input_code,
+            device_id=device_id,
+            link_code=link_code,
+            tenant_id=tenant_id,
+            user_context=user_context,
+        )
+
+        if isinstance(response, ConsumeCodeExpiredUserInputCodeError):
+            return ConsumeCodePostExpiredUserInputCodeError(
+                failed_code_input_attempt_count=response.failed_code_input_attempt_count,
+                maximum_code_input_attempts=response.maximum_code_input_attempts,
+            )
+        if isinstance(response, ConsumeCodeIncorrectUserInputCodeError):
+            return ConsumeCodePostIncorrectUserInputCodeError(
+                failed_code_input_attempt_count=response.failed_code_input_attempt_count,
+                maximum_code_input_attempts=response.maximum_code_input_attempts,
+            )
+        if isinstance(response, ConsumeCodeRestartFlowError):
+            return ConsumeCodePostRestartFlowError()
+
+        user = response.user
+
+        if user.email is not None:
+            ev_instance = EmailVerificationRecipe.get_instance_optional()
+            if ev_instance is not None:
+                token_response = await ev_instance.recipe_implementation.create_email_verification_token(
+                    user.user_id, user.email, tenant_id, user_context
+                )
+
+                if isinstance(token_response, CreateEmailVerificationTokenOkResult):
+                    await ev_instance.recipe_implementation.verify_email_using_token(
+                        token_response.token, tenant_id, user_context
+                    )
+
+        session = await create_new_session(
+            request=api_options.request,
+            tenant_id=tenant_id,
+            user_id=user.user_id,
+            access_token_payload={},
+            session_data_in_database={},
+            user_context=user_context,
+        )
+
+        return ConsumeCodePostOkResult(
+            created_new_user=response.created_new_user,
+            user=response.user,
+            session=session,
+        )
+
+    async def email_exists_get(
+        self,
+        email: str,
+        tenant_id: str,
+        api_options: APIOptions,
+        user_context: Dict[str, Any],
+    ) -> Union[EmailExistsGetOkResult, GeneralErrorResponse]:
+        response = await api_options.recipe_implementation.get_user_by_email(
+            email, tenant_id, user_context
+        )
+        return EmailExistsGetOkResult(exists=response is not None)
+
+    async def phone_number_exists_get(
+        self,
+        phone_number: str,
+        tenant_id: str,
+        api_options: APIOptions,
+        user_context: Dict[str, Any],
+    ) -> Union[PhoneNumberExistsGetOkResult, GeneralErrorResponse]:
+        response = await api_options.recipe_implementation.get_user_by_phone_number(
+            phone_number, tenant_id, user_context
+        )
+        return PhoneNumberExistsGetOkResult(exists=response is not None)
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class APIImplementation +
+
+
+
+ +Expand source code + +
class APIImplementation(APIInterface):
+    async def create_code_post(
+        self,
+        email: Union[str, None],
+        phone_number: Union[str, None],
+        tenant_id: str,
+        api_options: APIOptions,
+        user_context: Dict[str, Any],
+    ) -> Union[CreateCodePostOkResult, GeneralErrorResponse]:
+        user_input_code = None
+        if api_options.config.get_custom_user_input_code is not None:
+            user_input_code = await api_options.config.get_custom_user_input_code(
+                tenant_id, user_context
+            )
+        response = await api_options.recipe_implementation.create_code(
+            email, phone_number, user_input_code, tenant_id, user_context
+        )
+        magic_link = None
+        user_input_code = None
+        flow_type = api_options.config.flow_type
+        if flow_type in ("MAGIC_LINK", "USER_INPUT_CODE_AND_MAGIC_LINK"):
+            magic_link = (
+                api_options.app_info.get_origin(
+                    api_options.request, user_context
+                ).get_as_string_dangerous()
+                + api_options.app_info.website_base_path.get_as_string_dangerous()
+                + "/verify"
+                + "?preAuthSessionId="
+                + response.pre_auth_session_id
+                + "&tenantId="
+                + tenant_id
+                + "#"
+                + response.link_code
+            )
+        if flow_type in ("USER_INPUT_CODE", "USER_INPUT_CODE_AND_MAGIC_LINK"):
+            user_input_code = response.user_input_code
+
+        if isinstance(api_options.config.contact_config, ContactEmailOnlyConfig) or (
+            isinstance(api_options.config.contact_config, ContactEmailOrPhoneConfig)
+            and email is not None
+        ):
+            if email is None:
+                raise Exception("Should never come here")
+
+            log_debug_message("Sending passwordless login email to %s", email)
+            passwordless_email_delivery_input = PasswordlessLoginEmailTemplateVars(
+                email=email,
+                user_input_code=user_input_code,
+                url_with_link_code=magic_link,
+                code_life_time=response.code_life_time,
+                pre_auth_session_id=response.pre_auth_session_id,
+                tenant_id=tenant_id,
+            )
+            await api_options.email_delivery.ingredient_interface_impl.send_email(
+                passwordless_email_delivery_input, user_context
+            )
+        elif isinstance(
+            api_options.config.contact_config,
+            (ContactEmailOrPhoneConfig, ContactPhoneOnlyConfig),
+        ):
+            if phone_number is None:
+                raise Exception("Should never come here")
+            log_debug_message("Sending passwordless login SMS to %s", phone_number)
+            sms_input = PasswordlessLoginSMSTemplateVars(
+                phone_number=phone_number,
+                user_input_code=user_input_code,
+                url_with_link_code=magic_link,
+                code_life_time=response.code_life_time,
+                pre_auth_session_id=response.pre_auth_session_id,
+                tenant_id=tenant_id,
+            )
+            await api_options.sms_delivery.ingredient_interface_impl.send_sms(
+                sms_input, user_context
+            )
+
+        return CreateCodePostOkResult(
+            response.device_id, response.pre_auth_session_id, flow_type
+        )
+
+    async def resend_code_post(
+        self,
+        device_id: str,
+        pre_auth_session_id: str,
+        tenant_id: str,
+        api_options: APIOptions,
+        user_context: Dict[str, Any],
+    ) -> Union[
+        ResendCodePostOkResult, ResendCodePostRestartFlowError, GeneralErrorResponse
+    ]:
+        device_info = await api_options.recipe_implementation.list_codes_by_device_id(
+            device_id=device_id, tenant_id=tenant_id, user_context=user_context
+        )
+        if device_info is None:
+            return ResendCodePostRestartFlowError()
+        if (
+            isinstance(api_options.config.contact_config, ContactEmailOnlyConfig)
+            and device_info.email is None
+        ) or (
+            isinstance(api_options.config.contact_config, ContactPhoneOnlyConfig)
+            and device_info.phone_number is None
+        ):
+            return ResendCodePostRestartFlowError()
+        number_of_tries_to_create_new_code = 0
+        while True:
+            number_of_tries_to_create_new_code += 1
+            user_input_code = None
+            if api_options.config.get_custom_user_input_code is not None:
+                user_input_code = await api_options.config.get_custom_user_input_code(
+                    tenant_id, user_context
+                )
+            response = (
+                await api_options.recipe_implementation.create_new_code_for_device(
+                    device_id=device_id,
+                    user_input_code=user_input_code,
+                    tenant_id=tenant_id,
+                    user_context=user_context,
+                )
+            )
+            if isinstance(
+                response, CreateNewCodeForDeviceUserInputCodeAlreadyUsedError
+            ):
+                if number_of_tries_to_create_new_code >= 3:
+                    return GeneralErrorResponse(
+                        "Failed to generate a one time code. Please try again"
+                    )
+                continue
+
+            if isinstance(response, CreateNewCodeForDeviceOkResult):
+                magic_link = None
+                user_input_code = None
+                flow_type = api_options.config.flow_type
+                if flow_type in ("MAGIC_LINK", "USER_INPUT_CODE_AND_MAGIC_LINK"):
+                    magic_link = (
+                        api_options.app_info.get_origin(
+                            api_options.request, user_context
+                        ).get_as_string_dangerous()
+                        + api_options.app_info.website_base_path.get_as_string_dangerous()
+                        + "/verify"
+                        + "?preAuthSessionId="
+                        + response.pre_auth_session_id
+                        + "&tenantId="
+                        + tenant_id
+                        + "#"
+                        + response.link_code
+                    )
+                if flow_type in ("USER_INPUT_CODE", "USER_INPUT_CODE_AND_MAGIC_LINK"):
+                    user_input_code = response.user_input_code
+
+                if isinstance(
+                    api_options.config.contact_config, ContactEmailOnlyConfig
+                ) or (
+                    isinstance(
+                        api_options.config.contact_config, ContactEmailOrPhoneConfig
+                    )
+                    and device_info.email is not None
+                ):
+                    if device_info.email is None:
+                        raise Exception("Should never come here")
+
+                    log_debug_message(
+                        "Sending passwordless login email to %s", device_info.email
+                    )
+                    passwordless_email_delivery_input = (
+                        PasswordlessLoginEmailTemplateVars(
+                            email=device_info.email,
+                            user_input_code=user_input_code,
+                            url_with_link_code=magic_link,
+                            code_life_time=response.code_life_time,
+                            pre_auth_session_id=response.pre_auth_session_id,
+                            tenant_id=tenant_id,
+                        )
+                    )
+                    await api_options.email_delivery.ingredient_interface_impl.send_email(
+                        passwordless_email_delivery_input, user_context
+                    )
+                elif isinstance(
+                    api_options.config.contact_config,
+                    (ContactEmailOrPhoneConfig, ContactPhoneOnlyConfig),
+                ):
+                    if device_info.phone_number is None:
+                        raise Exception("Should never come here")
+                    log_debug_message(
+                        "Sending passwordless login SMS to %s", device_info.phone_number
+                    )
+                    sms_input = PasswordlessLoginSMSTemplateVars(
+                        phone_number=device_info.phone_number,
+                        user_input_code=user_input_code,
+                        url_with_link_code=magic_link,
+                        code_life_time=response.code_life_time,
+                        pre_auth_session_id=response.pre_auth_session_id,
+                        tenant_id=tenant_id,
+                    )
+                    await api_options.sms_delivery.ingredient_interface_impl.send_sms(
+                        sms_input, user_context
+                    )
+                return ResendCodePostOkResult()
+            return ResendCodePostRestartFlowError()
+
+    async def consume_code_post(
+        self,
+        pre_auth_session_id: str,
+        user_input_code: Union[str, None],
+        device_id: Union[str, None],
+        link_code: Union[str, None],
+        tenant_id: str,
+        api_options: APIOptions,
+        user_context: Dict[str, Any],
+    ) -> Union[
+        ConsumeCodePostOkResult,
+        ConsumeCodePostRestartFlowError,
+        ConsumeCodePostIncorrectUserInputCodeError,
+        ConsumeCodePostExpiredUserInputCodeError,
+        GeneralErrorResponse,
+    ]:
+        response = await api_options.recipe_implementation.consume_code(
+            pre_auth_session_id=pre_auth_session_id,
+            user_input_code=user_input_code,
+            device_id=device_id,
+            link_code=link_code,
+            tenant_id=tenant_id,
+            user_context=user_context,
+        )
+
+        if isinstance(response, ConsumeCodeExpiredUserInputCodeError):
+            return ConsumeCodePostExpiredUserInputCodeError(
+                failed_code_input_attempt_count=response.failed_code_input_attempt_count,
+                maximum_code_input_attempts=response.maximum_code_input_attempts,
+            )
+        if isinstance(response, ConsumeCodeIncorrectUserInputCodeError):
+            return ConsumeCodePostIncorrectUserInputCodeError(
+                failed_code_input_attempt_count=response.failed_code_input_attempt_count,
+                maximum_code_input_attempts=response.maximum_code_input_attempts,
+            )
+        if isinstance(response, ConsumeCodeRestartFlowError):
+            return ConsumeCodePostRestartFlowError()
+
+        user = response.user
+
+        if user.email is not None:
+            ev_instance = EmailVerificationRecipe.get_instance_optional()
+            if ev_instance is not None:
+                token_response = await ev_instance.recipe_implementation.create_email_verification_token(
+                    user.user_id, user.email, tenant_id, user_context
+                )
+
+                if isinstance(token_response, CreateEmailVerificationTokenOkResult):
+                    await ev_instance.recipe_implementation.verify_email_using_token(
+                        token_response.token, tenant_id, user_context
+                    )
+
+        session = await create_new_session(
+            request=api_options.request,
+            tenant_id=tenant_id,
+            user_id=user.user_id,
+            access_token_payload={},
+            session_data_in_database={},
+            user_context=user_context,
+        )
+
+        return ConsumeCodePostOkResult(
+            created_new_user=response.created_new_user,
+            user=response.user,
+            session=session,
+        )
+
+    async def email_exists_get(
+        self,
+        email: str,
+        tenant_id: str,
+        api_options: APIOptions,
+        user_context: Dict[str, Any],
+    ) -> Union[EmailExistsGetOkResult, GeneralErrorResponse]:
+        response = await api_options.recipe_implementation.get_user_by_email(
+            email, tenant_id, user_context
+        )
+        return EmailExistsGetOkResult(exists=response is not None)
+
+    async def phone_number_exists_get(
+        self,
+        phone_number: str,
+        tenant_id: str,
+        api_options: APIOptions,
+        user_context: Dict[str, Any],
+    ) -> Union[PhoneNumberExistsGetOkResult, GeneralErrorResponse]:
+        response = await api_options.recipe_implementation.get_user_by_phone_number(
+            phone_number, tenant_id, user_context
+        )
+        return PhoneNumberExistsGetOkResult(exists=response is not None)
+
+

Ancestors

+ +

Methods

+
+
+async def consume_code_post(self, pre_auth_session_id: str, user_input_code: Optional[str], device_id: Optional[str], link_code: Optional[str], tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[ConsumeCodePostOkResultConsumeCodePostRestartFlowErrorConsumeCodePostIncorrectUserInputCodeErrorConsumeCodePostExpiredUserInputCodeErrorGeneralErrorResponse] +
+
+
+
+ +Expand source code + +
async def consume_code_post(
+    self,
+    pre_auth_session_id: str,
+    user_input_code: Union[str, None],
+    device_id: Union[str, None],
+    link_code: Union[str, None],
+    tenant_id: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+) -> Union[
+    ConsumeCodePostOkResult,
+    ConsumeCodePostRestartFlowError,
+    ConsumeCodePostIncorrectUserInputCodeError,
+    ConsumeCodePostExpiredUserInputCodeError,
+    GeneralErrorResponse,
+]:
+    response = await api_options.recipe_implementation.consume_code(
+        pre_auth_session_id=pre_auth_session_id,
+        user_input_code=user_input_code,
+        device_id=device_id,
+        link_code=link_code,
+        tenant_id=tenant_id,
+        user_context=user_context,
+    )
+
+    if isinstance(response, ConsumeCodeExpiredUserInputCodeError):
+        return ConsumeCodePostExpiredUserInputCodeError(
+            failed_code_input_attempt_count=response.failed_code_input_attempt_count,
+            maximum_code_input_attempts=response.maximum_code_input_attempts,
+        )
+    if isinstance(response, ConsumeCodeIncorrectUserInputCodeError):
+        return ConsumeCodePostIncorrectUserInputCodeError(
+            failed_code_input_attempt_count=response.failed_code_input_attempt_count,
+            maximum_code_input_attempts=response.maximum_code_input_attempts,
+        )
+    if isinstance(response, ConsumeCodeRestartFlowError):
+        return ConsumeCodePostRestartFlowError()
+
+    user = response.user
+
+    if user.email is not None:
+        ev_instance = EmailVerificationRecipe.get_instance_optional()
+        if ev_instance is not None:
+            token_response = await ev_instance.recipe_implementation.create_email_verification_token(
+                user.user_id, user.email, tenant_id, user_context
+            )
+
+            if isinstance(token_response, CreateEmailVerificationTokenOkResult):
+                await ev_instance.recipe_implementation.verify_email_using_token(
+                    token_response.token, tenant_id, user_context
+                )
+
+    session = await create_new_session(
+        request=api_options.request,
+        tenant_id=tenant_id,
+        user_id=user.user_id,
+        access_token_payload={},
+        session_data_in_database={},
+        user_context=user_context,
+    )
+
+    return ConsumeCodePostOkResult(
+        created_new_user=response.created_new_user,
+        user=response.user,
+        session=session,
+    )
+
+
+
+async def create_code_post(self, email: Optional[str], phone_number: Optional[str], tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[CreateCodePostOkResultGeneralErrorResponse] +
+
+
+
+ +Expand source code + +
async def create_code_post(
+    self,
+    email: Union[str, None],
+    phone_number: Union[str, None],
+    tenant_id: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+) -> Union[CreateCodePostOkResult, GeneralErrorResponse]:
+    user_input_code = None
+    if api_options.config.get_custom_user_input_code is not None:
+        user_input_code = await api_options.config.get_custom_user_input_code(
+            tenant_id, user_context
+        )
+    response = await api_options.recipe_implementation.create_code(
+        email, phone_number, user_input_code, tenant_id, user_context
+    )
+    magic_link = None
+    user_input_code = None
+    flow_type = api_options.config.flow_type
+    if flow_type in ("MAGIC_LINK", "USER_INPUT_CODE_AND_MAGIC_LINK"):
+        magic_link = (
+            api_options.app_info.get_origin(
+                api_options.request, user_context
+            ).get_as_string_dangerous()
+            + api_options.app_info.website_base_path.get_as_string_dangerous()
+            + "/verify"
+            + "?preAuthSessionId="
+            + response.pre_auth_session_id
+            + "&tenantId="
+            + tenant_id
+            + "#"
+            + response.link_code
+        )
+    if flow_type in ("USER_INPUT_CODE", "USER_INPUT_CODE_AND_MAGIC_LINK"):
+        user_input_code = response.user_input_code
+
+    if isinstance(api_options.config.contact_config, ContactEmailOnlyConfig) or (
+        isinstance(api_options.config.contact_config, ContactEmailOrPhoneConfig)
+        and email is not None
+    ):
+        if email is None:
+            raise Exception("Should never come here")
+
+        log_debug_message("Sending passwordless login email to %s", email)
+        passwordless_email_delivery_input = PasswordlessLoginEmailTemplateVars(
+            email=email,
+            user_input_code=user_input_code,
+            url_with_link_code=magic_link,
+            code_life_time=response.code_life_time,
+            pre_auth_session_id=response.pre_auth_session_id,
+            tenant_id=tenant_id,
+        )
+        await api_options.email_delivery.ingredient_interface_impl.send_email(
+            passwordless_email_delivery_input, user_context
+        )
+    elif isinstance(
+        api_options.config.contact_config,
+        (ContactEmailOrPhoneConfig, ContactPhoneOnlyConfig),
+    ):
+        if phone_number is None:
+            raise Exception("Should never come here")
+        log_debug_message("Sending passwordless login SMS to %s", phone_number)
+        sms_input = PasswordlessLoginSMSTemplateVars(
+            phone_number=phone_number,
+            user_input_code=user_input_code,
+            url_with_link_code=magic_link,
+            code_life_time=response.code_life_time,
+            pre_auth_session_id=response.pre_auth_session_id,
+            tenant_id=tenant_id,
+        )
+        await api_options.sms_delivery.ingredient_interface_impl.send_sms(
+            sms_input, user_context
+        )
+
+    return CreateCodePostOkResult(
+        response.device_id, response.pre_auth_session_id, flow_type
+    )
+
+
+
+async def email_exists_get(self, email: str, tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[EmailExistsGetOkResultGeneralErrorResponse] +
+
+
+
+ +Expand source code + +
async def email_exists_get(
+    self,
+    email: str,
+    tenant_id: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+) -> Union[EmailExistsGetOkResult, GeneralErrorResponse]:
+    response = await api_options.recipe_implementation.get_user_by_email(
+        email, tenant_id, user_context
+    )
+    return EmailExistsGetOkResult(exists=response is not None)
+
+
+
+async def phone_number_exists_get(self, phone_number: str, tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[PhoneNumberExistsGetOkResultGeneralErrorResponse] +
+
+
+
+ +Expand source code + +
async def phone_number_exists_get(
+    self,
+    phone_number: str,
+    tenant_id: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+) -> Union[PhoneNumberExistsGetOkResult, GeneralErrorResponse]:
+    response = await api_options.recipe_implementation.get_user_by_phone_number(
+        phone_number, tenant_id, user_context
+    )
+    return PhoneNumberExistsGetOkResult(exists=response is not None)
+
+
+
+async def resend_code_post(self, device_id: str, pre_auth_session_id: str, tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[ResendCodePostOkResultResendCodePostRestartFlowErrorGeneralErrorResponse] +
+
+
+
+ +Expand source code + +
async def resend_code_post(
+    self,
+    device_id: str,
+    pre_auth_session_id: str,
+    tenant_id: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+) -> Union[
+    ResendCodePostOkResult, ResendCodePostRestartFlowError, GeneralErrorResponse
+]:
+    device_info = await api_options.recipe_implementation.list_codes_by_device_id(
+        device_id=device_id, tenant_id=tenant_id, user_context=user_context
+    )
+    if device_info is None:
+        return ResendCodePostRestartFlowError()
+    if (
+        isinstance(api_options.config.contact_config, ContactEmailOnlyConfig)
+        and device_info.email is None
+    ) or (
+        isinstance(api_options.config.contact_config, ContactPhoneOnlyConfig)
+        and device_info.phone_number is None
+    ):
+        return ResendCodePostRestartFlowError()
+    number_of_tries_to_create_new_code = 0
+    while True:
+        number_of_tries_to_create_new_code += 1
+        user_input_code = None
+        if api_options.config.get_custom_user_input_code is not None:
+            user_input_code = await api_options.config.get_custom_user_input_code(
+                tenant_id, user_context
+            )
+        response = (
+            await api_options.recipe_implementation.create_new_code_for_device(
+                device_id=device_id,
+                user_input_code=user_input_code,
+                tenant_id=tenant_id,
+                user_context=user_context,
+            )
+        )
+        if isinstance(
+            response, CreateNewCodeForDeviceUserInputCodeAlreadyUsedError
+        ):
+            if number_of_tries_to_create_new_code >= 3:
+                return GeneralErrorResponse(
+                    "Failed to generate a one time code. Please try again"
+                )
+            continue
+
+        if isinstance(response, CreateNewCodeForDeviceOkResult):
+            magic_link = None
+            user_input_code = None
+            flow_type = api_options.config.flow_type
+            if flow_type in ("MAGIC_LINK", "USER_INPUT_CODE_AND_MAGIC_LINK"):
+                magic_link = (
+                    api_options.app_info.get_origin(
+                        api_options.request, user_context
+                    ).get_as_string_dangerous()
+                    + api_options.app_info.website_base_path.get_as_string_dangerous()
+                    + "/verify"
+                    + "?preAuthSessionId="
+                    + response.pre_auth_session_id
+                    + "&tenantId="
+                    + tenant_id
+                    + "#"
+                    + response.link_code
+                )
+            if flow_type in ("USER_INPUT_CODE", "USER_INPUT_CODE_AND_MAGIC_LINK"):
+                user_input_code = response.user_input_code
+
+            if isinstance(
+                api_options.config.contact_config, ContactEmailOnlyConfig
+            ) or (
+                isinstance(
+                    api_options.config.contact_config, ContactEmailOrPhoneConfig
+                )
+                and device_info.email is not None
+            ):
+                if device_info.email is None:
+                    raise Exception("Should never come here")
+
+                log_debug_message(
+                    "Sending passwordless login email to %s", device_info.email
+                )
+                passwordless_email_delivery_input = (
+                    PasswordlessLoginEmailTemplateVars(
+                        email=device_info.email,
+                        user_input_code=user_input_code,
+                        url_with_link_code=magic_link,
+                        code_life_time=response.code_life_time,
+                        pre_auth_session_id=response.pre_auth_session_id,
+                        tenant_id=tenant_id,
+                    )
+                )
+                await api_options.email_delivery.ingredient_interface_impl.send_email(
+                    passwordless_email_delivery_input, user_context
+                )
+            elif isinstance(
+                api_options.config.contact_config,
+                (ContactEmailOrPhoneConfig, ContactPhoneOnlyConfig),
+            ):
+                if device_info.phone_number is None:
+                    raise Exception("Should never come here")
+                log_debug_message(
+                    "Sending passwordless login SMS to %s", device_info.phone_number
+                )
+                sms_input = PasswordlessLoginSMSTemplateVars(
+                    phone_number=device_info.phone_number,
+                    user_input_code=user_input_code,
+                    url_with_link_code=magic_link,
+                    code_life_time=response.code_life_time,
+                    pre_auth_session_id=response.pre_auth_session_id,
+                    tenant_id=tenant_id,
+                )
+                await api_options.sms_delivery.ingredient_interface_impl.send_sms(
+                    sms_input, user_context
+                )
+            return ResendCodePostOkResult()
+        return ResendCodePostRestartFlowError()
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/passwordless/api/index.html b/html/supertokens_python/recipe/passwordless/api/index.html new file mode 100644 index 000000000..2ba44e4dd --- /dev/null +++ b/html/supertokens_python/recipe/passwordless/api/index.html @@ -0,0 +1,114 @@ + + + + + + +supertokens_python.recipe.passwordless.api API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.passwordless.api

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from .consume_code import consume_code  # type: ignore
+from .create_code import create_code  # type: ignore
+from .email_exists import email_exists  # type: ignore
+from .phone_number_exists import phone_number_exists  # type: ignore
+from .resend_code import resend_code  # type: ignore
+
+
+
+

Sub-modules

+
+
supertokens_python.recipe.passwordless.api.consume_code
+
+
+
+
supertokens_python.recipe.passwordless.api.create_code
+
+
+
+
supertokens_python.recipe.passwordless.api.email_exists
+
+
+
+
supertokens_python.recipe.passwordless.api.implementation
+
+
+
+
supertokens_python.recipe.passwordless.api.phone_number_exists
+
+
+
+
supertokens_python.recipe.passwordless.api.resend_code
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/passwordless/api/phone_number_exists.html b/html/supertokens_python/recipe/passwordless/api/phone_number_exists.html new file mode 100644 index 000000000..6caf5508e --- /dev/null +++ b/html/supertokens_python/recipe/passwordless/api/phone_number_exists.html @@ -0,0 +1,130 @@ + + + + + + +supertokens_python.recipe.passwordless.api.phone_number_exists API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.passwordless.api.phone_number_exists

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from typing import Any, Dict
+from supertokens_python.exceptions import raise_bad_input_exception
+from supertokens_python.recipe.passwordless.interfaces import APIInterface, APIOptions
+from supertokens_python.utils import send_200_response
+
+
+async def phone_number_exists(
+    api_implementation: APIInterface,
+    tenant_id: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+):
+    if api_implementation.disable_phone_number_exists_get:
+        return None
+
+    phone_number = api_options.request.get_query_param("phoneNumber")
+    if phone_number is None:
+        raise_bad_input_exception("Please provide the phoneNumber as a GET param")
+
+    result = await api_implementation.phone_number_exists_get(
+        phone_number, tenant_id, api_options, user_context
+    )
+    return send_200_response(result.to_json(), api_options.response)
+
+
+
+
+
+
+
+

Functions

+
+
+async def phone_number_exists(api_implementation: APIInterface, tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) +
+
+
+
+ +Expand source code + +
async def phone_number_exists(
+    api_implementation: APIInterface,
+    tenant_id: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+):
+    if api_implementation.disable_phone_number_exists_get:
+        return None
+
+    phone_number = api_options.request.get_query_param("phoneNumber")
+    if phone_number is None:
+        raise_bad_input_exception("Please provide the phoneNumber as a GET param")
+
+    result = await api_implementation.phone_number_exists_get(
+        phone_number, tenant_id, api_options, user_context
+    )
+    return send_200_response(result.to_json(), api_options.response)
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/passwordless/api/resend_code.html b/html/supertokens_python/recipe/passwordless/api/resend_code.html new file mode 100644 index 000000000..25178d232 --- /dev/null +++ b/html/supertokens_python/recipe/passwordless/api/resend_code.html @@ -0,0 +1,148 @@ + + + + + + +supertokens_python.recipe.passwordless.api.resend_code API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.passwordless.api.resend_code

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from typing import Any, Dict
+from supertokens_python.exceptions import raise_bad_input_exception
+from supertokens_python.recipe.passwordless.interfaces import APIInterface, APIOptions
+from supertokens_python.utils import send_200_response
+
+
+async def resend_code(
+    api_implementation: APIInterface,
+    tenant_id: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+):
+    if api_implementation.disable_resend_code_post:
+        return None
+    body = await api_options.request.json()
+
+    if body is None:
+        raise_bad_input_exception("Please provide a JSON body")
+
+    if "preAuthSessionId" not in body:
+        raise_bad_input_exception("Please provide preAuthSessionId")
+
+    if "deviceId" not in body:
+        raise_bad_input_exception("Please provide deviceId")
+
+    pre_auth_session_id = body["preAuthSessionId"]
+    device_id = body["deviceId"]
+
+    result = await api_implementation.resend_code_post(
+        device_id, pre_auth_session_id, tenant_id, api_options, user_context
+    )
+    return send_200_response(result.to_json(), api_options.response)
+
+
+
+
+
+
+
+

Functions

+
+
+async def resend_code(api_implementation: APIInterface, tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) +
+
+
+
+ +Expand source code + +
async def resend_code(
+    api_implementation: APIInterface,
+    tenant_id: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+):
+    if api_implementation.disable_resend_code_post:
+        return None
+    body = await api_options.request.json()
+
+    if body is None:
+        raise_bad_input_exception("Please provide a JSON body")
+
+    if "preAuthSessionId" not in body:
+        raise_bad_input_exception("Please provide preAuthSessionId")
+
+    if "deviceId" not in body:
+        raise_bad_input_exception("Please provide deviceId")
+
+    pre_auth_session_id = body["preAuthSessionId"]
+    device_id = body["deviceId"]
+
+    result = await api_implementation.resend_code_post(
+        device_id, pre_auth_session_id, tenant_id, api_options, user_context
+    )
+    return send_200_response(result.to_json(), api_options.response)
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/passwordless/asyncio/index.html b/html/supertokens_python/recipe/passwordless/asyncio/index.html new file mode 100644 index 000000000..6b5c4df05 --- /dev/null +++ b/html/supertokens_python/recipe/passwordless/asyncio/index.html @@ -0,0 +1,848 @@ + + + + + + +supertokens_python.recipe.passwordless.asyncio API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.passwordless.asyncio

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from typing import Any, Dict, List, Union
+from supertokens_python import get_request_from_user_context
+
+from supertokens_python.recipe.passwordless.interfaces import (
+    ConsumeCodeExpiredUserInputCodeError,
+    ConsumeCodeIncorrectUserInputCodeError,
+    ConsumeCodeOkResult,
+    ConsumeCodeRestartFlowError,
+    CreateCodeOkResult,
+    CreateNewCodeForDeviceOkResult,
+    CreateNewCodeForDeviceRestartFlowError,
+    CreateNewCodeForDeviceUserInputCodeAlreadyUsedError,
+    DeleteUserInfoOkResult,
+    DeleteUserInfoUnknownUserIdError,
+    RevokeAllCodesOkResult,
+    RevokeCodeOkResult,
+    UpdateUserEmailAlreadyExistsError,
+    UpdateUserOkResult,
+    UpdateUserPhoneNumberAlreadyExistsError,
+    UpdateUserUnknownUserIdError,
+)
+from supertokens_python.recipe.passwordless.recipe import PasswordlessRecipe
+from supertokens_python.recipe.passwordless.types import (
+    DeviceType,
+    EmailTemplateVars,
+    SMSTemplateVars,
+    User,
+)
+
+
+async def create_code(
+    tenant_id: str,
+    email: Union[None, str] = None,
+    phone_number: Union[None, str] = None,
+    user_input_code: Union[None, str] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> CreateCodeOkResult:
+    if user_context is None:
+        user_context = {}
+    return await PasswordlessRecipe.get_instance().recipe_implementation.create_code(
+        email=email,
+        phone_number=phone_number,
+        user_input_code=user_input_code,
+        tenant_id=tenant_id,
+        user_context=user_context,
+    )
+
+
+async def create_new_code_for_device(
+    tenant_id: str,
+    device_id: str,
+    user_input_code: Union[str, None] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> Union[
+    CreateNewCodeForDeviceOkResult,
+    CreateNewCodeForDeviceRestartFlowError,
+    CreateNewCodeForDeviceUserInputCodeAlreadyUsedError,
+]:
+    if user_context is None:
+        user_context = {}
+    return await PasswordlessRecipe.get_instance().recipe_implementation.create_new_code_for_device(
+        device_id=device_id,
+        user_input_code=user_input_code,
+        tenant_id=tenant_id,
+        user_context=user_context,
+    )
+
+
+async def consume_code(
+    tenant_id: str,
+    pre_auth_session_id: str,
+    user_input_code: Union[str, None] = None,
+    device_id: Union[str, None] = None,
+    link_code: Union[str, None] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> Union[
+    ConsumeCodeOkResult,
+    ConsumeCodeIncorrectUserInputCodeError,
+    ConsumeCodeExpiredUserInputCodeError,
+    ConsumeCodeRestartFlowError,
+]:
+    if user_context is None:
+        user_context = {}
+    return await PasswordlessRecipe.get_instance().recipe_implementation.consume_code(
+        pre_auth_session_id=pre_auth_session_id,
+        user_input_code=user_input_code,
+        device_id=device_id,
+        link_code=link_code,
+        tenant_id=tenant_id,
+        user_context=user_context,
+    )
+
+
+async def get_user_by_id(
+    user_id: str, user_context: Union[None, Dict[str, Any]] = None
+) -> Union[User, None]:
+    if user_context is None:
+        user_context = {}
+    return await PasswordlessRecipe.get_instance().recipe_implementation.get_user_by_id(
+        user_id=user_id, user_context=user_context
+    )
+
+
+async def get_user_by_email(
+    tenant_id: str, email: str, user_context: Union[None, Dict[str, Any]] = None
+) -> Union[User, None]:
+    if user_context is None:
+        user_context = {}
+    return (
+        await PasswordlessRecipe.get_instance().recipe_implementation.get_user_by_email(
+            email=email,
+            tenant_id=tenant_id,
+            user_context=user_context,
+        )
+    )
+
+
+async def get_user_by_phone_number(
+    tenant_id: str, phone_number: str, user_context: Union[None, Dict[str, Any]] = None
+) -> Union[User, None]:
+    if user_context is None:
+        user_context = {}
+    return await PasswordlessRecipe.get_instance().recipe_implementation.get_user_by_phone_number(
+        phone_number=phone_number,
+        tenant_id=tenant_id,
+        user_context=user_context,
+    )
+
+
+async def update_user(
+    user_id: str,
+    email: Union[str, None] = None,
+    phone_number: Union[str, None] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> Union[
+    UpdateUserOkResult,
+    UpdateUserUnknownUserIdError,
+    UpdateUserEmailAlreadyExistsError,
+    UpdateUserPhoneNumberAlreadyExistsError,
+]:
+    if user_context is None:
+        user_context = {}
+    return await PasswordlessRecipe.get_instance().recipe_implementation.update_user(
+        user_id=user_id,
+        email=email,
+        phone_number=phone_number,
+        user_context=user_context,
+    )
+
+
+async def delete_email_for_user(
+    user_id: str, user_context: Union[None, Dict[str, Any]] = None
+) -> Union[DeleteUserInfoOkResult, DeleteUserInfoUnknownUserIdError]:
+    if user_context is None:
+        user_context = {}
+    return await PasswordlessRecipe.get_instance().recipe_implementation.delete_email_for_user(
+        user_id=user_id, user_context=user_context
+    )
+
+
+async def delete_phone_number_for_user(
+    user_id: str, user_context: Union[None, Dict[str, Any]] = None
+) -> Union[DeleteUserInfoOkResult, DeleteUserInfoUnknownUserIdError]:
+    if user_context is None:
+        user_context = {}
+    return await PasswordlessRecipe.get_instance().recipe_implementation.delete_phone_number_for_user(
+        user_id=user_id, user_context=user_context
+    )
+
+
+async def revoke_all_codes(
+    tenant_id: str,
+    email: Union[str, None] = None,
+    phone_number: Union[str, None] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> RevokeAllCodesOkResult:
+    if user_context is None:
+        user_context = {}
+    return (
+        await PasswordlessRecipe.get_instance().recipe_implementation.revoke_all_codes(
+            email=email,
+            phone_number=phone_number,
+            tenant_id=tenant_id,
+            user_context=user_context,
+        )
+    )
+
+
+async def revoke_code(
+    tenant_id: str, code_id: str, user_context: Union[None, Dict[str, Any]] = None
+) -> RevokeCodeOkResult:
+    if user_context is None:
+        user_context = {}
+    return await PasswordlessRecipe.get_instance().recipe_implementation.revoke_code(
+        tenant_id=tenant_id,
+        code_id=code_id,
+        user_context=user_context,
+    )
+
+
+async def list_codes_by_email(
+    tenant_id: str, email: str, user_context: Union[None, Dict[str, Any]] = None
+) -> List[DeviceType]:
+    if user_context is None:
+        user_context = {}
+    return await PasswordlessRecipe.get_instance().recipe_implementation.list_codes_by_email(
+        email=email, tenant_id=tenant_id, user_context=user_context
+    )
+
+
+async def list_codes_by_phone_number(
+    tenant_id: str, phone_number: str, user_context: Union[None, Dict[str, Any]] = None
+) -> List[DeviceType]:
+    if user_context is None:
+        user_context = {}
+    return await PasswordlessRecipe.get_instance().recipe_implementation.list_codes_by_phone_number(
+        phone_number=phone_number,
+        tenant_id=tenant_id,
+        user_context=user_context,
+    )
+
+
+async def list_codes_by_device_id(
+    tenant_id: str, device_id: str, user_context: Union[None, Dict[str, Any]] = None
+) -> Union[DeviceType, None]:
+    if user_context is None:
+        user_context = {}
+    return await PasswordlessRecipe.get_instance().recipe_implementation.list_codes_by_device_id(
+        device_id=device_id,
+        tenant_id=tenant_id,
+        user_context=user_context,
+    )
+
+
+async def list_codes_by_pre_auth_session_id(
+    tenant_id: str,
+    pre_auth_session_id: str,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> Union[DeviceType, None]:
+    if user_context is None:
+        user_context = {}
+    return await PasswordlessRecipe.get_instance().recipe_implementation.list_codes_by_pre_auth_session_id(
+        pre_auth_session_id=pre_auth_session_id,
+        tenant_id=tenant_id,
+        user_context=user_context,
+    )
+
+
+async def create_magic_link(
+    tenant_id: str,
+    email: Union[str, None],
+    phone_number: Union[str, None],
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> str:
+    if user_context is None:
+        user_context = {}
+    request = get_request_from_user_context(user_context)
+    return await PasswordlessRecipe.get_instance().create_magic_link(
+        email=email,
+        phone_number=phone_number,
+        tenant_id=tenant_id,
+        request=request,
+        user_context=user_context,
+    )
+
+
+async def signinup(
+    tenant_id: str,
+    email: Union[str, None],
+    phone_number: Union[str, None],
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> ConsumeCodeOkResult:
+    if user_context is None:
+        user_context = {}
+    return await PasswordlessRecipe.get_instance().signinup(
+        email=email,
+        phone_number=phone_number,
+        tenant_id=tenant_id,
+        user_context=user_context,
+    )
+
+
+async def send_email(
+    input_: EmailTemplateVars,
+    user_context: Union[None, Dict[str, Any]] = None,
+):
+    if user_context is None:
+        user_context = {}
+
+    return await PasswordlessRecipe.get_instance().email_delivery.ingredient_interface_impl.send_email(
+        input_, user_context
+    )
+
+
+async def send_sms(
+    input_: SMSTemplateVars,
+    user_context: Union[None, Dict[str, Any]] = None,
+):
+    if user_context is None:
+        user_context = {}
+
+    return await PasswordlessRecipe.get_instance().sms_delivery.ingredient_interface_impl.send_sms(
+        input_, user_context
+    )
+
+
+
+
+
+
+
+

Functions

+
+
+async def consume_code(tenant_id: str, pre_auth_session_id: str, user_input_code: Optional[str] = None, device_id: Optional[str] = None, link_code: Optional[str] = None, user_context: Optional[Dict[str, Any]] = None) ‑> Union[ConsumeCodeOkResultConsumeCodeIncorrectUserInputCodeErrorConsumeCodeExpiredUserInputCodeErrorConsumeCodeRestartFlowError] +
+
+
+
+ +Expand source code + +
async def consume_code(
+    tenant_id: str,
+    pre_auth_session_id: str,
+    user_input_code: Union[str, None] = None,
+    device_id: Union[str, None] = None,
+    link_code: Union[str, None] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> Union[
+    ConsumeCodeOkResult,
+    ConsumeCodeIncorrectUserInputCodeError,
+    ConsumeCodeExpiredUserInputCodeError,
+    ConsumeCodeRestartFlowError,
+]:
+    if user_context is None:
+        user_context = {}
+    return await PasswordlessRecipe.get_instance().recipe_implementation.consume_code(
+        pre_auth_session_id=pre_auth_session_id,
+        user_input_code=user_input_code,
+        device_id=device_id,
+        link_code=link_code,
+        tenant_id=tenant_id,
+        user_context=user_context,
+    )
+
+
+
+async def create_code(tenant_id: str, email: Optional[str] = None, phone_number: Optional[str] = None, user_input_code: Optional[str] = None, user_context: Optional[Dict[str, Any]] = None) ‑> CreateCodeOkResult +
+
+
+
+ +Expand source code + +
async def create_code(
+    tenant_id: str,
+    email: Union[None, str] = None,
+    phone_number: Union[None, str] = None,
+    user_input_code: Union[None, str] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> CreateCodeOkResult:
+    if user_context is None:
+        user_context = {}
+    return await PasswordlessRecipe.get_instance().recipe_implementation.create_code(
+        email=email,
+        phone_number=phone_number,
+        user_input_code=user_input_code,
+        tenant_id=tenant_id,
+        user_context=user_context,
+    )
+
+
+ +
+
+
+ +Expand source code + +
async def create_magic_link(
+    tenant_id: str,
+    email: Union[str, None],
+    phone_number: Union[str, None],
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> str:
+    if user_context is None:
+        user_context = {}
+    request = get_request_from_user_context(user_context)
+    return await PasswordlessRecipe.get_instance().create_magic_link(
+        email=email,
+        phone_number=phone_number,
+        tenant_id=tenant_id,
+        request=request,
+        user_context=user_context,
+    )
+
+
+
+async def create_new_code_for_device(tenant_id: str, device_id: str, user_input_code: Optional[str] = None, user_context: Optional[Dict[str, Any]] = None) ‑> Union[CreateNewCodeForDeviceOkResultCreateNewCodeForDeviceRestartFlowErrorCreateNewCodeForDeviceUserInputCodeAlreadyUsedError] +
+
+
+
+ +Expand source code + +
async def create_new_code_for_device(
+    tenant_id: str,
+    device_id: str,
+    user_input_code: Union[str, None] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> Union[
+    CreateNewCodeForDeviceOkResult,
+    CreateNewCodeForDeviceRestartFlowError,
+    CreateNewCodeForDeviceUserInputCodeAlreadyUsedError,
+]:
+    if user_context is None:
+        user_context = {}
+    return await PasswordlessRecipe.get_instance().recipe_implementation.create_new_code_for_device(
+        device_id=device_id,
+        user_input_code=user_input_code,
+        tenant_id=tenant_id,
+        user_context=user_context,
+    )
+
+
+
+async def delete_email_for_user(user_id: str, user_context: Optional[Dict[str, Any]] = None) ‑> Union[DeleteUserInfoOkResultDeleteUserInfoUnknownUserIdError] +
+
+
+
+ +Expand source code + +
async def delete_email_for_user(
+    user_id: str, user_context: Union[None, Dict[str, Any]] = None
+) -> Union[DeleteUserInfoOkResult, DeleteUserInfoUnknownUserIdError]:
+    if user_context is None:
+        user_context = {}
+    return await PasswordlessRecipe.get_instance().recipe_implementation.delete_email_for_user(
+        user_id=user_id, user_context=user_context
+    )
+
+
+
+async def delete_phone_number_for_user(user_id: str, user_context: Optional[Dict[str, Any]] = None) ‑> Union[DeleteUserInfoOkResultDeleteUserInfoUnknownUserIdError] +
+
+
+
+ +Expand source code + +
async def delete_phone_number_for_user(
+    user_id: str, user_context: Union[None, Dict[str, Any]] = None
+) -> Union[DeleteUserInfoOkResult, DeleteUserInfoUnknownUserIdError]:
+    if user_context is None:
+        user_context = {}
+    return await PasswordlessRecipe.get_instance().recipe_implementation.delete_phone_number_for_user(
+        user_id=user_id, user_context=user_context
+    )
+
+
+
+async def get_user_by_email(tenant_id: str, email: str, user_context: Optional[Dict[str, Any]] = None) ‑> Optional[User] +
+
+
+
+ +Expand source code + +
async def get_user_by_email(
+    tenant_id: str, email: str, user_context: Union[None, Dict[str, Any]] = None
+) -> Union[User, None]:
+    if user_context is None:
+        user_context = {}
+    return (
+        await PasswordlessRecipe.get_instance().recipe_implementation.get_user_by_email(
+            email=email,
+            tenant_id=tenant_id,
+            user_context=user_context,
+        )
+    )
+
+
+
+async def get_user_by_id(user_id: str, user_context: Optional[Dict[str, Any]] = None) ‑> Optional[User] +
+
+
+
+ +Expand source code + +
async def get_user_by_id(
+    user_id: str, user_context: Union[None, Dict[str, Any]] = None
+) -> Union[User, None]:
+    if user_context is None:
+        user_context = {}
+    return await PasswordlessRecipe.get_instance().recipe_implementation.get_user_by_id(
+        user_id=user_id, user_context=user_context
+    )
+
+
+
+async def get_user_by_phone_number(tenant_id: str, phone_number: str, user_context: Optional[Dict[str, Any]] = None) ‑> Optional[User] +
+
+
+
+ +Expand source code + +
async def get_user_by_phone_number(
+    tenant_id: str, phone_number: str, user_context: Union[None, Dict[str, Any]] = None
+) -> Union[User, None]:
+    if user_context is None:
+        user_context = {}
+    return await PasswordlessRecipe.get_instance().recipe_implementation.get_user_by_phone_number(
+        phone_number=phone_number,
+        tenant_id=tenant_id,
+        user_context=user_context,
+    )
+
+
+
+async def list_codes_by_device_id(tenant_id: str, device_id: str, user_context: Optional[Dict[str, Any]] = None) ‑> Optional[DeviceType] +
+
+
+
+ +Expand source code + +
async def list_codes_by_device_id(
+    tenant_id: str, device_id: str, user_context: Union[None, Dict[str, Any]] = None
+) -> Union[DeviceType, None]:
+    if user_context is None:
+        user_context = {}
+    return await PasswordlessRecipe.get_instance().recipe_implementation.list_codes_by_device_id(
+        device_id=device_id,
+        tenant_id=tenant_id,
+        user_context=user_context,
+    )
+
+
+
+async def list_codes_by_email(tenant_id: str, email: str, user_context: Optional[Dict[str, Any]] = None) ‑> List[DeviceType] +
+
+
+
+ +Expand source code + +
async def list_codes_by_email(
+    tenant_id: str, email: str, user_context: Union[None, Dict[str, Any]] = None
+) -> List[DeviceType]:
+    if user_context is None:
+        user_context = {}
+    return await PasswordlessRecipe.get_instance().recipe_implementation.list_codes_by_email(
+        email=email, tenant_id=tenant_id, user_context=user_context
+    )
+
+
+
+async def list_codes_by_phone_number(tenant_id: str, phone_number: str, user_context: Optional[Dict[str, Any]] = None) ‑> List[DeviceType] +
+
+
+
+ +Expand source code + +
async def list_codes_by_phone_number(
+    tenant_id: str, phone_number: str, user_context: Union[None, Dict[str, Any]] = None
+) -> List[DeviceType]:
+    if user_context is None:
+        user_context = {}
+    return await PasswordlessRecipe.get_instance().recipe_implementation.list_codes_by_phone_number(
+        phone_number=phone_number,
+        tenant_id=tenant_id,
+        user_context=user_context,
+    )
+
+
+
+async def list_codes_by_pre_auth_session_id(tenant_id: str, pre_auth_session_id: str, user_context: Optional[Dict[str, Any]] = None) ‑> Optional[DeviceType] +
+
+
+
+ +Expand source code + +
async def list_codes_by_pre_auth_session_id(
+    tenant_id: str,
+    pre_auth_session_id: str,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> Union[DeviceType, None]:
+    if user_context is None:
+        user_context = {}
+    return await PasswordlessRecipe.get_instance().recipe_implementation.list_codes_by_pre_auth_session_id(
+        pre_auth_session_id=pre_auth_session_id,
+        tenant_id=tenant_id,
+        user_context=user_context,
+    )
+
+
+
+async def revoke_all_codes(tenant_id: str, email: Optional[str] = None, phone_number: Optional[str] = None, user_context: Optional[Dict[str, Any]] = None) ‑> RevokeAllCodesOkResult +
+
+
+
+ +Expand source code + +
async def revoke_all_codes(
+    tenant_id: str,
+    email: Union[str, None] = None,
+    phone_number: Union[str, None] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> RevokeAllCodesOkResult:
+    if user_context is None:
+        user_context = {}
+    return (
+        await PasswordlessRecipe.get_instance().recipe_implementation.revoke_all_codes(
+            email=email,
+            phone_number=phone_number,
+            tenant_id=tenant_id,
+            user_context=user_context,
+        )
+    )
+
+
+
+async def revoke_code(tenant_id: str, code_id: str, user_context: Optional[Dict[str, Any]] = None) ‑> RevokeCodeOkResult +
+
+
+
+ +Expand source code + +
async def revoke_code(
+    tenant_id: str, code_id: str, user_context: Union[None, Dict[str, Any]] = None
+) -> RevokeCodeOkResult:
+    if user_context is None:
+        user_context = {}
+    return await PasswordlessRecipe.get_instance().recipe_implementation.revoke_code(
+        tenant_id=tenant_id,
+        code_id=code_id,
+        user_context=user_context,
+    )
+
+
+
+async def send_email(input_: CreateAndSendCustomEmailParameters, user_context: Optional[Dict[str, Any]] = None) +
+
+
+
+ +Expand source code + +
async def send_email(
+    input_: EmailTemplateVars,
+    user_context: Union[None, Dict[str, Any]] = None,
+):
+    if user_context is None:
+        user_context = {}
+
+    return await PasswordlessRecipe.get_instance().email_delivery.ingredient_interface_impl.send_email(
+        input_, user_context
+    )
+
+
+
+async def send_sms(input_: CreateAndSendCustomTextMessageParameters, user_context: Optional[Dict[str, Any]] = None) +
+
+
+
+ +Expand source code + +
async def send_sms(
+    input_: SMSTemplateVars,
+    user_context: Union[None, Dict[str, Any]] = None,
+):
+    if user_context is None:
+        user_context = {}
+
+    return await PasswordlessRecipe.get_instance().sms_delivery.ingredient_interface_impl.send_sms(
+        input_, user_context
+    )
+
+
+
+async def signinup(tenant_id: str, email: Optional[str], phone_number: Optional[str], user_context: Optional[Dict[str, Any]] = None) ‑> ConsumeCodeOkResult +
+
+
+
+ +Expand source code + +
async def signinup(
+    tenant_id: str,
+    email: Union[str, None],
+    phone_number: Union[str, None],
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> ConsumeCodeOkResult:
+    if user_context is None:
+        user_context = {}
+    return await PasswordlessRecipe.get_instance().signinup(
+        email=email,
+        phone_number=phone_number,
+        tenant_id=tenant_id,
+        user_context=user_context,
+    )
+
+
+
+async def update_user(user_id: str, email: Optional[str] = None, phone_number: Optional[str] = None, user_context: Optional[Dict[str, Any]] = None) ‑> Union[UpdateUserOkResultUpdateUserUnknownUserIdErrorUpdateUserEmailAlreadyExistsErrorUpdateUserPhoneNumberAlreadyExistsError] +
+
+
+
+ +Expand source code + +
async def update_user(
+    user_id: str,
+    email: Union[str, None] = None,
+    phone_number: Union[str, None] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> Union[
+    UpdateUserOkResult,
+    UpdateUserUnknownUserIdError,
+    UpdateUserEmailAlreadyExistsError,
+    UpdateUserPhoneNumberAlreadyExistsError,
+]:
+    if user_context is None:
+        user_context = {}
+    return await PasswordlessRecipe.get_instance().recipe_implementation.update_user(
+        user_id=user_id,
+        email=email,
+        phone_number=phone_number,
+        user_context=user_context,
+    )
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/passwordless/constants.html b/html/supertokens_python/recipe/passwordless/constants.html new file mode 100644 index 000000000..eab31385c --- /dev/null +++ b/html/supertokens_python/recipe/passwordless/constants.html @@ -0,0 +1,79 @@ + + + + + + +supertokens_python.recipe.passwordless.constants API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.passwordless.constants

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+CREATE_CODE_API = "/signinup/code"
+RESEND_CODE_API = "/signinup/code/resend"
+CONSUME_CODE_API = "/signinup/code/consume"
+DOES_EMAIL_EXIST_API_OLD = "/signup/email/exists"
+DOES_PHONE_NUMBER_EXIST_API_OLD = "/signup/phonenumber/exists"
+DOES_EMAIL_EXIST_API = "/passwordless/email/exists"
+DOES_PHONE_NUMBER_EXIST_API = "/passwordless/phonenumber/exists"
+
+
+
+
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/passwordless/emaildelivery/index.html b/html/supertokens_python/recipe/passwordless/emaildelivery/index.html new file mode 100644 index 000000000..dc4476125 --- /dev/null +++ b/html/supertokens_python/recipe/passwordless/emaildelivery/index.html @@ -0,0 +1,83 @@ + + + + + + +supertokens_python.recipe.passwordless.emaildelivery API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.passwordless.emaildelivery

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+
+
+

Sub-modules

+
+
supertokens_python.recipe.passwordless.emaildelivery.services
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/passwordless/emaildelivery/services/backward_compatibility/index.html b/html/supertokens_python/recipe/passwordless/emaildelivery/services/backward_compatibility/index.html new file mode 100644 index 000000000..e6be99a9f --- /dev/null +++ b/html/supertokens_python/recipe/passwordless/emaildelivery/services/backward_compatibility/index.html @@ -0,0 +1,267 @@ + + + + + + +supertokens_python.recipe.passwordless.emaildelivery.services.backward_compatibility API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.passwordless.emaildelivery.services.backward_compatibility

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from __future__ import annotations
+
+import json
+from os import environ
+from typing import Any, Dict
+
+from httpx import AsyncClient, HTTPStatusError
+from supertokens_python.ingredients.emaildelivery import EmailDeliveryInterface
+from supertokens_python.logger import log_debug_message
+from supertokens_python.recipe.passwordless.types import (
+    PasswordlessLoginEmailTemplateVars,
+)
+from supertokens_python.supertokens import AppInfo
+from supertokens_python.utils import handle_httpx_client_exceptions
+
+
+async def create_and_send_email_with_supertokens_service(
+    app_info: AppInfo, input_: PasswordlessLoginEmailTemplateVars
+) -> None:
+    if ("SUPERTOKENS_ENV" in environ) and (environ["SUPERTOKENS_ENV"] == "testing"):
+        return
+
+    data = {
+        "email": input_.email,
+        "appName": app_info.app_name,
+        "codeLifetime": input_.code_life_time,
+    }
+    if input_.url_with_link_code:
+        data["urlWithLinkCode"] = input_.url_with_link_code
+    if input_.user_input_code:
+        data["userInputCode"] = input_.user_input_code
+
+    try:
+        async with AsyncClient(timeout=30.0) as client:
+            resp = await client.post("https://api.supertokens.io/0/st/auth/passwordless/login", json=data, headers={"api-version": "0"})  # type: ignore
+            resp.raise_for_status()
+            log_debug_message("Passwordless login email sent to %s", input_.email)
+    except Exception as e:
+        log_debug_message("Error sending passwordless login email")
+        handle_httpx_client_exceptions(e, data)
+        # If the error is thrown from the API:
+        if isinstance(e, HTTPStatusError):
+            body: Dict[str, Any] = e.response.json()  # type: ignore
+            if body.get("err"):
+                msg = body["err"]
+            else:
+                msg = json.dumps(body)
+
+            raise Exception(msg)
+
+        raise e
+
+
+class BackwardCompatibilityService(
+    EmailDeliveryInterface[PasswordlessLoginEmailTemplateVars]
+):
+    def __init__(
+        self,
+        app_info: AppInfo,
+    ) -> None:
+        self.app_info = app_info
+
+    async def send_email(
+        self,
+        template_vars: PasswordlessLoginEmailTemplateVars,
+        user_context: Dict[str, Any],
+    ) -> None:
+        await create_and_send_email_with_supertokens_service(
+            self.app_info, template_vars
+        )  # Note: intentionally not using try-except (unlike other recipes)
+
+
+
+
+
+
+
+

Functions

+
+
+async def create_and_send_email_with_supertokens_service(app_info: AppInfo, input_: PasswordlessLoginEmailTemplateVars) ‑> None +
+
+
+
+ +Expand source code + +
async def create_and_send_email_with_supertokens_service(
+    app_info: AppInfo, input_: PasswordlessLoginEmailTemplateVars
+) -> None:
+    if ("SUPERTOKENS_ENV" in environ) and (environ["SUPERTOKENS_ENV"] == "testing"):
+        return
+
+    data = {
+        "email": input_.email,
+        "appName": app_info.app_name,
+        "codeLifetime": input_.code_life_time,
+    }
+    if input_.url_with_link_code:
+        data["urlWithLinkCode"] = input_.url_with_link_code
+    if input_.user_input_code:
+        data["userInputCode"] = input_.user_input_code
+
+    try:
+        async with AsyncClient(timeout=30.0) as client:
+            resp = await client.post("https://api.supertokens.io/0/st/auth/passwordless/login", json=data, headers={"api-version": "0"})  # type: ignore
+            resp.raise_for_status()
+            log_debug_message("Passwordless login email sent to %s", input_.email)
+    except Exception as e:
+        log_debug_message("Error sending passwordless login email")
+        handle_httpx_client_exceptions(e, data)
+        # If the error is thrown from the API:
+        if isinstance(e, HTTPStatusError):
+            body: Dict[str, Any] = e.response.json()  # type: ignore
+            if body.get("err"):
+                msg = body["err"]
+            else:
+                msg = json.dumps(body)
+
+            raise Exception(msg)
+
+        raise e
+
+
+
+
+
+

Classes

+
+
+class BackwardCompatibilityService +(app_info: AppInfo) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class BackwardCompatibilityService(
+    EmailDeliveryInterface[PasswordlessLoginEmailTemplateVars]
+):
+    def __init__(
+        self,
+        app_info: AppInfo,
+    ) -> None:
+        self.app_info = app_info
+
+    async def send_email(
+        self,
+        template_vars: PasswordlessLoginEmailTemplateVars,
+        user_context: Dict[str, Any],
+    ) -> None:
+        await create_and_send_email_with_supertokens_service(
+            self.app_info, template_vars
+        )  # Note: intentionally not using try-except (unlike other recipes)
+
+

Ancestors

+ +

Methods

+
+
+async def send_email(self, template_vars: PasswordlessLoginEmailTemplateVars, user_context: Dict[str, Any]) ‑> None +
+
+
+
+ +Expand source code + +
async def send_email(
+    self,
+    template_vars: PasswordlessLoginEmailTemplateVars,
+    user_context: Dict[str, Any],
+) -> None:
+    await create_and_send_email_with_supertokens_service(
+        self.app_info, template_vars
+    )  # Note: intentionally not using try-except (unlike other recipes)
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/passwordless/emaildelivery/services/index.html b/html/supertokens_python/recipe/passwordless/emaildelivery/services/index.html new file mode 100644 index 000000000..6a23ea0a8 --- /dev/null +++ b/html/supertokens_python/recipe/passwordless/emaildelivery/services/index.html @@ -0,0 +1,92 @@ + + + + + + +supertokens_python.recipe.passwordless.emaildelivery.services API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.passwordless.emaildelivery.services

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from . import smtp
+
+SMTPService = smtp.SMTPService
+
+
+
+

Sub-modules

+
+
supertokens_python.recipe.passwordless.emaildelivery.services.backward_compatibility
+
+
+
+
supertokens_python.recipe.passwordless.emaildelivery.services.smtp
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/passwordless/emaildelivery/services/smtp/index.html b/html/supertokens_python/recipe/passwordless/emaildelivery/services/smtp/index.html new file mode 100644 index 000000000..0b0fc2c31 --- /dev/null +++ b/html/supertokens_python/recipe/passwordless/emaildelivery/services/smtp/index.html @@ -0,0 +1,217 @@ + + + + + + +supertokens_python.recipe.passwordless.emaildelivery.services.smtp API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.passwordless.emaildelivery.services.smtp

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from __future__ import annotations
+
+from typing import Any, Dict, Callable, Union
+
+from supertokens_python.ingredients.emaildelivery.services.smtp import Transporter
+from supertokens_python.ingredients.emaildelivery.types import (
+    EmailDeliveryInterface,
+    SMTPServiceInterface,
+    SMTPSettings,
+)
+from supertokens_python.recipe.passwordless.types import (
+    PasswordlessLoginEmailTemplateVars,
+    SMTPOverrideInput,
+)
+
+from .service_implementation import ServiceImplementation
+
+
+class SMTPService(EmailDeliveryInterface[PasswordlessLoginEmailTemplateVars]):
+    service_implementation: SMTPServiceInterface[PasswordlessLoginEmailTemplateVars]
+
+    def __init__(
+        self,
+        smtp_settings: SMTPSettings,
+        override: Union[Callable[[SMTPOverrideInput], SMTPOverrideInput], None] = None,
+    ) -> None:
+        self.transporter = Transporter(smtp_settings)
+        oi = ServiceImplementation(self.transporter)
+        self.service_implementation = oi if override is None else override(oi)
+
+    async def send_email(
+        self,
+        template_vars: PasswordlessLoginEmailTemplateVars,
+        user_context: Dict[str, Any],
+    ) -> None:
+        content = await self.service_implementation.get_content(
+            template_vars, user_context
+        )
+        await self.service_implementation.send_raw_email(content, user_context)
+
+
+
+

Sub-modules

+
+
supertokens_python.recipe.passwordless.emaildelivery.services.smtp.pless_login
+
+
+
+
supertokens_python.recipe.passwordless.emaildelivery.services.smtp.pless_login_email
+
+
+
+
supertokens_python.recipe.passwordless.emaildelivery.services.smtp.service_implementation
+
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class SMTPService +(smtp_settings: SMTPSettings, override: Union[Callable[[SMTPOverrideInput], SMTPOverrideInput], None] = None) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class SMTPService(EmailDeliveryInterface[PasswordlessLoginEmailTemplateVars]):
+    service_implementation: SMTPServiceInterface[PasswordlessLoginEmailTemplateVars]
+
+    def __init__(
+        self,
+        smtp_settings: SMTPSettings,
+        override: Union[Callable[[SMTPOverrideInput], SMTPOverrideInput], None] = None,
+    ) -> None:
+        self.transporter = Transporter(smtp_settings)
+        oi = ServiceImplementation(self.transporter)
+        self.service_implementation = oi if override is None else override(oi)
+
+    async def send_email(
+        self,
+        template_vars: PasswordlessLoginEmailTemplateVars,
+        user_context: Dict[str, Any],
+    ) -> None:
+        content = await self.service_implementation.get_content(
+            template_vars, user_context
+        )
+        await self.service_implementation.send_raw_email(content, user_context)
+
+

Ancestors

+ +

Class variables

+
+
var service_implementationSMTPServiceInterface[CreateAndSendCustomEmailParameters]
+
+
+
+
+

Methods

+
+
+async def send_email(self, template_vars: PasswordlessLoginEmailTemplateVars, user_context: Dict[str, Any]) ‑> None +
+
+
+
+ +Expand source code + +
async def send_email(
+    self,
+    template_vars: PasswordlessLoginEmailTemplateVars,
+    user_context: Dict[str, Any],
+) -> None:
+    content = await self.service_implementation.get_content(
+        template_vars, user_context
+    )
+    await self.service_implementation.send_raw_email(content, user_context)
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/passwordless/emaildelivery/services/smtp/pless_login.html b/html/supertokens_python/recipe/passwordless/emaildelivery/services/smtp/pless_login.html new file mode 100644 index 000000000..e660dbf3a --- /dev/null +++ b/html/supertokens_python/recipe/passwordless/emaildelivery/services/smtp/pless_login.html @@ -0,0 +1,193 @@ + + + + + + +supertokens_python.recipe.passwordless.emaildelivery.services.smtp.pless_login API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.passwordless.emaildelivery.services.smtp.pless_login

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from string import Template
+from typing import TYPE_CHECKING, Union
+
+from supertokens_python.ingredients.emaildelivery.types import EmailContent
+from supertokens_python.supertokens import Supertokens
+from supertokens_python.utils import humanize_time
+
+from .pless_login_email import magic_link_body, otp_and_magic_link_body, otp_body
+
+if TYPE_CHECKING:
+    from supertokens_python.recipe.passwordless.interfaces import (
+        PasswordlessLoginEmailTemplateVars,
+    )
+
+
+def pless_email_content(input_: PasswordlessLoginEmailTemplateVars) -> EmailContent:
+    supertokens = Supertokens.get_instance()
+    app_name = supertokens.app_info.app_name
+    code_lifetime = humanize_time(input_.code_life_time)
+    body = get_pless_email_html(
+        app_name,
+        code_lifetime,
+        input_.email,
+        input_.url_with_link_code,
+        input_.user_input_code,
+    )
+    content_result = EmailContent(body, "Login to your account", input_.email, True)
+    return content_result
+
+
+def get_pless_email_html(
+    app_name: str,
+    code_lifetime: str,
+    email: str,
+    url_with_link_code: Union[str, None] = None,
+    user_input_code: Union[str, None] = None,
+):
+    if (user_input_code is not None) and (url_with_link_code is not None):
+        html_template = otp_and_magic_link_body
+    elif user_input_code is not None:
+        html_template = otp_body
+    elif url_with_link_code is not None:
+        html_template = magic_link_body
+    else:
+        raise Exception("This should never be thrown.")
+
+    return Template(html_template).substitute(
+        appname=app_name,
+        time=code_lifetime,
+        toEmail=email,
+        otp=user_input_code,
+        urlWithLinkCode=url_with_link_code,
+    )
+
+
+
+
+
+
+
+

Functions

+
+
+def get_pless_email_html(app_name: str, code_lifetime: str, email: str, url_with_link_code: Union[str, None] = None, user_input_code: Union[str, None] = None) +
+
+
+
+ +Expand source code + +
def get_pless_email_html(
+    app_name: str,
+    code_lifetime: str,
+    email: str,
+    url_with_link_code: Union[str, None] = None,
+    user_input_code: Union[str, None] = None,
+):
+    if (user_input_code is not None) and (url_with_link_code is not None):
+        html_template = otp_and_magic_link_body
+    elif user_input_code is not None:
+        html_template = otp_body
+    elif url_with_link_code is not None:
+        html_template = magic_link_body
+    else:
+        raise Exception("This should never be thrown.")
+
+    return Template(html_template).substitute(
+        appname=app_name,
+        time=code_lifetime,
+        toEmail=email,
+        otp=user_input_code,
+        urlWithLinkCode=url_with_link_code,
+    )
+
+
+
+def pless_email_content(input_: PasswordlessLoginEmailTemplateVars) ‑> EmailContent +
+
+
+
+ +Expand source code + +
def pless_email_content(input_: PasswordlessLoginEmailTemplateVars) -> EmailContent:
+    supertokens = Supertokens.get_instance()
+    app_name = supertokens.app_info.app_name
+    code_lifetime = humanize_time(input_.code_life_time)
+    body = get_pless_email_html(
+        app_name,
+        code_lifetime,
+        input_.email,
+        input_.url_with_link_code,
+        input_.user_input_code,
+    )
+    content_result = EmailContent(body, "Login to your account", input_.email, True)
+    return content_result
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/passwordless/emaildelivery/services/smtp/pless_login_email.html b/html/supertokens_python/recipe/passwordless/emaildelivery/services/smtp/pless_login_email.html new file mode 100644 index 000000000..69c5987b9 --- /dev/null +++ b/html/supertokens_python/recipe/passwordless/emaildelivery/services/smtp/pless_login_email.html @@ -0,0 +1,2879 @@ + + + + + + +supertokens_python.recipe.passwordless.emaildelivery.services.smtp.pless_login_email API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.passwordless.emaildelivery.services.smtp.pless_login_email

+
+
+
+ +Expand source code + +
otp_body = """
+<!doctype html>
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml"
+        xmlns:o="urn:schemas-microsoft-com:office:office">
+
+<head>
+        <meta charset="UTF-8">
+        <meta http-equiv="X-UA-Compatible" content="IE=edge">
+        <meta name="viewport" content="width=device-width, initial-scale=1">
+        <title>*|MC:SUBJECT|*</title>
+
+        <style type="text/css">
+        body {
+                        max-width: 100vw;
+                        overflow: hidden;
+                }
+                p {
+                        margin: 10px 0;
+                        padding: 0;
+                }
+
+                table {
+                        border-collapse: collapse;
+                }
+
+                h1,
+                h2,
+                h3,
+                h4,
+                h5,
+                h6 {
+                        display: block;
+                        margin: 0;
+                        padding: 0;
+                }
+
+                img,
+                a img {
+                        border: 0;
+                        height: auto;
+                        outline: none;
+                        text-decoration: none;
+                }
+
+                body,
+                #bodyTable,
+                #bodyCell {
+                        height: 100%;
+                        margin: 0;
+                        padding: 0;
+                        width: 100%;
+                }
+
+                .mcnPreviewText {
+                        display: none !important;
+                }
+
+                #outlook a {
+                        padding: 0;
+                }
+
+                img {
+                        -ms-interpolation-mode: bicubic;
+                }
+
+                table {
+                        mso-table-lspace: 0pt;
+                        mso-table-rspace: 0pt;
+                }
+
+                .ReadMsgBody {
+                        width: 100%;
+                }
+
+                .ExternalClass {
+                        width: 100%;
+                }
+
+                p,
+                a,
+                li,
+                td,
+                blockquote {
+                        mso-line-height-rule: exactly;
+                }
+
+                a[href^=tel],
+                a[href^=sms] {
+                        color: inherit;
+                        cursor: default;
+                        text-decoration: none;
+                }
+
+                p,
+                a,
+                li,
+                td,
+                body,
+                table,
+                blockquote {
+                        -ms-text-size-adjust: 100%;
+                        -webkit-text-size-adjust: 100%;
+                }
+
+                .ExternalClass,
+                .ExternalClass p,
+                .ExternalClass td,
+                .ExternalClass div,
+                .ExternalClass span,
+                .ExternalClass font {
+                        line-height: 100%;
+                }
+
+                a[x-apple-data-detectors] {
+                        color: inherit !important;
+                        text-decoration: none !important;
+                        font-size: inherit !important;
+                        font-family: inherit !important;
+                        font-weight: inherit !important;
+                        line-height: inherit !important;
+                }
+
+                .templateContainer {
+                        max-width: 600px !important;
+                }
+
+                a.mcnButton {
+                        display: block;
+                }
+
+                .mcnImage,
+                .mcnRetinaImage {
+                        vertical-align: bottom;
+                }
+
+                .mcnTextContent {
+                        word-break: break-word;
+                }
+
+                .mcnTextContent img {
+                        height: auto !important;
+                }
+
+                .mcnDividerBlock {
+                        table-layout: fixed !important;
+                }
+
+                /*
+        @tab Page
+        @section Heading 1
+        @style heading 1
+        */
+                h1 {
+                        /*@editable*/
+                        color: #222222;
+                        /*@editable*/
+                        font-family: 'Open Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
+                        /*@editable*/
+                        font-size: 40px;
+                        /*@editable*/
+                        font-style: normal;
+                        /*@editable*/
+                        font-weight: bold;
+                        /*@editable*/
+                        line-height: 150%;
+                        /*@editable*/
+                        letter-spacing: normal;
+                        /*@editable*/
+                        text-align: center;
+                }
+
+                /*
+        @tab Page
+        @section Heading 2
+        @style heading 2
+        */
+                h2 {
+                        /*@editable*/
+                        color: #222222;
+                        /*@editable*/
+                        font-family: Helvetica;
+                        /*@editable*/
+                        font-size: 34px;
+                        /*@editable*/
+                        font-style: normal;
+                        /*@editable*/
+                        font-weight: bold;
+                        /*@editable*/
+                        line-height: 150%;
+                        /*@editable*/
+                        letter-spacing: normal;
+                        /*@editable*/
+                        text-align: left;
+                }
+
+                /*
+        @tab Page
+        @section Heading 3
+        @style heading 3
+        */
+                h3 {
+                        /*@editable*/
+                        color: #444444;
+                        /*@editable*/
+                        font-family: Helvetica;
+                        /*@editable*/
+                        font-size: 22px;
+                        /*@editable*/
+                        font-style: normal;
+                        /*@editable*/
+                        font-weight: bold;
+                        /*@editable*/
+                        line-height: 150%;
+                        /*@editable*/
+                        letter-spacing: normal;
+                        /*@editable*/
+                        text-align: left;
+                }
+
+                /*
+        @tab Page
+        @section Heading 4
+        @style heading 4
+        */
+                h4 {
+                        /*@editable*/
+                        color: #949494;
+                        /*@editable*/
+                        font-family: Georgia;
+                        /*@editable*/
+                        font-size: 20px;
+                        /*@editable*/
+                        font-style: italic;
+                        /*@editable*/
+                        font-weight: normal;
+                        /*@editable*/
+                        line-height: 125%;
+                        /*@editable*/
+                        letter-spacing: normal;
+                        /*@editable*/
+                        text-align: left;
+                }
+
+                /*
+        @tab Header
+        @section Header Container Style
+        */
+                #templateHeader {
+                        /*@editable*/
+                        background-color: #f4f4f4;
+                        /*@editable*/
+                        background-image: none;
+                        /*@editable*/
+                        background-repeat: no-repeat;
+                        /*@editable*/
+                        background-position: center;
+                        /*@editable*/
+                        background-size: cover;
+                        /*@editable*/
+                        border-top: 0;
+                        /*@editable*/
+                        border-bottom: 0;
+                        /*@editable*/
+                        padding-top: 0px;
+                        /*@editable*/
+                        padding-bottom: 0px;
+                }
+
+                /*
+        @tab Header
+        @section Header Interior Style
+        */
+                .headerContainer {
+                        /*@editable*/
+                        background-color: #transparent;
+                        /*@editable*/
+                        background-image: none;
+                        /*@editable*/
+                        background-repeat: no-repeat;
+                        /*@editable*/
+                        background-position: center;
+                        /*@editable*/
+                        background-size: cover;
+                        /*@editable*/
+                        border-top: 0;
+                        /*@editable*/
+                        border-bottom: 0;
+                        /*@editable*/
+                        padding-top: 0;
+                        /*@editable*/
+                        padding-bottom: 0;
+                }
+
+                /*
+        @tab Header
+        @section Header Text
+        */
+                .headerContainer .mcnTextContent,
+                .headerContainer .mcnTextContent p {
+                        /*@editable*/
+                        color: #757575;
+                        /*@editable*/
+                        font-family: Helvetica;
+                        /*@editable*/
+                        font-size: 16px;
+                        /*@editable*/
+                        line-height: 150%;
+                        /*@editable*/
+                        text-align: left;
+                }
+
+                /*
+        @tab Header
+        @section Header Link
+        */
+                .headerContainer .mcnTextContent a,
+                .headerContainer .mcnTextContent p a {
+                        /*@editable*/
+                        color: #007C89;
+                        /*@editable*/
+                        font-weight: normal;
+                        /*@editable*/
+                        text-decoration: underline;
+                }
+
+                /*
+        @tab Body
+        @section Body Container Style
+        */
+                #templateBody {
+                        /*@editable*/
+                        background-color: #f4f4f4;
+                        /*@editable*/
+                        background-image: none;
+                        /*@editable*/
+                        background-repeat: no-repeat;
+                        /*@editable*/
+                        background-position: center;
+                        /*@editable*/
+                        background-size: cover;
+                        /*@editable*/
+                        border-top: 0;
+                        /*@editable*/
+                        border-bottom: 0;
+                        /*@editable*/
+                        padding-top: 0px;
+                        /*@editable*/
+                        padding-bottom: 20px;
+                }
+
+                /*
+        @tab Body
+        @section Body Interior Style
+        */
+                .bodyContainer {
+                        /*@editable*/
+                        background-color: #f4f4f4;
+                        /*@editable*/
+                        background-image: none;
+                        /*@editable*/
+                        background-repeat: no-repeat;
+                        /*@editable*/
+                        background-position: center;
+                        /*@editable*/
+                        background-size: cover;
+                        /*@editable*/
+                        border-top: 2px none #ff9933;
+                        /*@editable*/
+                        border-bottom: 2px none #ff9933;
+                        /*@editable*/
+                        padding-top: 10px;
+                        /*@editable*/
+                        padding-bottom: 10px;
+                }
+
+                /*
+        @tab Body
+        @section Body Text
+        */
+                .bodyContainer .mcnTextContent,
+                .bodyContainer .mcnTextContent p {
+                        /*@editable*/
+                        color: #757575;
+                        /*@editable*/
+                        font-family: Helvetica;
+                        /*@editable*/
+                        font-size: 16px;
+                        /*@editable*/
+                        line-height: 150%;
+                        /*@editable*/
+                        text-align: left;
+                }
+
+                /*
+        @tab Body
+        @section Body Link
+        */
+                .bodyContainer .mcnTextContent a,
+                .bodyContainer .mcnTextContent p a {
+                        /*@editable*/
+                        color: #222222;
+                        /*@editable*/
+                        font-weight: normal;
+                        /*@editable*/
+                        text-decoration: underline;
+                }
+
+                /*
+        @tab Footer
+        @section Footer Style
+        */
+                #templateFooter {
+                        /*@editable*/
+                        background-color: #f4f4f4;
+                        /*@editable*/
+                        background-image: none;
+                        /*@editable*/
+                        background-repeat: no-repeat;
+                        /*@editable*/
+                        background-position: center;
+                        /*@editable*/
+                        background-size: cover;
+                        /*@editable*/
+                        border-top: 0;
+                        /*@editable*/
+                        border-bottom: 0;
+                        /*@editable*/
+                        padding-top: 0px;
+                        /*@editable*/
+                        padding-bottom: 20px;
+                }
+
+                /*
+        @tab Footer
+        @section Footer Interior Style
+        */
+                .footerContainer {
+                        /*@editable*/
+                        background-color: #transparent;
+                        /*@editable*/
+                        background-image: none;
+                        /*@editable*/
+                        background-repeat: no-repeat;
+                        /*@editable*/
+                        background-position: center;
+                        /*@editable*/
+                        background-size: cover;
+                        /*@editable*/
+                        border-top: 0;
+                        /*@editable*/
+                        border-bottom: 0;
+                        /*@editable*/
+                        padding-top: 0;
+                        /*@editable*/
+                        padding-bottom: 0;
+                }
+
+                /*
+        @tab Footer
+        @section Footer Text
+        */
+                .footerContainer .mcnTextContent,
+                .footerContainer .mcnTextContent p {
+                        /*@editable*/
+                        color: #FFFFFF;
+                        /*@editable*/
+                        font-family: 'Helvetica Neue', Helvetica, Arial, Verdana, sans-serif;
+                        /*@editable*/
+                        font-size: 12px;
+                        /*@editable*/
+                        line-height: 150%;
+                        /*@editable*/
+                        text-align: center;
+                }
+
+                /*
+        @tab Footer
+        @section Footer Link
+        */
+                .footerContainer .mcnTextContent a,
+                .footerContainer .mcnTextContent p a {
+                        /*@editable*/
+                        color: #FFFFFF;
+                        /*@editable*/
+                        font-weight: normal;
+                        /*@editable*/
+                        text-decoration: underline;
+                }
+
+                @media only screen and (max-width: 480px) {
+
+                        body,
+                        table,
+                        td,
+                        p,
+                        a,
+                        li,
+                        blockquote {
+                                -webkit-text-size-adjust: none !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+                        body {
+                                width: 100% !important;
+                                min-width: 100% !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+                        .mcnRetinaImage {
+                                max-width: 100% !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+                        .mcnImage {
+                                width: 100% !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+
+                        .mcnCartContainer,
+                        .mcnCaptionTopContent,
+                        .mcnRecContentContainer,
+                        .mcnCaptionBottomContent,
+                        .mcnTextContentContainer,
+                        .mcnBoxedTextContentContainer,
+                        .mcnImageGroupContentContainer,
+                        .mcnCaptionLeftTextContentContainer,
+                        .mcnCaptionRightTextContentContainer,
+                        .mcnCaptionLeftImageContentContainer,
+                        .mcnCaptionRightImageContentContainer,
+                        .mcnImageCardLeftTextContentContainer,
+                        .mcnImageCardRightTextContentContainer,
+                        .mcnImageCardLeftImageContentContainer,
+                        .mcnImageCardRightImageContentContainer {
+                                max-width: 100% !important;
+                                width: 100% !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+                        .mcnBoxedTextContentContainer {
+                                min-width: 100% !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+                        .mcnImageGroupContent {
+                                padding: 9px !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+
+                        .mcnCaptionLeftContentOuter .mcnTextContent,
+                        .mcnCaptionRightContentOuter .mcnTextContent {
+                                padding-top: 9px !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+
+                        .mcnImageCardTopImageContent,
+                        .mcnCaptionBottomContent:last-child .mcnCaptionBottomImageContent,
+                        .mcnCaptionBlockInner .mcnCaptionTopContent:last-child .mcnTextContent {
+                                padding-top: 18px !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+                        .mcnImageCardBottomImageContent {
+                                padding-bottom: 9px !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+                        .mcnImageGroupBlockInner {
+                                padding-top: 0 !important;
+                                padding-bottom: 0 !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+                        .mcnImageGroupBlockOuter {
+                                padding-top: 9px !important;
+                                padding-bottom: 9px !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+
+                        .mcnTextContent,
+                        .mcnBoxedTextContentColumn {
+                                padding-right: 18px !important;
+                                padding-left: 18px !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+
+                        .mcnImageCardLeftImageContent,
+                        .mcnImageCardRightImageContent {
+                                padding-right: 18px !important;
+                                padding-bottom: 0 !important;
+                                padding-left: 18px !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+                        .mcpreview-image-uploader {
+                                display: none !important;
+                                width: 100% !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+
+                        /*
+        @tab Mobile Styles
+        @section Heading 1
+        @tip Make the first-level headings larger in size for better readability on small screens.
+        */
+                        h1 {
+                                /*@editable*/
+                                font-size: 30px !important;
+                                /*@editable*/
+                                line-height: 125% !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+
+                        /*
+        @tab Mobile Styles
+        @section Heading 2
+        @tip Make the second-level headings larger in size for better readability on small screens.
+        */
+                        h2 {
+                                /*@editable*/
+                                font-size: 26px !important;
+                                /*@editable*/
+                                line-height: 125% !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+
+                        /*
+        @tab Mobile Styles
+        @section Heading 3
+        @tip Make the third-level headings larger in size for better readability on small screens.
+        */
+                        h3 {
+                                /*@editable*/
+                                font-size: 20px !important;
+                                /*@editable*/
+                                line-height: 150% !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+
+                        /*
+        @tab Mobile Styles
+        @section Heading 4
+        @tip Make the fourth-level headings larger in size for better readability on small screens.
+        */
+                        h4 {
+                                /*@editable*/
+                                font-size: 18px !important;
+                                /*@editable*/
+                                line-height: 150% !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+
+                        /*
+        @tab Mobile Styles
+        @section Boxed Text
+        @tip Make the boxed text larger in size for better readability on small screens. We recommend a font size of at least 16px.
+        */
+                        .mcnBoxedTextContentContainer .mcnTextContent,
+                        .mcnBoxedTextContentContainer .mcnTextContent p {
+                                /*@editable*/
+                                font-size: 14px !important;
+                                /*@editable*/
+                                line-height: 150% !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+
+                        /*
+        @tab Mobile Styles
+        @section Header Text
+        @tip Make the header text larger in size for better readability on small screens.
+        */
+                        .headerContainer .mcnTextContent,
+                        .headerContainer .mcnTextContent p {
+                                /*@editable*/
+                                font-size: 16px !important;
+                                /*@editable*/
+                                line-height: 150% !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+
+                        /*
+        @tab Mobile Styles
+        @section Body Text
+        @tip Make the body text larger in size for better readability on small screens. We recommend a font size of at least 16px.
+        */
+                        .bodyContainer .mcnTextContent,
+                        .bodyContainer .mcnTextContent p {
+                                /*@editable*/
+                                font-size: 16px !important;
+                                /*@editable*/
+                                line-height: 150% !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+
+                        /*
+        @tab Mobile Styles
+        @section Footer Text
+        @tip Make the footer content text larger in size for better readability on small screens.
+        */
+                        .footerContainer .mcnTextContent,
+                        .footerContainer .mcnTextContent p {
+                                /*@editable*/
+                                font-size: 14px !important;
+                                /*@editable*/
+                                line-height: 150% !important;
+                        }
+
+                }
+        </style>
+</head>
+
+<body>
+        <!--*|IF:MC_PREVIEW_TEXT|*-->
+        <!--[if !gte mso 9]><!----><span class="mcnPreviewText"
+                style="display:none; font-size:0px; line-height:0px; max-height:0px; max-width:0px; opacity:0; overflow:hidden; visibility:hidden; mso-hide:all;"></span>
+        <!--<![endif]-->
+        <!--*|END:IF|*-->
+        <center>
+                <table align="center" border="0" cellpadding="0" cellspacing="0" height="100%" width="100%" id="bodyTable">
+                        <tr>
+                                <td align="center" valign="top" id="bodyCell">
+                                        <!-- BEGIN TEMPLATE // -->
+                                        <table border="0" cellpadding="0" cellspacing="0" width="100%">
+                                                <tr>
+                                                        <td align="center" valign="top" id="templateHeader" data-template-container>
+                                                                <!--[if (gte mso 9)|(IE)]>
+                                    <table align="center" border="0" cellspacing="0" cellpadding="0" width="600" style="width:600px;">
+                                    <tr>
+                                    <td align="center" valign="top" width="600" style="width:600px;">
+                                    <![endif]-->
+                                                                <table align="center" border="0" cellpadding="0" cellspacing="0" width="100%"
+                                                                        class="templateContainer">
+                                                                        <tr>
+                                                                                <td valign="top" class="headerContainer"></td>
+                                                                        </tr>
+                                                                </table>
+                                                                <!--[if (gte mso 9)|(IE)]>
+                                    </td>
+                                    </tr>
+                                    </table>
+                                    <![endif]-->
+                                                        </td>
+                                                </tr>
+                                                <tr>
+                                                        <td align="center" valign="top" id="templateBody" data-template-container>
+                                                                <!--[if (gte mso 9)|(IE)]>
+                                    <table align="center" border="0" cellspacing="0" cellpadding="0" width="600" style="width:600px;">
+                                    <tr>
+                                    <td align="center" valign="top" width="600" style="width:600px;">
+                                    <![endif]-->
+                                                                <table align="center" border="0" cellpadding="0" cellspacing="0" width="100%"
+                                                                        class="templateContainer">
+                                                                        <tr>
+                                                                                <td valign="top" class="bodyContainer">
+                                                                                        <table border="0" cellpadding="0" cellspacing="0" width="100%"
+                                                                                                class="mcnCodeBlock">
+                                                                                                <tbody class="mcnTextBlockOuter">
+                                                                                                        <tr>
+                                                                                                                <td valign="top" class="mcnTextBlockInner">
+                                                                                                                        <p
+                                                                                                                                style="font-family:'Helvetica', sans-serif; margin-left: 3%; margin-right: 3%; font-size: 28px; line-height: 26px; font-weight:700; margin-bottom: 40px; margin-top: 48px; text-align: center; color: #222">
+                                                                                                                                Login to ${appname}</p>
+                                                                                                                </td>
+                                                                                                        </tr>
+                                                                                                </tbody>
+                                                                                        </table>
+                                                                                        <table border="0" cellpadding="0" cellspacing="0" width="100%"
+                                                                                                class="mcnCodeBlock">
+                                                                                                <tbody class="mcnTextBlockOuter">
+                                                                                                        <tr>
+                                                                                                                <td valign="top" class="mcnTextBlockInner">
+
+
+                                                                                                                        <div
+                                                                                                                                style="background-color:#fff; margin-left: 3%; margin-right: 3%; border: 1px solid #ddd; border-radius: 6px;">
+                                                                                                                                <div style="padding-left: 15%; padding-right: 15%;">
+
+                                                                                                                                        <p
+                                                                                                                                                style="font-family:'Helvetica', sans-serif; font-size: 16px; line-height: 26px; font-weight:700; text-align: center; padding-top: 24px; padding-bottom: 24px; padding-left: 8%; padding-right: 8%; ">
+                                                                                                                                                Enter the below OTP in your login screen. Note
+                                                                                                                                                that the OTP expires in ${time}.</p>
+
+                                                                                                                                        <div
+                                                                                                                                                style="display: block; flex-direction: row; justify-content: center; margin-bottom: 40px; text-align: center">
+                                                                                                                                                <div class="mcnTextContent"
+                                                                                                                                                        style="padding: 10px 20px; background-color: #fafafa; border: 1px solid #DDD; color: #222; font-family: 'Helvetica', sans-serif; font-size: 32px; line-height: 40px; font-weight: 700; text-align: center; display: block; border-radius: 6px; width: fit-content;margin: 0 auto">
+                                                                                                                                                        ${otp}</div>
+
+                                                                                                                                        </div>
+                                                                                                                                </div>
+                                                                                                                        </div>
+                                                                                                                </td>
+                                                                                                        </tr>
+                                                                                                </tbody>
+                                                                                        </table>
+                                                                                        <table border="0" cellpadding="0" cellspacing="0" width="100%"
+                                                                                                class="mcnCodeBlock">
+                                                                                                <tbody class="mcnTextBlockOuter">
+                                                                                                        <tr>
+                                                                                                                <td valign="top" class="mcnTextBlockInner">
+
+
+                                                                                                                        <p
+                                                                                                                                style="font-family:'Helvetica', sans-serif; font-size: 16px; line-height: 26px; font-weight:400; margin-top: 40px; text-align: center; color: #808080">
+                                                                                                                                This email is meant for <a
+                                                                                                                                        style="font-family: 'Helvetica', sans-serif; text-align: center; word-break: break-all; font-weight: 400; font-size: 16px; line-height: 26px; color: #808080 !important;"
+                                                                                                                                        target="_blank"
+                                                                                                                                        href="mailto:${toEmail}">${toEmail}</a>
+                                                                                                                        </p>
+                                                                                                                </td>
+                                                                                                        </tr>
+                                                                                                </tbody>
+                                                                                        </table>
+                                                                                </td>
+                                                                        </tr>
+                                                                </table>
+                                                                <!--[if (gte mso 9)|(IE)]>
+                                    </td>
+                                    </tr>
+                                    </table>
+                                    <![endif]-->
+                                                        </td>
+                                                </tr>
+                                                <tr>
+                                                        <td align="center" valign="top" id="templateFooter" data-template-container>
+                                                                <!--[if (gte mso 9)|(IE)]>
+                                    <table align="center" border="0" cellspacing="0" cellpadding="0" width="600" style="width:600px;">
+                                    <tr>
+                                    <td align="center" valign="top" width="600" style="width:600px;">
+                                    <![endif]-->
+                                                                <table align="center" border="0" cellpadding="0" cellspacing="0" width="100%"
+                                                                        class="templateContainer">
+                                                                        <tr>
+                                                                                <td valign="top" class="footerContainer"></td>
+                                                                        </tr>
+                                                                </table>
+                                                                <!--[if (gte mso 9)|(IE)]>
+                                    </td>
+                                    </tr>
+                                    </table>
+                                    <![endif]-->
+                                                        </td>
+                                                </tr>
+                                        </table>
+                                        <!-- // END TEMPLATE -->
+                                </td>
+                        </tr>
+                </table>
+        </center>
+</body>
+
+</html>
+"""
+
+magic_link_body = """
+<!doctype html>
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml"
+        xmlns:o="urn:schemas-microsoft-com:office:office">
+
+<head>
+        <meta charset="UTF-8">
+        <meta http-equiv="X-UA-Compatible" content="IE=edge">
+        <meta name="viewport" content="width=device-width, initial-scale=1">
+        <title>*|MC:SUBJECT|*</title>
+
+        <style type="text/css">
+            body {
+                        max-width: 100vw;
+                        overflow: hidden;
+                }
+                p {
+                        margin: 10px 0;
+                        padding: 0;
+                }
+
+                table {
+                        border-collapse: collapse;
+                }
+
+                h1,
+                h2,
+                h3,
+                h4,
+                h5,
+                h6 {
+                        display: block;
+                        margin: 0;
+                        padding: 0;
+                }
+
+                img,
+                a img {
+                        border: 0;
+                        height: auto;
+                        outline: none;
+                        text-decoration: none;
+                }
+
+                body,
+                #bodyTable,
+                #bodyCell {
+                        height: 100%;
+                        margin: 0;
+                        padding: 0;
+                        width: 100%;
+                }
+
+                .mcnPreviewText {
+                        display: none !important;
+                }
+
+                #outlook a {
+                        padding: 0;
+                }
+
+                img {
+                        -ms-interpolation-mode: bicubic;
+                }
+
+                table {
+                        mso-table-lspace: 0pt;
+                        mso-table-rspace: 0pt;
+                }
+
+                .ReadMsgBody {
+                        width: 100%;
+                }
+
+                .ExternalClass {
+                        width: 100%;
+                }
+
+                p,
+                a,
+                li,
+                td,
+                blockquote {
+                        mso-line-height-rule: exactly;
+                }
+
+                a[href^=tel],
+                a[href^=sms] {
+                        color: inherit;
+                        cursor: default;
+                        text-decoration: none;
+                }
+
+                p,
+                a,
+                li,
+                td,
+                body,
+                table,
+                blockquote {
+                        -ms-text-size-adjust: 100%;
+                        -webkit-text-size-adjust: 100%;
+                }
+
+                .ExternalClass,
+                .ExternalClass p,
+                .ExternalClass td,
+                .ExternalClass div,
+                .ExternalClass span,
+                .ExternalClass font {
+                        line-height: 100%;
+                }
+
+                a[x-apple-data-detectors] {
+                        color: inherit !important;
+                        text-decoration: none !important;
+                        font-size: inherit !important;
+                        font-family: inherit !important;
+                        font-weight: inherit !important;
+                        line-height: inherit !important;
+                }
+
+                .templateContainer {
+                        max-width: 600px !important;
+                }
+
+                a.mcnButton {
+                        display: block;
+                }
+
+                .mcnImage,
+                .mcnRetinaImage {
+                        vertical-align: bottom;
+                }
+
+                .mcnTextContent {
+                        word-break: break-word;
+                }
+
+                .mcnTextContent img {
+                        height: auto !important;
+                }
+
+                .mcnDividerBlock {
+                        table-layout: fixed !important;
+                }
+
+                /*
+        @tab Page
+        @section Heading 1
+        @style heading 1
+        */
+                h1 {
+                        /*@editable*/
+                        color: #222222;
+                        /*@editable*/
+                        font-family: 'Open Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
+                        /*@editable*/
+                        font-size: 40px;
+                        /*@editable*/
+                        font-style: normal;
+                        /*@editable*/
+                        font-weight: bold;
+                        /*@editable*/
+                        line-height: 150%;
+                        /*@editable*/
+                        letter-spacing: normal;
+                        /*@editable*/
+                        text-align: center;
+                }
+
+                /*
+        @tab Page
+        @section Heading 2
+        @style heading 2
+        */
+                h2 {
+                        /*@editable*/
+                        color: #222222;
+                        /*@editable*/
+                        font-family: Helvetica;
+                        /*@editable*/
+                        font-size: 34px;
+                        /*@editable*/
+                        font-style: normal;
+                        /*@editable*/
+                        font-weight: bold;
+                        /*@editable*/
+                        line-height: 150%;
+                        /*@editable*/
+                        letter-spacing: normal;
+                        /*@editable*/
+                        text-align: left;
+                }
+
+                /*
+        @tab Page
+        @section Heading 3
+        @style heading 3
+        */
+                h3 {
+                        /*@editable*/
+                        color: #444444;
+                        /*@editable*/
+                        font-family: Helvetica;
+                        /*@editable*/
+                        font-size: 22px;
+                        /*@editable*/
+                        font-style: normal;
+                        /*@editable*/
+                        font-weight: bold;
+                        /*@editable*/
+                        line-height: 150%;
+                        /*@editable*/
+                        letter-spacing: normal;
+                        /*@editable*/
+                        text-align: left;
+                }
+
+                /*
+        @tab Page
+        @section Heading 4
+        @style heading 4
+        */
+                h4 {
+                        /*@editable*/
+                        color: #949494;
+                        /*@editable*/
+                        font-family: Georgia;
+                        /*@editable*/
+                        font-size: 20px;
+                        /*@editable*/
+                        font-style: italic;
+                        /*@editable*/
+                        font-weight: normal;
+                        /*@editable*/
+                        line-height: 125%;
+                        /*@editable*/
+                        letter-spacing: normal;
+                        /*@editable*/
+                        text-align: left;
+                }
+
+                /*
+        @tab Header
+        @section Header Container Style
+        */
+                #templateHeader {
+                        /*@editable*/
+                        background-color: #f4f4f4;
+                        /*@editable*/
+                        background-image: none;
+                        /*@editable*/
+                        background-repeat: no-repeat;
+                        /*@editable*/
+                        background-position: center;
+                        /*@editable*/
+                        background-size: cover;
+                        /*@editable*/
+                        border-top: 0;
+                        /*@editable*/
+                        border-bottom: 0;
+                        /*@editable*/
+                        padding-top: 0px;
+                        /*@editable*/
+                        padding-bottom: 0px;
+                }
+
+                /*
+        @tab Header
+        @section Header Interior Style
+        */
+                .headerContainer {
+                        /*@editable*/
+                        background-color: #transparent;
+                        /*@editable*/
+                        background-image: none;
+                        /*@editable*/
+                        background-repeat: no-repeat;
+                        /*@editable*/
+                        background-position: center;
+                        /*@editable*/
+                        background-size: cover;
+                        /*@editable*/
+                        border-top: 0;
+                        /*@editable*/
+                        border-bottom: 0;
+                        /*@editable*/
+                        padding-top: 0;
+                        /*@editable*/
+                        padding-bottom: 0;
+                }
+
+                /*
+        @tab Header
+        @section Header Text
+        */
+                .headerContainer .mcnTextContent,
+                .headerContainer .mcnTextContent p {
+                        /*@editable*/
+                        color: #757575;
+                        /*@editable*/
+                        font-family: Helvetica;
+                        /*@editable*/
+                        font-size: 16px;
+                        /*@editable*/
+                        line-height: 150%;
+                        /*@editable*/
+                        text-align: left;
+                }
+
+                /*
+        @tab Header
+        @section Header Link
+        */
+                .headerContainer .mcnTextContent a,
+                .headerContainer .mcnTextContent p a {
+                        /*@editable*/
+                        color: #007C89;
+                        /*@editable*/
+                        font-weight: normal;
+                        /*@editable*/
+                        text-decoration: underline;
+                }
+
+                /*
+        @tab Body
+        @section Body Container Style
+        */
+                #templateBody {
+                        /*@editable*/
+                        background-color: #f4f4f4;
+                        /*@editable*/
+                        background-image: none;
+                        /*@editable*/
+                        background-repeat: no-repeat;
+                        /*@editable*/
+                        background-position: center;
+                        /*@editable*/
+                        background-size: cover;
+                        /*@editable*/
+                        border-top: 0;
+                        /*@editable*/
+                        border-bottom: 0;
+                        /*@editable*/
+                        padding-top: 0px;
+                        /*@editable*/
+                        padding-bottom: 20px;
+                }
+
+                /*
+        @tab Body
+        @section Body Interior Style
+        */
+                .bodyContainer {
+                        /*@editable*/
+                        background-color: #f4f4f4;
+                        /*@editable*/
+                        background-image: none;
+                        /*@editable*/
+                        background-repeat: no-repeat;
+                        /*@editable*/
+                        background-position: center;
+                        /*@editable*/
+                        background-size: cover;
+                        /*@editable*/
+                        border-top: 2px none #ff9933;
+                        /*@editable*/
+                        border-bottom: 2px none #ff9933;
+                        /*@editable*/
+                        padding-top: 10px;
+                        /*@editable*/
+                        padding-bottom: 10px;
+                }
+
+                /*
+        @tab Body
+        @section Body Text
+        */
+                .bodyContainer .mcnTextContent,
+                .bodyContainer .mcnTextContent p {
+                        /*@editable*/
+                        color: #757575;
+                        /*@editable*/
+                        font-family: Helvetica;
+                        /*@editable*/
+                        font-size: 16px;
+                        /*@editable*/
+                        line-height: 150%;
+                        /*@editable*/
+                        text-align: left;
+                }
+
+                /*
+        @tab Body
+        @section Body Link
+        */
+                .bodyContainer .mcnTextContent a,
+                .bodyContainer .mcnTextContent p a {
+                        /*@editable*/
+                        color: #222222;
+                        /*@editable*/
+                        font-weight: normal;
+                        /*@editable*/
+                        text-decoration: underline;
+                }
+
+                /*
+        @tab Footer
+        @section Footer Style
+        */
+                #templateFooter {
+                        /*@editable*/
+                        background-color: #f4f4f4;
+                        /*@editable*/
+                        background-image: none;
+                        /*@editable*/
+                        background-repeat: no-repeat;
+                        /*@editable*/
+                        background-position: center;
+                        /*@editable*/
+                        background-size: cover;
+                        /*@editable*/
+                        border-top: 0;
+                        /*@editable*/
+                        border-bottom: 0;
+                        /*@editable*/
+                        padding-top: 0px;
+                        /*@editable*/
+                        padding-bottom: 20px;
+                }
+
+                /*
+        @tab Footer
+        @section Footer Interior Style
+        */
+                .footerContainer {
+                        /*@editable*/
+                        background-color: #transparent;
+                        /*@editable*/
+                        background-image: none;
+                        /*@editable*/
+                        background-repeat: no-repeat;
+                        /*@editable*/
+                        background-position: center;
+                        /*@editable*/
+                        background-size: cover;
+                        /*@editable*/
+                        border-top: 0;
+                        /*@editable*/
+                        border-bottom: 0;
+                        /*@editable*/
+                        padding-top: 0;
+                        /*@editable*/
+                        padding-bottom: 0;
+                }
+
+                /*
+        @tab Footer
+        @section Footer Text
+        */
+                .footerContainer .mcnTextContent,
+                .footerContainer .mcnTextContent p {
+                        /*@editable*/
+                        color: #FFFFFF;
+                        /*@editable*/
+                        font-family: 'Helvetica Neue', Helvetica, Arial, Verdana, sans-serif;
+                        /*@editable*/
+                        font-size: 12px;
+                        /*@editable*/
+                        line-height: 150%;
+                        /*@editable*/
+                        text-align: center;
+                }
+
+                /*
+        @tab Footer
+        @section Footer Link
+        */
+                .footerContainer .mcnTextContent a,
+                .footerContainer .mcnTextContent p a {
+                        /*@editable*/
+                        color: #FFFFFF;
+                        /*@editable*/
+                        font-weight: normal;
+                        /*@editable*/
+                        text-decoration: underline;
+                }
+
+                @media only screen and (max-width: 480px) {
+
+                        body,
+                        table,
+                        td,
+                        p,
+                        a,
+                        li,
+                        blockquote {
+                                -webkit-text-size-adjust: none !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+                        body {
+                                width: 100% !important;
+                                min-width: 100% !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+                        .mcnRetinaImage {
+                                max-width: 100% !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+                        .mcnImage {
+                                width: 100% !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+
+                        .mcnCartContainer,
+                        .mcnCaptionTopContent,
+                        .mcnRecContentContainer,
+                        .mcnCaptionBottomContent,
+                        .mcnTextContentContainer,
+                        .mcnBoxedTextContentContainer,
+                        .mcnImageGroupContentContainer,
+                        .mcnCaptionLeftTextContentContainer,
+                        .mcnCaptionRightTextContentContainer,
+                        .mcnCaptionLeftImageContentContainer,
+                        .mcnCaptionRightImageContentContainer,
+                        .mcnImageCardLeftTextContentContainer,
+                        .mcnImageCardRightTextContentContainer,
+                        .mcnImageCardLeftImageContentContainer,
+                        .mcnImageCardRightImageContentContainer {
+                                max-width: 100% !important;
+                                width: 100% !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+                        .mcnBoxedTextContentContainer {
+                                min-width: 100% !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+                        .mcnImageGroupContent {
+                                padding: 9px !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+
+                        .mcnCaptionLeftContentOuter .mcnTextContent,
+                        .mcnCaptionRightContentOuter .mcnTextContent {
+                                padding-top: 9px !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+
+                        .mcnImageCardTopImageContent,
+                        .mcnCaptionBottomContent:last-child .mcnCaptionBottomImageContent,
+                        .mcnCaptionBlockInner .mcnCaptionTopContent:last-child .mcnTextContent {
+                                padding-top: 18px !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+                        .mcnImageCardBottomImageContent {
+                                padding-bottom: 9px !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+                        .mcnImageGroupBlockInner {
+                                padding-top: 0 !important;
+                                padding-bottom: 0 !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+                        .mcnImageGroupBlockOuter {
+                                padding-top: 9px !important;
+                                padding-bottom: 9px !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+
+                        .mcnTextContent,
+                        .mcnBoxedTextContentColumn {
+                                padding-right: 18px !important;
+                                padding-left: 18px !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+
+                        .mcnImageCardLeftImageContent,
+                        .mcnImageCardRightImageContent {
+                                padding-right: 18px !important;
+                                padding-bottom: 0 !important;
+                                padding-left: 18px !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+                        .mcpreview-image-uploader {
+                                display: none !important;
+                                width: 100% !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+
+                        /*
+        @tab Mobile Styles
+        @section Heading 1
+        @tip Make the first-level headings larger in size for better readability on small screens.
+        */
+                        h1 {
+                                /*@editable*/
+                                font-size: 30px !important;
+                                /*@editable*/
+                                line-height: 125% !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+
+                        /*
+        @tab Mobile Styles
+        @section Heading 2
+        @tip Make the second-level headings larger in size for better readability on small screens.
+        */
+                        h2 {
+                                /*@editable*/
+                                font-size: 26px !important;
+                                /*@editable*/
+                                line-height: 125% !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+
+                        /*
+        @tab Mobile Styles
+        @section Heading 3
+        @tip Make the third-level headings larger in size for better readability on small screens.
+        */
+                        h3 {
+                                /*@editable*/
+                                font-size: 20px !important;
+                                /*@editable*/
+                                line-height: 150% !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+
+                        /*
+        @tab Mobile Styles
+        @section Heading 4
+        @tip Make the fourth-level headings larger in size for better readability on small screens.
+        */
+                        h4 {
+                                /*@editable*/
+                                font-size: 18px !important;
+                                /*@editable*/
+                                line-height: 150% !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+
+                        /*
+        @tab Mobile Styles
+        @section Boxed Text
+        @tip Make the boxed text larger in size for better readability on small screens. We recommend a font size of at least 16px.
+        */
+                        .mcnBoxedTextContentContainer .mcnTextContent,
+                        .mcnBoxedTextContentContainer .mcnTextContent p {
+                                /*@editable*/
+                                font-size: 14px !important;
+                                /*@editable*/
+                                line-height: 150% !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+
+                        /*
+        @tab Mobile Styles
+        @section Header Text
+        @tip Make the header text larger in size for better readability on small screens.
+        */
+                        .headerContainer .mcnTextContent,
+                        .headerContainer .mcnTextContent p {
+                                /*@editable*/
+                                font-size: 16px !important;
+                                /*@editable*/
+                                line-height: 150% !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+
+                        /*
+        @tab Mobile Styles
+        @section Body Text
+        @tip Make the body text larger in size for better readability on small screens. We recommend a font size of at least 16px.
+        */
+                        .bodyContainer .mcnTextContent,
+                        .bodyContainer .mcnTextContent p {
+                                /*@editable*/
+                                font-size: 16px !important;
+                                /*@editable*/
+                                line-height: 150% !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+
+                        /*
+        @tab Mobile Styles
+        @section Footer Text
+        @tip Make the footer content text larger in size for better readability on small screens.
+        */
+                        .footerContainer .mcnTextContent,
+                        .footerContainer .mcnTextContent p {
+                                /*@editable*/
+                                font-size: 14px !important;
+                                /*@editable*/
+                                line-height: 150% !important;
+                        }
+
+                }
+        </style>
+</head>
+
+<body>
+        <!--*|IF:MC_PREVIEW_TEXT|*-->
+        <!--[if !gte mso 9]><!----><span class="mcnPreviewText"
+                style="display:none; font-size:0px; line-height:0px; max-height:0px; max-width:0px; opacity:0; overflow:hidden; visibility:hidden; mso-hide:all;"></span>
+        <!--<![endif]-->
+        <!--*|END:IF|*-->
+        <center>
+                <table align="center" border="0" cellpadding="0" cellspacing="0" height="100%" width="100%" id="bodyTable">
+                        <tr>
+                                <td align="center" valign="top" id="bodyCell">
+                                        <!-- BEGIN TEMPLATE // -->
+                                        <table border="0" cellpadding="0" cellspacing="0" width="100%">
+                                                <tr>
+                                                        <td align="center" valign="top" id="templateHeader" data-template-container>
+                                                                <!--[if (gte mso 9)|(IE)]>
+                                    <table align="center" border="0" cellspacing="0" cellpadding="0" width="600" style="width:600px;">
+                                    <tr>
+                                    <td align="center" valign="top" width="600" style="width:600px;">
+                                    <![endif]-->
+                                                                <table align="center" border="0" cellpadding="0" cellspacing="0" width="100%"
+                                                                        class="templateContainer">
+                                                                        <tr>
+                                                                                <td valign="top" class="headerContainer"></td>
+                                                                        </tr>
+                                                                </table>
+                                                                <!--[if (gte mso 9)|(IE)]>
+                                    </td>
+                                    </tr>
+                                    </table>
+                                    <![endif]-->
+                                                        </td>
+                                                </tr>
+                                                <tr>
+                                                        <td align="center" valign="top" id="templateBody" data-template-container>
+                                                                <!--[if (gte mso 9)|(IE)]>
+                                    <table align="center" border="0" cellspacing="0" cellpadding="0" width="600" style="width:600px;">
+                                    <tr>
+                                    <td align="center" valign="top" width="600" style="width:600px;">
+                                    <![endif]-->
+                                                                <table align="center" border="0" cellpadding="0" cellspacing="0" width="100%"
+                                                                        class="templateContainer">
+                                                                        <tr>
+                                                                                <td valign="top" class="bodyContainer">
+                                                                                        <table border="0" cellpadding="0" cellspacing="0" width="100%"
+                                                                                                class="mcnCodeBlock">
+                                                                                                <tbody class="mcnTextBlockOuter">
+                                                                                                        <tr>
+                                                                                                                <td valign="top" class="mcnTextBlockInner">
+                                                                                                                        <p
+                                                                                                                                style="font-family:'Helvetica', sans-serif; margin-left: 3%; margin-right: 3%; font-size: 28px; line-height: 26px; font-weight:700; margin-bottom: 40px; margin-top: 48px; text-align: center; color: #222">
+                                                                                                                                Login to ${appname}</p>
+                                                                                                                </td>
+                                                                                                        </tr>
+                                                                                                </tbody>
+                                                                                        </table>
+                                                                                        <table border="0" cellpadding="0" cellspacing="0" width="100%"
+                                                                                                class="mcnCodeBlock">
+                                                                                                <tbody class="mcnTextBlockOuter">
+                                                                                                        <tr>
+                                                                                                                <td valign="top" class="mcnTextBlockInner">
+
+
+                                                                                                                        <div
+                                                                                                                                style="background-color:#fff; margin-left: 3%; margin-right: 3%; border: 1px solid #ddd; border-radius: 6px;">
+                                                                                                                                <div style="padding-left: 15%; padding-right: 15%;">
+
+                                                                                                                                        <p
+                                                                                                                                                style="font-family:'Helvetica', sans-serif; font-size: 16px; line-height: 26px; font-weight:700; text-align: center; padding-top: 24px; padding-bottom: 24px; padding-left: 8%; padding-right: 8%; ">
+                                                                                                                                                Please click the button below to sign in / up.
+                                                                                                                                                Note that the link expires in ${time}.
+                                                                                                                                        </p>
+
+                                                                                                                                        <div class="button-td button-td-primary"
+                                                                                                                                                style="border-radius: 6px; margin-bottom: 40px; display: block; text-align: center;">
+                                                                                                                                                <a class="button-a button-a-primary"
+                                                                                                                                                        href="${urlWithLinkCode}" target="_blank"
+                                                                                                                                                        style="background: #52B56E;font-size: 17px;line-height: 24px;font-weight: 700;font-family: 'Helvetica', sans-serif;text-decoration: none;padding: 9px 25px 9px 25px;color: #ffffff;display: block;border-radius: 6px;width: fit-content;margin: 0 auto;">Login</a>
+                                                                                                                                        </div>
+                                                                                                                                </div>
+                                                                                                                                <div
+                                                                                                                                        style="background-color:#fafafa; border-top: 1px solid #ddd; padding-left: 15%; padding-right: 15%; padding-bottom: 24px; padding-top: 24px">
+                                                                                                                                        <p
+                                                                                                                                                style="font-family: 'Hevetica', sans-serif; font-size: 14px; line-height: 23px; font-weight:400;  text-align: center; color: #808080;">
+                                                                                                                                                Alternatively, you can directly paste this link
+                                                                                                                                                in your browser <br>
+                                                                                                                                                <a style="font-family: 'Helvetica', sans-serif, sans-serif; text-align: center; word-break: break-all; font-weight: 400; font-size: 14px; line-height: 23px; color: #007aff !important;"
+                                                                                                                                                        target="_blank"
+                                                                                                                                                        href="${urlWithLinkCode}">${urlWithLinkCode}</a>
+                                                                                                                                        </p>
+                                                                                                                                </div>
+                                                                                                                        </div>
+
+
+
+
+                                                                                                                </td>
+                                                                                                        </tr>
+                                                                                                </tbody>
+                                                                                        </table>
+                                                                                        <table border="0" cellpadding="0" cellspacing="0" width="100%"
+                                                                                                class="mcnCodeBlock">
+                                                                                                <tbody class="mcnTextBlockOuter">
+                                                                                                        <tr>
+                                                                                                                <td valign="top" class="mcnTextBlockInner">
+
+
+                                                                                                                        <p
+                                                                                                                                style="font-family:'Helvetica', sans-serif; font-size: 16px; line-height: 26px; font-weight:400; margin-top: 40px; text-align: center; color: #808080">
+                                                                                                                                This email is meant for <a
+                                                                                                                                        style="font-family: 'Helvetica', sans-serif; text-align: center; word-break: break-all; font-weight: 400; font-size: 16px; line-height: 26px; color: #808080 !important;"
+                                                                                                                                        target="_blank"
+                                                                                                                                        href="mailto:${toEmail}">${toEmail}</a>
+                                                                                                                        </p>
+                                                                                                                </td>
+                                                                                                        </tr>
+                                                                                                </tbody>
+                                                                                        </table>
+                                                                                </td>
+                                                                        </tr>
+                                                                </table>
+                                                                <!--[if (gte mso 9)|(IE)]>
+                                    </td>
+                                    </tr>
+                                    </table>
+                                    <![endif]-->
+                                                        </td>
+                                                </tr>
+                                                <tr>
+                                                        <td align="center" valign="top" id="templateFooter" data-template-container>
+                                                                <!--[if (gte mso 9)|(IE)]>
+                                    <table align="center" border="0" cellspacing="0" cellpadding="0" width="600" style="width:600px;">
+                                    <tr>
+                                    <td align="center" valign="top" width="600" style="width:600px;">
+                                    <![endif]-->
+                                                                <table align="center" border="0" cellpadding="0" cellspacing="0" width="100%"
+                                                                        class="templateContainer">
+                                                                        <tr>
+                                                                                <td valign="top" class="footerContainer"></td>
+                                                                        </tr>
+                                                                </table>
+                                                                <!--[if (gte mso 9)|(IE)]>
+                                    </td>
+                                    </tr>
+                                    </table>
+                                    <![endif]-->
+                                                        </td>
+                                                </tr>
+                                        </table>
+                                        <!-- // END TEMPLATE -->
+                                </td>
+                        </tr>
+                </table>
+        </center>
+</body>
+
+</html>
+"""
+
+
+otp_and_magic_link_body = """
+<!doctype html>
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml"
+        xmlns:o="urn:schemas-microsoft-com:office:office">
+
+<head>
+        <meta charset="UTF-8">
+        <meta http-equiv="X-UA-Compatible" content="IE=edge">
+        <meta name="viewport" content="width=device-width, initial-scale=1">
+        <title>*|MC:SUBJECT|*</title>
+
+        <style type="text/css">
+            body {
+                        max-width: 100vw;
+                        overflow: hidden;
+                }
+                p {
+                        margin: 10px 0;
+                        padding: 0;
+                }
+
+                table {
+                        border-collapse: collapse;
+                }
+
+                h1,
+                h2,
+                h3,
+                h4,
+                h5,
+                h6 {
+                        display: block;
+                        margin: 0;
+                        padding: 0;
+                }
+
+                img,
+                a img {
+                        border: 0;
+                        height: auto;
+                        outline: none;
+                        text-decoration: none;
+                }
+
+                body,
+                #bodyTable,
+                #bodyCell {
+                        height: 100%;
+                        margin: 0;
+                        padding: 0;
+                        width: 100%;
+                }
+
+                .mcnPreviewText {
+                        display: none !important;
+                }
+
+                #outlook a {
+                        padding: 0;
+                }
+
+                img {
+                        -ms-interpolation-mode: bicubic;
+                }
+
+                table {
+                        mso-table-lspace: 0pt;
+                        mso-table-rspace: 0pt;
+                }
+
+                .ReadMsgBody {
+                        width: 100%;
+                }
+
+                .ExternalClass {
+                        width: 100%;
+                }
+
+                p,
+                a,
+                li,
+                td,
+                blockquote {
+                        mso-line-height-rule: exactly;
+                }
+
+                a[href^=tel],
+                a[href^=sms] {
+                        color: inherit;
+                        cursor: default;
+                        text-decoration: none;
+                }
+
+                p,
+                a,
+                li,
+                td,
+                body,
+                table,
+                blockquote {
+                        -ms-text-size-adjust: 100%;
+                        -webkit-text-size-adjust: 100%;
+                }
+
+                .ExternalClass,
+                .ExternalClass p,
+                .ExternalClass td,
+                .ExternalClass div,
+                .ExternalClass span,
+                .ExternalClass font {
+                        line-height: 100%;
+                }
+
+                a[x-apple-data-detectors] {
+                        color: inherit !important;
+                        text-decoration: none !important;
+                        font-size: inherit !important;
+                        font-family: inherit !important;
+                        font-weight: inherit !important;
+                        line-height: inherit !important;
+                }
+
+                .templateContainer {
+                        max-width: 600px !important;
+                }
+
+                a.mcnButton {
+                        display: block;
+                }
+
+                .mcnImage,
+                .mcnRetinaImage {
+                        vertical-align: bottom;
+                }
+
+                .mcnTextContent {
+                        word-break: break-word;
+                }
+
+                .mcnTextContent img {
+                        height: auto !important;
+                }
+
+                .mcnDividerBlock {
+                        table-layout: fixed !important;
+                }
+
+                /*
+        @tab Page
+        @section Heading 1
+        @style heading 1
+        */
+                h1 {
+                        /*@editable*/
+                        color: #222222;
+                        /*@editable*/
+                        font-family: 'Open Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
+                        /*@editable*/
+                        font-size: 40px;
+                        /*@editable*/
+                        font-style: normal;
+                        /*@editable*/
+                        font-weight: bold;
+                        /*@editable*/
+                        line-height: 150%;
+                        /*@editable*/
+                        letter-spacing: normal;
+                        /*@editable*/
+                        text-align: center;
+                }
+
+                /*
+        @tab Page
+        @section Heading 2
+        @style heading 2
+        */
+                h2 {
+                        /*@editable*/
+                        color: #222222;
+                        /*@editable*/
+                        font-family: Helvetica;
+                        /*@editable*/
+                        font-size: 34px;
+                        /*@editable*/
+                        font-style: normal;
+                        /*@editable*/
+                        font-weight: bold;
+                        /*@editable*/
+                        line-height: 150%;
+                        /*@editable*/
+                        letter-spacing: normal;
+                        /*@editable*/
+                        text-align: left;
+                }
+
+                /*
+        @tab Page
+        @section Heading 3
+        @style heading 3
+        */
+                h3 {
+                        /*@editable*/
+                        color: #444444;
+                        /*@editable*/
+                        font-family: Helvetica;
+                        /*@editable*/
+                        font-size: 22px;
+                        /*@editable*/
+                        font-style: normal;
+                        /*@editable*/
+                        font-weight: bold;
+                        /*@editable*/
+                        line-height: 150%;
+                        /*@editable*/
+                        letter-spacing: normal;
+                        /*@editable*/
+                        text-align: left;
+                }
+
+                /*
+        @tab Page
+        @section Heading 4
+        @style heading 4
+        */
+                h4 {
+                        /*@editable*/
+                        color: #949494;
+                        /*@editable*/
+                        font-family: Georgia;
+                        /*@editable*/
+                        font-size: 20px;
+                        /*@editable*/
+                        font-style: italic;
+                        /*@editable*/
+                        font-weight: normal;
+                        /*@editable*/
+                        line-height: 125%;
+                        /*@editable*/
+                        letter-spacing: normal;
+                        /*@editable*/
+                        text-align: left;
+                }
+
+                /*
+        @tab Header
+        @section Header Container Style
+        */
+                #templateHeader {
+                        /*@editable*/
+                        background-color: #f4f4f4;
+                        /*@editable*/
+                        background-image: none;
+                        /*@editable*/
+                        background-repeat: no-repeat;
+                        /*@editable*/
+                        background-position: center;
+                        /*@editable*/
+                        background-size: cover;
+                        /*@editable*/
+                        border-top: 0;
+                        /*@editable*/
+                        border-bottom: 0;
+                        /*@editable*/
+                        padding-top: 0px;
+                        /*@editable*/
+                        padding-bottom: 0px;
+                }
+
+                /*
+        @tab Header
+        @section Header Interior Style
+        */
+                .headerContainer {
+                        /*@editable*/
+                        background-color: #transparent;
+                        /*@editable*/
+                        background-image: none;
+                        /*@editable*/
+                        background-repeat: no-repeat;
+                        /*@editable*/
+                        background-position: center;
+                        /*@editable*/
+                        background-size: cover;
+                        /*@editable*/
+                        border-top: 0;
+                        /*@editable*/
+                        border-bottom: 0;
+                        /*@editable*/
+                        padding-top: 0;
+                        /*@editable*/
+                        padding-bottom: 0;
+                }
+
+                /*
+        @tab Header
+        @section Header Text
+        */
+                .headerContainer .mcnTextContent,
+                .headerContainer .mcnTextContent p {
+                        /*@editable*/
+                        color: #757575;
+                        /*@editable*/
+                        font-family: Helvetica;
+                        /*@editable*/
+                        font-size: 16px;
+                        /*@editable*/
+                        line-height: 150%;
+                        /*@editable*/
+                        text-align: left;
+                }
+
+                /*
+        @tab Header
+        @section Header Link
+        */
+                .headerContainer .mcnTextContent a,
+                .headerContainer .mcnTextContent p a {
+                        /*@editable*/
+                        color: #007C89;
+                        /*@editable*/
+                        font-weight: normal;
+                        /*@editable*/
+                        text-decoration: underline;
+                }
+
+                /*
+        @tab Body
+        @section Body Container Style
+        */
+                #templateBody {
+                        /*@editable*/
+                        background-color: #f4f4f4;
+                        /*@editable*/
+                        background-image: none;
+                        /*@editable*/
+                        background-repeat: no-repeat;
+                        /*@editable*/
+                        background-position: center;
+                        /*@editable*/
+                        background-size: cover;
+                        /*@editable*/
+                        border-top: 0;
+                        /*@editable*/
+                        border-bottom: 0;
+                        /*@editable*/
+                        padding-top: 0px;
+                        /*@editable*/
+                        padding-bottom: 20px;
+                }
+
+                /*
+        @tab Body
+        @section Body Interior Style
+        */
+                .bodyContainer {
+                        /*@editable*/
+                        background-color: #f4f4f4;
+                        /*@editable*/
+                        background-image: none;
+                        /*@editable*/
+                        background-repeat: no-repeat;
+                        /*@editable*/
+                        background-position: center;
+                        /*@editable*/
+                        background-size: cover;
+                        /*@editable*/
+                        border-top: 2px none #ff9933;
+                        /*@editable*/
+                        border-bottom: 2px none #ff9933;
+                        /*@editable*/
+                        padding-top: 10px;
+                        /*@editable*/
+                        padding-bottom: 10px;
+                }
+
+                /*
+        @tab Body
+        @section Body Text
+        */
+                .bodyContainer .mcnTextContent,
+                .bodyContainer .mcnTextContent p {
+                        /*@editable*/
+                        color: #757575;
+                        /*@editable*/
+                        font-family: Helvetica;
+                        /*@editable*/
+                        font-size: 16px;
+                        /*@editable*/
+                        line-height: 150%;
+                        /*@editable*/
+                        text-align: left;
+                }
+
+                /*
+        @tab Body
+        @section Body Link
+        */
+                .bodyContainer .mcnTextContent a,
+                .bodyContainer .mcnTextContent p a {
+                        /*@editable*/
+                        color: #222222;
+                        /*@editable*/
+                        font-weight: normal;
+                        /*@editable*/
+                        text-decoration: underline;
+                }
+
+                /*
+        @tab Footer
+        @section Footer Style
+        */
+                #templateFooter {
+                        /*@editable*/
+                        background-color: #f4f4f4;
+                        /*@editable*/
+                        background-image: none;
+                        /*@editable*/
+                        background-repeat: no-repeat;
+                        /*@editable*/
+                        background-position: center;
+                        /*@editable*/
+                        background-size: cover;
+                        /*@editable*/
+                        border-top: 0;
+                        /*@editable*/
+                        border-bottom: 0;
+                        /*@editable*/
+                        padding-top: 0px;
+                        /*@editable*/
+                        padding-bottom: 20px;
+                }
+
+                /*
+        @tab Footer
+        @section Footer Interior Style
+        */
+                .footerContainer {
+                        /*@editable*/
+                        background-color: #transparent;
+                        /*@editable*/
+                        background-image: none;
+                        /*@editable*/
+                        background-repeat: no-repeat;
+                        /*@editable*/
+                        background-position: center;
+                        /*@editable*/
+                        background-size: cover;
+                        /*@editable*/
+                        border-top: 0;
+                        /*@editable*/
+                        border-bottom: 0;
+                        /*@editable*/
+                        padding-top: 0;
+                        /*@editable*/
+                        padding-bottom: 0;
+                }
+
+                /*
+        @tab Footer
+        @section Footer Text
+        */
+                .footerContainer .mcnTextContent,
+                .footerContainer .mcnTextContent p {
+                        /*@editable*/
+                        color: #FFFFFF;
+                        /*@editable*/
+                        font-family: 'Helvetica Neue', Helvetica, Arial, Verdana, sans-serif;
+                        /*@editable*/
+                        font-size: 12px;
+                        /*@editable*/
+                        line-height: 150%;
+                        /*@editable*/
+                        text-align: center;
+                }
+
+                /*
+        @tab Footer
+        @section Footer Link
+        */
+                .footerContainer .mcnTextContent a,
+                .footerContainer .mcnTextContent p a {
+                        /*@editable*/
+                        color: #FFFFFF;
+                        /*@editable*/
+                        font-weight: normal;
+                        /*@editable*/
+                        text-decoration: underline;
+                }
+
+                @media only screen and (max-width: 480px) {
+
+                        body,
+                        table,
+                        td,
+                        p,
+                        a,
+                        li,
+                        blockquote {
+                                -webkit-text-size-adjust: none !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+                        body {
+                                width: 100% !important;
+                                min-width: 100% !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+                        .mcnRetinaImage {
+                                max-width: 100% !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+                        .mcnImage {
+                                width: 100% !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+
+                        .mcnCartContainer,
+                        .mcnCaptionTopContent,
+                        .mcnRecContentContainer,
+                        .mcnCaptionBottomContent,
+                        .mcnTextContentContainer,
+                        .mcnBoxedTextContentContainer,
+                        .mcnImageGroupContentContainer,
+                        .mcnCaptionLeftTextContentContainer,
+                        .mcnCaptionRightTextContentContainer,
+                        .mcnCaptionLeftImageContentContainer,
+                        .mcnCaptionRightImageContentContainer,
+                        .mcnImageCardLeftTextContentContainer,
+                        .mcnImageCardRightTextContentContainer,
+                        .mcnImageCardLeftImageContentContainer,
+                        .mcnImageCardRightImageContentContainer {
+                                max-width: 100% !important;
+                                width: 100% !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+                        .mcnBoxedTextContentContainer {
+                                min-width: 100% !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+                        .mcnImageGroupContent {
+                                padding: 9px !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+
+                        .mcnCaptionLeftContentOuter .mcnTextContent,
+                        .mcnCaptionRightContentOuter .mcnTextContent {
+                                padding-top: 9px !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+
+                        .mcnImageCardTopImageContent,
+                        .mcnCaptionBottomContent:last-child .mcnCaptionBottomImageContent,
+                        .mcnCaptionBlockInner .mcnCaptionTopContent:last-child .mcnTextContent {
+                                padding-top: 18px !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+                        .mcnImageCardBottomImageContent {
+                                padding-bottom: 9px !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+                        .mcnImageGroupBlockInner {
+                                padding-top: 0 !important;
+                                padding-bottom: 0 !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+                        .mcnImageGroupBlockOuter {
+                                padding-top: 9px !important;
+                                padding-bottom: 9px !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+
+                        .mcnTextContent,
+                        .mcnBoxedTextContentColumn {
+                                padding-right: 18px !important;
+                                padding-left: 18px !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+
+                        .mcnImageCardLeftImageContent,
+                        .mcnImageCardRightImageContent {
+                                padding-right: 18px !important;
+                                padding-bottom: 0 !important;
+                                padding-left: 18px !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+                        .mcpreview-image-uploader {
+                                display: none !important;
+                                width: 100% !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+
+                        /*
+        @tab Mobile Styles
+        @section Heading 1
+        @tip Make the first-level headings larger in size for better readability on small screens.
+        */
+                        h1 {
+                                /*@editable*/
+                                font-size: 30px !important;
+                                /*@editable*/
+                                line-height: 125% !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+
+                        /*
+        @tab Mobile Styles
+        @section Heading 2
+        @tip Make the second-level headings larger in size for better readability on small screens.
+        */
+                        h2 {
+                                /*@editable*/
+                                font-size: 26px !important;
+                                /*@editable*/
+                                line-height: 125% !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+
+                        /*
+        @tab Mobile Styles
+        @section Heading 3
+        @tip Make the third-level headings larger in size for better readability on small screens.
+        */
+                        h3 {
+                                /*@editable*/
+                                font-size: 20px !important;
+                                /*@editable*/
+                                line-height: 150% !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+
+                        /*
+        @tab Mobile Styles
+        @section Heading 4
+        @tip Make the fourth-level headings larger in size for better readability on small screens.
+        */
+                        h4 {
+                                /*@editable*/
+                                font-size: 18px !important;
+                                /*@editable*/
+                                line-height: 150% !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+
+                        /*
+        @tab Mobile Styles
+        @section Boxed Text
+        @tip Make the boxed text larger in size for better readability on small screens. We recommend a font size of at least 16px.
+        */
+                        .mcnBoxedTextContentContainer .mcnTextContent,
+                        .mcnBoxedTextContentContainer .mcnTextContent p {
+                                /*@editable*/
+                                font-size: 14px !important;
+                                /*@editable*/
+                                line-height: 150% !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+
+                        /*
+        @tab Mobile Styles
+        @section Header Text
+        @tip Make the header text larger in size for better readability on small screens.
+        */
+                        .headerContainer .mcnTextContent,
+                        .headerContainer .mcnTextContent p {
+                                /*@editable*/
+                                font-size: 16px !important;
+                                /*@editable*/
+                                line-height: 150% !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+
+                        /*
+        @tab Mobile Styles
+        @section Body Text
+        @tip Make the body text larger in size for better readability on small screens. We recommend a font size of at least 16px.
+        */
+                        .bodyContainer .mcnTextContent,
+                        .bodyContainer .mcnTextContent p {
+                                /*@editable*/
+                                font-size: 16px !important;
+                                /*@editable*/
+                                line-height: 150% !important;
+                        }
+
+                }
+
+                @media only screen and (max-width: 480px) {
+
+                        /*
+        @tab Mobile Styles
+        @section Footer Text
+        @tip Make the footer content text larger in size for better readability on small screens.
+        */
+                        .footerContainer .mcnTextContent,
+                        .footerContainer .mcnTextContent p {
+                                /*@editable*/
+                                font-size: 14px !important;
+                                /*@editable*/
+                                line-height: 150% !important;
+                        }
+
+                }
+        </style>
+</head>
+
+<body>
+        <!--*|IF:MC_PREVIEW_TEXT|*-->
+        <!--[if !gte mso 9]><!----><span class="mcnPreviewText"
+                style="display:none; font-size:0px; line-height:0px; max-height:0px; max-width:0px; opacity:0; overflow:hidden; visibility:hidden; mso-hide:all;"></span>
+        <!--<![endif]-->
+        <!--*|END:IF|*-->
+        <center>
+                <table align="center" border="0" cellpadding="0" cellspacing="0" height="100%" width="100%" id="bodyTable">
+                        <tr>
+                                <td align="center" valign="top" id="bodyCell">
+                                        <!-- BEGIN TEMPLATE // -->
+                                        <table border="0" cellpadding="0" cellspacing="0" width="100%">
+                                                <tr>
+                                                        <td align="center" valign="top" id="templateHeader" data-template-container>
+                                                                <!--[if (gte mso 9)|(IE)]>
+                                    <table align="center" border="0" cellspacing="0" cellpadding="0" width="600" style="width:600px;">
+                                    <tr>
+                                    <td align="center" valign="top" width="600" style="width:600px;">
+                                    <![endif]-->
+                                                                <table align="center" border="0" cellpadding="0" cellspacing="0" width="100%"
+                                                                        class="templateContainer">
+                                                                        <tr>
+                                                                                <td valign="top" class="headerContainer"></td>
+                                                                        </tr>
+                                                                </table>
+                                                                <!--[if (gte mso 9)|(IE)]>
+                                    </td>
+                                    </tr>
+                                    </table>
+                                    <![endif]-->
+                                                        </td>
+                                                </tr>
+                                                <tr>
+                                                        <td align="center" valign="top" id="templateBody" data-template-container>
+                                                                <!--[if (gte mso 9)|(IE)]>
+                                    <table align="center" border="0" cellspacing="0" cellpadding="0" width="600" style="width:600px;">
+                                    <tr>
+                                    <td align="center" valign="top" width="600" style="width:600px;">
+                                    <![endif]-->
+                                                                <table align="center" border="0" cellpadding="0" cellspacing="0" width="100%"
+                                                                        class="templateContainer">
+                                                                        <tr>
+                                                                                <td valign="top" class="bodyContainer">
+                                                                                        <table border="0" cellpadding="0" cellspacing="0" width="100%"
+                                                                                                class="mcnCodeBlock">
+                                                                                                <tbody class="mcnTextBlockOuter">
+                                                                                                        <tr>
+                                                                                                                <td valign="top" class="mcnTextBlockInner">
+                                                                                                                        <p
+                                                                                                                                style="font-family:'Helvetica', sans-serif; margin-left: 3%; margin-right: 3%; font-size: 28px; line-height: 26px; font-weight:700; margin-bottom: 40px; margin-top: 48px; text-align: center; color: #222">
+                                                                                                                                Login to ${appname}</p>
+                                                                                                                </td>
+                                                                                                        </tr>
+                                                                                                </tbody>
+                                                                                        </table>
+                                                                                        <table border="0" cellpadding="0" cellspacing="0" width="100%"
+                                                                                                class="mcnCodeBlock">
+                                                                                                <tbody class="mcnTextBlockOuter">
+                                                                                                        <tr>
+                                                                                                                <td valign="top" class="mcnTextBlockInner">
+                                                                                                                        <div
+                                                                                                                                style="background-color:#fff; margin-left: 3%; margin-right: 3%; border: 1px solid #ddd; border-radius: 6px">
+                                                                                                                                <div style="padding-left: 15%; padding-right: 15%;">
+                                                                                                                                        <p
+                                                                                                                                                style="font-family: 'Helvetica' , sans-serif; font-size: 16px; line-height: 26px; font-weight:700; text-align: center; padding-top: 24px; padding-bottom: 8px; padding-left: 10%; padding-right: 10%; ">
+                                                                                                                                                Enter the below OTP in your login screen. Note
+                                                                                                                                                that the OTP expires in ${time}.</p>
+                                                                                                                                </div>
+
+                                                                                                                                <div
+                                                                                                                                        style="display: block; flex-direction: row; justify-content: center; margin-bottom: 40px">
+                                                                                                                                        <div class="mcnTextContent"
+                                                                                                                                                style="padding: 10px 20px; background-color: #fafafa; border: 1px solid #DDD; color: #222; font-family: 'Helvetica' , sans-serif; font-size: 32px; line-height: 40px; font-weight: 700; text-align: center; display: block; width: fit-content; border-radius: 6px; margin: 0 auto; margin-top: 8px;">
+                                                                                                                                                ${otp}</div>
+                                                                                                                                </div>
+                                                                                                                        </div>
+                                                                                                                </td>
+                                                                                                        </tr>
+                                                                                                </tbody>
+                                                                                        </table>
+                                                                                        <table border="0" cellpadding="0" cellspacing="0" width="100%"
+                                                                                                class="mcnTextBlock" style="min-width:100%;">
+                                                                                                <tbody class="mcnTextBlockOuter">
+                                                                                                        <tr>
+                                                                                                                <td valign="top" class="mcnTextBlockInner"
+                                                                                                                        style="padding-top:9px;">
+                                                                                                                        <!--[if mso]>
+                                <table align="left" border="0" cellspacing="0" cellpadding="0" width="100%" style="width:100%;">
+                                <tr>
+                                <![endif]-->
+
+                                                                                                                        <!--[if mso]>
+                                <td valign="top" width="600" style="width:600px;">
+                                <![endif]-->
+                                                                                                                        <table align="left" border="0" cellpadding="0"
+                                                                                                                                cellspacing="0" style="max-width:100%; min-width:100%;"
+                                                                                                                                width="100%" class="mcnTextContentContainer">
+                                                                                                                                <tbody>
+                                                                                                                                        <tr>
+
+                                                                                                                                                <td valign="top" class="mcnTextContent"
+                                                                                                                                                        style="padding: 0px 18px 9px; text-align: center;">
+
+                                                                                                                                                        or
+                                                                                                                                                </td>
+                                                                                                                                        </tr>
+                                                                                                                                </tbody>
+                                                                                                                        </table>
+                                                                                                                        <!--[if mso]>
+                                </td>
+                                <![endif]-->
+
+                                                                                                                        <!--[if mso]>
+                                </tr>
+                                </table>
+                                <![endif]-->
+                                                                                                                </td>
+                                                                                                        </tr>
+                                                                                                </tbody>
+                                                                                        </table>
+                                                                                        <table border="0" cellpadding="0" cellspacing="0" width="100%"
+                                                                                                class="mcnCodeBlock">
+                                                                                                <tbody class="mcnTextBlockOuter">
+                                                                                                        <tr>
+                                                                                                                <td valign="top" class="mcnTextBlockInner">
+
+
+                                                                                                                        <div
+                                                                                                                                style="background-color:#fff; margin-left: 3%; margin-right: 3%; border: 1px solid #ddd; border-radius: 6px; ">
+                                                                                                                                <div style="padding-left: 15%; padding-right: 15%;">
+
+                                                                                                                                        <p
+                                                                                                                                                style="font-family: 'Helvetica' , sans-serif; font-size: 16px; line-height: 26px; font-weight:700; text-align: center; padding-top: 24px; padding-bottom: 24px; padding-left: 10%; padding-right: 10%; ">
+                                                                                                                                                Please click the button below to sign in / up.
+                                                                                                                                                Note that the link expires in ${time}.</p>
+
+                                                                                                                                        <div class="button-td button-td-primary"
+                                                                                                                                                style="border-radius: 6px; margin-bottom: 40px; display: block; flex-direction: row; justify-content: center;">
+                                                                                                                                                <a class="button-a button-a-primary"
+                                                                                                                                                        href="${urlWithLinkCode}" target="_blank"
+                                                                                                                                                        style="background: #52B56E;font-size: 17px;line-height: 24px;font-weight: 700;font-family: 'Helvetica' , sans-serif;text-decoration: none;padding: 9px 25px 9px 25px;color: #ffffff;margin: 0 auto;width: fit-content;display: block;border-radius: 6px;">Login</a>
+                                                                                                                                        </div>
+                                                                                                                                </div>
+
+                                                                                                                                <div
+                                                                                                                                        style="background-color:#fafafa; border-top: 1px solid #ddd; padding-left: 15%; padding-right: 15%; padding-bottom: 24px; padding-top: 24px">
+                                                                                                                                        <p
+                                                                                                                                                style="font-family: 'Hevetica', sans-serif; font-size: 14px; line-height: 23px; font-weight:400;  text-align: center; color: #808080;">
+                                                                                                                                                Alternatively, you can directly paste this link
+                                                                                                                                                in your browser <br>
+                                                                                                                                                <a style="font-family: 'Helvetica', sans-serif, sans-serif; text-align: center; word-break: break-all; font-weight: 400; font-size: 14px; line-height: 23px; color: #007aff !important;"
+                                                                                                                                                        target="_blank"
+                                                                                                                                                        href="${urlWithLinkCode}">${urlWithLinkCode}</a>
+                                                                                                                                        </p>
+                                                                                                                                </div>
+                                                                                                                        </div>
+
+
+                                                                                                                </td>
+                                                                                                        </tr>
+                                                                                                </tbody>
+                                                                                        </table>
+                                                                                        <table border="0" cellpadding="0" cellspacing="0" width="100%"
+                                                                                                class="mcnCodeBlock">
+                                                                                                <tbody class="mcnTextBlockOuter">
+                                                                                                        <tr>
+                                                                                                                <td valign="top" class="mcnTextBlockInner">
+                                                                                                                        <p
+                                                                                                                                style="font-family:'Helvetica', sans-serif; font-size: 16px;margin-left: 3%; margin-right: 3%; line-height: 26px; font-weight:400; margin-top: 40px; text-align: center; color: #808080">
+                                                                                                                                This email is meant for <a
+                                                                                                                                        style="font-family: 'Helvetica', sans-serif; text-align: center; word-break: break-all; font-weight: 400; font-size: 16px; line-height: 26px; color: #808080 !important;"
+                                                                                                                                        target="_blank"
+                                                                                                                                        href="mailto:${toEmail}">${toEmail}</a>
+                                                                                                                        </p>
+                                                                                                                </td>
+                                                                                                        </tr>
+                                                                                                </tbody>
+                                                                                        </table>
+                                                                                </td>
+                                                                        </tr>
+                                                                </table>
+                                                                <!--[if (gte mso 9)|(IE)]>
+                                    </td>
+                                    </tr>
+                                    </table>
+                                    <![endif]-->
+                                                        </td>
+                                                </tr>
+                                                <tr>
+                                                        <td align="center" valign="top" id="templateFooter" data-template-container>
+                                                                <!--[if (gte mso 9)|(IE)]>
+                                    <table align="center" border="0" cellspacing="0" cellpadding="0" width="600" style="width:600px;">
+                                    <tr>
+                                    <td align="center" valign="top" width="600" style="width:600px;">
+                                    <![endif]-->
+                                                                <table align="center" border="0" cellpadding="0" cellspacing="0" width="100%"
+                                                                        class="templateContainer">
+                                                                        <tr>
+                                                                                <td valign="top" class="footerContainer"></td>
+                                                                        </tr>
+                                                                </table>
+                                                                <!--[if (gte mso 9)|(IE)]>
+                                    </td>
+                                    </tr>
+                                    </table>
+                                    <![endif]-->
+                                                        </td>
+                                                </tr>
+                                        </table>
+                                        <!-- // END TEMPLATE -->
+                                </td>
+                        </tr>
+                </table>
+        </center>
+</body>
+
+</html>
+"""
+
+
+
+
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/passwordless/emaildelivery/services/smtp/service_implementation.html b/html/supertokens_python/recipe/passwordless/emaildelivery/services/smtp/service_implementation.html new file mode 100644 index 000000000..36ddcc9be --- /dev/null +++ b/html/supertokens_python/recipe/passwordless/emaildelivery/services/smtp/service_implementation.html @@ -0,0 +1,183 @@ + + + + + + +supertokens_python.recipe.passwordless.emaildelivery.services.smtp.service_implementation API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.passwordless.emaildelivery.services.smtp.service_implementation

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from __future__ import annotations
+
+from typing import Any, Dict
+
+from supertokens_python.ingredients.emaildelivery.types import (
+    EmailContent,
+    SMTPServiceInterface,
+)
+from supertokens_python.recipe.passwordless.emaildelivery.services.smtp.pless_login import (
+    pless_email_content,
+)
+from supertokens_python.recipe.passwordless.types import (
+    PasswordlessLoginEmailTemplateVars,
+)
+
+
+class ServiceImplementation(SMTPServiceInterface[PasswordlessLoginEmailTemplateVars]):
+    async def send_raw_email(
+        self, content: EmailContent, user_context: Dict[str, Any]
+    ) -> None:
+        await self.transporter.send_email(content, user_context)
+
+    async def get_content(
+        self,
+        template_vars: PasswordlessLoginEmailTemplateVars,
+        user_context: Dict[str, Any],
+    ) -> EmailContent:
+        _ = user_context
+        return pless_email_content(template_vars)
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class ServiceImplementation +(transporter: Transporter) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class ServiceImplementation(SMTPServiceInterface[PasswordlessLoginEmailTemplateVars]):
+    async def send_raw_email(
+        self, content: EmailContent, user_context: Dict[str, Any]
+    ) -> None:
+        await self.transporter.send_email(content, user_context)
+
+    async def get_content(
+        self,
+        template_vars: PasswordlessLoginEmailTemplateVars,
+        user_context: Dict[str, Any],
+    ) -> EmailContent:
+        _ = user_context
+        return pless_email_content(template_vars)
+
+

Ancestors

+ +

Methods

+
+
+async def get_content(self, template_vars: PasswordlessLoginEmailTemplateVars, user_context: Dict[str, Any]) ‑> EmailContent +
+
+
+
+ +Expand source code + +
async def get_content(
+    self,
+    template_vars: PasswordlessLoginEmailTemplateVars,
+    user_context: Dict[str, Any],
+) -> EmailContent:
+    _ = user_context
+    return pless_email_content(template_vars)
+
+
+
+async def send_raw_email(self, content: EmailContent, user_context: Dict[str, Any]) ‑> None +
+
+
+
+ +Expand source code + +
async def send_raw_email(
+    self, content: EmailContent, user_context: Dict[str, Any]
+) -> None:
+    await self.transporter.send_email(content, user_context)
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/passwordless/exceptions.html b/html/supertokens_python/recipe/passwordless/exceptions.html new file mode 100644 index 000000000..9879ab17f --- /dev/null +++ b/html/supertokens_python/recipe/passwordless/exceptions.html @@ -0,0 +1,108 @@ + + + + + + +supertokens_python.recipe.passwordless.exceptions API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.passwordless.exceptions

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from supertokens_python.exceptions import SuperTokensError
+
+
+class SuperTokensPasswordlessError(SuperTokensError):
+    pass
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class SuperTokensPasswordlessError +(*args, **kwargs) +
+
+

Common base class for all non-exit exceptions.

+
+ +Expand source code + +
class SuperTokensPasswordlessError(SuperTokensError):
+    pass
+
+

Ancestors

+ +
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/passwordless/index.html b/html/supertokens_python/recipe/passwordless/index.html new file mode 100644 index 000000000..815de9276 --- /dev/null +++ b/html/supertokens_python/recipe/passwordless/index.html @@ -0,0 +1,237 @@ + + + + + + +supertokens_python.recipe.passwordless API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.passwordless

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Any, Awaitable, Callable, Dict, Union
+
+from supertokens_python.ingredients.emaildelivery.types import EmailDeliveryConfig
+from supertokens_python.ingredients.smsdelivery.types import SMSDeliveryConfig
+from supertokens_python.recipe.passwordless.types import (
+    EmailTemplateVars,
+    SMSTemplateVars,
+)
+from typing_extensions import Literal
+
+from . import types, utils
+from .emaildelivery import services as emaildelivery_services
+from .recipe import PasswordlessRecipe
+from .smsdelivery import services as smsdelivery_services
+
+if TYPE_CHECKING:
+    from supertokens_python.supertokens import AppInfo
+
+    from ...recipe_module import RecipeModule
+
+InputOverrideConfig = utils.OverrideConfig
+ContactEmailOnlyConfig = utils.ContactEmailOnlyConfig
+ContactConfig = utils.ContactConfig
+PhoneOrEmailInput = utils.PhoneOrEmailInput
+CreateAndSendCustomTextMessageParameters = (
+    types.CreateAndSendCustomTextMessageParameters
+)
+CreateAndSendCustomEmailParameters = types.CreateAndSendCustomEmailParameters
+ContactPhoneOnlyConfig = utils.ContactPhoneOnlyConfig
+ContactEmailOrPhoneConfig = utils.ContactEmailOrPhoneConfig
+SMTPService = emaildelivery_services.SMTPService
+TwilioService = smsdelivery_services.TwilioService
+SuperTokensSMSService = smsdelivery_services.SuperTokensSMSService
+EmailDeliveryInterface = types.EmailDeliveryInterface
+SMSDeliveryInterface = types.SMSDeliveryInterface
+
+
+def init(
+    contact_config: ContactConfig,
+    flow_type: Literal[
+        "USER_INPUT_CODE", "MAGIC_LINK", "USER_INPUT_CODE_AND_MAGIC_LINK"
+    ],
+    override: Union[InputOverrideConfig, None] = None,
+    get_custom_user_input_code: Union[
+        Callable[[str, Dict[str, Any]], Awaitable[str]], None
+    ] = None,
+    email_delivery: Union[EmailDeliveryConfig[EmailTemplateVars], None] = None,
+    sms_delivery: Union[SMSDeliveryConfig[SMSTemplateVars], None] = None,
+) -> Callable[[AppInfo], RecipeModule]:
+    return PasswordlessRecipe.init(
+        contact_config,
+        flow_type,
+        override,
+        get_custom_user_input_code,
+        email_delivery,
+        sms_delivery,
+    )
+
+
+
+

Sub-modules

+
+
supertokens_python.recipe.passwordless.api
+
+
+
+
supertokens_python.recipe.passwordless.asyncio
+
+
+
+
supertokens_python.recipe.passwordless.constants
+
+
+
+
supertokens_python.recipe.passwordless.emaildelivery
+
+
+
+
supertokens_python.recipe.passwordless.exceptions
+
+
+
+
supertokens_python.recipe.passwordless.interfaces
+
+
+
+
supertokens_python.recipe.passwordless.recipe
+
+
+
+
supertokens_python.recipe.passwordless.recipe_implementation
+
+
+
+
supertokens_python.recipe.passwordless.smsdelivery
+
+
+
+
supertokens_python.recipe.passwordless.syncio
+
+
+
+
supertokens_python.recipe.passwordless.types
+
+
+
+
supertokens_python.recipe.passwordless.utils
+
+
+
+
+
+
+
+
+

Functions

+
+
+def init(contact_config: ContactConfig, flow_type: "Literal[('USER_INPUT_CODE', 'MAGIC_LINK', 'USER_INPUT_CODE_AND_MAGIC_LINK')]", override: Union[InputOverrideConfig, None] = None, get_custom_user_input_code: Union[Callable[[str, Dict[str, Any]], Awaitable[str]], None] = None, email_delivery: Union[EmailDeliveryConfig[EmailTemplateVars], None] = None, sms_delivery: Union[SMSDeliveryConfig[SMSTemplateVars], None] = None) ‑> Callable[[AppInfo], RecipeModule] +
+
+
+
+ +Expand source code + +
def init(
+    contact_config: ContactConfig,
+    flow_type: Literal[
+        "USER_INPUT_CODE", "MAGIC_LINK", "USER_INPUT_CODE_AND_MAGIC_LINK"
+    ],
+    override: Union[InputOverrideConfig, None] = None,
+    get_custom_user_input_code: Union[
+        Callable[[str, Dict[str, Any]], Awaitable[str]], None
+    ] = None,
+    email_delivery: Union[EmailDeliveryConfig[EmailTemplateVars], None] = None,
+    sms_delivery: Union[SMSDeliveryConfig[SMSTemplateVars], None] = None,
+) -> Callable[[AppInfo], RecipeModule]:
+    return PasswordlessRecipe.init(
+        contact_config,
+        flow_type,
+        override,
+        get_custom_user_input_code,
+        email_delivery,
+        sms_delivery,
+    )
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/passwordless/interfaces.html b/html/supertokens_python/recipe/passwordless/interfaces.html new file mode 100644 index 000000000..3aeb128a2 --- /dev/null +++ b/html/supertokens_python/recipe/passwordless/interfaces.html @@ -0,0 +1,2105 @@ + + + + + + +supertokens_python.recipe.passwordless.interfaces API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.passwordless.interfaces

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from abc import ABC, abstractmethod
+from typing import Any, Dict, List, Union
+
+from typing_extensions import Literal
+
+from supertokens_python.framework import BaseRequest, BaseResponse
+from supertokens_python.ingredients.emaildelivery import EmailDeliveryIngredient
+from supertokens_python.recipe.session import SessionContainer
+from supertokens_python.types import APIResponse, GeneralErrorResponse
+
+from ...supertokens import AppInfo
+
+# if TYPE_CHECKING:
+from .types import (
+    DeviceType,
+    PasswordlessLoginEmailTemplateVars,
+    PasswordlessLoginSMSTemplateVars,
+    SMSDeliveryIngredient,
+    User,
+)
+from .utils import PasswordlessConfig
+
+
+class CreateCodeOkResult:
+    def __init__(
+        self,
+        pre_auth_session_id: str,
+        code_id: str,
+        device_id: str,
+        user_input_code: str,
+        link_code: str,
+        code_life_time: int,
+        time_created: int,
+    ):
+        self.pre_auth_session_id = pre_auth_session_id
+        self.code_id = code_id
+        self.device_id = device_id
+        self.user_input_code = user_input_code
+        self.link_code = link_code
+        self.code_life_time = code_life_time
+        self.time_created = time_created
+
+
+class CreateNewCodeForDeviceOkResult:
+    def __init__(
+        self,
+        pre_auth_session_id: str,
+        code_id: str,
+        device_id: str,
+        user_input_code: str,
+        link_code: str,
+        code_life_time: int,
+        time_created: int,
+    ):
+        self.pre_auth_session_id = pre_auth_session_id
+        self.code_id = code_id
+        self.device_id = device_id
+        self.user_input_code = user_input_code
+        self.link_code = link_code
+        self.code_life_time = code_life_time
+        self.time_created = time_created
+
+
+class CreateNewCodeForDeviceRestartFlowError:
+    pass
+
+
+class CreateNewCodeForDeviceUserInputCodeAlreadyUsedError:
+    pass
+
+
+class ConsumeCodeOkResult:
+    def __init__(self, created_new_user: bool, user: User):
+        self.created_new_user = created_new_user
+        self.user = user
+
+
+class ConsumeCodeIncorrectUserInputCodeError:
+    def __init__(
+        self, failed_code_input_attempt_count: int, maximum_code_input_attempts: int
+    ):
+        self.failed_code_input_attempt_count = failed_code_input_attempt_count
+        self.maximum_code_input_attempts = maximum_code_input_attempts
+
+
+class ConsumeCodeExpiredUserInputCodeError:
+    def __init__(
+        self, failed_code_input_attempt_count: int, maximum_code_input_attempts: int
+    ):
+        self.failed_code_input_attempt_count = failed_code_input_attempt_count
+        self.maximum_code_input_attempts = maximum_code_input_attempts
+
+
+class ConsumeCodeRestartFlowError:
+    pass
+
+
+class UpdateUserOkResult:
+    pass
+
+
+class UpdateUserUnknownUserIdError:
+    pass
+
+
+class UpdateUserEmailAlreadyExistsError:
+    pass
+
+
+class UpdateUserPhoneNumberAlreadyExistsError:
+    pass
+
+
+class DeleteUserInfoOkResult:
+    pass
+
+
+class DeleteUserInfoUnknownUserIdError:
+    pass
+
+
+class RevokeAllCodesOkResult:
+    pass
+
+
+class RevokeCodeOkResult:
+    pass
+
+
+class RecipeInterface(ABC):
+    def __init__(self):
+        pass
+
+    @abstractmethod
+    async def create_code(
+        self,
+        email: Union[None, str],
+        phone_number: Union[None, str],
+        user_input_code: Union[None, str],
+        tenant_id: str,
+        user_context: Dict[str, Any],
+    ) -> CreateCodeOkResult:
+        pass
+
+    @abstractmethod
+    async def create_new_code_for_device(
+        self,
+        device_id: str,
+        user_input_code: Union[str, None],
+        tenant_id: str,
+        user_context: Dict[str, Any],
+    ) -> Union[
+        CreateNewCodeForDeviceOkResult,
+        CreateNewCodeForDeviceRestartFlowError,
+        CreateNewCodeForDeviceUserInputCodeAlreadyUsedError,
+    ]:
+        pass
+
+    @abstractmethod
+    async def consume_code(
+        self,
+        pre_auth_session_id: str,
+        user_input_code: Union[str, None],
+        device_id: Union[str, None],
+        link_code: Union[str, None],
+        tenant_id: str,
+        user_context: Dict[str, Any],
+    ) -> Union[
+        ConsumeCodeOkResult,
+        ConsumeCodeIncorrectUserInputCodeError,
+        ConsumeCodeExpiredUserInputCodeError,
+        ConsumeCodeRestartFlowError,
+    ]:
+        pass
+
+    @abstractmethod
+    async def get_user_by_id(
+        self, user_id: str, user_context: Dict[str, Any]
+    ) -> Union[User, None]:
+        pass
+
+    @abstractmethod
+    async def get_user_by_email(
+        self, email: str, tenant_id: str, user_context: Dict[str, Any]
+    ) -> Union[User, None]:
+        pass
+
+    @abstractmethod
+    async def get_user_by_phone_number(
+        self, phone_number: str, tenant_id: str, user_context: Dict[str, Any]
+    ) -> Union[User, None]:
+        pass
+
+    @abstractmethod
+    async def update_user(
+        self,
+        user_id: str,
+        email: Union[str, None],
+        phone_number: Union[str, None],
+        user_context: Dict[str, Any],
+    ) -> Union[
+        UpdateUserOkResult,
+        UpdateUserUnknownUserIdError,
+        UpdateUserEmailAlreadyExistsError,
+        UpdateUserPhoneNumberAlreadyExistsError,
+    ]:
+        pass
+
+    @abstractmethod
+    async def delete_email_for_user(
+        self, user_id: str, user_context: Dict[str, Any]
+    ) -> Union[DeleteUserInfoOkResult, DeleteUserInfoUnknownUserIdError]:
+        pass
+
+    @abstractmethod
+    async def delete_phone_number_for_user(
+        self, user_id: str, user_context: Dict[str, Any]
+    ) -> Union[DeleteUserInfoOkResult, DeleteUserInfoUnknownUserIdError]:
+        pass
+
+    @abstractmethod
+    async def revoke_all_codes(
+        self,
+        email: Union[str, None],
+        phone_number: Union[str, None],
+        tenant_id: str,
+        user_context: Dict[str, Any],
+    ) -> RevokeAllCodesOkResult:
+        pass
+
+    @abstractmethod
+    async def revoke_code(
+        self, code_id: str, tenant_id: str, user_context: Dict[str, Any]
+    ) -> RevokeCodeOkResult:
+        pass
+
+    @abstractmethod
+    async def list_codes_by_email(
+        self, email: str, tenant_id: str, user_context: Dict[str, Any]
+    ) -> List[DeviceType]:
+        pass
+
+    @abstractmethod
+    async def list_codes_by_phone_number(
+        self, phone_number: str, tenant_id: str, user_context: Dict[str, Any]
+    ) -> List[DeviceType]:
+        pass
+
+    @abstractmethod
+    async def list_codes_by_device_id(
+        self, device_id: str, tenant_id: str, user_context: Dict[str, Any]
+    ) -> Union[DeviceType, None]:
+        pass
+
+    @abstractmethod
+    async def list_codes_by_pre_auth_session_id(
+        self, pre_auth_session_id: str, tenant_id: str, user_context: Dict[str, Any]
+    ) -> Union[DeviceType, None]:
+        pass
+
+
+class APIOptions:
+    def __init__(
+        self,
+        request: BaseRequest,
+        response: BaseResponse,
+        recipe_id: str,
+        config: PasswordlessConfig,
+        recipe_implementation: RecipeInterface,
+        app_info: AppInfo,
+        email_delivery: EmailDeliveryIngredient[PasswordlessLoginEmailTemplateVars],
+        sms_delivery: SMSDeliveryIngredient[PasswordlessLoginSMSTemplateVars],
+    ):
+        self.request = request
+        self.response = response
+        self.recipe_id = recipe_id
+        self.config = config
+        self.recipe_implementation = recipe_implementation
+        self.app_info = app_info
+        self.email_delivery = email_delivery
+        self.sms_delivery = sms_delivery
+
+
+class CreateCodePostOkResult(APIResponse):
+    status: str = "OK"
+
+    def __init__(
+        self,
+        device_id: str,
+        pre_auth_session_id: str,
+        flow_type: Literal[
+            "USER_INPUT_CODE", "MAGIC_LINK", "USER_INPUT_CODE_AND_MAGIC_LINK"
+        ],
+    ):
+        self.device_id = device_id
+        self.pre_auth_session_id = pre_auth_session_id
+        self.flow_type = flow_type
+
+    def to_json(self):
+        return {
+            "status": self.status,
+            "deviceId": self.device_id,
+            "preAuthSessionId": self.pre_auth_session_id,
+            "flowType": self.flow_type,
+        }
+
+
+class ResendCodePostOkResult(APIResponse):
+    status: str = "OK"
+
+    def to_json(self):
+        return {"status": self.status}
+
+
+class ResendCodePostRestartFlowError(APIResponse):
+    status: str = "RESTART_FLOW_ERROR"
+
+    def to_json(self):
+        return {"status": self.status}
+
+
+class ConsumeCodePostOkResult(APIResponse):
+    status: str = "OK"
+
+    def __init__(self, created_new_user: bool, user: User, session: SessionContainer):
+        self.created_new_user = created_new_user
+        self.user = user
+        self.session = session
+
+    def to_json(self):
+        user = {"id": self.user.user_id, "time_joined": self.user.time_joined}
+        if self.user.email is not None:
+            user = {**user, "email": self.user.email}
+        if self.user.phone_number is not None:
+            user = {**user, "phoneNumber": self.user.phone_number}
+        return {
+            "status": self.status,
+            "createdNewUser": self.created_new_user,
+            "user": user,
+        }
+
+
+class ConsumeCodePostRestartFlowError(APIResponse):
+    status: str = "RESTART_FLOW_ERROR"
+
+    def to_json(self):
+        return {"status": self.status}
+
+
+class ConsumeCodePostIncorrectUserInputCodeError(APIResponse):
+    status: str = "INCORRECT_USER_INPUT_CODE_ERROR"
+
+    def __init__(
+        self, failed_code_input_attempt_count: int, maximum_code_input_attempts: int
+    ):
+        self.failed_code_input_attempt_count = failed_code_input_attempt_count
+        self.maximum_code_input_attempts = maximum_code_input_attempts
+
+    def to_json(self):
+        return {
+            "status": self.status,
+            "failedCodeInputAttemptCount": self.failed_code_input_attempt_count,
+            "maximumCodeInputAttempts": self.maximum_code_input_attempts,
+        }
+
+
+class ConsumeCodePostExpiredUserInputCodeError(APIResponse):
+    status: str = "EXPIRED_USER_INPUT_CODE_ERROR"
+
+    def __init__(
+        self, failed_code_input_attempt_count: int, maximum_code_input_attempts: int
+    ):
+        self.failed_code_input_attempt_count = failed_code_input_attempt_count
+        self.maximum_code_input_attempts = maximum_code_input_attempts
+
+    def to_json(self):
+        return {
+            "status": self.status,
+            "failedCodeInputAttemptCount": self.failed_code_input_attempt_count,
+            "maximumCodeInputAttempts": self.maximum_code_input_attempts,
+        }
+
+
+class PhoneNumberExistsGetOkResult(APIResponse):
+    status: str = "OK"
+
+    def __init__(self, exists: bool):
+        self.exists = exists
+
+    def to_json(self):
+        return {"status": self.status, "exists": self.exists}
+
+
+class EmailExistsGetOkResult(APIResponse):
+    status: str = "OK"
+
+    def __init__(self, exists: bool):
+        self.exists = exists
+
+    def to_json(self):
+        return {"status": self.status, "exists": self.exists}
+
+
+class APIInterface:
+    def __init__(self):
+        self.disable_create_code_post = False
+        self.disable_resend_code_post = False
+        self.disable_consume_code_post = False
+        self.disable_email_exists_get = False
+        self.disable_phone_number_exists_get = False
+
+    @abstractmethod
+    async def create_code_post(
+        self,
+        email: Union[str, None],
+        phone_number: Union[str, None],
+        tenant_id: str,
+        api_options: APIOptions,
+        user_context: Dict[str, Any],
+    ) -> Union[CreateCodePostOkResult, GeneralErrorResponse]:
+        pass
+
+    @abstractmethod
+    async def resend_code_post(
+        self,
+        device_id: str,
+        pre_auth_session_id: str,
+        tenant_id: str,
+        api_options: APIOptions,
+        user_context: Dict[str, Any],
+    ) -> Union[
+        ResendCodePostOkResult, ResendCodePostRestartFlowError, GeneralErrorResponse
+    ]:
+        pass
+
+    @abstractmethod
+    async def consume_code_post(
+        self,
+        pre_auth_session_id: str,
+        user_input_code: Union[str, None],
+        device_id: Union[str, None],
+        link_code: Union[str, None],
+        tenant_id: str,
+        api_options: APIOptions,
+        user_context: Dict[str, Any],
+    ) -> Union[
+        ConsumeCodePostOkResult,
+        ConsumeCodePostRestartFlowError,
+        GeneralErrorResponse,
+        ConsumeCodePostIncorrectUserInputCodeError,
+        ConsumeCodePostExpiredUserInputCodeError,
+    ]:
+        pass
+
+    @abstractmethod
+    async def email_exists_get(
+        self,
+        email: str,
+        tenant_id: str,
+        api_options: APIOptions,
+        user_context: Dict[str, Any],
+    ) -> Union[EmailExistsGetOkResult, GeneralErrorResponse]:
+        pass
+
+    @abstractmethod
+    async def phone_number_exists_get(
+        self,
+        phone_number: str,
+        tenant_id: str,
+        api_options: APIOptions,
+        user_context: Dict[str, Any],
+    ) -> Union[PhoneNumberExistsGetOkResult, GeneralErrorResponse]:
+        pass
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class APIInterface +
+
+
+
+ +Expand source code + +
class APIInterface:
+    def __init__(self):
+        self.disable_create_code_post = False
+        self.disable_resend_code_post = False
+        self.disable_consume_code_post = False
+        self.disable_email_exists_get = False
+        self.disable_phone_number_exists_get = False
+
+    @abstractmethod
+    async def create_code_post(
+        self,
+        email: Union[str, None],
+        phone_number: Union[str, None],
+        tenant_id: str,
+        api_options: APIOptions,
+        user_context: Dict[str, Any],
+    ) -> Union[CreateCodePostOkResult, GeneralErrorResponse]:
+        pass
+
+    @abstractmethod
+    async def resend_code_post(
+        self,
+        device_id: str,
+        pre_auth_session_id: str,
+        tenant_id: str,
+        api_options: APIOptions,
+        user_context: Dict[str, Any],
+    ) -> Union[
+        ResendCodePostOkResult, ResendCodePostRestartFlowError, GeneralErrorResponse
+    ]:
+        pass
+
+    @abstractmethod
+    async def consume_code_post(
+        self,
+        pre_auth_session_id: str,
+        user_input_code: Union[str, None],
+        device_id: Union[str, None],
+        link_code: Union[str, None],
+        tenant_id: str,
+        api_options: APIOptions,
+        user_context: Dict[str, Any],
+    ) -> Union[
+        ConsumeCodePostOkResult,
+        ConsumeCodePostRestartFlowError,
+        GeneralErrorResponse,
+        ConsumeCodePostIncorrectUserInputCodeError,
+        ConsumeCodePostExpiredUserInputCodeError,
+    ]:
+        pass
+
+    @abstractmethod
+    async def email_exists_get(
+        self,
+        email: str,
+        tenant_id: str,
+        api_options: APIOptions,
+        user_context: Dict[str, Any],
+    ) -> Union[EmailExistsGetOkResult, GeneralErrorResponse]:
+        pass
+
+    @abstractmethod
+    async def phone_number_exists_get(
+        self,
+        phone_number: str,
+        tenant_id: str,
+        api_options: APIOptions,
+        user_context: Dict[str, Any],
+    ) -> Union[PhoneNumberExistsGetOkResult, GeneralErrorResponse]:
+        pass
+
+

Subclasses

+ +

Methods

+
+
+async def consume_code_post(self, pre_auth_session_id: str, user_input_code: Union[str, None], device_id: Union[str, None], link_code: Union[str, None], tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[ConsumeCodePostOkResultConsumeCodePostRestartFlowErrorConsumeCodePostIncorrectUserInputCodeErrorConsumeCodePostExpiredUserInputCodeErrorGeneralErrorResponse] +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def consume_code_post(
+    self,
+    pre_auth_session_id: str,
+    user_input_code: Union[str, None],
+    device_id: Union[str, None],
+    link_code: Union[str, None],
+    tenant_id: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+) -> Union[
+    ConsumeCodePostOkResult,
+    ConsumeCodePostRestartFlowError,
+    GeneralErrorResponse,
+    ConsumeCodePostIncorrectUserInputCodeError,
+    ConsumeCodePostExpiredUserInputCodeError,
+]:
+    pass
+
+
+
+async def create_code_post(self, email: Union[str, None], phone_number: Union[str, None], tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[CreateCodePostOkResultGeneralErrorResponse] +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def create_code_post(
+    self,
+    email: Union[str, None],
+    phone_number: Union[str, None],
+    tenant_id: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+) -> Union[CreateCodePostOkResult, GeneralErrorResponse]:
+    pass
+
+
+
+async def email_exists_get(self, email: str, tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[EmailExistsGetOkResultGeneralErrorResponse] +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def email_exists_get(
+    self,
+    email: str,
+    tenant_id: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+) -> Union[EmailExistsGetOkResult, GeneralErrorResponse]:
+    pass
+
+
+
+async def phone_number_exists_get(self, phone_number: str, tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[PhoneNumberExistsGetOkResultGeneralErrorResponse] +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def phone_number_exists_get(
+    self,
+    phone_number: str,
+    tenant_id: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+) -> Union[PhoneNumberExistsGetOkResult, GeneralErrorResponse]:
+    pass
+
+
+
+async def resend_code_post(self, device_id: str, pre_auth_session_id: str, tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[ResendCodePostOkResultResendCodePostRestartFlowErrorGeneralErrorResponse] +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def resend_code_post(
+    self,
+    device_id: str,
+    pre_auth_session_id: str,
+    tenant_id: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+) -> Union[
+    ResendCodePostOkResult, ResendCodePostRestartFlowError, GeneralErrorResponse
+]:
+    pass
+
+
+
+
+
+class APIOptions +(request: BaseRequest, response: BaseResponse, recipe_id: str, config: PasswordlessConfig, recipe_implementation: RecipeInterface, app_info: AppInfo, email_delivery: EmailDeliveryIngredient[PasswordlessLoginEmailTemplateVars], sms_delivery: SMSDeliveryIngredient[PasswordlessLoginSMSTemplateVars]) +
+
+
+
+ +Expand source code + +
class APIOptions:
+    def __init__(
+        self,
+        request: BaseRequest,
+        response: BaseResponse,
+        recipe_id: str,
+        config: PasswordlessConfig,
+        recipe_implementation: RecipeInterface,
+        app_info: AppInfo,
+        email_delivery: EmailDeliveryIngredient[PasswordlessLoginEmailTemplateVars],
+        sms_delivery: SMSDeliveryIngredient[PasswordlessLoginSMSTemplateVars],
+    ):
+        self.request = request
+        self.response = response
+        self.recipe_id = recipe_id
+        self.config = config
+        self.recipe_implementation = recipe_implementation
+        self.app_info = app_info
+        self.email_delivery = email_delivery
+        self.sms_delivery = sms_delivery
+
+
+
+class ConsumeCodeExpiredUserInputCodeError +(failed_code_input_attempt_count: int, maximum_code_input_attempts: int) +
+
+
+
+ +Expand source code + +
class ConsumeCodeExpiredUserInputCodeError:
+    def __init__(
+        self, failed_code_input_attempt_count: int, maximum_code_input_attempts: int
+    ):
+        self.failed_code_input_attempt_count = failed_code_input_attempt_count
+        self.maximum_code_input_attempts = maximum_code_input_attempts
+
+
+
+class ConsumeCodeIncorrectUserInputCodeError +(failed_code_input_attempt_count: int, maximum_code_input_attempts: int) +
+
+
+
+ +Expand source code + +
class ConsumeCodeIncorrectUserInputCodeError:
+    def __init__(
+        self, failed_code_input_attempt_count: int, maximum_code_input_attempts: int
+    ):
+        self.failed_code_input_attempt_count = failed_code_input_attempt_count
+        self.maximum_code_input_attempts = maximum_code_input_attempts
+
+
+
+class ConsumeCodeOkResult +(created_new_user: bool, user: User) +
+
+
+
+ +Expand source code + +
class ConsumeCodeOkResult:
+    def __init__(self, created_new_user: bool, user: User):
+        self.created_new_user = created_new_user
+        self.user = user
+
+
+
+class ConsumeCodePostExpiredUserInputCodeError +(failed_code_input_attempt_count: int, maximum_code_input_attempts: int) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class ConsumeCodePostExpiredUserInputCodeError(APIResponse):
+    status: str = "EXPIRED_USER_INPUT_CODE_ERROR"
+
+    def __init__(
+        self, failed_code_input_attempt_count: int, maximum_code_input_attempts: int
+    ):
+        self.failed_code_input_attempt_count = failed_code_input_attempt_count
+        self.maximum_code_input_attempts = maximum_code_input_attempts
+
+    def to_json(self):
+        return {
+            "status": self.status,
+            "failedCodeInputAttemptCount": self.failed_code_input_attempt_count,
+            "maximumCodeInputAttempts": self.maximum_code_input_attempts,
+        }
+
+

Ancestors

+ +

Class variables

+
+
var status : str
+
+
+
+
+

Methods

+
+
+def to_json(self) +
+
+
+
+ +Expand source code + +
def to_json(self):
+    return {
+        "status": self.status,
+        "failedCodeInputAttemptCount": self.failed_code_input_attempt_count,
+        "maximumCodeInputAttempts": self.maximum_code_input_attempts,
+    }
+
+
+
+
+
+class ConsumeCodePostIncorrectUserInputCodeError +(failed_code_input_attempt_count: int, maximum_code_input_attempts: int) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class ConsumeCodePostIncorrectUserInputCodeError(APIResponse):
+    status: str = "INCORRECT_USER_INPUT_CODE_ERROR"
+
+    def __init__(
+        self, failed_code_input_attempt_count: int, maximum_code_input_attempts: int
+    ):
+        self.failed_code_input_attempt_count = failed_code_input_attempt_count
+        self.maximum_code_input_attempts = maximum_code_input_attempts
+
+    def to_json(self):
+        return {
+            "status": self.status,
+            "failedCodeInputAttemptCount": self.failed_code_input_attempt_count,
+            "maximumCodeInputAttempts": self.maximum_code_input_attempts,
+        }
+
+

Ancestors

+ +

Class variables

+
+
var status : str
+
+
+
+
+

Methods

+
+
+def to_json(self) +
+
+
+
+ +Expand source code + +
def to_json(self):
+    return {
+        "status": self.status,
+        "failedCodeInputAttemptCount": self.failed_code_input_attempt_count,
+        "maximumCodeInputAttempts": self.maximum_code_input_attempts,
+    }
+
+
+
+
+
+class ConsumeCodePostOkResult +(created_new_user: bool, user: User, session: SessionContainer) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class ConsumeCodePostOkResult(APIResponse):
+    status: str = "OK"
+
+    def __init__(self, created_new_user: bool, user: User, session: SessionContainer):
+        self.created_new_user = created_new_user
+        self.user = user
+        self.session = session
+
+    def to_json(self):
+        user = {"id": self.user.user_id, "time_joined": self.user.time_joined}
+        if self.user.email is not None:
+            user = {**user, "email": self.user.email}
+        if self.user.phone_number is not None:
+            user = {**user, "phoneNumber": self.user.phone_number}
+        return {
+            "status": self.status,
+            "createdNewUser": self.created_new_user,
+            "user": user,
+        }
+
+

Ancestors

+ +

Class variables

+
+
var status : str
+
+
+
+
+

Methods

+
+
+def to_json(self) +
+
+
+
+ +Expand source code + +
def to_json(self):
+    user = {"id": self.user.user_id, "time_joined": self.user.time_joined}
+    if self.user.email is not None:
+        user = {**user, "email": self.user.email}
+    if self.user.phone_number is not None:
+        user = {**user, "phoneNumber": self.user.phone_number}
+    return {
+        "status": self.status,
+        "createdNewUser": self.created_new_user,
+        "user": user,
+    }
+
+
+
+
+
+class ConsumeCodePostRestartFlowError +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class ConsumeCodePostRestartFlowError(APIResponse):
+    status: str = "RESTART_FLOW_ERROR"
+
+    def to_json(self):
+        return {"status": self.status}
+
+

Ancestors

+ +

Class variables

+
+
var status : str
+
+
+
+
+

Methods

+
+
+def to_json(self) +
+
+
+
+ +Expand source code + +
def to_json(self):
+    return {"status": self.status}
+
+
+
+
+
+class ConsumeCodeRestartFlowError +
+
+
+
+ +Expand source code + +
class ConsumeCodeRestartFlowError:
+    pass
+
+
+
+class CreateCodeOkResult +(pre_auth_session_id: str, code_id: str, device_id: str, user_input_code: str, link_code: str, code_life_time: int, time_created: int) +
+
+
+
+ +Expand source code + +
class CreateCodeOkResult:
+    def __init__(
+        self,
+        pre_auth_session_id: str,
+        code_id: str,
+        device_id: str,
+        user_input_code: str,
+        link_code: str,
+        code_life_time: int,
+        time_created: int,
+    ):
+        self.pre_auth_session_id = pre_auth_session_id
+        self.code_id = code_id
+        self.device_id = device_id
+        self.user_input_code = user_input_code
+        self.link_code = link_code
+        self.code_life_time = code_life_time
+        self.time_created = time_created
+
+
+
+class CreateCodePostOkResult +(device_id: str, pre_auth_session_id: str, flow_type: "Literal[('USER_INPUT_CODE', 'MAGIC_LINK', 'USER_INPUT_CODE_AND_MAGIC_LINK')]") +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class CreateCodePostOkResult(APIResponse):
+    status: str = "OK"
+
+    def __init__(
+        self,
+        device_id: str,
+        pre_auth_session_id: str,
+        flow_type: Literal[
+            "USER_INPUT_CODE", "MAGIC_LINK", "USER_INPUT_CODE_AND_MAGIC_LINK"
+        ],
+    ):
+        self.device_id = device_id
+        self.pre_auth_session_id = pre_auth_session_id
+        self.flow_type = flow_type
+
+    def to_json(self):
+        return {
+            "status": self.status,
+            "deviceId": self.device_id,
+            "preAuthSessionId": self.pre_auth_session_id,
+            "flowType": self.flow_type,
+        }
+
+

Ancestors

+ +

Class variables

+
+
var status : str
+
+
+
+
+

Methods

+
+
+def to_json(self) +
+
+
+
+ +Expand source code + +
def to_json(self):
+    return {
+        "status": self.status,
+        "deviceId": self.device_id,
+        "preAuthSessionId": self.pre_auth_session_id,
+        "flowType": self.flow_type,
+    }
+
+
+
+
+
+class CreateNewCodeForDeviceOkResult +(pre_auth_session_id: str, code_id: str, device_id: str, user_input_code: str, link_code: str, code_life_time: int, time_created: int) +
+
+
+
+ +Expand source code + +
class CreateNewCodeForDeviceOkResult:
+    def __init__(
+        self,
+        pre_auth_session_id: str,
+        code_id: str,
+        device_id: str,
+        user_input_code: str,
+        link_code: str,
+        code_life_time: int,
+        time_created: int,
+    ):
+        self.pre_auth_session_id = pre_auth_session_id
+        self.code_id = code_id
+        self.device_id = device_id
+        self.user_input_code = user_input_code
+        self.link_code = link_code
+        self.code_life_time = code_life_time
+        self.time_created = time_created
+
+
+
+class CreateNewCodeForDeviceRestartFlowError +
+
+
+
+ +Expand source code + +
class CreateNewCodeForDeviceRestartFlowError:
+    pass
+
+
+
+class CreateNewCodeForDeviceUserInputCodeAlreadyUsedError +
+
+
+
+ +Expand source code + +
class CreateNewCodeForDeviceUserInputCodeAlreadyUsedError:
+    pass
+
+
+
+class DeleteUserInfoOkResult +
+
+
+
+ +Expand source code + +
class DeleteUserInfoOkResult:
+    pass
+
+
+
+class DeleteUserInfoUnknownUserIdError +
+
+
+
+ +Expand source code + +
class DeleteUserInfoUnknownUserIdError:
+    pass
+
+
+
+class EmailExistsGetOkResult +(exists: bool) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class EmailExistsGetOkResult(APIResponse):
+    status: str = "OK"
+
+    def __init__(self, exists: bool):
+        self.exists = exists
+
+    def to_json(self):
+        return {"status": self.status, "exists": self.exists}
+
+

Ancestors

+ +

Class variables

+
+
var status : str
+
+
+
+
+

Methods

+
+
+def to_json(self) +
+
+
+
+ +Expand source code + +
def to_json(self):
+    return {"status": self.status, "exists": self.exists}
+
+
+
+
+
+class PhoneNumberExistsGetOkResult +(exists: bool) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class PhoneNumberExistsGetOkResult(APIResponse):
+    status: str = "OK"
+
+    def __init__(self, exists: bool):
+        self.exists = exists
+
+    def to_json(self):
+        return {"status": self.status, "exists": self.exists}
+
+

Ancestors

+ +

Class variables

+
+
var status : str
+
+
+
+
+

Methods

+
+
+def to_json(self) +
+
+
+
+ +Expand source code + +
def to_json(self):
+    return {"status": self.status, "exists": self.exists}
+
+
+
+
+
+class RecipeInterface +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class RecipeInterface(ABC):
+    def __init__(self):
+        pass
+
+    @abstractmethod
+    async def create_code(
+        self,
+        email: Union[None, str],
+        phone_number: Union[None, str],
+        user_input_code: Union[None, str],
+        tenant_id: str,
+        user_context: Dict[str, Any],
+    ) -> CreateCodeOkResult:
+        pass
+
+    @abstractmethod
+    async def create_new_code_for_device(
+        self,
+        device_id: str,
+        user_input_code: Union[str, None],
+        tenant_id: str,
+        user_context: Dict[str, Any],
+    ) -> Union[
+        CreateNewCodeForDeviceOkResult,
+        CreateNewCodeForDeviceRestartFlowError,
+        CreateNewCodeForDeviceUserInputCodeAlreadyUsedError,
+    ]:
+        pass
+
+    @abstractmethod
+    async def consume_code(
+        self,
+        pre_auth_session_id: str,
+        user_input_code: Union[str, None],
+        device_id: Union[str, None],
+        link_code: Union[str, None],
+        tenant_id: str,
+        user_context: Dict[str, Any],
+    ) -> Union[
+        ConsumeCodeOkResult,
+        ConsumeCodeIncorrectUserInputCodeError,
+        ConsumeCodeExpiredUserInputCodeError,
+        ConsumeCodeRestartFlowError,
+    ]:
+        pass
+
+    @abstractmethod
+    async def get_user_by_id(
+        self, user_id: str, user_context: Dict[str, Any]
+    ) -> Union[User, None]:
+        pass
+
+    @abstractmethod
+    async def get_user_by_email(
+        self, email: str, tenant_id: str, user_context: Dict[str, Any]
+    ) -> Union[User, None]:
+        pass
+
+    @abstractmethod
+    async def get_user_by_phone_number(
+        self, phone_number: str, tenant_id: str, user_context: Dict[str, Any]
+    ) -> Union[User, None]:
+        pass
+
+    @abstractmethod
+    async def update_user(
+        self,
+        user_id: str,
+        email: Union[str, None],
+        phone_number: Union[str, None],
+        user_context: Dict[str, Any],
+    ) -> Union[
+        UpdateUserOkResult,
+        UpdateUserUnknownUserIdError,
+        UpdateUserEmailAlreadyExistsError,
+        UpdateUserPhoneNumberAlreadyExistsError,
+    ]:
+        pass
+
+    @abstractmethod
+    async def delete_email_for_user(
+        self, user_id: str, user_context: Dict[str, Any]
+    ) -> Union[DeleteUserInfoOkResult, DeleteUserInfoUnknownUserIdError]:
+        pass
+
+    @abstractmethod
+    async def delete_phone_number_for_user(
+        self, user_id: str, user_context: Dict[str, Any]
+    ) -> Union[DeleteUserInfoOkResult, DeleteUserInfoUnknownUserIdError]:
+        pass
+
+    @abstractmethod
+    async def revoke_all_codes(
+        self,
+        email: Union[str, None],
+        phone_number: Union[str, None],
+        tenant_id: str,
+        user_context: Dict[str, Any],
+    ) -> RevokeAllCodesOkResult:
+        pass
+
+    @abstractmethod
+    async def revoke_code(
+        self, code_id: str, tenant_id: str, user_context: Dict[str, Any]
+    ) -> RevokeCodeOkResult:
+        pass
+
+    @abstractmethod
+    async def list_codes_by_email(
+        self, email: str, tenant_id: str, user_context: Dict[str, Any]
+    ) -> List[DeviceType]:
+        pass
+
+    @abstractmethod
+    async def list_codes_by_phone_number(
+        self, phone_number: str, tenant_id: str, user_context: Dict[str, Any]
+    ) -> List[DeviceType]:
+        pass
+
+    @abstractmethod
+    async def list_codes_by_device_id(
+        self, device_id: str, tenant_id: str, user_context: Dict[str, Any]
+    ) -> Union[DeviceType, None]:
+        pass
+
+    @abstractmethod
+    async def list_codes_by_pre_auth_session_id(
+        self, pre_auth_session_id: str, tenant_id: str, user_context: Dict[str, Any]
+    ) -> Union[DeviceType, None]:
+        pass
+
+

Ancestors

+
    +
  • abc.ABC
  • +
+

Subclasses

+ +

Methods

+
+
+async def consume_code(self, pre_auth_session_id: str, user_input_code: Union[str, None], device_id: Union[str, None], link_code: Union[str, None], tenant_id: str, user_context: Dict[str, Any]) ‑> Union[ConsumeCodeOkResultConsumeCodeIncorrectUserInputCodeErrorConsumeCodeExpiredUserInputCodeErrorConsumeCodeRestartFlowError] +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def consume_code(
+    self,
+    pre_auth_session_id: str,
+    user_input_code: Union[str, None],
+    device_id: Union[str, None],
+    link_code: Union[str, None],
+    tenant_id: str,
+    user_context: Dict[str, Any],
+) -> Union[
+    ConsumeCodeOkResult,
+    ConsumeCodeIncorrectUserInputCodeError,
+    ConsumeCodeExpiredUserInputCodeError,
+    ConsumeCodeRestartFlowError,
+]:
+    pass
+
+
+
+async def create_code(self, email: Union[None, str], phone_number: Union[None, str], user_input_code: Union[None, str], tenant_id: str, user_context: Dict[str, Any]) ‑> CreateCodeOkResult +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def create_code(
+    self,
+    email: Union[None, str],
+    phone_number: Union[None, str],
+    user_input_code: Union[None, str],
+    tenant_id: str,
+    user_context: Dict[str, Any],
+) -> CreateCodeOkResult:
+    pass
+
+
+
+async def create_new_code_for_device(self, device_id: str, user_input_code: Union[str, None], tenant_id: str, user_context: Dict[str, Any]) ‑> Union[CreateNewCodeForDeviceOkResultCreateNewCodeForDeviceRestartFlowErrorCreateNewCodeForDeviceUserInputCodeAlreadyUsedError] +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def create_new_code_for_device(
+    self,
+    device_id: str,
+    user_input_code: Union[str, None],
+    tenant_id: str,
+    user_context: Dict[str, Any],
+) -> Union[
+    CreateNewCodeForDeviceOkResult,
+    CreateNewCodeForDeviceRestartFlowError,
+    CreateNewCodeForDeviceUserInputCodeAlreadyUsedError,
+]:
+    pass
+
+
+
+async def delete_email_for_user(self, user_id: str, user_context: Dict[str, Any]) ‑> Union[DeleteUserInfoOkResultDeleteUserInfoUnknownUserIdError] +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def delete_email_for_user(
+    self, user_id: str, user_context: Dict[str, Any]
+) -> Union[DeleteUserInfoOkResult, DeleteUserInfoUnknownUserIdError]:
+    pass
+
+
+
+async def delete_phone_number_for_user(self, user_id: str, user_context: Dict[str, Any]) ‑> Union[DeleteUserInfoOkResultDeleteUserInfoUnknownUserIdError] +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def delete_phone_number_for_user(
+    self, user_id: str, user_context: Dict[str, Any]
+) -> Union[DeleteUserInfoOkResult, DeleteUserInfoUnknownUserIdError]:
+    pass
+
+
+
+async def get_user_by_email(self, email: str, tenant_id: str, user_context: Dict[str, Any]) ‑> Optional[User] +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def get_user_by_email(
+    self, email: str, tenant_id: str, user_context: Dict[str, Any]
+) -> Union[User, None]:
+    pass
+
+
+
+async def get_user_by_id(self, user_id: str, user_context: Dict[str, Any]) ‑> Optional[User] +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def get_user_by_id(
+    self, user_id: str, user_context: Dict[str, Any]
+) -> Union[User, None]:
+    pass
+
+
+
+async def get_user_by_phone_number(self, phone_number: str, tenant_id: str, user_context: Dict[str, Any]) ‑> Optional[User] +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def get_user_by_phone_number(
+    self, phone_number: str, tenant_id: str, user_context: Dict[str, Any]
+) -> Union[User, None]:
+    pass
+
+
+
+async def list_codes_by_device_id(self, device_id: str, tenant_id: str, user_context: Dict[str, Any]) ‑> Optional[DeviceType] +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def list_codes_by_device_id(
+    self, device_id: str, tenant_id: str, user_context: Dict[str, Any]
+) -> Union[DeviceType, None]:
+    pass
+
+
+
+async def list_codes_by_email(self, email: str, tenant_id: str, user_context: Dict[str, Any]) ‑> List[DeviceType] +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def list_codes_by_email(
+    self, email: str, tenant_id: str, user_context: Dict[str, Any]
+) -> List[DeviceType]:
+    pass
+
+
+
+async def list_codes_by_phone_number(self, phone_number: str, tenant_id: str, user_context: Dict[str, Any]) ‑> List[DeviceType] +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def list_codes_by_phone_number(
+    self, phone_number: str, tenant_id: str, user_context: Dict[str, Any]
+) -> List[DeviceType]:
+    pass
+
+
+
+async def list_codes_by_pre_auth_session_id(self, pre_auth_session_id: str, tenant_id: str, user_context: Dict[str, Any]) ‑> Optional[DeviceType] +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def list_codes_by_pre_auth_session_id(
+    self, pre_auth_session_id: str, tenant_id: str, user_context: Dict[str, Any]
+) -> Union[DeviceType, None]:
+    pass
+
+
+
+async def revoke_all_codes(self, email: Union[str, None], phone_number: Union[str, None], tenant_id: str, user_context: Dict[str, Any]) ‑> RevokeAllCodesOkResult +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def revoke_all_codes(
+    self,
+    email: Union[str, None],
+    phone_number: Union[str, None],
+    tenant_id: str,
+    user_context: Dict[str, Any],
+) -> RevokeAllCodesOkResult:
+    pass
+
+
+
+async def revoke_code(self, code_id: str, tenant_id: str, user_context: Dict[str, Any]) ‑> RevokeCodeOkResult +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def revoke_code(
+    self, code_id: str, tenant_id: str, user_context: Dict[str, Any]
+) -> RevokeCodeOkResult:
+    pass
+
+
+
+async def update_user(self, user_id: str, email: Union[str, None], phone_number: Union[str, None], user_context: Dict[str, Any]) ‑> Union[UpdateUserOkResultUpdateUserUnknownUserIdErrorUpdateUserEmailAlreadyExistsErrorUpdateUserPhoneNumberAlreadyExistsError] +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def update_user(
+    self,
+    user_id: str,
+    email: Union[str, None],
+    phone_number: Union[str, None],
+    user_context: Dict[str, Any],
+) -> Union[
+    UpdateUserOkResult,
+    UpdateUserUnknownUserIdError,
+    UpdateUserEmailAlreadyExistsError,
+    UpdateUserPhoneNumberAlreadyExistsError,
+]:
+    pass
+
+
+
+
+
+class ResendCodePostOkResult +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class ResendCodePostOkResult(APIResponse):
+    status: str = "OK"
+
+    def to_json(self):
+        return {"status": self.status}
+
+

Ancestors

+ +

Class variables

+
+
var status : str
+
+
+
+
+

Methods

+
+
+def to_json(self) +
+
+
+
+ +Expand source code + +
def to_json(self):
+    return {"status": self.status}
+
+
+
+
+
+class ResendCodePostRestartFlowError +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class ResendCodePostRestartFlowError(APIResponse):
+    status: str = "RESTART_FLOW_ERROR"
+
+    def to_json(self):
+        return {"status": self.status}
+
+

Ancestors

+ +

Class variables

+
+
var status : str
+
+
+
+
+

Methods

+
+
+def to_json(self) +
+
+
+
+ +Expand source code + +
def to_json(self):
+    return {"status": self.status}
+
+
+
+
+
+class RevokeAllCodesOkResult +
+
+
+
+ +Expand source code + +
class RevokeAllCodesOkResult:
+    pass
+
+
+
+class RevokeCodeOkResult +
+
+
+
+ +Expand source code + +
class RevokeCodeOkResult:
+    pass
+
+
+
+class UpdateUserEmailAlreadyExistsError +
+
+
+
+ +Expand source code + +
class UpdateUserEmailAlreadyExistsError:
+    pass
+
+
+
+class UpdateUserOkResult +
+
+
+
+ +Expand source code + +
class UpdateUserOkResult:
+    pass
+
+
+
+class UpdateUserPhoneNumberAlreadyExistsError +
+
+
+
+ +Expand source code + +
class UpdateUserPhoneNumberAlreadyExistsError:
+    pass
+
+
+
+class UpdateUserUnknownUserIdError +
+
+
+
+ +Expand source code + +
class UpdateUserUnknownUserIdError:
+    pass
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/passwordless/recipe.html b/html/supertokens_python/recipe/passwordless/recipe.html new file mode 100644 index 000000000..e044eadeb --- /dev/null +++ b/html/supertokens_python/recipe/passwordless/recipe.html @@ -0,0 +1,1165 @@ + + + + + + +supertokens_python.recipe.passwordless.recipe API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.passwordless.recipe

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from os import environ
+from typing import TYPE_CHECKING, Any, Awaitable, Callable, Dict, List, Union, Optional
+
+from supertokens_python.ingredients.emaildelivery import EmailDeliveryIngredient
+from supertokens_python.ingredients.emaildelivery.types import EmailDeliveryConfig
+from supertokens_python.ingredients.smsdelivery import SMSDeliveryIngredient
+from supertokens_python.querier import Querier
+from supertokens_python.recipe.passwordless.types import (
+    PasswordlessIngredients,
+    PasswordlessLoginSMSTemplateVars,
+)
+from typing_extensions import Literal
+
+from .api import (
+    consume_code,
+    create_code,
+    email_exists,
+    phone_number_exists,
+    resend_code,
+)
+from .api.implementation import APIImplementation
+from .constants import (
+    CONSUME_CODE_API,
+    CREATE_CODE_API,
+    DOES_EMAIL_EXIST_API,
+    DOES_PHONE_NUMBER_EXIST_API,
+    DOES_EMAIL_EXIST_API_OLD,
+    DOES_PHONE_NUMBER_EXIST_API_OLD,
+    RESEND_CODE_API,
+)
+from .exceptions import SuperTokensPasswordlessError
+from .interfaces import (
+    APIOptions,
+    ConsumeCodeOkResult,
+    RecipeInterface,
+    PasswordlessLoginEmailTemplateVars,
+)
+from .recipe_implementation import RecipeImplementation
+from .utils import (
+    ContactConfig,
+    OverrideConfig,
+    validate_and_normalise_user_input,
+)
+from ..emailverification import EmailVerificationRecipe
+from ..emailverification.interfaces import (
+    GetEmailForUserIdOkResult,
+    EmailDoesNotExistError,
+    UnknownUserIdError,
+)
+from ...post_init_callbacks import PostSTInitCallbacks
+
+if TYPE_CHECKING:
+    from supertokens_python.framework.request import BaseRequest
+    from supertokens_python.framework.response import BaseResponse
+    from supertokens_python.supertokens import AppInfo
+
+from supertokens_python.exceptions import SuperTokensError, raise_general_exception
+from supertokens_python.ingredients.smsdelivery.types import SMSDeliveryConfig
+from supertokens_python.normalised_url_path import NormalisedURLPath
+from supertokens_python.recipe_module import APIHandled, RecipeModule
+
+
+class PasswordlessRecipe(RecipeModule):
+    recipe_id = "passwordless"
+    __instance = None
+    email_delivery: EmailDeliveryIngredient[PasswordlessLoginEmailTemplateVars]
+    sms_delivery: SMSDeliveryIngredient[PasswordlessLoginSMSTemplateVars]
+
+    def __init__(
+        self,
+        recipe_id: str,
+        app_info: AppInfo,
+        contact_config: ContactConfig,
+        flow_type: Literal[
+            "USER_INPUT_CODE", "MAGIC_LINK", "USER_INPUT_CODE_AND_MAGIC_LINK"
+        ],
+        ingredients: PasswordlessIngredients,
+        override: Union[OverrideConfig, None] = None,
+        get_custom_user_input_code: Union[
+            Callable[[str, Dict[str, Any]], Awaitable[str]], None
+        ] = None,
+        email_delivery: Union[
+            EmailDeliveryConfig[PasswordlessLoginEmailTemplateVars], None
+        ] = None,
+        sms_delivery: Union[
+            SMSDeliveryConfig[PasswordlessLoginSMSTemplateVars], None
+        ] = None,
+    ):
+        super().__init__(recipe_id, app_info)
+        self.config = validate_and_normalise_user_input(
+            app_info,
+            contact_config,
+            flow_type,
+            override,
+            get_custom_user_input_code,
+            email_delivery,
+            sms_delivery,
+        )
+
+        recipe_implementation = RecipeImplementation(Querier.get_instance(recipe_id))
+        self.recipe_implementation: RecipeInterface = (
+            recipe_implementation
+            if self.config.override.functions is None
+            else self.config.override.functions(recipe_implementation)
+        )
+
+        api_implementation = APIImplementation()
+        self.api_implementation = (
+            api_implementation
+            if self.config.override.apis is None
+            else self.config.override.apis(api_implementation)
+        )
+
+        email_delivery_ingredient = ingredients.email_delivery
+        if email_delivery_ingredient is None:
+            self.email_delivery = EmailDeliveryIngredient(
+                self.config.get_email_delivery_config()
+            )
+        else:
+            self.email_delivery = email_delivery_ingredient
+
+        sms_delivery_ingredient = ingredients.sms_delivery
+        self.sms_delivery = (
+            SMSDeliveryIngredient(self.config.get_sms_delivery_config())
+            if sms_delivery_ingredient is None
+            else sms_delivery_ingredient
+        )
+
+        def callback():
+            ev_recipe = EmailVerificationRecipe.get_instance_optional()
+            if ev_recipe:
+                ev_recipe.add_get_email_for_user_id_func(self.get_email_for_user_id)
+
+        PostSTInitCallbacks.add_post_init_callback(callback)
+
+    def get_apis_handled(self) -> List[APIHandled]:
+        return [
+            APIHandled(
+                method="post",
+                path_without_api_base_path=NormalisedURLPath(CONSUME_CODE_API),
+                request_id=CONSUME_CODE_API,
+                disabled=self.api_implementation.disable_consume_code_post,
+            ),
+            APIHandled(
+                method="post",
+                path_without_api_base_path=NormalisedURLPath(CREATE_CODE_API),
+                request_id=CREATE_CODE_API,
+                disabled=self.api_implementation.disable_create_code_post,
+            ),
+            APIHandled(
+                method="get",
+                path_without_api_base_path=NormalisedURLPath(DOES_EMAIL_EXIST_API),
+                request_id=DOES_EMAIL_EXIST_API,
+                disabled=self.api_implementation.disable_email_exists_get,
+            ),
+            APIHandled(
+                method="get",
+                path_without_api_base_path=NormalisedURLPath(
+                    DOES_PHONE_NUMBER_EXIST_API
+                ),
+                request_id=DOES_PHONE_NUMBER_EXIST_API,
+                disabled=self.api_implementation.disable_phone_number_exists_get,
+            ),
+            APIHandled(
+                method="get",
+                path_without_api_base_path=NormalisedURLPath(DOES_EMAIL_EXIST_API_OLD),
+                request_id=DOES_EMAIL_EXIST_API_OLD,
+                disabled=self.api_implementation.disable_email_exists_get,
+            ),
+            APIHandled(
+                method="get",
+                path_without_api_base_path=NormalisedURLPath(
+                    DOES_PHONE_NUMBER_EXIST_API_OLD
+                ),
+                request_id=DOES_PHONE_NUMBER_EXIST_API_OLD,
+                disabled=self.api_implementation.disable_phone_number_exists_get,
+            ),
+            APIHandled(
+                method="post",
+                path_without_api_base_path=NormalisedURLPath(RESEND_CODE_API),
+                request_id=RESEND_CODE_API,
+                disabled=self.api_implementation.disable_resend_code_post,
+            ),
+        ]
+
+    async def handle_api_request(
+        self,
+        request_id: str,
+        tenant_id: str,
+        request: BaseRequest,
+        path: NormalisedURLPath,
+        method: str,
+        response: BaseResponse,
+        user_context: Dict[str, Any],
+    ):
+        options = APIOptions(
+            request,
+            response,
+            self.get_recipe_id(),
+            self.config,
+            self.recipe_implementation,
+            self.get_app_info(),
+            self.email_delivery,
+            self.sms_delivery,
+        )
+        if request_id == CONSUME_CODE_API:
+            return await consume_code(
+                self.api_implementation, tenant_id, options, user_context
+            )
+        if request_id == CREATE_CODE_API:
+            return await create_code(
+                self.api_implementation, tenant_id, options, user_context
+            )
+        if request_id in (DOES_EMAIL_EXIST_API, DOES_EMAIL_EXIST_API_OLD):
+            return await email_exists(
+                self.api_implementation, tenant_id, options, user_context
+            )
+        if request_id in (DOES_PHONE_NUMBER_EXIST_API, DOES_PHONE_NUMBER_EXIST_API_OLD):
+            return await phone_number_exists(
+                self.api_implementation, tenant_id, options, user_context
+            )
+        return await resend_code(
+            self.api_implementation, tenant_id, options, user_context
+        )
+
+    async def handle_error(
+        self,
+        request: BaseRequest,
+        err: SuperTokensError,
+        response: BaseResponse,
+        user_context: Dict[str, Any],
+    ) -> BaseResponse:  # type: ignore
+        raise err
+
+    def get_all_cors_headers(self) -> List[str]:
+        return []
+
+    def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
+        return isinstance(err, SuperTokensError) and isinstance(
+            err, SuperTokensPasswordlessError
+        )
+
+    @staticmethod
+    def init(
+        contact_config: ContactConfig,
+        flow_type: Literal[
+            "USER_INPUT_CODE", "MAGIC_LINK", "USER_INPUT_CODE_AND_MAGIC_LINK"
+        ],
+        override: Union[OverrideConfig, None] = None,
+        get_custom_user_input_code: Union[
+            Callable[[str, Dict[str, Any]], Awaitable[str]], None
+        ] = None,
+        email_delivery: Union[
+            EmailDeliveryConfig[PasswordlessLoginEmailTemplateVars], None
+        ] = None,
+        sms_delivery: Union[
+            SMSDeliveryConfig[PasswordlessLoginSMSTemplateVars], None
+        ] = None,
+    ):
+        def func(app_info: AppInfo):
+            if PasswordlessRecipe.__instance is None:
+                ingredients = PasswordlessIngredients(None, None)
+                PasswordlessRecipe.__instance = PasswordlessRecipe(
+                    PasswordlessRecipe.recipe_id,
+                    app_info,
+                    contact_config,
+                    flow_type,
+                    ingredients,
+                    override,
+                    get_custom_user_input_code,
+                    email_delivery,
+                    sms_delivery,
+                )
+                return PasswordlessRecipe.__instance
+            raise_general_exception(
+                "Passwordless recipe has already been initialised. Please check "
+                "your code for bugs."
+            )
+
+        return func
+
+    @staticmethod
+    def get_instance() -> PasswordlessRecipe:
+        if PasswordlessRecipe.__instance is not None:
+            return PasswordlessRecipe.__instance
+        raise_general_exception(
+            "Initialisation not done. Did you forget to call the SuperTokens.init function?"
+        )
+
+    @staticmethod
+    def reset():
+        if ("SUPERTOKENS_ENV" not in environ) or (
+            environ["SUPERTOKENS_ENV"] != "testing"
+        ):
+            raise_general_exception("calling testing function in non testing env")
+        PasswordlessRecipe.__instance = None
+
+    async def create_magic_link(
+        self,
+        email: Union[str, None],
+        phone_number: Union[str, None],
+        tenant_id: str,
+        request: Optional[BaseRequest],
+        user_context: Dict[str, Any],
+    ) -> str:
+        user_input_code = None
+        if self.config.get_custom_user_input_code is not None:
+            user_input_code = await self.config.get_custom_user_input_code(
+                tenant_id, user_context
+            )
+
+        code_info = await self.recipe_implementation.create_code(
+            email=email,
+            phone_number=phone_number,
+            tenant_id=tenant_id,
+            user_input_code=user_input_code,
+            user_context=user_context,
+        )
+
+        app_info = self.get_app_info()
+
+        magic_link = (
+            app_info.get_origin(request, user_context).get_as_string_dangerous()
+            + app_info.website_base_path.get_as_string_dangerous()
+            + "/verify"
+            + "?preAuthSessionId="
+            + code_info.pre_auth_session_id
+            + "&tenantId="
+            + tenant_id
+            + "#"
+            + code_info.link_code
+        )
+        return magic_link
+
+    async def signinup(
+        self,
+        email: Union[str, None],
+        phone_number: Union[str, None],
+        tenant_id: str,
+        user_context: Dict[str, Any],
+    ) -> ConsumeCodeOkResult:
+        code_info = await self.recipe_implementation.create_code(
+            email=email,
+            phone_number=phone_number,
+            user_input_code=None,
+            tenant_id=tenant_id,
+            user_context=user_context,
+        )
+        consume_code_result = await self.recipe_implementation.consume_code(
+            link_code=code_info.link_code,
+            pre_auth_session_id=code_info.pre_auth_session_id,
+            device_id=code_info.device_id,
+            user_input_code=code_info.user_input_code,
+            tenant_id=tenant_id,
+            user_context=user_context,
+        )
+        if isinstance(consume_code_result, ConsumeCodeOkResult):
+            return consume_code_result
+        raise Exception("Failed to create user. Please retry")
+
+    async def get_email_for_user_id(
+        self, user_id: str, user_context: Dict[str, Any]
+    ) -> Union[GetEmailForUserIdOkResult, EmailDoesNotExistError, UnknownUserIdError]:
+        user_info = await self.recipe_implementation.get_user_by_id(
+            user_id, user_context
+        )
+        if user_info is not None:
+            if user_info.email is not None:
+                return GetEmailForUserIdOkResult(user_info.email)
+            return EmailDoesNotExistError()
+        return UnknownUserIdError()
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class PasswordlessRecipe +(recipe_id: str, app_info: AppInfo, contact_config: ContactConfig, flow_type: "Literal[('USER_INPUT_CODE', 'MAGIC_LINK', 'USER_INPUT_CODE_AND_MAGIC_LINK')]", ingredients: PasswordlessIngredients, override: Union[OverrideConfig, None] = None, get_custom_user_input_code: Union[Callable[[str, Dict[str, Any]], Awaitable[str]], None] = None, email_delivery: Union[EmailDeliveryConfig[PasswordlessLoginEmailTemplateVars], None] = None, sms_delivery: Union[SMSDeliveryConfig[PasswordlessLoginSMSTemplateVars], None] = None) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class PasswordlessRecipe(RecipeModule):
+    recipe_id = "passwordless"
+    __instance = None
+    email_delivery: EmailDeliveryIngredient[PasswordlessLoginEmailTemplateVars]
+    sms_delivery: SMSDeliveryIngredient[PasswordlessLoginSMSTemplateVars]
+
+    def __init__(
+        self,
+        recipe_id: str,
+        app_info: AppInfo,
+        contact_config: ContactConfig,
+        flow_type: Literal[
+            "USER_INPUT_CODE", "MAGIC_LINK", "USER_INPUT_CODE_AND_MAGIC_LINK"
+        ],
+        ingredients: PasswordlessIngredients,
+        override: Union[OverrideConfig, None] = None,
+        get_custom_user_input_code: Union[
+            Callable[[str, Dict[str, Any]], Awaitable[str]], None
+        ] = None,
+        email_delivery: Union[
+            EmailDeliveryConfig[PasswordlessLoginEmailTemplateVars], None
+        ] = None,
+        sms_delivery: Union[
+            SMSDeliveryConfig[PasswordlessLoginSMSTemplateVars], None
+        ] = None,
+    ):
+        super().__init__(recipe_id, app_info)
+        self.config = validate_and_normalise_user_input(
+            app_info,
+            contact_config,
+            flow_type,
+            override,
+            get_custom_user_input_code,
+            email_delivery,
+            sms_delivery,
+        )
+
+        recipe_implementation = RecipeImplementation(Querier.get_instance(recipe_id))
+        self.recipe_implementation: RecipeInterface = (
+            recipe_implementation
+            if self.config.override.functions is None
+            else self.config.override.functions(recipe_implementation)
+        )
+
+        api_implementation = APIImplementation()
+        self.api_implementation = (
+            api_implementation
+            if self.config.override.apis is None
+            else self.config.override.apis(api_implementation)
+        )
+
+        email_delivery_ingredient = ingredients.email_delivery
+        if email_delivery_ingredient is None:
+            self.email_delivery = EmailDeliveryIngredient(
+                self.config.get_email_delivery_config()
+            )
+        else:
+            self.email_delivery = email_delivery_ingredient
+
+        sms_delivery_ingredient = ingredients.sms_delivery
+        self.sms_delivery = (
+            SMSDeliveryIngredient(self.config.get_sms_delivery_config())
+            if sms_delivery_ingredient is None
+            else sms_delivery_ingredient
+        )
+
+        def callback():
+            ev_recipe = EmailVerificationRecipe.get_instance_optional()
+            if ev_recipe:
+                ev_recipe.add_get_email_for_user_id_func(self.get_email_for_user_id)
+
+        PostSTInitCallbacks.add_post_init_callback(callback)
+
+    def get_apis_handled(self) -> List[APIHandled]:
+        return [
+            APIHandled(
+                method="post",
+                path_without_api_base_path=NormalisedURLPath(CONSUME_CODE_API),
+                request_id=CONSUME_CODE_API,
+                disabled=self.api_implementation.disable_consume_code_post,
+            ),
+            APIHandled(
+                method="post",
+                path_without_api_base_path=NormalisedURLPath(CREATE_CODE_API),
+                request_id=CREATE_CODE_API,
+                disabled=self.api_implementation.disable_create_code_post,
+            ),
+            APIHandled(
+                method="get",
+                path_without_api_base_path=NormalisedURLPath(DOES_EMAIL_EXIST_API),
+                request_id=DOES_EMAIL_EXIST_API,
+                disabled=self.api_implementation.disable_email_exists_get,
+            ),
+            APIHandled(
+                method="get",
+                path_without_api_base_path=NormalisedURLPath(
+                    DOES_PHONE_NUMBER_EXIST_API
+                ),
+                request_id=DOES_PHONE_NUMBER_EXIST_API,
+                disabled=self.api_implementation.disable_phone_number_exists_get,
+            ),
+            APIHandled(
+                method="get",
+                path_without_api_base_path=NormalisedURLPath(DOES_EMAIL_EXIST_API_OLD),
+                request_id=DOES_EMAIL_EXIST_API_OLD,
+                disabled=self.api_implementation.disable_email_exists_get,
+            ),
+            APIHandled(
+                method="get",
+                path_without_api_base_path=NormalisedURLPath(
+                    DOES_PHONE_NUMBER_EXIST_API_OLD
+                ),
+                request_id=DOES_PHONE_NUMBER_EXIST_API_OLD,
+                disabled=self.api_implementation.disable_phone_number_exists_get,
+            ),
+            APIHandled(
+                method="post",
+                path_without_api_base_path=NormalisedURLPath(RESEND_CODE_API),
+                request_id=RESEND_CODE_API,
+                disabled=self.api_implementation.disable_resend_code_post,
+            ),
+        ]
+
+    async def handle_api_request(
+        self,
+        request_id: str,
+        tenant_id: str,
+        request: BaseRequest,
+        path: NormalisedURLPath,
+        method: str,
+        response: BaseResponse,
+        user_context: Dict[str, Any],
+    ):
+        options = APIOptions(
+            request,
+            response,
+            self.get_recipe_id(),
+            self.config,
+            self.recipe_implementation,
+            self.get_app_info(),
+            self.email_delivery,
+            self.sms_delivery,
+        )
+        if request_id == CONSUME_CODE_API:
+            return await consume_code(
+                self.api_implementation, tenant_id, options, user_context
+            )
+        if request_id == CREATE_CODE_API:
+            return await create_code(
+                self.api_implementation, tenant_id, options, user_context
+            )
+        if request_id in (DOES_EMAIL_EXIST_API, DOES_EMAIL_EXIST_API_OLD):
+            return await email_exists(
+                self.api_implementation, tenant_id, options, user_context
+            )
+        if request_id in (DOES_PHONE_NUMBER_EXIST_API, DOES_PHONE_NUMBER_EXIST_API_OLD):
+            return await phone_number_exists(
+                self.api_implementation, tenant_id, options, user_context
+            )
+        return await resend_code(
+            self.api_implementation, tenant_id, options, user_context
+        )
+
+    async def handle_error(
+        self,
+        request: BaseRequest,
+        err: SuperTokensError,
+        response: BaseResponse,
+        user_context: Dict[str, Any],
+    ) -> BaseResponse:  # type: ignore
+        raise err
+
+    def get_all_cors_headers(self) -> List[str]:
+        return []
+
+    def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
+        return isinstance(err, SuperTokensError) and isinstance(
+            err, SuperTokensPasswordlessError
+        )
+
+    @staticmethod
+    def init(
+        contact_config: ContactConfig,
+        flow_type: Literal[
+            "USER_INPUT_CODE", "MAGIC_LINK", "USER_INPUT_CODE_AND_MAGIC_LINK"
+        ],
+        override: Union[OverrideConfig, None] = None,
+        get_custom_user_input_code: Union[
+            Callable[[str, Dict[str, Any]], Awaitable[str]], None
+        ] = None,
+        email_delivery: Union[
+            EmailDeliveryConfig[PasswordlessLoginEmailTemplateVars], None
+        ] = None,
+        sms_delivery: Union[
+            SMSDeliveryConfig[PasswordlessLoginSMSTemplateVars], None
+        ] = None,
+    ):
+        def func(app_info: AppInfo):
+            if PasswordlessRecipe.__instance is None:
+                ingredients = PasswordlessIngredients(None, None)
+                PasswordlessRecipe.__instance = PasswordlessRecipe(
+                    PasswordlessRecipe.recipe_id,
+                    app_info,
+                    contact_config,
+                    flow_type,
+                    ingredients,
+                    override,
+                    get_custom_user_input_code,
+                    email_delivery,
+                    sms_delivery,
+                )
+                return PasswordlessRecipe.__instance
+            raise_general_exception(
+                "Passwordless recipe has already been initialised. Please check "
+                "your code for bugs."
+            )
+
+        return func
+
+    @staticmethod
+    def get_instance() -> PasswordlessRecipe:
+        if PasswordlessRecipe.__instance is not None:
+            return PasswordlessRecipe.__instance
+        raise_general_exception(
+            "Initialisation not done. Did you forget to call the SuperTokens.init function?"
+        )
+
+    @staticmethod
+    def reset():
+        if ("SUPERTOKENS_ENV" not in environ) or (
+            environ["SUPERTOKENS_ENV"] != "testing"
+        ):
+            raise_general_exception("calling testing function in non testing env")
+        PasswordlessRecipe.__instance = None
+
+    async def create_magic_link(
+        self,
+        email: Union[str, None],
+        phone_number: Union[str, None],
+        tenant_id: str,
+        request: Optional[BaseRequest],
+        user_context: Dict[str, Any],
+    ) -> str:
+        user_input_code = None
+        if self.config.get_custom_user_input_code is not None:
+            user_input_code = await self.config.get_custom_user_input_code(
+                tenant_id, user_context
+            )
+
+        code_info = await self.recipe_implementation.create_code(
+            email=email,
+            phone_number=phone_number,
+            tenant_id=tenant_id,
+            user_input_code=user_input_code,
+            user_context=user_context,
+        )
+
+        app_info = self.get_app_info()
+
+        magic_link = (
+            app_info.get_origin(request, user_context).get_as_string_dangerous()
+            + app_info.website_base_path.get_as_string_dangerous()
+            + "/verify"
+            + "?preAuthSessionId="
+            + code_info.pre_auth_session_id
+            + "&tenantId="
+            + tenant_id
+            + "#"
+            + code_info.link_code
+        )
+        return magic_link
+
+    async def signinup(
+        self,
+        email: Union[str, None],
+        phone_number: Union[str, None],
+        tenant_id: str,
+        user_context: Dict[str, Any],
+    ) -> ConsumeCodeOkResult:
+        code_info = await self.recipe_implementation.create_code(
+            email=email,
+            phone_number=phone_number,
+            user_input_code=None,
+            tenant_id=tenant_id,
+            user_context=user_context,
+        )
+        consume_code_result = await self.recipe_implementation.consume_code(
+            link_code=code_info.link_code,
+            pre_auth_session_id=code_info.pre_auth_session_id,
+            device_id=code_info.device_id,
+            user_input_code=code_info.user_input_code,
+            tenant_id=tenant_id,
+            user_context=user_context,
+        )
+        if isinstance(consume_code_result, ConsumeCodeOkResult):
+            return consume_code_result
+        raise Exception("Failed to create user. Please retry")
+
+    async def get_email_for_user_id(
+        self, user_id: str, user_context: Dict[str, Any]
+    ) -> Union[GetEmailForUserIdOkResult, EmailDoesNotExistError, UnknownUserIdError]:
+        user_info = await self.recipe_implementation.get_user_by_id(
+            user_id, user_context
+        )
+        if user_info is not None:
+            if user_info.email is not None:
+                return GetEmailForUserIdOkResult(user_info.email)
+            return EmailDoesNotExistError()
+        return UnknownUserIdError()
+
+

Ancestors

+ +

Class variables

+
+
var email_deliveryEmailDeliveryIngredient[CreateAndSendCustomEmailParameters]
+
+
+
+
var recipe_id
+
+
+
+
var sms_deliverySMSDeliveryIngredient[CreateAndSendCustomTextMessageParameters]
+
+
+
+
+

Static methods

+
+
+def get_instance() ‑> PasswordlessRecipe +
+
+
+
+ +Expand source code + +
@staticmethod
+def get_instance() -> PasswordlessRecipe:
+    if PasswordlessRecipe.__instance is not None:
+        return PasswordlessRecipe.__instance
+    raise_general_exception(
+        "Initialisation not done. Did you forget to call the SuperTokens.init function?"
+    )
+
+
+
+def init(contact_config: ContactConfig, flow_type: "Literal[('USER_INPUT_CODE', 'MAGIC_LINK', 'USER_INPUT_CODE_AND_MAGIC_LINK')]", override: Union[OverrideConfig, None] = None, get_custom_user_input_code: Union[Callable[[str, Dict[str, Any]], Awaitable[str]], None] = None, email_delivery: Union[EmailDeliveryConfig[PasswordlessLoginEmailTemplateVars], None] = None, sms_delivery: Union[SMSDeliveryConfig[PasswordlessLoginSMSTemplateVars], None] = None) +
+
+
+
+ +Expand source code + +
@staticmethod
+def init(
+    contact_config: ContactConfig,
+    flow_type: Literal[
+        "USER_INPUT_CODE", "MAGIC_LINK", "USER_INPUT_CODE_AND_MAGIC_LINK"
+    ],
+    override: Union[OverrideConfig, None] = None,
+    get_custom_user_input_code: Union[
+        Callable[[str, Dict[str, Any]], Awaitable[str]], None
+    ] = None,
+    email_delivery: Union[
+        EmailDeliveryConfig[PasswordlessLoginEmailTemplateVars], None
+    ] = None,
+    sms_delivery: Union[
+        SMSDeliveryConfig[PasswordlessLoginSMSTemplateVars], None
+    ] = None,
+):
+    def func(app_info: AppInfo):
+        if PasswordlessRecipe.__instance is None:
+            ingredients = PasswordlessIngredients(None, None)
+            PasswordlessRecipe.__instance = PasswordlessRecipe(
+                PasswordlessRecipe.recipe_id,
+                app_info,
+                contact_config,
+                flow_type,
+                ingredients,
+                override,
+                get_custom_user_input_code,
+                email_delivery,
+                sms_delivery,
+            )
+            return PasswordlessRecipe.__instance
+        raise_general_exception(
+            "Passwordless recipe has already been initialised. Please check "
+            "your code for bugs."
+        )
+
+    return func
+
+
+
+def reset() +
+
+
+
+ +Expand source code + +
@staticmethod
+def reset():
+    if ("SUPERTOKENS_ENV" not in environ) or (
+        environ["SUPERTOKENS_ENV"] != "testing"
+    ):
+        raise_general_exception("calling testing function in non testing env")
+    PasswordlessRecipe.__instance = None
+
+
+
+

Methods

+
+ +
+
+
+ +Expand source code + +
async def create_magic_link(
+    self,
+    email: Union[str, None],
+    phone_number: Union[str, None],
+    tenant_id: str,
+    request: Optional[BaseRequest],
+    user_context: Dict[str, Any],
+) -> str:
+    user_input_code = None
+    if self.config.get_custom_user_input_code is not None:
+        user_input_code = await self.config.get_custom_user_input_code(
+            tenant_id, user_context
+        )
+
+    code_info = await self.recipe_implementation.create_code(
+        email=email,
+        phone_number=phone_number,
+        tenant_id=tenant_id,
+        user_input_code=user_input_code,
+        user_context=user_context,
+    )
+
+    app_info = self.get_app_info()
+
+    magic_link = (
+        app_info.get_origin(request, user_context).get_as_string_dangerous()
+        + app_info.website_base_path.get_as_string_dangerous()
+        + "/verify"
+        + "?preAuthSessionId="
+        + code_info.pre_auth_session_id
+        + "&tenantId="
+        + tenant_id
+        + "#"
+        + code_info.link_code
+    )
+    return magic_link
+
+
+
+def get_all_cors_headers(self) ‑> List[str] +
+
+
+
+ +Expand source code + +
def get_all_cors_headers(self) -> List[str]:
+    return []
+
+
+
+def get_apis_handled(self) ‑> List[APIHandled] +
+
+
+
+ +Expand source code + +
def get_apis_handled(self) -> List[APIHandled]:
+    return [
+        APIHandled(
+            method="post",
+            path_without_api_base_path=NormalisedURLPath(CONSUME_CODE_API),
+            request_id=CONSUME_CODE_API,
+            disabled=self.api_implementation.disable_consume_code_post,
+        ),
+        APIHandled(
+            method="post",
+            path_without_api_base_path=NormalisedURLPath(CREATE_CODE_API),
+            request_id=CREATE_CODE_API,
+            disabled=self.api_implementation.disable_create_code_post,
+        ),
+        APIHandled(
+            method="get",
+            path_without_api_base_path=NormalisedURLPath(DOES_EMAIL_EXIST_API),
+            request_id=DOES_EMAIL_EXIST_API,
+            disabled=self.api_implementation.disable_email_exists_get,
+        ),
+        APIHandled(
+            method="get",
+            path_without_api_base_path=NormalisedURLPath(
+                DOES_PHONE_NUMBER_EXIST_API
+            ),
+            request_id=DOES_PHONE_NUMBER_EXIST_API,
+            disabled=self.api_implementation.disable_phone_number_exists_get,
+        ),
+        APIHandled(
+            method="get",
+            path_without_api_base_path=NormalisedURLPath(DOES_EMAIL_EXIST_API_OLD),
+            request_id=DOES_EMAIL_EXIST_API_OLD,
+            disabled=self.api_implementation.disable_email_exists_get,
+        ),
+        APIHandled(
+            method="get",
+            path_without_api_base_path=NormalisedURLPath(
+                DOES_PHONE_NUMBER_EXIST_API_OLD
+            ),
+            request_id=DOES_PHONE_NUMBER_EXIST_API_OLD,
+            disabled=self.api_implementation.disable_phone_number_exists_get,
+        ),
+        APIHandled(
+            method="post",
+            path_without_api_base_path=NormalisedURLPath(RESEND_CODE_API),
+            request_id=RESEND_CODE_API,
+            disabled=self.api_implementation.disable_resend_code_post,
+        ),
+    ]
+
+
+
+async def get_email_for_user_id(self, user_id: str, user_context: Dict[str, Any]) ‑> Union[GetEmailForUserIdOkResultEmailDoesNotExistErrorUnknownUserIdError] +
+
+
+
+ +Expand source code + +
async def get_email_for_user_id(
+    self, user_id: str, user_context: Dict[str, Any]
+) -> Union[GetEmailForUserIdOkResult, EmailDoesNotExistError, UnknownUserIdError]:
+    user_info = await self.recipe_implementation.get_user_by_id(
+        user_id, user_context
+    )
+    if user_info is not None:
+        if user_info.email is not None:
+            return GetEmailForUserIdOkResult(user_info.email)
+        return EmailDoesNotExistError()
+    return UnknownUserIdError()
+
+
+
+async def handle_api_request(self, request_id: str, tenant_id: str, request: BaseRequest, path: NormalisedURLPath, method: str, response: BaseResponse, user_context: Dict[str, Any]) +
+
+
+
+ +Expand source code + +
async def handle_api_request(
+    self,
+    request_id: str,
+    tenant_id: str,
+    request: BaseRequest,
+    path: NormalisedURLPath,
+    method: str,
+    response: BaseResponse,
+    user_context: Dict[str, Any],
+):
+    options = APIOptions(
+        request,
+        response,
+        self.get_recipe_id(),
+        self.config,
+        self.recipe_implementation,
+        self.get_app_info(),
+        self.email_delivery,
+        self.sms_delivery,
+    )
+    if request_id == CONSUME_CODE_API:
+        return await consume_code(
+            self.api_implementation, tenant_id, options, user_context
+        )
+    if request_id == CREATE_CODE_API:
+        return await create_code(
+            self.api_implementation, tenant_id, options, user_context
+        )
+    if request_id in (DOES_EMAIL_EXIST_API, DOES_EMAIL_EXIST_API_OLD):
+        return await email_exists(
+            self.api_implementation, tenant_id, options, user_context
+        )
+    if request_id in (DOES_PHONE_NUMBER_EXIST_API, DOES_PHONE_NUMBER_EXIST_API_OLD):
+        return await phone_number_exists(
+            self.api_implementation, tenant_id, options, user_context
+        )
+    return await resend_code(
+        self.api_implementation, tenant_id, options, user_context
+    )
+
+
+
+async def handle_error(self, request: BaseRequest, err: SuperTokensError, response: BaseResponse, user_context: Dict[str, Any]) ‑> BaseResponse +
+
+
+
+ +Expand source code + +
async def handle_error(
+    self,
+    request: BaseRequest,
+    err: SuperTokensError,
+    response: BaseResponse,
+    user_context: Dict[str, Any],
+) -> BaseResponse:  # type: ignore
+    raise err
+
+
+
+def is_error_from_this_recipe_based_on_instance(self, err: Exception) ‑> bool +
+
+
+
+ +Expand source code + +
def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
+    return isinstance(err, SuperTokensError) and isinstance(
+        err, SuperTokensPasswordlessError
+    )
+
+
+
+async def signinup(self, email: Union[str, None], phone_number: Union[str, None], tenant_id: str, user_context: Dict[str, Any]) ‑> ConsumeCodeOkResult +
+
+
+
+ +Expand source code + +
async def signinup(
+    self,
+    email: Union[str, None],
+    phone_number: Union[str, None],
+    tenant_id: str,
+    user_context: Dict[str, Any],
+) -> ConsumeCodeOkResult:
+    code_info = await self.recipe_implementation.create_code(
+        email=email,
+        phone_number=phone_number,
+        user_input_code=None,
+        tenant_id=tenant_id,
+        user_context=user_context,
+    )
+    consume_code_result = await self.recipe_implementation.consume_code(
+        link_code=code_info.link_code,
+        pre_auth_session_id=code_info.pre_auth_session_id,
+        device_id=code_info.device_id,
+        user_input_code=code_info.user_input_code,
+        tenant_id=tenant_id,
+        user_context=user_context,
+    )
+    if isinstance(consume_code_result, ConsumeCodeOkResult):
+        return consume_code_result
+    raise Exception("Failed to create user. Please retry")
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/passwordless/recipe_implementation.html b/html/supertokens_python/recipe/passwordless/recipe_implementation.html new file mode 100644 index 000000000..3973b1c13 --- /dev/null +++ b/html/supertokens_python/recipe/passwordless/recipe_implementation.html @@ -0,0 +1,1625 @@ + + + + + + +supertokens_python.recipe.passwordless.recipe_implementation API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.passwordless.recipe_implementation

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from typing import Any, Dict, List, Union
+
+from supertokens_python.querier import Querier
+
+from .types import DeviceCode, DeviceType, User
+
+from supertokens_python.normalised_url_path import NormalisedURLPath
+
+from .interfaces import (
+    ConsumeCodeExpiredUserInputCodeError,
+    ConsumeCodeIncorrectUserInputCodeError,
+    ConsumeCodeOkResult,
+    ConsumeCodeRestartFlowError,
+    CreateCodeOkResult,
+    CreateNewCodeForDeviceOkResult,
+    CreateNewCodeForDeviceRestartFlowError,
+    CreateNewCodeForDeviceUserInputCodeAlreadyUsedError,
+    DeleteUserInfoOkResult,
+    DeleteUserInfoUnknownUserIdError,
+    RecipeInterface,
+    RevokeAllCodesOkResult,
+    RevokeCodeOkResult,
+    UpdateUserEmailAlreadyExistsError,
+    UpdateUserOkResult,
+    UpdateUserPhoneNumberAlreadyExistsError,
+    UpdateUserUnknownUserIdError,
+)
+
+
+class RecipeImplementation(RecipeInterface):
+    def __init__(self, querier: Querier):
+        super().__init__()
+        self.querier = querier
+
+    async def create_code(
+        self,
+        email: Union[None, str],
+        phone_number: Union[None, str],
+        user_input_code: Union[None, str],
+        tenant_id: str,
+        user_context: Dict[str, Any],
+    ) -> CreateCodeOkResult:
+        data: Dict[str, Any] = {}
+        if user_input_code is not None:
+            data = {**data, "userInputCode": user_input_code}
+        if email is not None:
+            data = {**data, "email": email}
+        if phone_number is not None:
+            data = {**data, "phoneNumber": phone_number}
+        result = await self.querier.send_post_request(
+            NormalisedURLPath(f"{tenant_id}/recipe/signinup/code"),
+            data,
+            user_context=user_context,
+        )
+        return CreateCodeOkResult(
+            pre_auth_session_id=result["preAuthSessionId"],
+            code_id=result["codeId"],
+            device_id=result["deviceId"],
+            user_input_code=result["userInputCode"],
+            link_code=result["linkCode"],
+            time_created=result["timeCreated"],
+            code_life_time=result["codeLifetime"],
+        )
+
+    async def create_new_code_for_device(
+        self,
+        device_id: str,
+        user_input_code: Union[str, None],
+        tenant_id: str,
+        user_context: Dict[str, Any],
+    ) -> Union[
+        CreateNewCodeForDeviceOkResult,
+        CreateNewCodeForDeviceRestartFlowError,
+        CreateNewCodeForDeviceUserInputCodeAlreadyUsedError,
+    ]:
+        data = {"deviceId": device_id}
+        if user_input_code is not None:
+            data = {**data, "userInputCode": user_input_code}
+        result = await self.querier.send_post_request(
+            NormalisedURLPath(f"{tenant_id}/recipe/signinup/code"),
+            data,
+            user_context=user_context,
+        )
+        if result["status"] == "RESTART_FLOW_ERROR":
+            return CreateNewCodeForDeviceRestartFlowError()
+        if result["status"] == "USER_INPUT_CODE_ALREADY_USED_ERROR":
+            return CreateNewCodeForDeviceUserInputCodeAlreadyUsedError()
+        return CreateNewCodeForDeviceOkResult(
+            pre_auth_session_id=result["preAuthSessionId"],
+            code_id=result["codeId"],
+            device_id=result["deviceId"],
+            user_input_code=result["userInputCode"],
+            link_code=result["linkCode"],
+            code_life_time=result["codeLifetime"],
+            time_created=result["timeCreated"],
+        )
+
+    async def consume_code(
+        self,
+        pre_auth_session_id: str,
+        user_input_code: Union[str, None],
+        device_id: Union[str, None],
+        link_code: Union[str, None],
+        tenant_id: str,
+        user_context: Dict[str, Any],
+    ) -> Union[
+        ConsumeCodeOkResult,
+        ConsumeCodeIncorrectUserInputCodeError,
+        ConsumeCodeExpiredUserInputCodeError,
+        ConsumeCodeRestartFlowError,
+    ]:
+        data = {"preAuthSessionId": pre_auth_session_id}
+        if device_id is not None:
+            data = {**data, "deviceId": device_id, "userInputCode": user_input_code}
+        else:
+            data = {**data, "linkCode": link_code}
+        result = await self.querier.send_post_request(
+            NormalisedURLPath(f"{tenant_id}/recipe/signinup/code/consume"),
+            data,
+            user_context=user_context,
+        )
+        if result["status"] == "OK":
+            email = None
+            phone_number = None
+            if "email" in result["user"]:
+                email = result["user"]["email"]
+            if "phoneNumber" in result["user"]:
+                phone_number = result["user"]["phoneNumber"]
+            user = User(
+                user_id=result["user"]["id"],
+                email=email,
+                phone_number=phone_number,
+                time_joined=result["user"]["timeJoined"],
+                tenant_ids=result["user"]["tenantIds"],
+            )
+            return ConsumeCodeOkResult(result["createdNewUser"], user)
+        if result["status"] == "RESTART_FLOW_ERROR":
+            return ConsumeCodeRestartFlowError()
+        if result["status"] == "INCORRECT_USER_INPUT_CODE_ERROR":
+            return ConsumeCodeIncorrectUserInputCodeError(
+                failed_code_input_attempt_count=result["failedCodeInputAttemptCount"],
+                maximum_code_input_attempts=result["maximumCodeInputAttempts"],
+            )
+        return ConsumeCodeExpiredUserInputCodeError(
+            failed_code_input_attempt_count=result["failedCodeInputAttemptCount"],
+            maximum_code_input_attempts=result["maximumCodeInputAttempts"],
+        )
+
+    async def get_user_by_id(
+        self, user_id: str, user_context: Dict[str, Any]
+    ) -> Union[User, None]:
+        param = {"userId": user_id}
+        result = await self.querier.send_get_request(
+            NormalisedURLPath("/recipe/user"),
+            param,
+            user_context=user_context,
+        )
+        if result["status"] == "OK":
+            email = None
+            phone_number = None
+            if "email" in result["user"]:
+                email = result["user"]["email"]
+            if "phoneNumber" in result["user"]:
+                phone_number = result["user"]["phoneNumber"]
+            return User(
+                user_id=result["user"]["id"],
+                email=email,
+                phone_number=phone_number,
+                time_joined=result["user"]["timeJoined"],
+                tenant_ids=result["user"]["tenantIds"],
+            )
+        return None
+
+    async def get_user_by_email(
+        self, email: str, tenant_id: str, user_context: Dict[str, Any]
+    ) -> Union[User, None]:
+        param = {"email": email}
+        result = await self.querier.send_get_request(
+            NormalisedURLPath(f"{tenant_id}/recipe/user"),
+            param,
+            user_context=user_context,
+        )
+        if result["status"] == "OK":
+            email_resp = None
+            phone_number_resp = None
+            if "email" in result["user"]:
+                email_resp = result["user"]["email"]
+            if "phoneNumber" in result["user"]:
+                phone_number_resp = result["user"]["phoneNumber"]
+            return User(
+                user_id=result["user"]["id"],
+                email=email_resp,
+                phone_number=phone_number_resp,
+                tenant_ids=result["user"]["tenantIds"],
+                time_joined=result["user"]["timeJoined"],
+            )
+        return None
+
+    async def get_user_by_phone_number(
+        self, phone_number: str, tenant_id: str, user_context: Dict[str, Any]
+    ) -> Union[User, None]:
+        param = {"phoneNumber": phone_number}
+        result = await self.querier.send_get_request(
+            NormalisedURLPath(f"{tenant_id}/recipe/user"),
+            param,
+            user_context=user_context,
+        )
+        if result["status"] == "OK":
+            email_resp = None
+            phone_number_resp = None
+            if "email" in result["user"]:
+                email_resp = result["user"]["email"]
+            if "phoneNumber" in result["user"]:
+                phone_number_resp = result["user"]["phoneNumber"]
+            return User(
+                user_id=result["user"]["id"],
+                email=email_resp,
+                phone_number=phone_number_resp,
+                time_joined=result["user"]["timeJoined"],
+                tenant_ids=result["user"]["tenantIds"],
+            )
+        return None
+
+    async def update_user(
+        self,
+        user_id: str,
+        email: Union[str, None],
+        phone_number: Union[str, None],
+        user_context: Dict[str, Any],
+    ) -> Union[
+        UpdateUserOkResult,
+        UpdateUserUnknownUserIdError,
+        UpdateUserEmailAlreadyExistsError,
+        UpdateUserPhoneNumberAlreadyExistsError,
+    ]:
+        data = {"userId": user_id}
+        if email is not None:
+            data = {**data, "email": email}
+        if phone_number is not None:
+            data = {**data, "phoneNumber": phone_number}
+        result = await self.querier.send_put_request(
+            NormalisedURLPath("/recipe/user"),
+            data,
+            user_context=user_context,
+        )
+        if result["status"] == "OK":
+            return UpdateUserOkResult()
+        if result["status"] == "UNKNOWN_USER_ID_ERROR":
+            return UpdateUserUnknownUserIdError()
+        if result["status"] == "EMAIL_ALREADY_EXISTS_ERROR":
+            return UpdateUserEmailAlreadyExistsError()
+        return UpdateUserPhoneNumberAlreadyExistsError()
+
+    async def delete_email_for_user(
+        self, user_id: str, user_context: Dict[str, Any]
+    ) -> Union[DeleteUserInfoOkResult, DeleteUserInfoUnknownUserIdError]:
+        data = {"userId": user_id, "email": None}
+        result = await self.querier.send_put_request(
+            NormalisedURLPath("/recipe/user"),
+            data,
+            user_context=user_context,
+        )
+        if result["status"] == "OK":
+            return DeleteUserInfoOkResult()
+        if result.get("EMAIL_ALREADY_EXISTS_ERROR"):
+            raise Exception("Should never come here")
+        if result.get("PHONE_NUMBER_ALREADY_EXISTS_ERROR"):
+            raise Exception("Should never come here")
+        return DeleteUserInfoUnknownUserIdError()
+
+    async def delete_phone_number_for_user(
+        self, user_id: str, user_context: Dict[str, Any]
+    ) -> Union[DeleteUserInfoOkResult, DeleteUserInfoUnknownUserIdError]:
+        data = {"userId": user_id, "phoneNumber": None}
+        result = await self.querier.send_put_request(
+            NormalisedURLPath("/recipe/user"),
+            data,
+            user_context=user_context,
+        )
+        if result["status"] == "OK":
+            return DeleteUserInfoOkResult()
+        if result.get("EMAIL_ALREADY_EXISTS_ERROR"):
+            raise Exception("Should never come here")
+        if result.get("PHONE_NUMBER_ALREADY_EXISTS_ERROR"):
+            raise Exception("Should never come here")
+        return DeleteUserInfoUnknownUserIdError()
+
+    async def revoke_all_codes(
+        self,
+        email: Union[str, None],
+        phone_number: Union[str, None],
+        tenant_id: str,
+        user_context: Dict[str, Any],
+    ) -> RevokeAllCodesOkResult:
+        data: Dict[str, Any] = {}
+        if email is not None:
+            data = {**data, "email": email}
+        if phone_number is not None:
+            data = {**data, "email": phone_number}
+        await self.querier.send_post_request(
+            NormalisedURLPath(f"{tenant_id}/recipe/signinup/codes/remove"),
+            data,
+            user_context=user_context,
+        )
+        return RevokeAllCodesOkResult()
+
+    async def revoke_code(
+        self, code_id: str, tenant_id: str, user_context: Dict[str, Any]
+    ) -> RevokeCodeOkResult:
+        data = {"codeId": code_id}
+        await self.querier.send_post_request(
+            NormalisedURLPath(f"{tenant_id}/recipe/signinup/code/remove"),
+            data,
+            user_context=user_context,
+        )
+        return RevokeCodeOkResult()
+
+    async def list_codes_by_email(
+        self, email: str, tenant_id: str, user_context: Dict[str, Any]
+    ) -> List[DeviceType]:
+        param = {"email": email}
+        result = await self.querier.send_get_request(
+            NormalisedURLPath(f"{tenant_id}/recipe/signinup/codes"),
+            param,
+            user_context=user_context,
+        )
+        devices: List[DeviceType] = []
+        if "devices" in result:
+            for device in result["devices"]:
+                codes: List[DeviceCode] = []
+                if "code" in device:
+                    for code in device:
+                        codes.append(
+                            DeviceCode(
+                                code_id=code["codeId"],
+                                time_created=code["timeCreated"],
+                                code_life_time=code["codeLifetime"],
+                            )
+                        )
+                email_resp = None
+                phone_number_resp = None
+                if "email" in device:
+                    email_resp = device["email"]
+                if "phoneNumber" in device:
+                    phone_number_resp = device["phoneNumber"]
+                devices.append(
+                    DeviceType(
+                        pre_auth_session_id=device["preAuthSessionId"],
+                        failed_code_input_attempt_count=device[
+                            "failedCodeInputAttemptCount"
+                        ],
+                        codes=codes,
+                        email=email_resp,
+                        phone_number=phone_number_resp,
+                    )
+                )
+        return devices
+
+    async def list_codes_by_phone_number(
+        self, phone_number: str, tenant_id: str, user_context: Dict[str, Any]
+    ) -> List[DeviceType]:
+        param = {"phoneNumber": phone_number}
+        result = await self.querier.send_get_request(
+            NormalisedURLPath(f"{tenant_id}/recipe/signinup/codes"),
+            param,
+            user_context=user_context,
+        )
+        devices: List[DeviceType] = []
+        if "devices" in result:
+            for device in result["devices"]:
+                codes: List[DeviceCode] = []
+                if "code" in device:
+                    for code in device:
+                        codes.append(
+                            DeviceCode(
+                                code_id=code["codeId"],
+                                time_created=code["timeCreated"],
+                                code_life_time=code["codeLifetime"],
+                            )
+                        )
+                email_resp = None
+                phone_number_resp = None
+                if "email" in device:
+                    email_resp = device["email"]
+                if "phoneNumber" in device:
+                    phone_number_resp = device["phoneNumber"]
+                devices.append(
+                    DeviceType(
+                        pre_auth_session_id=device["preAuthSessionId"],
+                        failed_code_input_attempt_count=device[
+                            "failedCodeInputAttemptCount"
+                        ],
+                        codes=codes,
+                        email=email_resp,
+                        phone_number=phone_number_resp,
+                    )
+                )
+        return devices
+
+    async def list_codes_by_device_id(
+        self, device_id: str, tenant_id: str, user_context: Dict[str, Any]
+    ) -> Union[DeviceType, None]:
+        param = {"deviceId": device_id}
+        result = await self.querier.send_get_request(
+            NormalisedURLPath(f"{tenant_id}/recipe/signinup/codes"),
+            param,
+            user_context=user_context,
+        )
+        if "devices" in result and len(result["devices"]) == 1:
+            codes: List[DeviceCode] = []
+            if "code" in result["devices"][0]:
+                for code in result["devices"][0]:
+                    codes.append(
+                        DeviceCode(
+                            code_id=code["codeId"],
+                            time_created=code["timeCreated"],
+                            code_life_time=code["codeLifetime"],
+                        )
+                    )
+            email = None
+            phone_number = None
+            if "email" in result["devices"][0]:
+                email = result["devices"][0]["email"]
+            if "phoneNumber" in result["devices"][0]:
+                phone_number = result["devices"][0]["phoneNumber"]
+            return DeviceType(
+                pre_auth_session_id=result["devices"][0]["preAuthSessionId"],
+                failed_code_input_attempt_count=result["devices"][0][
+                    "failedCodeInputAttemptCount"
+                ],
+                codes=codes,
+                email=email,
+                phone_number=phone_number,
+            )
+        return None
+
+    async def list_codes_by_pre_auth_session_id(
+        self, pre_auth_session_id: str, tenant_id: str, user_context: Dict[str, Any]
+    ) -> Union[DeviceType, None]:
+        param = {"preAuthSessionId": pre_auth_session_id}
+        result = await self.querier.send_get_request(
+            NormalisedURLPath(f"{tenant_id}/recipe/signinup/codes"),
+            param,
+            user_context=user_context,
+        )
+        if "devices" in result and len(result["devices"]) == 1:
+            codes: List[DeviceCode] = []
+            if "code" in result["devices"][0]:
+                for code in result["devices"][0]:
+                    codes.append(
+                        DeviceCode(
+                            code_id=code["codeId"],
+                            time_created=code["timeCreated"],
+                            code_life_time=code["codeLifetime"],
+                        )
+                    )
+            email = None
+            phone_number = None
+            if "email" in result["devices"][0]:
+                email = result["devices"][0]["email"]
+            if "phoneNumber" in result["devices"][0]:
+                phone_number = result["devices"][0]["phoneNumber"]
+            return DeviceType(
+                pre_auth_session_id=result["devices"][0]["preAuthSessionId"],
+                failed_code_input_attempt_count=result["devices"][0][
+                    "failedCodeInputAttemptCount"
+                ],
+                codes=codes,
+                email=email,
+                phone_number=phone_number,
+            )
+        return None
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class RecipeImplementation +(querier: Querier) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class RecipeImplementation(RecipeInterface):
+    def __init__(self, querier: Querier):
+        super().__init__()
+        self.querier = querier
+
+    async def create_code(
+        self,
+        email: Union[None, str],
+        phone_number: Union[None, str],
+        user_input_code: Union[None, str],
+        tenant_id: str,
+        user_context: Dict[str, Any],
+    ) -> CreateCodeOkResult:
+        data: Dict[str, Any] = {}
+        if user_input_code is not None:
+            data = {**data, "userInputCode": user_input_code}
+        if email is not None:
+            data = {**data, "email": email}
+        if phone_number is not None:
+            data = {**data, "phoneNumber": phone_number}
+        result = await self.querier.send_post_request(
+            NormalisedURLPath(f"{tenant_id}/recipe/signinup/code"),
+            data,
+            user_context=user_context,
+        )
+        return CreateCodeOkResult(
+            pre_auth_session_id=result["preAuthSessionId"],
+            code_id=result["codeId"],
+            device_id=result["deviceId"],
+            user_input_code=result["userInputCode"],
+            link_code=result["linkCode"],
+            time_created=result["timeCreated"],
+            code_life_time=result["codeLifetime"],
+        )
+
+    async def create_new_code_for_device(
+        self,
+        device_id: str,
+        user_input_code: Union[str, None],
+        tenant_id: str,
+        user_context: Dict[str, Any],
+    ) -> Union[
+        CreateNewCodeForDeviceOkResult,
+        CreateNewCodeForDeviceRestartFlowError,
+        CreateNewCodeForDeviceUserInputCodeAlreadyUsedError,
+    ]:
+        data = {"deviceId": device_id}
+        if user_input_code is not None:
+            data = {**data, "userInputCode": user_input_code}
+        result = await self.querier.send_post_request(
+            NormalisedURLPath(f"{tenant_id}/recipe/signinup/code"),
+            data,
+            user_context=user_context,
+        )
+        if result["status"] == "RESTART_FLOW_ERROR":
+            return CreateNewCodeForDeviceRestartFlowError()
+        if result["status"] == "USER_INPUT_CODE_ALREADY_USED_ERROR":
+            return CreateNewCodeForDeviceUserInputCodeAlreadyUsedError()
+        return CreateNewCodeForDeviceOkResult(
+            pre_auth_session_id=result["preAuthSessionId"],
+            code_id=result["codeId"],
+            device_id=result["deviceId"],
+            user_input_code=result["userInputCode"],
+            link_code=result["linkCode"],
+            code_life_time=result["codeLifetime"],
+            time_created=result["timeCreated"],
+        )
+
+    async def consume_code(
+        self,
+        pre_auth_session_id: str,
+        user_input_code: Union[str, None],
+        device_id: Union[str, None],
+        link_code: Union[str, None],
+        tenant_id: str,
+        user_context: Dict[str, Any],
+    ) -> Union[
+        ConsumeCodeOkResult,
+        ConsumeCodeIncorrectUserInputCodeError,
+        ConsumeCodeExpiredUserInputCodeError,
+        ConsumeCodeRestartFlowError,
+    ]:
+        data = {"preAuthSessionId": pre_auth_session_id}
+        if device_id is not None:
+            data = {**data, "deviceId": device_id, "userInputCode": user_input_code}
+        else:
+            data = {**data, "linkCode": link_code}
+        result = await self.querier.send_post_request(
+            NormalisedURLPath(f"{tenant_id}/recipe/signinup/code/consume"),
+            data,
+            user_context=user_context,
+        )
+        if result["status"] == "OK":
+            email = None
+            phone_number = None
+            if "email" in result["user"]:
+                email = result["user"]["email"]
+            if "phoneNumber" in result["user"]:
+                phone_number = result["user"]["phoneNumber"]
+            user = User(
+                user_id=result["user"]["id"],
+                email=email,
+                phone_number=phone_number,
+                time_joined=result["user"]["timeJoined"],
+                tenant_ids=result["user"]["tenantIds"],
+            )
+            return ConsumeCodeOkResult(result["createdNewUser"], user)
+        if result["status"] == "RESTART_FLOW_ERROR":
+            return ConsumeCodeRestartFlowError()
+        if result["status"] == "INCORRECT_USER_INPUT_CODE_ERROR":
+            return ConsumeCodeIncorrectUserInputCodeError(
+                failed_code_input_attempt_count=result["failedCodeInputAttemptCount"],
+                maximum_code_input_attempts=result["maximumCodeInputAttempts"],
+            )
+        return ConsumeCodeExpiredUserInputCodeError(
+            failed_code_input_attempt_count=result["failedCodeInputAttemptCount"],
+            maximum_code_input_attempts=result["maximumCodeInputAttempts"],
+        )
+
+    async def get_user_by_id(
+        self, user_id: str, user_context: Dict[str, Any]
+    ) -> Union[User, None]:
+        param = {"userId": user_id}
+        result = await self.querier.send_get_request(
+            NormalisedURLPath("/recipe/user"),
+            param,
+            user_context=user_context,
+        )
+        if result["status"] == "OK":
+            email = None
+            phone_number = None
+            if "email" in result["user"]:
+                email = result["user"]["email"]
+            if "phoneNumber" in result["user"]:
+                phone_number = result["user"]["phoneNumber"]
+            return User(
+                user_id=result["user"]["id"],
+                email=email,
+                phone_number=phone_number,
+                time_joined=result["user"]["timeJoined"],
+                tenant_ids=result["user"]["tenantIds"],
+            )
+        return None
+
+    async def get_user_by_email(
+        self, email: str, tenant_id: str, user_context: Dict[str, Any]
+    ) -> Union[User, None]:
+        param = {"email": email}
+        result = await self.querier.send_get_request(
+            NormalisedURLPath(f"{tenant_id}/recipe/user"),
+            param,
+            user_context=user_context,
+        )
+        if result["status"] == "OK":
+            email_resp = None
+            phone_number_resp = None
+            if "email" in result["user"]:
+                email_resp = result["user"]["email"]
+            if "phoneNumber" in result["user"]:
+                phone_number_resp = result["user"]["phoneNumber"]
+            return User(
+                user_id=result["user"]["id"],
+                email=email_resp,
+                phone_number=phone_number_resp,
+                tenant_ids=result["user"]["tenantIds"],
+                time_joined=result["user"]["timeJoined"],
+            )
+        return None
+
+    async def get_user_by_phone_number(
+        self, phone_number: str, tenant_id: str, user_context: Dict[str, Any]
+    ) -> Union[User, None]:
+        param = {"phoneNumber": phone_number}
+        result = await self.querier.send_get_request(
+            NormalisedURLPath(f"{tenant_id}/recipe/user"),
+            param,
+            user_context=user_context,
+        )
+        if result["status"] == "OK":
+            email_resp = None
+            phone_number_resp = None
+            if "email" in result["user"]:
+                email_resp = result["user"]["email"]
+            if "phoneNumber" in result["user"]:
+                phone_number_resp = result["user"]["phoneNumber"]
+            return User(
+                user_id=result["user"]["id"],
+                email=email_resp,
+                phone_number=phone_number_resp,
+                time_joined=result["user"]["timeJoined"],
+                tenant_ids=result["user"]["tenantIds"],
+            )
+        return None
+
+    async def update_user(
+        self,
+        user_id: str,
+        email: Union[str, None],
+        phone_number: Union[str, None],
+        user_context: Dict[str, Any],
+    ) -> Union[
+        UpdateUserOkResult,
+        UpdateUserUnknownUserIdError,
+        UpdateUserEmailAlreadyExistsError,
+        UpdateUserPhoneNumberAlreadyExistsError,
+    ]:
+        data = {"userId": user_id}
+        if email is not None:
+            data = {**data, "email": email}
+        if phone_number is not None:
+            data = {**data, "phoneNumber": phone_number}
+        result = await self.querier.send_put_request(
+            NormalisedURLPath("/recipe/user"),
+            data,
+            user_context=user_context,
+        )
+        if result["status"] == "OK":
+            return UpdateUserOkResult()
+        if result["status"] == "UNKNOWN_USER_ID_ERROR":
+            return UpdateUserUnknownUserIdError()
+        if result["status"] == "EMAIL_ALREADY_EXISTS_ERROR":
+            return UpdateUserEmailAlreadyExistsError()
+        return UpdateUserPhoneNumberAlreadyExistsError()
+
+    async def delete_email_for_user(
+        self, user_id: str, user_context: Dict[str, Any]
+    ) -> Union[DeleteUserInfoOkResult, DeleteUserInfoUnknownUserIdError]:
+        data = {"userId": user_id, "email": None}
+        result = await self.querier.send_put_request(
+            NormalisedURLPath("/recipe/user"),
+            data,
+            user_context=user_context,
+        )
+        if result["status"] == "OK":
+            return DeleteUserInfoOkResult()
+        if result.get("EMAIL_ALREADY_EXISTS_ERROR"):
+            raise Exception("Should never come here")
+        if result.get("PHONE_NUMBER_ALREADY_EXISTS_ERROR"):
+            raise Exception("Should never come here")
+        return DeleteUserInfoUnknownUserIdError()
+
+    async def delete_phone_number_for_user(
+        self, user_id: str, user_context: Dict[str, Any]
+    ) -> Union[DeleteUserInfoOkResult, DeleteUserInfoUnknownUserIdError]:
+        data = {"userId": user_id, "phoneNumber": None}
+        result = await self.querier.send_put_request(
+            NormalisedURLPath("/recipe/user"),
+            data,
+            user_context=user_context,
+        )
+        if result["status"] == "OK":
+            return DeleteUserInfoOkResult()
+        if result.get("EMAIL_ALREADY_EXISTS_ERROR"):
+            raise Exception("Should never come here")
+        if result.get("PHONE_NUMBER_ALREADY_EXISTS_ERROR"):
+            raise Exception("Should never come here")
+        return DeleteUserInfoUnknownUserIdError()
+
+    async def revoke_all_codes(
+        self,
+        email: Union[str, None],
+        phone_number: Union[str, None],
+        tenant_id: str,
+        user_context: Dict[str, Any],
+    ) -> RevokeAllCodesOkResult:
+        data: Dict[str, Any] = {}
+        if email is not None:
+            data = {**data, "email": email}
+        if phone_number is not None:
+            data = {**data, "email": phone_number}
+        await self.querier.send_post_request(
+            NormalisedURLPath(f"{tenant_id}/recipe/signinup/codes/remove"),
+            data,
+            user_context=user_context,
+        )
+        return RevokeAllCodesOkResult()
+
+    async def revoke_code(
+        self, code_id: str, tenant_id: str, user_context: Dict[str, Any]
+    ) -> RevokeCodeOkResult:
+        data = {"codeId": code_id}
+        await self.querier.send_post_request(
+            NormalisedURLPath(f"{tenant_id}/recipe/signinup/code/remove"),
+            data,
+            user_context=user_context,
+        )
+        return RevokeCodeOkResult()
+
+    async def list_codes_by_email(
+        self, email: str, tenant_id: str, user_context: Dict[str, Any]
+    ) -> List[DeviceType]:
+        param = {"email": email}
+        result = await self.querier.send_get_request(
+            NormalisedURLPath(f"{tenant_id}/recipe/signinup/codes"),
+            param,
+            user_context=user_context,
+        )
+        devices: List[DeviceType] = []
+        if "devices" in result:
+            for device in result["devices"]:
+                codes: List[DeviceCode] = []
+                if "code" in device:
+                    for code in device:
+                        codes.append(
+                            DeviceCode(
+                                code_id=code["codeId"],
+                                time_created=code["timeCreated"],
+                                code_life_time=code["codeLifetime"],
+                            )
+                        )
+                email_resp = None
+                phone_number_resp = None
+                if "email" in device:
+                    email_resp = device["email"]
+                if "phoneNumber" in device:
+                    phone_number_resp = device["phoneNumber"]
+                devices.append(
+                    DeviceType(
+                        pre_auth_session_id=device["preAuthSessionId"],
+                        failed_code_input_attempt_count=device[
+                            "failedCodeInputAttemptCount"
+                        ],
+                        codes=codes,
+                        email=email_resp,
+                        phone_number=phone_number_resp,
+                    )
+                )
+        return devices
+
+    async def list_codes_by_phone_number(
+        self, phone_number: str, tenant_id: str, user_context: Dict[str, Any]
+    ) -> List[DeviceType]:
+        param = {"phoneNumber": phone_number}
+        result = await self.querier.send_get_request(
+            NormalisedURLPath(f"{tenant_id}/recipe/signinup/codes"),
+            param,
+            user_context=user_context,
+        )
+        devices: List[DeviceType] = []
+        if "devices" in result:
+            for device in result["devices"]:
+                codes: List[DeviceCode] = []
+                if "code" in device:
+                    for code in device:
+                        codes.append(
+                            DeviceCode(
+                                code_id=code["codeId"],
+                                time_created=code["timeCreated"],
+                                code_life_time=code["codeLifetime"],
+                            )
+                        )
+                email_resp = None
+                phone_number_resp = None
+                if "email" in device:
+                    email_resp = device["email"]
+                if "phoneNumber" in device:
+                    phone_number_resp = device["phoneNumber"]
+                devices.append(
+                    DeviceType(
+                        pre_auth_session_id=device["preAuthSessionId"],
+                        failed_code_input_attempt_count=device[
+                            "failedCodeInputAttemptCount"
+                        ],
+                        codes=codes,
+                        email=email_resp,
+                        phone_number=phone_number_resp,
+                    )
+                )
+        return devices
+
+    async def list_codes_by_device_id(
+        self, device_id: str, tenant_id: str, user_context: Dict[str, Any]
+    ) -> Union[DeviceType, None]:
+        param = {"deviceId": device_id}
+        result = await self.querier.send_get_request(
+            NormalisedURLPath(f"{tenant_id}/recipe/signinup/codes"),
+            param,
+            user_context=user_context,
+        )
+        if "devices" in result and len(result["devices"]) == 1:
+            codes: List[DeviceCode] = []
+            if "code" in result["devices"][0]:
+                for code in result["devices"][0]:
+                    codes.append(
+                        DeviceCode(
+                            code_id=code["codeId"],
+                            time_created=code["timeCreated"],
+                            code_life_time=code["codeLifetime"],
+                        )
+                    )
+            email = None
+            phone_number = None
+            if "email" in result["devices"][0]:
+                email = result["devices"][0]["email"]
+            if "phoneNumber" in result["devices"][0]:
+                phone_number = result["devices"][0]["phoneNumber"]
+            return DeviceType(
+                pre_auth_session_id=result["devices"][0]["preAuthSessionId"],
+                failed_code_input_attempt_count=result["devices"][0][
+                    "failedCodeInputAttemptCount"
+                ],
+                codes=codes,
+                email=email,
+                phone_number=phone_number,
+            )
+        return None
+
+    async def list_codes_by_pre_auth_session_id(
+        self, pre_auth_session_id: str, tenant_id: str, user_context: Dict[str, Any]
+    ) -> Union[DeviceType, None]:
+        param = {"preAuthSessionId": pre_auth_session_id}
+        result = await self.querier.send_get_request(
+            NormalisedURLPath(f"{tenant_id}/recipe/signinup/codes"),
+            param,
+            user_context=user_context,
+        )
+        if "devices" in result and len(result["devices"]) == 1:
+            codes: List[DeviceCode] = []
+            if "code" in result["devices"][0]:
+                for code in result["devices"][0]:
+                    codes.append(
+                        DeviceCode(
+                            code_id=code["codeId"],
+                            time_created=code["timeCreated"],
+                            code_life_time=code["codeLifetime"],
+                        )
+                    )
+            email = None
+            phone_number = None
+            if "email" in result["devices"][0]:
+                email = result["devices"][0]["email"]
+            if "phoneNumber" in result["devices"][0]:
+                phone_number = result["devices"][0]["phoneNumber"]
+            return DeviceType(
+                pre_auth_session_id=result["devices"][0]["preAuthSessionId"],
+                failed_code_input_attempt_count=result["devices"][0][
+                    "failedCodeInputAttemptCount"
+                ],
+                codes=codes,
+                email=email,
+                phone_number=phone_number,
+            )
+        return None
+
+

Ancestors

+ +

Methods

+
+
+async def consume_code(self, pre_auth_session_id: str, user_input_code: Union[str, None], device_id: Union[str, None], link_code: Union[str, None], tenant_id: str, user_context: Dict[str, Any]) ‑> Union[ConsumeCodeOkResultConsumeCodeIncorrectUserInputCodeErrorConsumeCodeExpiredUserInputCodeErrorConsumeCodeRestartFlowError] +
+
+
+
+ +Expand source code + +
async def consume_code(
+    self,
+    pre_auth_session_id: str,
+    user_input_code: Union[str, None],
+    device_id: Union[str, None],
+    link_code: Union[str, None],
+    tenant_id: str,
+    user_context: Dict[str, Any],
+) -> Union[
+    ConsumeCodeOkResult,
+    ConsumeCodeIncorrectUserInputCodeError,
+    ConsumeCodeExpiredUserInputCodeError,
+    ConsumeCodeRestartFlowError,
+]:
+    data = {"preAuthSessionId": pre_auth_session_id}
+    if device_id is not None:
+        data = {**data, "deviceId": device_id, "userInputCode": user_input_code}
+    else:
+        data = {**data, "linkCode": link_code}
+    result = await self.querier.send_post_request(
+        NormalisedURLPath(f"{tenant_id}/recipe/signinup/code/consume"),
+        data,
+        user_context=user_context,
+    )
+    if result["status"] == "OK":
+        email = None
+        phone_number = None
+        if "email" in result["user"]:
+            email = result["user"]["email"]
+        if "phoneNumber" in result["user"]:
+            phone_number = result["user"]["phoneNumber"]
+        user = User(
+            user_id=result["user"]["id"],
+            email=email,
+            phone_number=phone_number,
+            time_joined=result["user"]["timeJoined"],
+            tenant_ids=result["user"]["tenantIds"],
+        )
+        return ConsumeCodeOkResult(result["createdNewUser"], user)
+    if result["status"] == "RESTART_FLOW_ERROR":
+        return ConsumeCodeRestartFlowError()
+    if result["status"] == "INCORRECT_USER_INPUT_CODE_ERROR":
+        return ConsumeCodeIncorrectUserInputCodeError(
+            failed_code_input_attempt_count=result["failedCodeInputAttemptCount"],
+            maximum_code_input_attempts=result["maximumCodeInputAttempts"],
+        )
+    return ConsumeCodeExpiredUserInputCodeError(
+        failed_code_input_attempt_count=result["failedCodeInputAttemptCount"],
+        maximum_code_input_attempts=result["maximumCodeInputAttempts"],
+    )
+
+
+
+async def create_code(self, email: Union[None, str], phone_number: Union[None, str], user_input_code: Union[None, str], tenant_id: str, user_context: Dict[str, Any]) ‑> CreateCodeOkResult +
+
+
+
+ +Expand source code + +
async def create_code(
+    self,
+    email: Union[None, str],
+    phone_number: Union[None, str],
+    user_input_code: Union[None, str],
+    tenant_id: str,
+    user_context: Dict[str, Any],
+) -> CreateCodeOkResult:
+    data: Dict[str, Any] = {}
+    if user_input_code is not None:
+        data = {**data, "userInputCode": user_input_code}
+    if email is not None:
+        data = {**data, "email": email}
+    if phone_number is not None:
+        data = {**data, "phoneNumber": phone_number}
+    result = await self.querier.send_post_request(
+        NormalisedURLPath(f"{tenant_id}/recipe/signinup/code"),
+        data,
+        user_context=user_context,
+    )
+    return CreateCodeOkResult(
+        pre_auth_session_id=result["preAuthSessionId"],
+        code_id=result["codeId"],
+        device_id=result["deviceId"],
+        user_input_code=result["userInputCode"],
+        link_code=result["linkCode"],
+        time_created=result["timeCreated"],
+        code_life_time=result["codeLifetime"],
+    )
+
+
+
+async def create_new_code_for_device(self, device_id: str, user_input_code: Union[str, None], tenant_id: str, user_context: Dict[str, Any]) ‑> Union[CreateNewCodeForDeviceOkResultCreateNewCodeForDeviceRestartFlowErrorCreateNewCodeForDeviceUserInputCodeAlreadyUsedError] +
+
+
+
+ +Expand source code + +
async def create_new_code_for_device(
+    self,
+    device_id: str,
+    user_input_code: Union[str, None],
+    tenant_id: str,
+    user_context: Dict[str, Any],
+) -> Union[
+    CreateNewCodeForDeviceOkResult,
+    CreateNewCodeForDeviceRestartFlowError,
+    CreateNewCodeForDeviceUserInputCodeAlreadyUsedError,
+]:
+    data = {"deviceId": device_id}
+    if user_input_code is not None:
+        data = {**data, "userInputCode": user_input_code}
+    result = await self.querier.send_post_request(
+        NormalisedURLPath(f"{tenant_id}/recipe/signinup/code"),
+        data,
+        user_context=user_context,
+    )
+    if result["status"] == "RESTART_FLOW_ERROR":
+        return CreateNewCodeForDeviceRestartFlowError()
+    if result["status"] == "USER_INPUT_CODE_ALREADY_USED_ERROR":
+        return CreateNewCodeForDeviceUserInputCodeAlreadyUsedError()
+    return CreateNewCodeForDeviceOkResult(
+        pre_auth_session_id=result["preAuthSessionId"],
+        code_id=result["codeId"],
+        device_id=result["deviceId"],
+        user_input_code=result["userInputCode"],
+        link_code=result["linkCode"],
+        code_life_time=result["codeLifetime"],
+        time_created=result["timeCreated"],
+    )
+
+
+
+async def delete_email_for_user(self, user_id: str, user_context: Dict[str, Any]) ‑> Union[DeleteUserInfoOkResultDeleteUserInfoUnknownUserIdError] +
+
+
+
+ +Expand source code + +
async def delete_email_for_user(
+    self, user_id: str, user_context: Dict[str, Any]
+) -> Union[DeleteUserInfoOkResult, DeleteUserInfoUnknownUserIdError]:
+    data = {"userId": user_id, "email": None}
+    result = await self.querier.send_put_request(
+        NormalisedURLPath("/recipe/user"),
+        data,
+        user_context=user_context,
+    )
+    if result["status"] == "OK":
+        return DeleteUserInfoOkResult()
+    if result.get("EMAIL_ALREADY_EXISTS_ERROR"):
+        raise Exception("Should never come here")
+    if result.get("PHONE_NUMBER_ALREADY_EXISTS_ERROR"):
+        raise Exception("Should never come here")
+    return DeleteUserInfoUnknownUserIdError()
+
+
+
+async def delete_phone_number_for_user(self, user_id: str, user_context: Dict[str, Any]) ‑> Union[DeleteUserInfoOkResultDeleteUserInfoUnknownUserIdError] +
+
+
+
+ +Expand source code + +
async def delete_phone_number_for_user(
+    self, user_id: str, user_context: Dict[str, Any]
+) -> Union[DeleteUserInfoOkResult, DeleteUserInfoUnknownUserIdError]:
+    data = {"userId": user_id, "phoneNumber": None}
+    result = await self.querier.send_put_request(
+        NormalisedURLPath("/recipe/user"),
+        data,
+        user_context=user_context,
+    )
+    if result["status"] == "OK":
+        return DeleteUserInfoOkResult()
+    if result.get("EMAIL_ALREADY_EXISTS_ERROR"):
+        raise Exception("Should never come here")
+    if result.get("PHONE_NUMBER_ALREADY_EXISTS_ERROR"):
+        raise Exception("Should never come here")
+    return DeleteUserInfoUnknownUserIdError()
+
+
+
+async def get_user_by_email(self, email: str, tenant_id: str, user_context: Dict[str, Any]) ‑> Optional[User] +
+
+
+
+ +Expand source code + +
async def get_user_by_email(
+    self, email: str, tenant_id: str, user_context: Dict[str, Any]
+) -> Union[User, None]:
+    param = {"email": email}
+    result = await self.querier.send_get_request(
+        NormalisedURLPath(f"{tenant_id}/recipe/user"),
+        param,
+        user_context=user_context,
+    )
+    if result["status"] == "OK":
+        email_resp = None
+        phone_number_resp = None
+        if "email" in result["user"]:
+            email_resp = result["user"]["email"]
+        if "phoneNumber" in result["user"]:
+            phone_number_resp = result["user"]["phoneNumber"]
+        return User(
+            user_id=result["user"]["id"],
+            email=email_resp,
+            phone_number=phone_number_resp,
+            tenant_ids=result["user"]["tenantIds"],
+            time_joined=result["user"]["timeJoined"],
+        )
+    return None
+
+
+
+async def get_user_by_id(self, user_id: str, user_context: Dict[str, Any]) ‑> Optional[User] +
+
+
+
+ +Expand source code + +
async def get_user_by_id(
+    self, user_id: str, user_context: Dict[str, Any]
+) -> Union[User, None]:
+    param = {"userId": user_id}
+    result = await self.querier.send_get_request(
+        NormalisedURLPath("/recipe/user"),
+        param,
+        user_context=user_context,
+    )
+    if result["status"] == "OK":
+        email = None
+        phone_number = None
+        if "email" in result["user"]:
+            email = result["user"]["email"]
+        if "phoneNumber" in result["user"]:
+            phone_number = result["user"]["phoneNumber"]
+        return User(
+            user_id=result["user"]["id"],
+            email=email,
+            phone_number=phone_number,
+            time_joined=result["user"]["timeJoined"],
+            tenant_ids=result["user"]["tenantIds"],
+        )
+    return None
+
+
+
+async def get_user_by_phone_number(self, phone_number: str, tenant_id: str, user_context: Dict[str, Any]) ‑> Optional[User] +
+
+
+
+ +Expand source code + +
async def get_user_by_phone_number(
+    self, phone_number: str, tenant_id: str, user_context: Dict[str, Any]
+) -> Union[User, None]:
+    param = {"phoneNumber": phone_number}
+    result = await self.querier.send_get_request(
+        NormalisedURLPath(f"{tenant_id}/recipe/user"),
+        param,
+        user_context=user_context,
+    )
+    if result["status"] == "OK":
+        email_resp = None
+        phone_number_resp = None
+        if "email" in result["user"]:
+            email_resp = result["user"]["email"]
+        if "phoneNumber" in result["user"]:
+            phone_number_resp = result["user"]["phoneNumber"]
+        return User(
+            user_id=result["user"]["id"],
+            email=email_resp,
+            phone_number=phone_number_resp,
+            time_joined=result["user"]["timeJoined"],
+            tenant_ids=result["user"]["tenantIds"],
+        )
+    return None
+
+
+
+async def list_codes_by_device_id(self, device_id: str, tenant_id: str, user_context: Dict[str, Any]) ‑> Optional[DeviceType] +
+
+
+
+ +Expand source code + +
async def list_codes_by_device_id(
+    self, device_id: str, tenant_id: str, user_context: Dict[str, Any]
+) -> Union[DeviceType, None]:
+    param = {"deviceId": device_id}
+    result = await self.querier.send_get_request(
+        NormalisedURLPath(f"{tenant_id}/recipe/signinup/codes"),
+        param,
+        user_context=user_context,
+    )
+    if "devices" in result and len(result["devices"]) == 1:
+        codes: List[DeviceCode] = []
+        if "code" in result["devices"][0]:
+            for code in result["devices"][0]:
+                codes.append(
+                    DeviceCode(
+                        code_id=code["codeId"],
+                        time_created=code["timeCreated"],
+                        code_life_time=code["codeLifetime"],
+                    )
+                )
+        email = None
+        phone_number = None
+        if "email" in result["devices"][0]:
+            email = result["devices"][0]["email"]
+        if "phoneNumber" in result["devices"][0]:
+            phone_number = result["devices"][0]["phoneNumber"]
+        return DeviceType(
+            pre_auth_session_id=result["devices"][0]["preAuthSessionId"],
+            failed_code_input_attempt_count=result["devices"][0][
+                "failedCodeInputAttemptCount"
+            ],
+            codes=codes,
+            email=email,
+            phone_number=phone_number,
+        )
+    return None
+
+
+
+async def list_codes_by_email(self, email: str, tenant_id: str, user_context: Dict[str, Any]) ‑> List[DeviceType] +
+
+
+
+ +Expand source code + +
async def list_codes_by_email(
+    self, email: str, tenant_id: str, user_context: Dict[str, Any]
+) -> List[DeviceType]:
+    param = {"email": email}
+    result = await self.querier.send_get_request(
+        NormalisedURLPath(f"{tenant_id}/recipe/signinup/codes"),
+        param,
+        user_context=user_context,
+    )
+    devices: List[DeviceType] = []
+    if "devices" in result:
+        for device in result["devices"]:
+            codes: List[DeviceCode] = []
+            if "code" in device:
+                for code in device:
+                    codes.append(
+                        DeviceCode(
+                            code_id=code["codeId"],
+                            time_created=code["timeCreated"],
+                            code_life_time=code["codeLifetime"],
+                        )
+                    )
+            email_resp = None
+            phone_number_resp = None
+            if "email" in device:
+                email_resp = device["email"]
+            if "phoneNumber" in device:
+                phone_number_resp = device["phoneNumber"]
+            devices.append(
+                DeviceType(
+                    pre_auth_session_id=device["preAuthSessionId"],
+                    failed_code_input_attempt_count=device[
+                        "failedCodeInputAttemptCount"
+                    ],
+                    codes=codes,
+                    email=email_resp,
+                    phone_number=phone_number_resp,
+                )
+            )
+    return devices
+
+
+
+async def list_codes_by_phone_number(self, phone_number: str, tenant_id: str, user_context: Dict[str, Any]) ‑> List[DeviceType] +
+
+
+
+ +Expand source code + +
async def list_codes_by_phone_number(
+    self, phone_number: str, tenant_id: str, user_context: Dict[str, Any]
+) -> List[DeviceType]:
+    param = {"phoneNumber": phone_number}
+    result = await self.querier.send_get_request(
+        NormalisedURLPath(f"{tenant_id}/recipe/signinup/codes"),
+        param,
+        user_context=user_context,
+    )
+    devices: List[DeviceType] = []
+    if "devices" in result:
+        for device in result["devices"]:
+            codes: List[DeviceCode] = []
+            if "code" in device:
+                for code in device:
+                    codes.append(
+                        DeviceCode(
+                            code_id=code["codeId"],
+                            time_created=code["timeCreated"],
+                            code_life_time=code["codeLifetime"],
+                        )
+                    )
+            email_resp = None
+            phone_number_resp = None
+            if "email" in device:
+                email_resp = device["email"]
+            if "phoneNumber" in device:
+                phone_number_resp = device["phoneNumber"]
+            devices.append(
+                DeviceType(
+                    pre_auth_session_id=device["preAuthSessionId"],
+                    failed_code_input_attempt_count=device[
+                        "failedCodeInputAttemptCount"
+                    ],
+                    codes=codes,
+                    email=email_resp,
+                    phone_number=phone_number_resp,
+                )
+            )
+    return devices
+
+
+
+async def list_codes_by_pre_auth_session_id(self, pre_auth_session_id: str, tenant_id: str, user_context: Dict[str, Any]) ‑> Optional[DeviceType] +
+
+
+
+ +Expand source code + +
async def list_codes_by_pre_auth_session_id(
+    self, pre_auth_session_id: str, tenant_id: str, user_context: Dict[str, Any]
+) -> Union[DeviceType, None]:
+    param = {"preAuthSessionId": pre_auth_session_id}
+    result = await self.querier.send_get_request(
+        NormalisedURLPath(f"{tenant_id}/recipe/signinup/codes"),
+        param,
+        user_context=user_context,
+    )
+    if "devices" in result and len(result["devices"]) == 1:
+        codes: List[DeviceCode] = []
+        if "code" in result["devices"][0]:
+            for code in result["devices"][0]:
+                codes.append(
+                    DeviceCode(
+                        code_id=code["codeId"],
+                        time_created=code["timeCreated"],
+                        code_life_time=code["codeLifetime"],
+                    )
+                )
+        email = None
+        phone_number = None
+        if "email" in result["devices"][0]:
+            email = result["devices"][0]["email"]
+        if "phoneNumber" in result["devices"][0]:
+            phone_number = result["devices"][0]["phoneNumber"]
+        return DeviceType(
+            pre_auth_session_id=result["devices"][0]["preAuthSessionId"],
+            failed_code_input_attempt_count=result["devices"][0][
+                "failedCodeInputAttemptCount"
+            ],
+            codes=codes,
+            email=email,
+            phone_number=phone_number,
+        )
+    return None
+
+
+
+async def revoke_all_codes(self, email: Union[str, None], phone_number: Union[str, None], tenant_id: str, user_context: Dict[str, Any]) ‑> RevokeAllCodesOkResult +
+
+
+
+ +Expand source code + +
async def revoke_all_codes(
+    self,
+    email: Union[str, None],
+    phone_number: Union[str, None],
+    tenant_id: str,
+    user_context: Dict[str, Any],
+) -> RevokeAllCodesOkResult:
+    data: Dict[str, Any] = {}
+    if email is not None:
+        data = {**data, "email": email}
+    if phone_number is not None:
+        data = {**data, "email": phone_number}
+    await self.querier.send_post_request(
+        NormalisedURLPath(f"{tenant_id}/recipe/signinup/codes/remove"),
+        data,
+        user_context=user_context,
+    )
+    return RevokeAllCodesOkResult()
+
+
+
+async def revoke_code(self, code_id: str, tenant_id: str, user_context: Dict[str, Any]) ‑> RevokeCodeOkResult +
+
+
+
+ +Expand source code + +
async def revoke_code(
+    self, code_id: str, tenant_id: str, user_context: Dict[str, Any]
+) -> RevokeCodeOkResult:
+    data = {"codeId": code_id}
+    await self.querier.send_post_request(
+        NormalisedURLPath(f"{tenant_id}/recipe/signinup/code/remove"),
+        data,
+        user_context=user_context,
+    )
+    return RevokeCodeOkResult()
+
+
+
+async def update_user(self, user_id: str, email: Union[str, None], phone_number: Union[str, None], user_context: Dict[str, Any]) ‑> Union[UpdateUserOkResultUpdateUserUnknownUserIdErrorUpdateUserEmailAlreadyExistsErrorUpdateUserPhoneNumberAlreadyExistsError] +
+
+
+
+ +Expand source code + +
async def update_user(
+    self,
+    user_id: str,
+    email: Union[str, None],
+    phone_number: Union[str, None],
+    user_context: Dict[str, Any],
+) -> Union[
+    UpdateUserOkResult,
+    UpdateUserUnknownUserIdError,
+    UpdateUserEmailAlreadyExistsError,
+    UpdateUserPhoneNumberAlreadyExistsError,
+]:
+    data = {"userId": user_id}
+    if email is not None:
+        data = {**data, "email": email}
+    if phone_number is not None:
+        data = {**data, "phoneNumber": phone_number}
+    result = await self.querier.send_put_request(
+        NormalisedURLPath("/recipe/user"),
+        data,
+        user_context=user_context,
+    )
+    if result["status"] == "OK":
+        return UpdateUserOkResult()
+    if result["status"] == "UNKNOWN_USER_ID_ERROR":
+        return UpdateUserUnknownUserIdError()
+    if result["status"] == "EMAIL_ALREADY_EXISTS_ERROR":
+        return UpdateUserEmailAlreadyExistsError()
+    return UpdateUserPhoneNumberAlreadyExistsError()
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/passwordless/smsdelivery/index.html b/html/supertokens_python/recipe/passwordless/smsdelivery/index.html new file mode 100644 index 000000000..b5d096612 --- /dev/null +++ b/html/supertokens_python/recipe/passwordless/smsdelivery/index.html @@ -0,0 +1,83 @@ + + + + + + +supertokens_python.recipe.passwordless.smsdelivery API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.passwordless.smsdelivery

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+
+
+

Sub-modules

+
+
supertokens_python.recipe.passwordless.smsdelivery.services
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/passwordless/smsdelivery/services/backward_compatibility/index.html b/html/supertokens_python/recipe/passwordless/smsdelivery/services/backward_compatibility/index.html new file mode 100644 index 000000000..822c8815e --- /dev/null +++ b/html/supertokens_python/recipe/passwordless/smsdelivery/services/backward_compatibility/index.html @@ -0,0 +1,305 @@ + + + + + + +supertokens_python.recipe.passwordless.smsdelivery.services.backward_compatibility API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.passwordless.smsdelivery.services.backward_compatibility

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import json
+from os import environ
+from typing import Any, Dict
+
+from httpx import AsyncClient, HTTPStatusError, Response
+from supertokens_python.ingredients.smsdelivery.services.supertokens import (
+    SUPERTOKENS_SMS_SERVICE_URL,
+)
+from supertokens_python.ingredients.smsdelivery.types import SMSDeliveryInterface
+from supertokens_python.logger import log_debug_message
+from supertokens_python.supertokens import AppInfo
+from supertokens_python.utils import handle_httpx_client_exceptions
+
+from ....types import PasswordlessLoginSMSTemplateVars
+
+
+async def create_and_send_sms_using_supertokens_service(
+    app_info: AppInfo, input_: PasswordlessLoginSMSTemplateVars
+):
+    if ("SUPERTOKENS_ENV" in environ) and (environ["SUPERTOKENS_ENV"] == "testing"):
+        return
+
+    sms_input_json = {
+        "appName": app_info.app_name,
+        "type": "PASSWORDLESS_LOGIN",
+        "phoneNumber": input_.phone_number,
+        "codeLifetime": input_.code_life_time,
+    }
+    if input_.user_input_code:
+        sms_input_json["userInputCode"] = input_.user_input_code
+    if input_.url_with_link_code:
+        sms_input_json["urlWithLinkCode"] = input_.url_with_link_code
+
+    try:
+        async with AsyncClient(timeout=30.0) as client:
+            res = await client.post(  # type: ignore
+                SUPERTOKENS_SMS_SERVICE_URL,
+                json={
+                    "smsInput": sms_input_json,
+                },
+                headers={"api-version": "0"},
+            )
+            res.raise_for_status()
+            log_debug_message("Passwordless login SMS sent to %s", input_.phone_number)
+            return
+    except Exception as e:
+        log_debug_message("Error sending passwordless login SMS")
+        handle_httpx_client_exceptions(e)
+
+        if isinstance(e, HTTPStatusError):  # type: ignore
+            res: Response = e.response  # type: ignore
+            if res.status_code != 429:  # type: ignore (429 == Too many requests)
+                data = res.json()
+                if "err" in data:
+                    raise Exception(data["err"])
+                if data:
+                    raise Exception(json.dumps(data))
+                if data is None:
+                    raise e
+            else:
+                pass  # Reach Point (1)
+        else:
+            log_debug_message("Error: %s", str(e))
+            raise e
+
+    # Point (1): Reached only when we get HTTPStatusError with e.response.status_code == 429
+    print(
+        "Free daily SMS quota reached. If you want to use SuperTokens to send SMS, please sign up on supertokens.com to get your SMS API key, else you can also define your own method by overriding the service. For now, we are logging it below:"
+    )
+    print("SMS content:\n", json.dumps(input_.__dict__, indent=2))
+
+
+class BackwardCompatibilityService(
+    SMSDeliveryInterface[PasswordlessLoginSMSTemplateVars]
+):
+    def __init__(
+        self,
+        app_info: AppInfo,
+    ) -> None:
+        self.app_info = app_info
+
+    async def send_sms(
+        self,
+        template_vars: PasswordlessLoginSMSTemplateVars,
+        user_context: Dict[str, Any],
+    ) -> None:
+        await create_and_send_sms_using_supertokens_service(
+            self.app_info, template_vars
+        )  # Note: intentionally not using try-except (unlike other recipes)
+
+
+
+
+
+
+
+

Functions

+
+
+async def create_and_send_sms_using_supertokens_service(app_info: AppInfo, input_: CreateAndSendCustomTextMessageParameters) +
+
+
+
+ +Expand source code + +
async def create_and_send_sms_using_supertokens_service(
+    app_info: AppInfo, input_: PasswordlessLoginSMSTemplateVars
+):
+    if ("SUPERTOKENS_ENV" in environ) and (environ["SUPERTOKENS_ENV"] == "testing"):
+        return
+
+    sms_input_json = {
+        "appName": app_info.app_name,
+        "type": "PASSWORDLESS_LOGIN",
+        "phoneNumber": input_.phone_number,
+        "codeLifetime": input_.code_life_time,
+    }
+    if input_.user_input_code:
+        sms_input_json["userInputCode"] = input_.user_input_code
+    if input_.url_with_link_code:
+        sms_input_json["urlWithLinkCode"] = input_.url_with_link_code
+
+    try:
+        async with AsyncClient(timeout=30.0) as client:
+            res = await client.post(  # type: ignore
+                SUPERTOKENS_SMS_SERVICE_URL,
+                json={
+                    "smsInput": sms_input_json,
+                },
+                headers={"api-version": "0"},
+            )
+            res.raise_for_status()
+            log_debug_message("Passwordless login SMS sent to %s", input_.phone_number)
+            return
+    except Exception as e:
+        log_debug_message("Error sending passwordless login SMS")
+        handle_httpx_client_exceptions(e)
+
+        if isinstance(e, HTTPStatusError):  # type: ignore
+            res: Response = e.response  # type: ignore
+            if res.status_code != 429:  # type: ignore (429 == Too many requests)
+                data = res.json()
+                if "err" in data:
+                    raise Exception(data["err"])
+                if data:
+                    raise Exception(json.dumps(data))
+                if data is None:
+                    raise e
+            else:
+                pass  # Reach Point (1)
+        else:
+            log_debug_message("Error: %s", str(e))
+            raise e
+
+    # Point (1): Reached only when we get HTTPStatusError with e.response.status_code == 429
+    print(
+        "Free daily SMS quota reached. If you want to use SuperTokens to send SMS, please sign up on supertokens.com to get your SMS API key, else you can also define your own method by overriding the service. For now, we are logging it below:"
+    )
+    print("SMS content:\n", json.dumps(input_.__dict__, indent=2))
+
+
+
+
+
+

Classes

+
+
+class BackwardCompatibilityService +(app_info: AppInfo) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class BackwardCompatibilityService(
+    SMSDeliveryInterface[PasswordlessLoginSMSTemplateVars]
+):
+    def __init__(
+        self,
+        app_info: AppInfo,
+    ) -> None:
+        self.app_info = app_info
+
+    async def send_sms(
+        self,
+        template_vars: PasswordlessLoginSMSTemplateVars,
+        user_context: Dict[str, Any],
+    ) -> None:
+        await create_and_send_sms_using_supertokens_service(
+            self.app_info, template_vars
+        )  # Note: intentionally not using try-except (unlike other recipes)
+
+

Ancestors

+ +

Methods

+
+
+async def send_sms(self, template_vars: CreateAndSendCustomTextMessageParameters, user_context: Dict[str, Any]) ‑> None +
+
+
+
+ +Expand source code + +
async def send_sms(
+    self,
+    template_vars: PasswordlessLoginSMSTemplateVars,
+    user_context: Dict[str, Any],
+) -> None:
+    await create_and_send_sms_using_supertokens_service(
+        self.app_info, template_vars
+    )  # Note: intentionally not using try-except (unlike other recipes)
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/passwordless/smsdelivery/services/index.html b/html/supertokens_python/recipe/passwordless/smsdelivery/services/index.html new file mode 100644 index 000000000..284b41a10 --- /dev/null +++ b/html/supertokens_python/recipe/passwordless/smsdelivery/services/index.html @@ -0,0 +1,98 @@ + + + + + + +supertokens_python.recipe.passwordless.smsdelivery.services API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.passwordless.smsdelivery.services

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from . import supertokens, twilio
+
+SuperTokensSMSService = supertokens.SuperTokensSMSService
+TwilioService = twilio.TwilioService
+
+
+
+

Sub-modules

+
+
supertokens_python.recipe.passwordless.smsdelivery.services.backward_compatibility
+
+
+
+
supertokens_python.recipe.passwordless.smsdelivery.services.supertokens
+
+
+
+
supertokens_python.recipe.passwordless.smsdelivery.services.twilio
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/passwordless/smsdelivery/services/supertokens/index.html b/html/supertokens_python/recipe/passwordless/smsdelivery/services/supertokens/index.html new file mode 100644 index 000000000..5ffaccf49 --- /dev/null +++ b/html/supertokens_python/recipe/passwordless/smsdelivery/services/supertokens/index.html @@ -0,0 +1,237 @@ + + + + + + +supertokens_python.recipe.passwordless.smsdelivery.services.supertokens API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.passwordless.smsdelivery.services.supertokens

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+
+from typing import Any, Dict
+
+from httpx import AsyncClient
+from supertokens_python.ingredients.smsdelivery.services.supertokens import (
+    SUPERTOKENS_SMS_SERVICE_URL,
+)
+from supertokens_python.ingredients.smsdelivery.types import SMSDeliveryInterface
+from supertokens_python.logger import log_debug_message
+from supertokens_python.supertokens import Supertokens
+from supertokens_python.utils import handle_httpx_client_exceptions
+
+from ....types import PasswordlessLoginSMSTemplateVars
+
+
+class SuperTokensSMSService(SMSDeliveryInterface[PasswordlessLoginSMSTemplateVars]):
+    def __init__(self, api_key: str) -> None:
+        self.api_key = api_key
+
+    async def send_sms(
+        self,
+        template_vars: PasswordlessLoginSMSTemplateVars,
+        user_context: Dict[str, Any],
+    ) -> None:
+        supertokens = Supertokens.get_instance()
+        app_name = supertokens.app_info.app_name
+
+        sms_input = {
+            "type": "PASSWORDLESS_LOGIN",
+            "phoneNumber": template_vars.phone_number,
+            "codeLifetime": template_vars.code_life_time,
+            "appName": app_name,
+        }
+        if template_vars.url_with_link_code:
+            sms_input["urlWithLinkCode"] = template_vars.url_with_link_code
+        if template_vars.user_input_code:
+            sms_input["userInputCode"] = template_vars.user_input_code
+        try:
+            async with AsyncClient(timeout=30.0) as client:
+                await client.post(  # type: ignore
+                    SUPERTOKENS_SMS_SERVICE_URL,
+                    json={
+                        "apiKey": self.api_key,
+                        "smsInput": sms_input,
+                    },
+                    headers={"api-version": "0"},
+                )
+        except Exception as e:
+            log_debug_message("Error sending passwordless login SMS")
+            handle_httpx_client_exceptions(e, sms_input)
+            raise e
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class SuperTokensSMSService +(api_key: str) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class SuperTokensSMSService(SMSDeliveryInterface[PasswordlessLoginSMSTemplateVars]):
+    def __init__(self, api_key: str) -> None:
+        self.api_key = api_key
+
+    async def send_sms(
+        self,
+        template_vars: PasswordlessLoginSMSTemplateVars,
+        user_context: Dict[str, Any],
+    ) -> None:
+        supertokens = Supertokens.get_instance()
+        app_name = supertokens.app_info.app_name
+
+        sms_input = {
+            "type": "PASSWORDLESS_LOGIN",
+            "phoneNumber": template_vars.phone_number,
+            "codeLifetime": template_vars.code_life_time,
+            "appName": app_name,
+        }
+        if template_vars.url_with_link_code:
+            sms_input["urlWithLinkCode"] = template_vars.url_with_link_code
+        if template_vars.user_input_code:
+            sms_input["userInputCode"] = template_vars.user_input_code
+        try:
+            async with AsyncClient(timeout=30.0) as client:
+                await client.post(  # type: ignore
+                    SUPERTOKENS_SMS_SERVICE_URL,
+                    json={
+                        "apiKey": self.api_key,
+                        "smsInput": sms_input,
+                    },
+                    headers={"api-version": "0"},
+                )
+        except Exception as e:
+            log_debug_message("Error sending passwordless login SMS")
+            handle_httpx_client_exceptions(e, sms_input)
+            raise e
+
+

Ancestors

+ +

Methods

+
+
+async def send_sms(self, template_vars: CreateAndSendCustomTextMessageParameters, user_context: Dict[str, Any]) ‑> None +
+
+
+
+ +Expand source code + +
async def send_sms(
+    self,
+    template_vars: PasswordlessLoginSMSTemplateVars,
+    user_context: Dict[str, Any],
+) -> None:
+    supertokens = Supertokens.get_instance()
+    app_name = supertokens.app_info.app_name
+
+    sms_input = {
+        "type": "PASSWORDLESS_LOGIN",
+        "phoneNumber": template_vars.phone_number,
+        "codeLifetime": template_vars.code_life_time,
+        "appName": app_name,
+    }
+    if template_vars.url_with_link_code:
+        sms_input["urlWithLinkCode"] = template_vars.url_with_link_code
+    if template_vars.user_input_code:
+        sms_input["userInputCode"] = template_vars.user_input_code
+    try:
+        async with AsyncClient(timeout=30.0) as client:
+            await client.post(  # type: ignore
+                SUPERTOKENS_SMS_SERVICE_URL,
+                json={
+                    "apiKey": self.api_key,
+                    "smsInput": sms_input,
+                },
+                headers={"api-version": "0"},
+            )
+    except Exception as e:
+        log_debug_message("Error sending passwordless login SMS")
+        handle_httpx_client_exceptions(e, sms_input)
+        raise e
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/passwordless/smsdelivery/services/twilio/index.html b/html/supertokens_python/recipe/passwordless/smsdelivery/services/twilio/index.html new file mode 100644 index 000000000..976653810 --- /dev/null +++ b/html/supertokens_python/recipe/passwordless/smsdelivery/services/twilio/index.html @@ -0,0 +1,244 @@ + + + + + + +supertokens_python.recipe.passwordless.smsdelivery.services.twilio API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.passwordless.smsdelivery.services.twilio

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from __future__ import annotations
+
+from typing import Any, Dict, Callable, Union, TypeVar
+
+from supertokens_python.ingredients.smsdelivery.services.twilio import (
+    normalize_twilio_settings,
+)
+from supertokens_python.ingredients.smsdelivery.types import (
+    SMSDeliveryInterface,
+    TwilioServiceInterface,
+    TwilioSettings,
+)
+from supertokens_python.recipe.passwordless.types import (
+    PasswordlessLoginSMSTemplateVars,
+)
+
+from twilio.rest import Client  # type: ignore
+
+from .service_implementation import ServiceImplementation
+
+_T = TypeVar("_T")
+
+
+class TwilioService(SMSDeliveryInterface[PasswordlessLoginSMSTemplateVars]):
+    service_implementation: TwilioServiceInterface[PasswordlessLoginSMSTemplateVars]
+
+    def __init__(
+        self,
+        twilio_settings: TwilioSettings,
+        override: Union[
+            Callable[[TwilioServiceInterface[_T]], TwilioServiceInterface[_T]], None
+        ] = None,
+    ) -> None:
+        self.config = normalize_twilio_settings(twilio_settings)
+        otps = twilio_settings.opts if twilio_settings.opts else {}
+        self.twilio_client = Client(  # type: ignore
+            twilio_settings.account_sid, twilio_settings.auth_token, **otps
+        )
+        oi = ServiceImplementation(self.twilio_client)  # type: ignore
+        self.service_implementation = oi if override is None else override(oi)
+
+    async def send_sms(
+        self,
+        template_vars: PasswordlessLoginSMSTemplateVars,
+        user_context: Dict[str, Any],
+    ) -> None:
+        content = await self.service_implementation.get_content(
+            template_vars, user_context
+        )
+        await self.service_implementation.send_raw_sms(
+            content,
+            user_context,
+            from_=self.config.from_,
+            messaging_service_sid=self.config.messaging_service_sid,
+        )
+
+
+
+

Sub-modules

+
+
supertokens_python.recipe.passwordless.smsdelivery.services.twilio.passwordless_login
+
+
+
+
supertokens_python.recipe.passwordless.smsdelivery.services.twilio.service_implementation
+
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class TwilioService +(twilio_settings: TwilioSettings, override: Union[Callable[[TwilioServiceInterface[_T]], TwilioServiceInterface[_T]], None] = None) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class TwilioService(SMSDeliveryInterface[PasswordlessLoginSMSTemplateVars]):
+    service_implementation: TwilioServiceInterface[PasswordlessLoginSMSTemplateVars]
+
+    def __init__(
+        self,
+        twilio_settings: TwilioSettings,
+        override: Union[
+            Callable[[TwilioServiceInterface[_T]], TwilioServiceInterface[_T]], None
+        ] = None,
+    ) -> None:
+        self.config = normalize_twilio_settings(twilio_settings)
+        otps = twilio_settings.opts if twilio_settings.opts else {}
+        self.twilio_client = Client(  # type: ignore
+            twilio_settings.account_sid, twilio_settings.auth_token, **otps
+        )
+        oi = ServiceImplementation(self.twilio_client)  # type: ignore
+        self.service_implementation = oi if override is None else override(oi)
+
+    async def send_sms(
+        self,
+        template_vars: PasswordlessLoginSMSTemplateVars,
+        user_context: Dict[str, Any],
+    ) -> None:
+        content = await self.service_implementation.get_content(
+            template_vars, user_context
+        )
+        await self.service_implementation.send_raw_sms(
+            content,
+            user_context,
+            from_=self.config.from_,
+            messaging_service_sid=self.config.messaging_service_sid,
+        )
+
+

Ancestors

+ +

Class variables

+
+
var service_implementationTwilioServiceInterface[CreateAndSendCustomTextMessageParameters]
+
+
+
+
+

Methods

+
+
+async def send_sms(self, template_vars: PasswordlessLoginSMSTemplateVars, user_context: Dict[str, Any]) ‑> None +
+
+
+
+ +Expand source code + +
async def send_sms(
+    self,
+    template_vars: PasswordlessLoginSMSTemplateVars,
+    user_context: Dict[str, Any],
+) -> None:
+    content = await self.service_implementation.get_content(
+        template_vars, user_context
+    )
+    await self.service_implementation.send_raw_sms(
+        content,
+        user_context,
+        from_=self.config.from_,
+        messaging_service_sid=self.config.messaging_service_sid,
+    )
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/passwordless/smsdelivery/services/twilio/passwordless_login.html b/html/supertokens_python/recipe/passwordless/smsdelivery/services/twilio/passwordless_login.html new file mode 100644 index 000000000..f503ab765 --- /dev/null +++ b/html/supertokens_python/recipe/passwordless/smsdelivery/services/twilio/passwordless_login.html @@ -0,0 +1,212 @@ + + + + + + +supertokens_python.recipe.passwordless.smsdelivery.services.twilio.passwordless_login API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.passwordless.smsdelivery.services.twilio.passwordless_login

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from string import Template
+from textwrap import dedent
+from typing import TYPE_CHECKING, Union
+
+from supertokens_python.ingredients.smsdelivery.types import SMSContent
+from supertokens_python.supertokens import Supertokens
+from supertokens_python.utils import humanize_time
+
+if TYPE_CHECKING:
+    from supertokens_python.recipe.passwordless.types import (
+        PasswordlessLoginSMSTemplateVars,
+    )
+
+
+def pless_sms_content(input_: PasswordlessLoginSMSTemplateVars) -> SMSContent:
+    supertokens = Supertokens.get_instance()
+    app_name = supertokens.app_info.app_name
+    code_lifetime = humanize_time(input_.code_life_time)
+    body = get_pless_sms_body(
+        app_name, code_lifetime, input_.url_with_link_code, input_.user_input_code
+    )
+    return SMSContent(body, input_.phone_number)
+
+
+def get_pless_sms_body(
+    app_name: str,
+    code_lifetime: str,
+    url_with_link_code: Union[str, None] = None,
+    user_input_code: Union[str, None] = None,
+) -> str:
+    if (url_with_link_code is not None) and (user_input_code is not None):
+        sms_template = dedent(
+            """\
+        OTP to login is ${otp} for ${appname}
+
+        Or click ${magicLink} to login.
+
+        This is valid for ${time}."""
+        )
+    elif url_with_link_code is not None:
+        sms_template = dedent(
+            """\
+        Click ${magicLink} to login to ${appname}
+
+        This is valid for ${time}."""
+        )
+    elif user_input_code is not None:
+        sms_template = dedent(
+            """\
+        OTP to login is ${otp} for ${appname}
+
+        This is valid for ${time}."""
+        )
+    else:
+        raise Exception("This should never be thrown.")
+
+    return Template(sms_template).substitute(
+        appname=app_name,
+        magicLink=url_with_link_code,
+        otp=user_input_code,
+        time=code_lifetime,
+    )
+
+
+
+
+
+
+
+

Functions

+
+
+def get_pless_sms_body(app_name: str, code_lifetime: str, url_with_link_code: Union[str, None] = None, user_input_code: Union[str, None] = None) ‑> str +
+
+
+
+ +Expand source code + +
def get_pless_sms_body(
+    app_name: str,
+    code_lifetime: str,
+    url_with_link_code: Union[str, None] = None,
+    user_input_code: Union[str, None] = None,
+) -> str:
+    if (url_with_link_code is not None) and (user_input_code is not None):
+        sms_template = dedent(
+            """\
+        OTP to login is ${otp} for ${appname}
+
+        Or click ${magicLink} to login.
+
+        This is valid for ${time}."""
+        )
+    elif url_with_link_code is not None:
+        sms_template = dedent(
+            """\
+        Click ${magicLink} to login to ${appname}
+
+        This is valid for ${time}."""
+        )
+    elif user_input_code is not None:
+        sms_template = dedent(
+            """\
+        OTP to login is ${otp} for ${appname}
+
+        This is valid for ${time}."""
+        )
+    else:
+        raise Exception("This should never be thrown.")
+
+    return Template(sms_template).substitute(
+        appname=app_name,
+        magicLink=url_with_link_code,
+        otp=user_input_code,
+        time=code_lifetime,
+    )
+
+
+
+def pless_sms_content(input_: PasswordlessLoginSMSTemplateVars) ‑> SMSContent +
+
+
+
+ +Expand source code + +
def pless_sms_content(input_: PasswordlessLoginSMSTemplateVars) -> SMSContent:
+    supertokens = Supertokens.get_instance()
+    app_name = supertokens.app_info.app_name
+    code_lifetime = humanize_time(input_.code_life_time)
+    body = get_pless_sms_body(
+        app_name, code_lifetime, input_.url_with_link_code, input_.user_input_code
+    )
+    return SMSContent(body, input_.phone_number)
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/passwordless/smsdelivery/services/twilio/service_implementation.html b/html/supertokens_python/recipe/passwordless/smsdelivery/services/twilio/service_implementation.html new file mode 100644 index 000000000..468c1aeb4 --- /dev/null +++ b/html/supertokens_python/recipe/passwordless/smsdelivery/services/twilio/service_implementation.html @@ -0,0 +1,228 @@ + + + + + + +supertokens_python.recipe.passwordless.smsdelivery.services.twilio.service_implementation API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.passwordless.smsdelivery.services.twilio.service_implementation

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from __future__ import annotations
+
+from typing import Any, Dict, Union
+
+from supertokens_python.ingredients.smsdelivery.types import (
+    SMSContent,
+    TwilioServiceInterface,
+)
+from supertokens_python.recipe.passwordless.smsdelivery.services.twilio.passwordless_login import (
+    pless_sms_content,
+)
+from supertokens_python.recipe.passwordless.types import (
+    PasswordlessLoginSMSTemplateVars,
+)
+
+
+class ServiceImplementation(TwilioServiceInterface[PasswordlessLoginSMSTemplateVars]):
+    async def send_raw_sms(
+        self,
+        content: SMSContent,
+        user_context: Dict[str, Any],
+        from_: Union[str, None] = None,
+        messaging_service_sid: Union[str, None] = None,
+    ) -> None:
+        if from_:
+            self.twilio_client.messages.create(  # type: ignore
+                to=content.to_phone,
+                body=content.body,
+                from_=from_,
+            )
+        else:
+            self.twilio_client.messages.create(  # type: ignore
+                to=content.to_phone,
+                body=content.body,
+                messaging_service_sid=messaging_service_sid,
+            )
+
+    async def get_content(
+        self,
+        template_vars: PasswordlessLoginSMSTemplateVars,
+        user_context: Dict[str, Any],
+    ) -> SMSContent:
+        _ = user_context
+        return pless_sms_content(template_vars)
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class ServiceImplementation +(twilio_client: twilio.rest.Client) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class ServiceImplementation(TwilioServiceInterface[PasswordlessLoginSMSTemplateVars]):
+    async def send_raw_sms(
+        self,
+        content: SMSContent,
+        user_context: Dict[str, Any],
+        from_: Union[str, None] = None,
+        messaging_service_sid: Union[str, None] = None,
+    ) -> None:
+        if from_:
+            self.twilio_client.messages.create(  # type: ignore
+                to=content.to_phone,
+                body=content.body,
+                from_=from_,
+            )
+        else:
+            self.twilio_client.messages.create(  # type: ignore
+                to=content.to_phone,
+                body=content.body,
+                messaging_service_sid=messaging_service_sid,
+            )
+
+    async def get_content(
+        self,
+        template_vars: PasswordlessLoginSMSTemplateVars,
+        user_context: Dict[str, Any],
+    ) -> SMSContent:
+        _ = user_context
+        return pless_sms_content(template_vars)
+
+

Ancestors

+ +

Methods

+
+
+async def get_content(self, template_vars: PasswordlessLoginSMSTemplateVars, user_context: Dict[str, Any]) ‑> SMSContent +
+
+
+
+ +Expand source code + +
async def get_content(
+    self,
+    template_vars: PasswordlessLoginSMSTemplateVars,
+    user_context: Dict[str, Any],
+) -> SMSContent:
+    _ = user_context
+    return pless_sms_content(template_vars)
+
+
+
+async def send_raw_sms(self, content: SMSContent, user_context: Dict[str, Any], from_: Union[str, None] = None, messaging_service_sid: Union[str, None] = None) ‑> None +
+
+
+
+ +Expand source code + +
async def send_raw_sms(
+    self,
+    content: SMSContent,
+    user_context: Dict[str, Any],
+    from_: Union[str, None] = None,
+    messaging_service_sid: Union[str, None] = None,
+) -> None:
+    if from_:
+        self.twilio_client.messages.create(  # type: ignore
+            to=content.to_phone,
+            body=content.body,
+            from_=from_,
+        )
+    else:
+        self.twilio_client.messages.create(  # type: ignore
+            to=content.to_phone,
+            body=content.body,
+            messaging_service_sid=messaging_service_sid,
+        )
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/passwordless/syncio/index.html b/html/supertokens_python/recipe/passwordless/syncio/index.html new file mode 100644 index 000000000..ad19877d3 --- /dev/null +++ b/html/supertokens_python/recipe/passwordless/syncio/index.html @@ -0,0 +1,770 @@ + + + + + + +supertokens_python.recipe.passwordless.syncio API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.passwordless.syncio

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from typing import Any, Dict, List, Union
+
+from supertokens_python.async_to_sync_wrapper import sync
+from supertokens_python.recipe.passwordless import asyncio
+from supertokens_python.recipe.passwordless.interfaces import (
+    ConsumeCodeExpiredUserInputCodeError,
+    ConsumeCodeIncorrectUserInputCodeError,
+    ConsumeCodeOkResult,
+    ConsumeCodeRestartFlowError,
+    CreateCodeOkResult,
+    CreateNewCodeForDeviceOkResult,
+    CreateNewCodeForDeviceRestartFlowError,
+    CreateNewCodeForDeviceUserInputCodeAlreadyUsedError,
+    DeleteUserInfoOkResult,
+    DeleteUserInfoUnknownUserIdError,
+    RevokeAllCodesOkResult,
+    RevokeCodeOkResult,
+    UpdateUserEmailAlreadyExistsError,
+    UpdateUserOkResult,
+    UpdateUserPhoneNumberAlreadyExistsError,
+    UpdateUserUnknownUserIdError,
+)
+from supertokens_python.recipe.passwordless.types import (
+    DeviceType,
+    PasswordlessLoginEmailTemplateVars,
+    PasswordlessLoginSMSTemplateVars,
+    User,
+)
+
+
+def create_code(
+    tenant_id: str,
+    email: Union[None, str] = None,
+    phone_number: Union[None, str] = None,
+    user_input_code: Union[None, str] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> CreateCodeOkResult:
+    return sync(
+        asyncio.create_code(
+            tenant_id,
+            email=email,
+            phone_number=phone_number,
+            user_input_code=user_input_code,
+            user_context=user_context,
+        )
+    )
+
+
+def create_new_code_for_device(
+    tenant_id: str,
+    device_id: str,
+    user_input_code: Union[str, None] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> Union[
+    CreateNewCodeForDeviceOkResult,
+    CreateNewCodeForDeviceRestartFlowError,
+    CreateNewCodeForDeviceUserInputCodeAlreadyUsedError,
+]:
+    return sync(
+        asyncio.create_new_code_for_device(
+            tenant_id,
+            device_id=device_id,
+            user_input_code=user_input_code,
+            user_context=user_context,
+        )
+    )
+
+
+def consume_code(
+    tenant_id: str,
+    pre_auth_session_id: str,
+    user_input_code: Union[str, None] = None,
+    device_id: Union[str, None] = None,
+    link_code: Union[str, None] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> Union[
+    ConsumeCodeOkResult,
+    ConsumeCodeIncorrectUserInputCodeError,
+    ConsumeCodeExpiredUserInputCodeError,
+    ConsumeCodeRestartFlowError,
+]:
+    return sync(
+        asyncio.consume_code(
+            tenant_id,
+            pre_auth_session_id=pre_auth_session_id,
+            user_input_code=user_input_code,
+            device_id=device_id,
+            link_code=link_code,
+            user_context=user_context,
+        )
+    )
+
+
+def get_user_by_id(
+    user_id: str, user_context: Union[None, Dict[str, Any]] = None
+) -> Union[User, None]:
+    return sync(asyncio.get_user_by_id(user_id=user_id, user_context=user_context))
+
+
+def get_user_by_email(
+    tenant_id: str, email: str, user_context: Union[None, Dict[str, Any]] = None
+) -> Union[User, None]:
+    return sync(
+        asyncio.get_user_by_email(tenant_id, email=email, user_context=user_context)
+    )
+
+
+def get_user_by_phone_number(
+    tenant_id: str,
+    phone_number: str,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> Union[User, None]:
+    return sync(
+        asyncio.get_user_by_phone_number(
+            tenant_id=tenant_id, phone_number=phone_number, user_context=user_context
+        )
+    )
+
+
+def update_user(
+    user_id: str,
+    email: Union[str, None] = None,
+    phone_number: Union[str, None] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> Union[
+    UpdateUserOkResult,
+    UpdateUserUnknownUserIdError,
+    UpdateUserEmailAlreadyExistsError,
+    UpdateUserPhoneNumberAlreadyExistsError,
+]:
+    return sync(
+        asyncio.update_user(
+            user_id=user_id,
+            email=email,
+            phone_number=phone_number,
+            user_context=user_context,
+        )
+    )
+
+
+def delete_email_for_user(
+    user_id: str, user_context: Union[None, Dict[str, Any]] = None
+) -> Union[DeleteUserInfoOkResult, DeleteUserInfoUnknownUserIdError]:
+    return sync(
+        asyncio.delete_email_for_user(user_id=user_id, user_context=user_context)
+    )
+
+
+def delete_phone_number_for_user(
+    user_id: str, user_context: Union[None, Dict[str, Any]] = None
+) -> Union[DeleteUserInfoOkResult, DeleteUserInfoUnknownUserIdError]:
+    return sync(
+        asyncio.delete_phone_number_for_user(user_id=user_id, user_context=user_context)
+    )
+
+
+def revoke_all_codes(
+    tenant_id: str,
+    email: Union[str, None] = None,
+    phone_number: Union[str, None] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> RevokeAllCodesOkResult:
+    return sync(
+        asyncio.revoke_all_codes(
+            tenant_id, email=email, phone_number=phone_number, user_context=user_context
+        )
+    )
+
+
+def revoke_code(
+    tenant_id: str, code_id: str, user_context: Union[None, Dict[str, Any]] = None
+) -> RevokeCodeOkResult:
+    return sync(
+        asyncio.revoke_code(tenant_id, code_id=code_id, user_context=user_context)
+    )
+
+
+def list_codes_by_email(
+    tenant_id: str, email: str, user_context: Union[None, Dict[str, Any]] = None
+) -> List[DeviceType]:
+    return sync(
+        asyncio.list_codes_by_email(tenant_id, email=email, user_context=user_context)
+    )
+
+
+def list_codes_by_phone_number(
+    tenant_id: str, phone_number: str, user_context: Union[None, Dict[str, Any]] = None
+) -> List[DeviceType]:
+    return sync(
+        asyncio.list_codes_by_phone_number(
+            tenant_id, phone_number=phone_number, user_context=user_context
+        )
+    )
+
+
+def list_codes_by_device_id(
+    tenant_id: str,
+    device_id: str,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> Union[DeviceType, None]:
+    return sync(
+        asyncio.list_codes_by_device_id(
+            tenant_id=tenant_id, device_id=device_id, user_context=user_context
+        )
+    )
+
+
+def list_codes_by_pre_auth_session_id(
+    tenant_id: str,
+    pre_auth_session_id: str,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> Union[DeviceType, None]:
+    return sync(
+        asyncio.list_codes_by_pre_auth_session_id(
+            tenant_id=tenant_id,
+            pre_auth_session_id=pre_auth_session_id,
+            user_context=user_context,
+        )
+    )
+
+
+def create_magic_link(
+    tenant_id: str,
+    email: Union[str, None],
+    phone_number: Union[str, None],
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> str:
+    return sync(
+        asyncio.create_magic_link(
+            tenant_id=tenant_id,
+            email=email,
+            phone_number=phone_number,
+            user_context=user_context,
+        )
+    )
+
+
+def signinup(
+    tenant_id: str,
+    email: Union[str, None],
+    phone_number: Union[str, None],
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> ConsumeCodeOkResult:
+    return sync(
+        asyncio.signinup(
+            tenant_id=tenant_id,
+            email=email,
+            phone_number=phone_number,
+            user_context=user_context,
+        )
+    )
+
+
+def send_email(
+    input_: PasswordlessLoginEmailTemplateVars,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> None:
+    return sync(asyncio.send_email(input_, user_context))
+
+
+def send_sms(
+    input_: PasswordlessLoginSMSTemplateVars,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> None:
+    return sync(asyncio.send_sms(input_, user_context))
+
+
+
+
+
+
+
+

Functions

+
+
+def consume_code(tenant_id: str, pre_auth_session_id: str, user_input_code: Optional[str] = None, device_id: Optional[str] = None, link_code: Optional[str] = None, user_context: Optional[Dict[str, Any]] = None) ‑> Union[ConsumeCodeOkResultConsumeCodeIncorrectUserInputCodeErrorConsumeCodeExpiredUserInputCodeErrorConsumeCodeRestartFlowError] +
+
+
+
+ +Expand source code + +
def consume_code(
+    tenant_id: str,
+    pre_auth_session_id: str,
+    user_input_code: Union[str, None] = None,
+    device_id: Union[str, None] = None,
+    link_code: Union[str, None] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> Union[
+    ConsumeCodeOkResult,
+    ConsumeCodeIncorrectUserInputCodeError,
+    ConsumeCodeExpiredUserInputCodeError,
+    ConsumeCodeRestartFlowError,
+]:
+    return sync(
+        asyncio.consume_code(
+            tenant_id,
+            pre_auth_session_id=pre_auth_session_id,
+            user_input_code=user_input_code,
+            device_id=device_id,
+            link_code=link_code,
+            user_context=user_context,
+        )
+    )
+
+
+
+def create_code(tenant_id: str, email: Optional[str] = None, phone_number: Optional[str] = None, user_input_code: Optional[str] = None, user_context: Optional[Dict[str, Any]] = None) ‑> CreateCodeOkResult +
+
+
+
+ +Expand source code + +
def create_code(
+    tenant_id: str,
+    email: Union[None, str] = None,
+    phone_number: Union[None, str] = None,
+    user_input_code: Union[None, str] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> CreateCodeOkResult:
+    return sync(
+        asyncio.create_code(
+            tenant_id,
+            email=email,
+            phone_number=phone_number,
+            user_input_code=user_input_code,
+            user_context=user_context,
+        )
+    )
+
+
+ +
+
+
+ +Expand source code + +
def create_magic_link(
+    tenant_id: str,
+    email: Union[str, None],
+    phone_number: Union[str, None],
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> str:
+    return sync(
+        asyncio.create_magic_link(
+            tenant_id=tenant_id,
+            email=email,
+            phone_number=phone_number,
+            user_context=user_context,
+        )
+    )
+
+
+
+def create_new_code_for_device(tenant_id: str, device_id: str, user_input_code: Optional[str] = None, user_context: Optional[Dict[str, Any]] = None) ‑> Union[CreateNewCodeForDeviceOkResultCreateNewCodeForDeviceRestartFlowErrorCreateNewCodeForDeviceUserInputCodeAlreadyUsedError] +
+
+
+
+ +Expand source code + +
def create_new_code_for_device(
+    tenant_id: str,
+    device_id: str,
+    user_input_code: Union[str, None] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> Union[
+    CreateNewCodeForDeviceOkResult,
+    CreateNewCodeForDeviceRestartFlowError,
+    CreateNewCodeForDeviceUserInputCodeAlreadyUsedError,
+]:
+    return sync(
+        asyncio.create_new_code_for_device(
+            tenant_id,
+            device_id=device_id,
+            user_input_code=user_input_code,
+            user_context=user_context,
+        )
+    )
+
+
+
+def delete_email_for_user(user_id: str, user_context: Optional[Dict[str, Any]] = None) ‑> Union[DeleteUserInfoOkResultDeleteUserInfoUnknownUserIdError] +
+
+
+
+ +Expand source code + +
def delete_email_for_user(
+    user_id: str, user_context: Union[None, Dict[str, Any]] = None
+) -> Union[DeleteUserInfoOkResult, DeleteUserInfoUnknownUserIdError]:
+    return sync(
+        asyncio.delete_email_for_user(user_id=user_id, user_context=user_context)
+    )
+
+
+
+def delete_phone_number_for_user(user_id: str, user_context: Optional[Dict[str, Any]] = None) ‑> Union[DeleteUserInfoOkResultDeleteUserInfoUnknownUserIdError] +
+
+
+
+ +Expand source code + +
def delete_phone_number_for_user(
+    user_id: str, user_context: Union[None, Dict[str, Any]] = None
+) -> Union[DeleteUserInfoOkResult, DeleteUserInfoUnknownUserIdError]:
+    return sync(
+        asyncio.delete_phone_number_for_user(user_id=user_id, user_context=user_context)
+    )
+
+
+
+def get_user_by_email(tenant_id: str, email: str, user_context: Optional[Dict[str, Any]] = None) ‑> Optional[User] +
+
+
+
+ +Expand source code + +
def get_user_by_email(
+    tenant_id: str, email: str, user_context: Union[None, Dict[str, Any]] = None
+) -> Union[User, None]:
+    return sync(
+        asyncio.get_user_by_email(tenant_id, email=email, user_context=user_context)
+    )
+
+
+
+def get_user_by_id(user_id: str, user_context: Optional[Dict[str, Any]] = None) ‑> Optional[User] +
+
+
+
+ +Expand source code + +
def get_user_by_id(
+    user_id: str, user_context: Union[None, Dict[str, Any]] = None
+) -> Union[User, None]:
+    return sync(asyncio.get_user_by_id(user_id=user_id, user_context=user_context))
+
+
+
+def get_user_by_phone_number(tenant_id: str, phone_number: str, user_context: Optional[Dict[str, Any]] = None) ‑> Optional[User] +
+
+
+
+ +Expand source code + +
def get_user_by_phone_number(
+    tenant_id: str,
+    phone_number: str,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> Union[User, None]:
+    return sync(
+        asyncio.get_user_by_phone_number(
+            tenant_id=tenant_id, phone_number=phone_number, user_context=user_context
+        )
+    )
+
+
+
+def list_codes_by_device_id(tenant_id: str, device_id: str, user_context: Optional[Dict[str, Any]] = None) ‑> Optional[DeviceType] +
+
+
+
+ +Expand source code + +
def list_codes_by_device_id(
+    tenant_id: str,
+    device_id: str,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> Union[DeviceType, None]:
+    return sync(
+        asyncio.list_codes_by_device_id(
+            tenant_id=tenant_id, device_id=device_id, user_context=user_context
+        )
+    )
+
+
+
+def list_codes_by_email(tenant_id: str, email: str, user_context: Optional[Dict[str, Any]] = None) ‑> List[DeviceType] +
+
+
+
+ +Expand source code + +
def list_codes_by_email(
+    tenant_id: str, email: str, user_context: Union[None, Dict[str, Any]] = None
+) -> List[DeviceType]:
+    return sync(
+        asyncio.list_codes_by_email(tenant_id, email=email, user_context=user_context)
+    )
+
+
+
+def list_codes_by_phone_number(tenant_id: str, phone_number: str, user_context: Optional[Dict[str, Any]] = None) ‑> List[DeviceType] +
+
+
+
+ +Expand source code + +
def list_codes_by_phone_number(
+    tenant_id: str, phone_number: str, user_context: Union[None, Dict[str, Any]] = None
+) -> List[DeviceType]:
+    return sync(
+        asyncio.list_codes_by_phone_number(
+            tenant_id, phone_number=phone_number, user_context=user_context
+        )
+    )
+
+
+
+def list_codes_by_pre_auth_session_id(tenant_id: str, pre_auth_session_id: str, user_context: Optional[Dict[str, Any]] = None) ‑> Optional[DeviceType] +
+
+
+
+ +Expand source code + +
def list_codes_by_pre_auth_session_id(
+    tenant_id: str,
+    pre_auth_session_id: str,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> Union[DeviceType, None]:
+    return sync(
+        asyncio.list_codes_by_pre_auth_session_id(
+            tenant_id=tenant_id,
+            pre_auth_session_id=pre_auth_session_id,
+            user_context=user_context,
+        )
+    )
+
+
+
+def revoke_all_codes(tenant_id: str, email: Optional[str] = None, phone_number: Optional[str] = None, user_context: Optional[Dict[str, Any]] = None) ‑> RevokeAllCodesOkResult +
+
+
+
+ +Expand source code + +
def revoke_all_codes(
+    tenant_id: str,
+    email: Union[str, None] = None,
+    phone_number: Union[str, None] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> RevokeAllCodesOkResult:
+    return sync(
+        asyncio.revoke_all_codes(
+            tenant_id, email=email, phone_number=phone_number, user_context=user_context
+        )
+    )
+
+
+
+def revoke_code(tenant_id: str, code_id: str, user_context: Optional[Dict[str, Any]] = None) ‑> RevokeCodeOkResult +
+
+
+
+ +Expand source code + +
def revoke_code(
+    tenant_id: str, code_id: str, user_context: Union[None, Dict[str, Any]] = None
+) -> RevokeCodeOkResult:
+    return sync(
+        asyncio.revoke_code(tenant_id, code_id=code_id, user_context=user_context)
+    )
+
+
+
+def send_email(input_: CreateAndSendCustomEmailParameters, user_context: Optional[Dict[str, Any]] = None) ‑> None +
+
+
+
+ +Expand source code + +
def send_email(
+    input_: PasswordlessLoginEmailTemplateVars,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> None:
+    return sync(asyncio.send_email(input_, user_context))
+
+
+
+def send_sms(input_: CreateAndSendCustomTextMessageParameters, user_context: Optional[Dict[str, Any]] = None) ‑> None +
+
+
+
+ +Expand source code + +
def send_sms(
+    input_: PasswordlessLoginSMSTemplateVars,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> None:
+    return sync(asyncio.send_sms(input_, user_context))
+
+
+
+def signinup(tenant_id: str, email: Optional[str], phone_number: Optional[str], user_context: Optional[Dict[str, Any]] = None) ‑> ConsumeCodeOkResult +
+
+
+
+ +Expand source code + +
def signinup(
+    tenant_id: str,
+    email: Union[str, None],
+    phone_number: Union[str, None],
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> ConsumeCodeOkResult:
+    return sync(
+        asyncio.signinup(
+            tenant_id=tenant_id,
+            email=email,
+            phone_number=phone_number,
+            user_context=user_context,
+        )
+    )
+
+
+
+def update_user(user_id: str, email: Optional[str] = None, phone_number: Optional[str] = None, user_context: Optional[Dict[str, Any]] = None) ‑> Union[UpdateUserOkResultUpdateUserUnknownUserIdErrorUpdateUserEmailAlreadyExistsErrorUpdateUserPhoneNumberAlreadyExistsError] +
+
+
+
+ +Expand source code + +
def update_user(
+    user_id: str,
+    email: Union[str, None] = None,
+    phone_number: Union[str, None] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> Union[
+    UpdateUserOkResult,
+    UpdateUserUnknownUserIdError,
+    UpdateUserEmailAlreadyExistsError,
+    UpdateUserPhoneNumberAlreadyExistsError,
+]:
+    return sync(
+        asyncio.update_user(
+            user_id=user_id,
+            email=email,
+            phone_number=phone_number,
+            user_context=user_context,
+        )
+    )
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/passwordless/types.html b/html/supertokens_python/recipe/passwordless/types.html new file mode 100644 index 000000000..cea370957 --- /dev/null +++ b/html/supertokens_python/recipe/passwordless/types.html @@ -0,0 +1,499 @@ + + + + + + +supertokens_python.recipe.passwordless.types API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.passwordless.types

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from typing import List, Union
+
+from supertokens_python.ingredients.emaildelivery import EmailDeliveryIngredient
+from supertokens_python.ingredients.emaildelivery.types import (
+    SMTPServiceInterface,
+    EmailDeliveryInterface,
+)
+from supertokens_python.ingredients.smsdelivery import SMSDeliveryIngredient
+from supertokens_python.ingredients.smsdelivery.types import (
+    TwilioServiceInterface,
+    SMSDeliveryInterface,
+)
+
+
+class User:
+    def __init__(
+        self,
+        user_id: str,
+        email: Union[str, None],
+        phone_number: Union[str, None],
+        time_joined: int,
+        tenant_ids: List[str],
+    ):
+        self.user_id = user_id
+        self.email = email
+        self.phone_number = phone_number
+        self.time_joined = time_joined
+        self.tenant_ids = tenant_ids
+
+    def __eq__(self, other: object) -> bool:
+        return (
+            isinstance(other, self.__class__)
+            and self.user_id == other.user_id
+            and self.email == other.email
+            and self.phone_number == other.phone_number
+            and self.time_joined == other.time_joined
+            and self.tenant_ids == other.tenant_ids
+        )
+
+
+class DeviceCode:
+    def __init__(self, code_id: str, time_created: str, code_life_time: int):
+        self.code_id = code_id
+        self.time_created = time_created
+        self.code_life_time = code_life_time
+
+
+class DeviceType:
+    def __init__(
+        self,
+        pre_auth_session_id: str,
+        failed_code_input_attempt_count: int,
+        codes: List[DeviceCode],
+        email: Union[str, None] = None,
+        phone_number: Union[str, None] = None,
+    ):
+        self.pre_auth_session_id = pre_auth_session_id
+        self.failed_code_input_attempt_count = failed_code_input_attempt_count
+        self.codes = codes
+        self.email = email
+        self.phone_number = phone_number
+
+
+class CreateAndSendCustomEmailParameters:
+    def __init__(
+        self,
+        tenant_id: str,
+        code_life_time: int,
+        pre_auth_session_id: str,
+        email: str,
+        user_input_code: Union[str, None] = None,
+        url_with_link_code: Union[str, None] = None,
+    ):
+        self.email = email
+        self.code_life_time = code_life_time
+        self.pre_auth_session_id = pre_auth_session_id
+        self.user_input_code = user_input_code
+        self.url_with_link_code = url_with_link_code
+        self.tenant_id = tenant_id
+
+
+PasswordlessLoginEmailTemplateVars = CreateAndSendCustomEmailParameters
+
+
+class CreateAndSendCustomTextMessageParameters:
+    def __init__(
+        self,
+        tenant_id: str,
+        code_life_time: int,
+        pre_auth_session_id: str,
+        phone_number: str,
+        user_input_code: Union[str, None] = None,
+        url_with_link_code: Union[str, None] = None,
+    ):
+        self.code_life_time = code_life_time
+        self.pre_auth_session_id = pre_auth_session_id
+        self.phone_number = phone_number
+        self.user_input_code = user_input_code
+        self.url_with_link_code = url_with_link_code
+        self.tenant_id = tenant_id
+
+
+PasswordlessLoginSMSTemplateVars = CreateAndSendCustomTextMessageParameters
+
+
+# Export:
+EmailTemplateVars = PasswordlessLoginEmailTemplateVars
+SMSTemplateVars = PasswordlessLoginSMSTemplateVars
+
+SMTPOverrideInput = SMTPServiceInterface[EmailTemplateVars]
+TwilioOverrideInput = TwilioServiceInterface[SMSTemplateVars]
+
+EmailDeliveryOverrideInput = EmailDeliveryInterface[EmailTemplateVars]
+SMSDeliveryOverrideInput = SMSDeliveryInterface[SMSTemplateVars]
+
+
+class PasswordlessIngredients:
+    def __init__(
+        self,
+        email_delivery: Union[EmailDeliveryIngredient[EmailTemplateVars], None] = None,
+        sms_delivery: Union[SMSDeliveryIngredient[SMSTemplateVars], None] = None,
+    ):
+        self.email_delivery = email_delivery
+        self.sms_delivery = sms_delivery
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class CreateAndSendCustomEmailParameters +(tenant_id: str, code_life_time: int, pre_auth_session_id: str, email: str, user_input_code: Optional[str] = None, url_with_link_code: Optional[str] = None) +
+
+
+
+ +Expand source code + +
class CreateAndSendCustomEmailParameters:
+    def __init__(
+        self,
+        tenant_id: str,
+        code_life_time: int,
+        pre_auth_session_id: str,
+        email: str,
+        user_input_code: Union[str, None] = None,
+        url_with_link_code: Union[str, None] = None,
+    ):
+        self.email = email
+        self.code_life_time = code_life_time
+        self.pre_auth_session_id = pre_auth_session_id
+        self.user_input_code = user_input_code
+        self.url_with_link_code = url_with_link_code
+        self.tenant_id = tenant_id
+
+
+
+class PasswordlessLoginEmailTemplateVars +(tenant_id: str, code_life_time: int, pre_auth_session_id: str, email: str, user_input_code: Optional[str] = None, url_with_link_code: Optional[str] = None) +
+
+
+
+ +Expand source code + +
class CreateAndSendCustomEmailParameters:
+    def __init__(
+        self,
+        tenant_id: str,
+        code_life_time: int,
+        pre_auth_session_id: str,
+        email: str,
+        user_input_code: Union[str, None] = None,
+        url_with_link_code: Union[str, None] = None,
+    ):
+        self.email = email
+        self.code_life_time = code_life_time
+        self.pre_auth_session_id = pre_auth_session_id
+        self.user_input_code = user_input_code
+        self.url_with_link_code = url_with_link_code
+        self.tenant_id = tenant_id
+
+
+
+class EmailTemplateVars +(tenant_id: str, code_life_time: int, pre_auth_session_id: str, email: str, user_input_code: Optional[str] = None, url_with_link_code: Optional[str] = None) +
+
+
+
+ +Expand source code + +
class CreateAndSendCustomEmailParameters:
+    def __init__(
+        self,
+        tenant_id: str,
+        code_life_time: int,
+        pre_auth_session_id: str,
+        email: str,
+        user_input_code: Union[str, None] = None,
+        url_with_link_code: Union[str, None] = None,
+    ):
+        self.email = email
+        self.code_life_time = code_life_time
+        self.pre_auth_session_id = pre_auth_session_id
+        self.user_input_code = user_input_code
+        self.url_with_link_code = url_with_link_code
+        self.tenant_id = tenant_id
+
+
+
+class CreateAndSendCustomTextMessageParameters +(tenant_id: str, code_life_time: int, pre_auth_session_id: str, phone_number: str, user_input_code: Optional[str] = None, url_with_link_code: Optional[str] = None) +
+
+
+
+ +Expand source code + +
class CreateAndSendCustomTextMessageParameters:
+    def __init__(
+        self,
+        tenant_id: str,
+        code_life_time: int,
+        pre_auth_session_id: str,
+        phone_number: str,
+        user_input_code: Union[str, None] = None,
+        url_with_link_code: Union[str, None] = None,
+    ):
+        self.code_life_time = code_life_time
+        self.pre_auth_session_id = pre_auth_session_id
+        self.phone_number = phone_number
+        self.user_input_code = user_input_code
+        self.url_with_link_code = url_with_link_code
+        self.tenant_id = tenant_id
+
+
+
+class PasswordlessLoginSMSTemplateVars +(tenant_id: str, code_life_time: int, pre_auth_session_id: str, phone_number: str, user_input_code: Optional[str] = None, url_with_link_code: Optional[str] = None) +
+
+
+
+ +Expand source code + +
class CreateAndSendCustomTextMessageParameters:
+    def __init__(
+        self,
+        tenant_id: str,
+        code_life_time: int,
+        pre_auth_session_id: str,
+        phone_number: str,
+        user_input_code: Union[str, None] = None,
+        url_with_link_code: Union[str, None] = None,
+    ):
+        self.code_life_time = code_life_time
+        self.pre_auth_session_id = pre_auth_session_id
+        self.phone_number = phone_number
+        self.user_input_code = user_input_code
+        self.url_with_link_code = url_with_link_code
+        self.tenant_id = tenant_id
+
+
+
+class SMSTemplateVars +(tenant_id: str, code_life_time: int, pre_auth_session_id: str, phone_number: str, user_input_code: Optional[str] = None, url_with_link_code: Optional[str] = None) +
+
+
+
+ +Expand source code + +
class CreateAndSendCustomTextMessageParameters:
+    def __init__(
+        self,
+        tenant_id: str,
+        code_life_time: int,
+        pre_auth_session_id: str,
+        phone_number: str,
+        user_input_code: Union[str, None] = None,
+        url_with_link_code: Union[str, None] = None,
+    ):
+        self.code_life_time = code_life_time
+        self.pre_auth_session_id = pre_auth_session_id
+        self.phone_number = phone_number
+        self.user_input_code = user_input_code
+        self.url_with_link_code = url_with_link_code
+        self.tenant_id = tenant_id
+
+
+
+class DeviceCode +(code_id: str, time_created: str, code_life_time: int) +
+
+
+
+ +Expand source code + +
class DeviceCode:
+    def __init__(self, code_id: str, time_created: str, code_life_time: int):
+        self.code_id = code_id
+        self.time_created = time_created
+        self.code_life_time = code_life_time
+
+
+
+class DeviceType +(pre_auth_session_id: str, failed_code_input_attempt_count: int, codes: List[DeviceCode], email: Optional[str] = None, phone_number: Optional[str] = None) +
+
+
+
+ +Expand source code + +
class DeviceType:
+    def __init__(
+        self,
+        pre_auth_session_id: str,
+        failed_code_input_attempt_count: int,
+        codes: List[DeviceCode],
+        email: Union[str, None] = None,
+        phone_number: Union[str, None] = None,
+    ):
+        self.pre_auth_session_id = pre_auth_session_id
+        self.failed_code_input_attempt_count = failed_code_input_attempt_count
+        self.codes = codes
+        self.email = email
+        self.phone_number = phone_number
+
+
+
+class PasswordlessIngredients +(email_delivery: Optional[EmailDeliveryIngredient[CreateAndSendCustomEmailParameters]] = None, sms_delivery: Optional[SMSDeliveryIngredient[CreateAndSendCustomTextMessageParameters]] = None) +
+
+
+
+ +Expand source code + +
class PasswordlessIngredients:
+    def __init__(
+        self,
+        email_delivery: Union[EmailDeliveryIngredient[EmailTemplateVars], None] = None,
+        sms_delivery: Union[SMSDeliveryIngredient[SMSTemplateVars], None] = None,
+    ):
+        self.email_delivery = email_delivery
+        self.sms_delivery = sms_delivery
+
+
+
+class User +(user_id: str, email: Optional[str], phone_number: Optional[str], time_joined: int, tenant_ids: List[str]) +
+
+
+
+ +Expand source code + +
class User:
+    def __init__(
+        self,
+        user_id: str,
+        email: Union[str, None],
+        phone_number: Union[str, None],
+        time_joined: int,
+        tenant_ids: List[str],
+    ):
+        self.user_id = user_id
+        self.email = email
+        self.phone_number = phone_number
+        self.time_joined = time_joined
+        self.tenant_ids = tenant_ids
+
+    def __eq__(self, other: object) -> bool:
+        return (
+            isinstance(other, self.__class__)
+            and self.user_id == other.user_id
+            and self.email == other.email
+            and self.phone_number == other.phone_number
+            and self.time_joined == other.time_joined
+            and self.tenant_ids == other.tenant_ids
+        )
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/passwordless/utils.html b/html/supertokens_python/recipe/passwordless/utils.html new file mode 100644 index 000000000..5dae5907f --- /dev/null +++ b/html/supertokens_python/recipe/passwordless/utils.html @@ -0,0 +1,657 @@ + + + + + + +supertokens_python.recipe.passwordless.utils API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.passwordless.utils

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from __future__ import annotations
+
+from abc import ABC
+from typing import TYPE_CHECKING, Any, Awaitable, Callable, Dict, Union
+
+from supertokens_python.ingredients.emaildelivery.types import (
+    EmailDeliveryConfig,
+    EmailDeliveryConfigWithService,
+)
+from supertokens_python.ingredients.smsdelivery.types import (
+    SMSDeliveryConfig,
+    SMSDeliveryConfigWithService,
+)
+from supertokens_python.recipe.passwordless.types import (
+    PasswordlessLoginSMSTemplateVars,
+)
+from typing_extensions import Literal
+
+if TYPE_CHECKING:
+    from .interfaces import (
+        APIInterface,
+        RecipeInterface,
+        PasswordlessLoginEmailTemplateVars,
+    )
+    from supertokens_python import AppInfo
+
+from re import fullmatch
+
+from phonenumbers import is_valid_number, parse  # type: ignore
+from supertokens_python.recipe.passwordless.emaildelivery.services.backward_compatibility import (
+    BackwardCompatibilityService,
+)
+from supertokens_python.recipe.passwordless.smsdelivery.services.backward_compatibility import (
+    BackwardCompatibilityService as SMSBackwardCompatibilityService,
+)
+
+
+async def default_validate_phone_number(value: str, _tenant_id: str):
+    try:
+        parsed_phone_number: Any = parse(value, None)
+        if not is_valid_number(parsed_phone_number):
+            return "Phone number is invalid"
+    except Exception:
+        return "Phone number is invalid"
+
+
+async def default_validate_email(value: str, _tenant_id: str):
+    pattern = r"^(([^<>()\[\]\\.,;:\s@\"]+(\.[^<>()\[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$"
+    if fullmatch(pattern, value) is None:
+        return "Email is invalid"
+
+
+class OverrideConfig:
+    def __init__(
+        self,
+        functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None,
+        apis: Union[Callable[[APIInterface], APIInterface], None] = None,
+    ):
+        self.functions = functions
+        self.apis = apis
+
+
+class ContactConfig(ABC):
+    def __init__(self, contact_method: Literal["PHONE", "EMAIL", "EMAIL_OR_PHONE"]):
+        self.contact_method = contact_method
+
+
+class ContactPhoneOnlyConfig(ContactConfig):
+    def __init__(
+        self,
+        validate_phone_number: Union[
+            Callable[[str, str], Awaitable[Union[str, None]]], None
+        ] = None,
+    ):
+        super().__init__("PHONE")
+
+        if validate_phone_number is None:
+            self.validate_phone_number = default_validate_phone_number
+        else:
+            self.validate_phone_number = validate_phone_number
+
+
+class ContactEmailOnlyConfig(ContactConfig):
+    def __init__(
+        self,
+        validate_email_address: Union[
+            Callable[[str, str], Awaitable[Union[str, None]]], None
+        ] = None,
+    ):
+        super().__init__("EMAIL")
+
+        if validate_email_address is None:
+            self.validate_email_address = default_validate_email
+        else:
+            self.validate_email_address = validate_email_address
+
+
+class ContactEmailOrPhoneConfig(ContactConfig):
+    def __init__(
+        self,
+        validate_email_address: Union[
+            Callable[[str, str], Awaitable[Union[str, None]]], None
+        ] = None,
+        validate_phone_number: Union[
+            Callable[[str, str], Awaitable[Union[str, None]]], None
+        ] = None,
+    ):
+        super().__init__("EMAIL_OR_PHONE")
+
+        if validate_email_address is None:
+            self.validate_email_address = default_validate_email
+        else:
+            self.validate_email_address = validate_email_address
+
+        if validate_phone_number is None:
+            self.validate_phone_number = default_validate_phone_number
+        else:
+            self.validate_phone_number = validate_phone_number
+
+
+class PhoneOrEmailInput:
+    def __init__(self, phone_number: Union[str, None], email: Union[str, None]):
+        self.phone_number = phone_number
+        self.email = email
+
+
+class PasswordlessConfig:
+    def __init__(
+        self,
+        contact_config: ContactConfig,
+        override: OverrideConfig,
+        flow_type: Literal[
+            "USER_INPUT_CODE", "MAGIC_LINK", "USER_INPUT_CODE_AND_MAGIC_LINK"
+        ],
+        get_email_delivery_config: Callable[
+            [], EmailDeliveryConfigWithService[PasswordlessLoginEmailTemplateVars]
+        ],
+        get_sms_delivery_config: Callable[
+            [], SMSDeliveryConfigWithService[PasswordlessLoginSMSTemplateVars]
+        ],
+        get_custom_user_input_code: Union[
+            Callable[[str, Dict[str, Any]], Awaitable[str]], None
+        ] = None,
+    ):
+        self.contact_config = contact_config
+        self.override = override
+        self.flow_type: Literal[
+            "USER_INPUT_CODE", "MAGIC_LINK", "USER_INPUT_CODE_AND_MAGIC_LINK"
+        ] = flow_type
+        self.get_custom_user_input_code = get_custom_user_input_code
+        self.get_email_delivery_config = get_email_delivery_config
+        self.get_sms_delivery_config = get_sms_delivery_config
+
+
+def validate_and_normalise_user_input(
+    app_info: AppInfo,
+    contact_config: ContactConfig,
+    flow_type: Literal[
+        "USER_INPUT_CODE", "MAGIC_LINK", "USER_INPUT_CODE_AND_MAGIC_LINK"
+    ],
+    override: Union[OverrideConfig, None] = None,
+    get_custom_user_input_code: Union[
+        Callable[[str, Dict[str, Any]], Awaitable[str]], None
+    ] = None,
+    email_delivery: Union[
+        EmailDeliveryConfig[PasswordlessLoginEmailTemplateVars], None
+    ] = None,
+    sms_delivery: Union[
+        SMSDeliveryConfig[PasswordlessLoginSMSTemplateVars], None
+    ] = None,
+) -> PasswordlessConfig:
+
+    if override is None:
+        override = OverrideConfig()
+
+    def get_email_delivery_config() -> EmailDeliveryConfigWithService[
+        PasswordlessLoginEmailTemplateVars
+    ]:
+        email_service = email_delivery.service if email_delivery is not None else None
+
+        if email_service is None:
+            email_service = BackwardCompatibilityService(app_info)
+
+        if email_delivery is not None and email_delivery.override is not None:
+            override = email_delivery.override
+        else:
+            override = None
+
+        return EmailDeliveryConfigWithService(email_service, override=override)
+
+    def get_sms_delivery_config() -> SMSDeliveryConfigWithService[
+        PasswordlessLoginSMSTemplateVars
+    ]:
+        sms_service = sms_delivery.service if sms_delivery is not None else None
+
+        if sms_service is None:
+            sms_service = SMSBackwardCompatibilityService(app_info)
+
+        if sms_delivery is not None and sms_delivery.override is not None:
+            override = sms_delivery.override
+        else:
+            override = None
+
+        return SMSDeliveryConfigWithService(sms_service, override=override)
+
+    if not isinstance(contact_config, ContactConfig):  # type: ignore user might not have linter enabled
+        raise ValueError("contact_config must be of type ContactConfig")
+
+    if flow_type not in [
+        "USER_INPUT_CODE",
+        "MAGIC_LINK",
+        "USER_INPUT_CODE_AND_MAGIC_LINK",
+    ]:
+        raise ValueError(
+            "flow_type must be one of USER_INPUT_CODE, MAGIC_LINK, USER_INPUT_CODE_AND_MAGIC_LINK"
+        )
+
+    if not isinstance(override, OverrideConfig):  # type: ignore user might not have linter enabled
+        raise ValueError("override must be of type OverrideConfig")
+
+    return PasswordlessConfig(
+        contact_config=contact_config,
+        override=OverrideConfig(functions=override.functions, apis=override.apis),
+        flow_type=flow_type,
+        get_email_delivery_config=get_email_delivery_config,
+        get_sms_delivery_config=get_sms_delivery_config,
+        get_custom_user_input_code=get_custom_user_input_code,
+    )
+
+
+
+
+
+
+
+

Functions

+
+
+async def default_validate_email(value: str, _tenant_id: str) +
+
+
+
+ +Expand source code + +
async def default_validate_email(value: str, _tenant_id: str):
+    pattern = r"^(([^<>()\[\]\\.,;:\s@\"]+(\.[^<>()\[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$"
+    if fullmatch(pattern, value) is None:
+        return "Email is invalid"
+
+
+
+async def default_validate_phone_number(value: str, _tenant_id: str) +
+
+
+
+ +Expand source code + +
async def default_validate_phone_number(value: str, _tenant_id: str):
+    try:
+        parsed_phone_number: Any = parse(value, None)
+        if not is_valid_number(parsed_phone_number):
+            return "Phone number is invalid"
+    except Exception:
+        return "Phone number is invalid"
+
+
+
+def validate_and_normalise_user_input(app_info: AppInfo, contact_config: ContactConfig, flow_type: "Literal[('USER_INPUT_CODE', 'MAGIC_LINK', 'USER_INPUT_CODE_AND_MAGIC_LINK')]", override: Union[OverrideConfig, None] = None, get_custom_user_input_code: Union[Callable[[str, Dict[str, Any]], Awaitable[str]], None] = None, email_delivery: Union[EmailDeliveryConfig[PasswordlessLoginEmailTemplateVars], None] = None, sms_delivery: Union[SMSDeliveryConfig[PasswordlessLoginSMSTemplateVars], None] = None) ‑> PasswordlessConfig +
+
+
+
+ +Expand source code + +
def validate_and_normalise_user_input(
+    app_info: AppInfo,
+    contact_config: ContactConfig,
+    flow_type: Literal[
+        "USER_INPUT_CODE", "MAGIC_LINK", "USER_INPUT_CODE_AND_MAGIC_LINK"
+    ],
+    override: Union[OverrideConfig, None] = None,
+    get_custom_user_input_code: Union[
+        Callable[[str, Dict[str, Any]], Awaitable[str]], None
+    ] = None,
+    email_delivery: Union[
+        EmailDeliveryConfig[PasswordlessLoginEmailTemplateVars], None
+    ] = None,
+    sms_delivery: Union[
+        SMSDeliveryConfig[PasswordlessLoginSMSTemplateVars], None
+    ] = None,
+) -> PasswordlessConfig:
+
+    if override is None:
+        override = OverrideConfig()
+
+    def get_email_delivery_config() -> EmailDeliveryConfigWithService[
+        PasswordlessLoginEmailTemplateVars
+    ]:
+        email_service = email_delivery.service if email_delivery is not None else None
+
+        if email_service is None:
+            email_service = BackwardCompatibilityService(app_info)
+
+        if email_delivery is not None and email_delivery.override is not None:
+            override = email_delivery.override
+        else:
+            override = None
+
+        return EmailDeliveryConfigWithService(email_service, override=override)
+
+    def get_sms_delivery_config() -> SMSDeliveryConfigWithService[
+        PasswordlessLoginSMSTemplateVars
+    ]:
+        sms_service = sms_delivery.service if sms_delivery is not None else None
+
+        if sms_service is None:
+            sms_service = SMSBackwardCompatibilityService(app_info)
+
+        if sms_delivery is not None and sms_delivery.override is not None:
+            override = sms_delivery.override
+        else:
+            override = None
+
+        return SMSDeliveryConfigWithService(sms_service, override=override)
+
+    if not isinstance(contact_config, ContactConfig):  # type: ignore user might not have linter enabled
+        raise ValueError("contact_config must be of type ContactConfig")
+
+    if flow_type not in [
+        "USER_INPUT_CODE",
+        "MAGIC_LINK",
+        "USER_INPUT_CODE_AND_MAGIC_LINK",
+    ]:
+        raise ValueError(
+            "flow_type must be one of USER_INPUT_CODE, MAGIC_LINK, USER_INPUT_CODE_AND_MAGIC_LINK"
+        )
+
+    if not isinstance(override, OverrideConfig):  # type: ignore user might not have linter enabled
+        raise ValueError("override must be of type OverrideConfig")
+
+    return PasswordlessConfig(
+        contact_config=contact_config,
+        override=OverrideConfig(functions=override.functions, apis=override.apis),
+        flow_type=flow_type,
+        get_email_delivery_config=get_email_delivery_config,
+        get_sms_delivery_config=get_sms_delivery_config,
+        get_custom_user_input_code=get_custom_user_input_code,
+    )
+
+
+
+
+
+

Classes

+
+
+class ContactConfig +(contact_method: "Literal[('PHONE', 'EMAIL', 'EMAIL_OR_PHONE')]") +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class ContactConfig(ABC):
+    def __init__(self, contact_method: Literal["PHONE", "EMAIL", "EMAIL_OR_PHONE"]):
+        self.contact_method = contact_method
+
+

Ancestors

+
    +
  • abc.ABC
  • +
+

Subclasses

+ +
+
+class ContactEmailOnlyConfig +(validate_email_address: Union[Callable[[str, str], Awaitable[Union[str, None]]], None] = None) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class ContactEmailOnlyConfig(ContactConfig):
+    def __init__(
+        self,
+        validate_email_address: Union[
+            Callable[[str, str], Awaitable[Union[str, None]]], None
+        ] = None,
+    ):
+        super().__init__("EMAIL")
+
+        if validate_email_address is None:
+            self.validate_email_address = default_validate_email
+        else:
+            self.validate_email_address = validate_email_address
+
+

Ancestors

+ +
+
+class ContactEmailOrPhoneConfig +(validate_email_address: Union[Callable[[str, str], Awaitable[Union[str, None]]], None] = None, validate_phone_number: Union[Callable[[str, str], Awaitable[Union[str, None]]], None] = None) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class ContactEmailOrPhoneConfig(ContactConfig):
+    def __init__(
+        self,
+        validate_email_address: Union[
+            Callable[[str, str], Awaitable[Union[str, None]]], None
+        ] = None,
+        validate_phone_number: Union[
+            Callable[[str, str], Awaitable[Union[str, None]]], None
+        ] = None,
+    ):
+        super().__init__("EMAIL_OR_PHONE")
+
+        if validate_email_address is None:
+            self.validate_email_address = default_validate_email
+        else:
+            self.validate_email_address = validate_email_address
+
+        if validate_phone_number is None:
+            self.validate_phone_number = default_validate_phone_number
+        else:
+            self.validate_phone_number = validate_phone_number
+
+

Ancestors

+ +
+
+class ContactPhoneOnlyConfig +(validate_phone_number: Union[Callable[[str, str], Awaitable[Union[str, None]]], None] = None) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class ContactPhoneOnlyConfig(ContactConfig):
+    def __init__(
+        self,
+        validate_phone_number: Union[
+            Callable[[str, str], Awaitable[Union[str, None]]], None
+        ] = None,
+    ):
+        super().__init__("PHONE")
+
+        if validate_phone_number is None:
+            self.validate_phone_number = default_validate_phone_number
+        else:
+            self.validate_phone_number = validate_phone_number
+
+

Ancestors

+ +
+
+class OverrideConfig +(functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None, apis: Union[Callable[[APIInterface], APIInterface], None] = None) +
+
+
+
+ +Expand source code + +
class OverrideConfig:
+    def __init__(
+        self,
+        functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None,
+        apis: Union[Callable[[APIInterface], APIInterface], None] = None,
+    ):
+        self.functions = functions
+        self.apis = apis
+
+
+
+class PasswordlessConfig +(contact_config: ContactConfig, override: OverrideConfig, flow_type: "Literal[('USER_INPUT_CODE', 'MAGIC_LINK', 'USER_INPUT_CODE_AND_MAGIC_LINK')]", get_email_delivery_config: Callable[[], EmailDeliveryConfigWithService[PasswordlessLoginEmailTemplateVars]], get_sms_delivery_config: Callable[[], SMSDeliveryConfigWithService[PasswordlessLoginSMSTemplateVars]], get_custom_user_input_code: Union[Callable[[str, Dict[str, Any]], Awaitable[str]], None] = None) +
+
+
+
+ +Expand source code + +
class PasswordlessConfig:
+    def __init__(
+        self,
+        contact_config: ContactConfig,
+        override: OverrideConfig,
+        flow_type: Literal[
+            "USER_INPUT_CODE", "MAGIC_LINK", "USER_INPUT_CODE_AND_MAGIC_LINK"
+        ],
+        get_email_delivery_config: Callable[
+            [], EmailDeliveryConfigWithService[PasswordlessLoginEmailTemplateVars]
+        ],
+        get_sms_delivery_config: Callable[
+            [], SMSDeliveryConfigWithService[PasswordlessLoginSMSTemplateVars]
+        ],
+        get_custom_user_input_code: Union[
+            Callable[[str, Dict[str, Any]], Awaitable[str]], None
+        ] = None,
+    ):
+        self.contact_config = contact_config
+        self.override = override
+        self.flow_type: Literal[
+            "USER_INPUT_CODE", "MAGIC_LINK", "USER_INPUT_CODE_AND_MAGIC_LINK"
+        ] = flow_type
+        self.get_custom_user_input_code = get_custom_user_input_code
+        self.get_email_delivery_config = get_email_delivery_config
+        self.get_sms_delivery_config = get_sms_delivery_config
+
+
+
+class PhoneOrEmailInput +(phone_number: Union[str, None], email: Union[str, None]) +
+
+
+
+ +Expand source code + +
class PhoneOrEmailInput:
+    def __init__(self, phone_number: Union[str, None], email: Union[str, None]):
+        self.phone_number = phone_number
+        self.email = email
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/session/access_token.html b/html/supertokens_python/recipe/session/access_token.html new file mode 100644 index 000000000..017fffd36 --- /dev/null +++ b/html/supertokens_python/recipe/session/access_token.html @@ -0,0 +1,428 @@ + + + + + + +supertokens_python.recipe.session.access_token API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.session.access_token

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from typing import Any, Dict, Optional, Union
+
+import jwt
+from jwt.exceptions import DecodeError
+
+from supertokens_python.logger import log_debug_message
+from supertokens_python.recipe.session.utils import SessionConfig
+from supertokens_python.utils import get_timestamp_ms
+
+from .exceptions import raise_try_refresh_token_exception
+from .jwt import ParsedJWTInfo
+
+from supertokens_python.recipe.multitenancy.constants import DEFAULT_TENANT_ID
+
+
+def sanitize_string(s: Any) -> Union[str, None]:
+    if s == "":
+        return s
+
+    if not isinstance(s, str):
+        return None
+
+    return s.strip()
+
+
+def sanitize_number(n: Any) -> Union[Union[int, float], None]:
+    if isinstance(n, (int, float)):
+        return n
+
+    return None
+
+
+from supertokens_python.recipe.session.jwks import get_latest_keys
+
+
+def get_info_from_access_token(
+    config: SessionConfig,
+    jwt_info: ParsedJWTInfo,
+    do_anti_csrf_check: bool,
+):
+    try:
+        payload: Optional[Dict[str, Any]] = None
+        decode_algo = (
+            jwt_info.parsed_header["alg"]
+            if jwt_info.parsed_header is not None
+            else "RS256"
+        )
+
+        if jwt_info.version >= 3:
+            matching_keys = get_latest_keys(config, jwt_info.kid)
+            payload = jwt.decode(  # type: ignore
+                jwt_info.raw_token_string,
+                matching_keys[0].key,  # type: ignore
+                algorithms=[decode_algo],
+                options={"verify_signature": True, "verify_exp": True},
+            )
+        else:
+            # It won't have kid. So we'll have to try the token against all the keys from all the jwk_clients
+            # If any of them work, we'll use that payload
+            for k in get_latest_keys(config):
+                try:
+                    payload = jwt.decode(  # type: ignore
+                        jwt_info.raw_token_string,
+                        k.key,  # type: ignore
+                        algorithms=[decode_algo],
+                        options={"verify_signature": True, "verify_exp": True},
+                    )
+                    break
+                except DecodeError:
+                    pass
+
+        if payload is None:
+            raise DecodeError("Could not decode the token")
+
+        validate_access_token_structure(payload, jwt_info.version)
+
+        if jwt_info.version == 2:
+            user_id = sanitize_string(payload.get("userId"))
+            expiry_time = sanitize_number(payload.get("expiryTime"))
+            time_created = sanitize_number(payload.get("timeCreated"))
+            user_data = payload.get("userData")
+        else:
+            user_id = sanitize_string(payload.get("sub"))
+            expiry_time = sanitize_number(payload.get("exp", 0) * 1000)
+            time_created = sanitize_number(payload.get("iat", 0) * 1000)
+            user_data = payload
+
+        session_handle = sanitize_string(payload.get("sessionHandle"))
+        refresh_token_hash_1 = sanitize_string(payload.get("refreshTokenHash1"))
+        parent_refresh_token_hash_1 = sanitize_string(
+            payload.get("parentRefreshTokenHash1")
+        )
+        anti_csrf_token = sanitize_string(payload.get("antiCsrfToken"))
+        tenant_id = DEFAULT_TENANT_ID
+
+        if jwt_info.version >= 4:
+            tenant_id = sanitize_string(payload.get("tId"))
+
+        if anti_csrf_token is None and do_anti_csrf_check:
+            raise Exception("Access token does not contain the anti-csrf token")
+
+        assert isinstance(expiry_time, (float, int))
+
+        if expiry_time < get_timestamp_ms():
+            raise Exception("Access token expired")
+
+        return {
+            "sessionHandle": session_handle,
+            "userId": user_id,
+            "refreshTokenHash1": refresh_token_hash_1,
+            "parentRefreshTokenHash1": parent_refresh_token_hash_1,
+            "userData": user_data,
+            "antiCsrfToken": anti_csrf_token,
+            "expiryTime": expiry_time,
+            "timeCreated": time_created,
+            "tenantId": tenant_id,
+        }
+    except Exception as e:
+        log_debug_message(
+            "getInfoFromAccessToken: Returning TRY_REFRESH_TOKEN because access token validation failed - %s",
+            e,
+        )
+        raise_try_refresh_token_exception(e)
+
+
+def validate_access_token_structure(payload: Dict[str, Any], version: int) -> None:
+    if version >= 3:
+        if (
+            not isinstance(payload.get("sub"), str)
+            or not isinstance(payload.get("exp"), (int, float))
+            or not isinstance(payload.get("iat"), (int, float))
+            or not isinstance(payload.get("sessionHandle"), str)
+            or not isinstance(payload.get("refreshTokenHash1"), str)
+        ):
+            log_debug_message(
+                "validateAccessTokenStructure: Access token is using version >= 3"
+            )
+            # The error message below will be logged by the error handler that translates this into a TRY_REFRESH_TOKEN_ERROR
+            raise Exception(
+                "Access token does not contain all the information. Maybe the structure has changed?"
+            )
+
+        if version >= 4:
+            if not isinstance(payload.get("tId"), str):
+                raise Exception(
+                    "Access token does not contain all the information. Maybe the structure has changed?"
+                )
+
+    elif (
+        not isinstance(payload.get("sessionHandle"), str)
+        or payload.get("userData") is None
+        or not isinstance(payload.get("refreshTokenHash1"), str)
+        or not isinstance(payload.get("expiryTime"), (float, int))
+        or not isinstance(payload.get("timeCreated"), (float, int))
+    ):
+        log_debug_message(
+            "validateAccessTokenStructure: Access token is using version < 3"
+        )
+        # The error message below will be logged by the error handler that translates this into a TRY_REFRESH_TOKEN_ERROR
+        raise Exception(
+            "Access token does not contain all the information. Maybe the structure has changed?"
+        )
+
+
+
+
+
+
+
+

Functions

+
+
+def get_info_from_access_token(config: SessionConfig, jwt_info: ParsedJWTInfo, do_anti_csrf_check: bool) +
+
+
+
+ +Expand source code + +
def get_info_from_access_token(
+    config: SessionConfig,
+    jwt_info: ParsedJWTInfo,
+    do_anti_csrf_check: bool,
+):
+    try:
+        payload: Optional[Dict[str, Any]] = None
+        decode_algo = (
+            jwt_info.parsed_header["alg"]
+            if jwt_info.parsed_header is not None
+            else "RS256"
+        )
+
+        if jwt_info.version >= 3:
+            matching_keys = get_latest_keys(config, jwt_info.kid)
+            payload = jwt.decode(  # type: ignore
+                jwt_info.raw_token_string,
+                matching_keys[0].key,  # type: ignore
+                algorithms=[decode_algo],
+                options={"verify_signature": True, "verify_exp": True},
+            )
+        else:
+            # It won't have kid. So we'll have to try the token against all the keys from all the jwk_clients
+            # If any of them work, we'll use that payload
+            for k in get_latest_keys(config):
+                try:
+                    payload = jwt.decode(  # type: ignore
+                        jwt_info.raw_token_string,
+                        k.key,  # type: ignore
+                        algorithms=[decode_algo],
+                        options={"verify_signature": True, "verify_exp": True},
+                    )
+                    break
+                except DecodeError:
+                    pass
+
+        if payload is None:
+            raise DecodeError("Could not decode the token")
+
+        validate_access_token_structure(payload, jwt_info.version)
+
+        if jwt_info.version == 2:
+            user_id = sanitize_string(payload.get("userId"))
+            expiry_time = sanitize_number(payload.get("expiryTime"))
+            time_created = sanitize_number(payload.get("timeCreated"))
+            user_data = payload.get("userData")
+        else:
+            user_id = sanitize_string(payload.get("sub"))
+            expiry_time = sanitize_number(payload.get("exp", 0) * 1000)
+            time_created = sanitize_number(payload.get("iat", 0) * 1000)
+            user_data = payload
+
+        session_handle = sanitize_string(payload.get("sessionHandle"))
+        refresh_token_hash_1 = sanitize_string(payload.get("refreshTokenHash1"))
+        parent_refresh_token_hash_1 = sanitize_string(
+            payload.get("parentRefreshTokenHash1")
+        )
+        anti_csrf_token = sanitize_string(payload.get("antiCsrfToken"))
+        tenant_id = DEFAULT_TENANT_ID
+
+        if jwt_info.version >= 4:
+            tenant_id = sanitize_string(payload.get("tId"))
+
+        if anti_csrf_token is None and do_anti_csrf_check:
+            raise Exception("Access token does not contain the anti-csrf token")
+
+        assert isinstance(expiry_time, (float, int))
+
+        if expiry_time < get_timestamp_ms():
+            raise Exception("Access token expired")
+
+        return {
+            "sessionHandle": session_handle,
+            "userId": user_id,
+            "refreshTokenHash1": refresh_token_hash_1,
+            "parentRefreshTokenHash1": parent_refresh_token_hash_1,
+            "userData": user_data,
+            "antiCsrfToken": anti_csrf_token,
+            "expiryTime": expiry_time,
+            "timeCreated": time_created,
+            "tenantId": tenant_id,
+        }
+    except Exception as e:
+        log_debug_message(
+            "getInfoFromAccessToken: Returning TRY_REFRESH_TOKEN because access token validation failed - %s",
+            e,
+        )
+        raise_try_refresh_token_exception(e)
+
+
+
+def sanitize_number(n: Any) ‑> Union[int, float, None] +
+
+
+
+ +Expand source code + +
def sanitize_number(n: Any) -> Union[Union[int, float], None]:
+    if isinstance(n, (int, float)):
+        return n
+
+    return None
+
+
+
+def sanitize_string(s: Any) ‑> Optional[str] +
+
+
+
+ +Expand source code + +
def sanitize_string(s: Any) -> Union[str, None]:
+    if s == "":
+        return s
+
+    if not isinstance(s, str):
+        return None
+
+    return s.strip()
+
+
+
+def validate_access_token_structure(payload: Dict[str, Any], version: int) ‑> None +
+
+
+
+ +Expand source code + +
def validate_access_token_structure(payload: Dict[str, Any], version: int) -> None:
+    if version >= 3:
+        if (
+            not isinstance(payload.get("sub"), str)
+            or not isinstance(payload.get("exp"), (int, float))
+            or not isinstance(payload.get("iat"), (int, float))
+            or not isinstance(payload.get("sessionHandle"), str)
+            or not isinstance(payload.get("refreshTokenHash1"), str)
+        ):
+            log_debug_message(
+                "validateAccessTokenStructure: Access token is using version >= 3"
+            )
+            # The error message below will be logged by the error handler that translates this into a TRY_REFRESH_TOKEN_ERROR
+            raise Exception(
+                "Access token does not contain all the information. Maybe the structure has changed?"
+            )
+
+        if version >= 4:
+            if not isinstance(payload.get("tId"), str):
+                raise Exception(
+                    "Access token does not contain all the information. Maybe the structure has changed?"
+                )
+
+    elif (
+        not isinstance(payload.get("sessionHandle"), str)
+        or payload.get("userData") is None
+        or not isinstance(payload.get("refreshTokenHash1"), str)
+        or not isinstance(payload.get("expiryTime"), (float, int))
+        or not isinstance(payload.get("timeCreated"), (float, int))
+    ):
+        log_debug_message(
+            "validateAccessTokenStructure: Access token is using version < 3"
+        )
+        # The error message below will be logged by the error handler that translates this into a TRY_REFRESH_TOKEN_ERROR
+        raise Exception(
+            "Access token does not contain all the information. Maybe the structure has changed?"
+        )
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/session/api/implementation.html b/html/supertokens_python/recipe/session/api/implementation.html new file mode 100644 index 000000000..73168803a --- /dev/null +++ b/html/supertokens_python/recipe/session/api/implementation.html @@ -0,0 +1,344 @@ + + + + + + +supertokens_python.recipe.session.api.implementation API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.session.api.implementation

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Callable, List, Optional, Union
+
+from supertokens_python.normalised_url_path import NormalisedURLPath
+from supertokens_python.recipe.session.interfaces import (
+    APIInterface,
+    SessionClaimValidator,
+    SignOutOkayResponse,
+)
+from supertokens_python.types import MaybeAwaitable
+from supertokens_python.utils import normalise_http_method
+
+if TYPE_CHECKING:
+    from supertokens_python.recipe.session.interfaces import APIOptions
+    from ..interfaces import SessionContainer
+
+from typing import Any, Dict
+
+from ..session_request_functions import (
+    get_session_from_request,
+    refresh_session_in_request,
+)
+
+
+class APIImplementation(APIInterface):
+    async def refresh_post(
+        self, api_options: APIOptions, user_context: Dict[str, Any]
+    ) -> SessionContainer:
+        return await refresh_session_in_request(
+            api_options.request,
+            user_context,
+            api_options.config,
+            api_options.recipe_implementation,
+        )
+
+    async def signout_post(
+        self,
+        session: Optional[SessionContainer],
+        api_options: APIOptions,
+        user_context: Dict[str, Any],
+    ) -> SignOutOkayResponse:
+        if session is not None:
+            await session.revoke_session(user_context)
+        return SignOutOkayResponse()
+
+    async def verify_session(
+        self,
+        api_options: APIOptions,
+        anti_csrf_check: Union[bool, None],
+        session_required: bool,
+        check_database: bool,
+        override_global_claim_validators: Optional[
+            Callable[
+                [List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
+                MaybeAwaitable[List[SessionClaimValidator]],
+            ]
+        ],
+        user_context: Dict[str, Any],
+    ) -> Union[SessionContainer, None]:
+        method = normalise_http_method(api_options.request.method())
+        if method in ("options", "trace"):
+            if session_required:
+                raise Exception(f"verify_session cannot be used with {method} method")
+            return None
+        incoming_path = NormalisedURLPath(api_options.request.get_path())
+        refresh_token_path = api_options.config.refresh_token_path
+
+        if incoming_path.equals(refresh_token_path) and method == "post":
+            return await refresh_session_in_request(
+                api_options.request,
+                user_context,
+                api_options.config,
+                api_options.recipe_implementation,
+            )
+
+        return await get_session_from_request(
+            api_options.request,
+            api_options.config,
+            api_options.recipe_implementation,
+            session_required=session_required,
+            anti_csrf_check=anti_csrf_check,
+            check_database=check_database,
+            override_global_claim_validators=override_global_claim_validators,
+            user_context=user_context,
+        )
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class APIImplementation +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class APIImplementation(APIInterface):
+    async def refresh_post(
+        self, api_options: APIOptions, user_context: Dict[str, Any]
+    ) -> SessionContainer:
+        return await refresh_session_in_request(
+            api_options.request,
+            user_context,
+            api_options.config,
+            api_options.recipe_implementation,
+        )
+
+    async def signout_post(
+        self,
+        session: Optional[SessionContainer],
+        api_options: APIOptions,
+        user_context: Dict[str, Any],
+    ) -> SignOutOkayResponse:
+        if session is not None:
+            await session.revoke_session(user_context)
+        return SignOutOkayResponse()
+
+    async def verify_session(
+        self,
+        api_options: APIOptions,
+        anti_csrf_check: Union[bool, None],
+        session_required: bool,
+        check_database: bool,
+        override_global_claim_validators: Optional[
+            Callable[
+                [List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
+                MaybeAwaitable[List[SessionClaimValidator]],
+            ]
+        ],
+        user_context: Dict[str, Any],
+    ) -> Union[SessionContainer, None]:
+        method = normalise_http_method(api_options.request.method())
+        if method in ("options", "trace"):
+            if session_required:
+                raise Exception(f"verify_session cannot be used with {method} method")
+            return None
+        incoming_path = NormalisedURLPath(api_options.request.get_path())
+        refresh_token_path = api_options.config.refresh_token_path
+
+        if incoming_path.equals(refresh_token_path) and method == "post":
+            return await refresh_session_in_request(
+                api_options.request,
+                user_context,
+                api_options.config,
+                api_options.recipe_implementation,
+            )
+
+        return await get_session_from_request(
+            api_options.request,
+            api_options.config,
+            api_options.recipe_implementation,
+            session_required=session_required,
+            anti_csrf_check=anti_csrf_check,
+            check_database=check_database,
+            override_global_claim_validators=override_global_claim_validators,
+            user_context=user_context,
+        )
+
+

Ancestors

+ +

Methods

+
+
+async def refresh_post(self, api_options: APIOptions, user_context: Dict[str, Any]) ‑> SessionContainer +
+
+
+
+ +Expand source code + +
async def refresh_post(
+    self, api_options: APIOptions, user_context: Dict[str, Any]
+) -> SessionContainer:
+    return await refresh_session_in_request(
+        api_options.request,
+        user_context,
+        api_options.config,
+        api_options.recipe_implementation,
+    )
+
+
+
+async def signout_post(self, session: Optional[SessionContainer], api_options: APIOptions, user_context: Dict[str, Any]) ‑> SignOutOkayResponse +
+
+
+
+ +Expand source code + +
async def signout_post(
+    self,
+    session: Optional[SessionContainer],
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+) -> SignOutOkayResponse:
+    if session is not None:
+        await session.revoke_session(user_context)
+    return SignOutOkayResponse()
+
+
+
+async def verify_session(self, api_options: APIOptions, anti_csrf_check: Union[bool, None], session_required: bool, check_database: bool, override_global_claim_validators: Optional[Callable[[List[SessionClaimValidator], SessionContainer, Dict[str, Any]], MaybeAwaitable[List[SessionClaimValidator]]]], user_context: Dict[str, Any]) ‑> Union[SessionContainer, None] +
+
+
+
+ +Expand source code + +
async def verify_session(
+    self,
+    api_options: APIOptions,
+    anti_csrf_check: Union[bool, None],
+    session_required: bool,
+    check_database: bool,
+    override_global_claim_validators: Optional[
+        Callable[
+            [List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
+            MaybeAwaitable[List[SessionClaimValidator]],
+        ]
+    ],
+    user_context: Dict[str, Any],
+) -> Union[SessionContainer, None]:
+    method = normalise_http_method(api_options.request.method())
+    if method in ("options", "trace"):
+        if session_required:
+            raise Exception(f"verify_session cannot be used with {method} method")
+        return None
+    incoming_path = NormalisedURLPath(api_options.request.get_path())
+    refresh_token_path = api_options.config.refresh_token_path
+
+    if incoming_path.equals(refresh_token_path) and method == "post":
+        return await refresh_session_in_request(
+            api_options.request,
+            user_context,
+            api_options.config,
+            api_options.recipe_implementation,
+        )
+
+    return await get_session_from_request(
+        api_options.request,
+        api_options.config,
+        api_options.recipe_implementation,
+        session_required=session_required,
+        anti_csrf_check=anti_csrf_check,
+        check_database=check_database,
+        override_global_claim_validators=override_global_claim_validators,
+        user_context=user_context,
+    )
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/session/api/index.html b/html/supertokens_python/recipe/session/api/index.html new file mode 100644 index 000000000..f8c809cfb --- /dev/null +++ b/html/supertokens_python/recipe/session/api/index.html @@ -0,0 +1,95 @@ + + + + + + +supertokens_python.recipe.session.api API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.session.api

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from .refresh import handle_refresh_api  # type: ignore
+from .signout import handle_signout_api  # type: ignore
+
+
+
+

Sub-modules

+
+
supertokens_python.recipe.session.api.implementation
+
+
+
+
supertokens_python.recipe.session.api.refresh
+
+
+
+
supertokens_python.recipe.session.api.signout
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/session/api/refresh.html b/html/supertokens_python/recipe/session/api/refresh.html new file mode 100644 index 000000000..621365eb0 --- /dev/null +++ b/html/supertokens_python/recipe/session/api/refresh.html @@ -0,0 +1,130 @@ + + + + + + +supertokens_python.recipe.session.api.refresh API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.session.api.refresh

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Any, Dict
+
+if TYPE_CHECKING:
+    from supertokens_python.recipe.session.interfaces import APIInterface, APIOptions
+
+from supertokens_python.utils import send_200_response
+
+
+async def handle_refresh_api(
+    api_implementation: APIInterface,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+):
+    if (
+        api_implementation.disable_refresh_post
+        or api_implementation.refresh_post is None
+    ):
+        return None
+
+    await api_implementation.refresh_post(api_options, user_context)
+    if api_options.response is None:
+        raise Exception("Should never come here")
+    return send_200_response({}, api_options.response)
+
+
+
+
+
+
+
+

Functions

+
+
+async def handle_refresh_api(api_implementation: APIInterface, api_options: APIOptions, user_context: Dict[str, Any]) +
+
+
+
+ +Expand source code + +
async def handle_refresh_api(
+    api_implementation: APIInterface,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+):
+    if (
+        api_implementation.disable_refresh_post
+        or api_implementation.refresh_post is None
+    ):
+        return None
+
+    await api_implementation.refresh_post(api_options, user_context)
+    if api_options.response is None:
+        raise Exception("Should never come here")
+    return send_200_response({}, api_options.response)
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/session/api/signout.html b/html/supertokens_python/recipe/session/api/signout.html new file mode 100644 index 000000000..aa44282dc --- /dev/null +++ b/html/supertokens_python/recipe/session/api/signout.html @@ -0,0 +1,155 @@ + + + + + + +supertokens_python.recipe.session.api.signout API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.session.api.signout

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Any, Dict
+
+from supertokens_python.recipe.session.session_request_functions import (
+    get_session_from_request,
+)
+
+if TYPE_CHECKING:
+    from supertokens_python.recipe.session.interfaces import (
+        APIInterface,
+        APIOptions,
+    )
+
+from supertokens_python.utils import send_200_response
+
+
+async def handle_signout_api(
+    api_implementation: APIInterface,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+):
+    if (
+        api_implementation.disable_signout_post
+        or api_implementation.signout_post is None
+    ):
+        return None
+
+    session = await get_session_from_request(
+        api_options.request,
+        api_options.config,
+        api_options.recipe_implementation,
+        session_required=True,
+        override_global_claim_validators=lambda _, __, ___: [],
+        user_context=user_context,
+    )
+
+    response = await api_implementation.signout_post(session, api_options, user_context)
+    if api_options.response is None:
+        raise Exception("Should never come here")
+    return send_200_response(response.to_json(), api_options.response)
+
+
+
+
+
+
+
+

Functions

+
+
+async def handle_signout_api(api_implementation: APIInterface, api_options: APIOptions, user_context: Dict[str, Any]) +
+
+
+
+ +Expand source code + +
async def handle_signout_api(
+    api_implementation: APIInterface,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+):
+    if (
+        api_implementation.disable_signout_post
+        or api_implementation.signout_post is None
+    ):
+        return None
+
+    session = await get_session_from_request(
+        api_options.request,
+        api_options.config,
+        api_options.recipe_implementation,
+        session_required=True,
+        override_global_claim_validators=lambda _, __, ___: [],
+        user_context=user_context,
+    )
+
+    response = await api_implementation.signout_post(session, api_options, user_context)
+    if api_options.response is None:
+        raise Exception("Should never come here")
+    return send_200_response(response.to_json(), api_options.response)
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/session/asyncio/index.html b/html/supertokens_python/recipe/session/asyncio/index.html new file mode 100644 index 000000000..4b08c49d3 --- /dev/null +++ b/html/supertokens_python/recipe/session/asyncio/index.html @@ -0,0 +1,1330 @@ + + + + + + +supertokens_python.recipe.session.asyncio API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.session.asyncio

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+from typing import Any, Callable, Dict, List, Optional, TypeVar, Union
+
+from supertokens_python.recipe.openid.interfaces import (
+    GetOpenIdDiscoveryConfigurationResult,
+)
+from supertokens_python.recipe.session.interfaces import (
+    ClaimsValidationResult,
+    GetClaimValueOkResult,
+    JSONObject,
+    SessionClaim,
+    SessionClaimValidator,
+    SessionContainer,
+    SessionDoesNotExistError,
+    SessionInformationResult,
+)
+from supertokens_python.recipe.session.recipe import SessionRecipe
+from supertokens_python.types import MaybeAwaitable
+from supertokens_python.utils import FRAMEWORKS, resolve
+
+from ...jwt.interfaces import (
+    CreateJwtOkResult,
+    CreateJwtResultUnsupportedAlgorithm,
+    GetJWKSResult,
+)
+from ..session_request_functions import (
+    create_new_session_in_request,
+    get_session_from_request,
+    refresh_session_in_request,
+)
+from ..constants import protected_props
+from ..utils import get_required_claim_validators
+
+from supertokens_python.recipe.multitenancy.constants import DEFAULT_TENANT_ID
+
+_T = TypeVar("_T")
+
+
+async def create_new_session(
+    request: Any,
+    tenant_id: str,
+    user_id: str,
+    access_token_payload: Union[Dict[str, Any], None] = None,
+    session_data_in_database: Union[Dict[str, Any], None] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> SessionContainer:
+    if user_context is None:
+        user_context = {}
+    if session_data_in_database is None:
+        session_data_in_database = {}
+    if access_token_payload is None:
+        access_token_payload = {}
+
+    recipe_instance = SessionRecipe.get_instance()
+    config = recipe_instance.config
+    app_info = recipe_instance.app_info
+
+    return await create_new_session_in_request(
+        request,
+        user_context,
+        recipe_instance,
+        access_token_payload,
+        user_id,
+        config,
+        app_info,
+        session_data_in_database,
+        tenant_id,
+    )
+
+
+async def create_new_session_without_request_response(
+    tenant_id: str,
+    user_id: str,
+    access_token_payload: Union[Dict[str, Any], None] = None,
+    session_data_in_database: Union[Dict[str, Any], None] = None,
+    disable_anti_csrf: bool = False,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> SessionContainer:
+    if user_context is None:
+        user_context = {}
+    if session_data_in_database is None:
+        session_data_in_database = {}
+    if access_token_payload is None:
+        access_token_payload = {}
+
+    claims_added_by_other_recipes = (
+        SessionRecipe.get_instance().get_claims_added_by_other_recipes()
+    )
+    app_info = SessionRecipe.get_instance().app_info
+    issuer = (
+        app_info.api_domain.get_as_string_dangerous()
+        + app_info.api_base_path.get_as_string_dangerous()
+    )
+
+    final_access_token_payload = {**access_token_payload, "iss": issuer}
+
+    for prop in protected_props:
+        if prop in final_access_token_payload:
+            del final_access_token_payload[prop]
+
+    for claim in claims_added_by_other_recipes:
+        update = await claim.build(user_id, tenant_id, user_context)
+        final_access_token_payload = {**final_access_token_payload, **update}
+
+    return await SessionRecipe.get_instance().recipe_implementation.create_new_session(
+        user_id,
+        final_access_token_payload,
+        session_data_in_database,
+        disable_anti_csrf,
+        tenant_id,
+        user_context=user_context,
+    )
+
+
+async def validate_claims_for_session_handle(
+    session_handle: str,
+    override_global_claim_validators: Optional[
+        Callable[
+            [
+                List[SessionClaimValidator],
+                SessionInformationResult,
+                Dict[str, Any],
+            ],
+            MaybeAwaitable[List[SessionClaimValidator]],
+        ]
+    ] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> Union[SessionDoesNotExistError, ClaimsValidationResult]:
+    if user_context is None:
+        user_context = {}
+
+    recipe_impl = SessionRecipe.get_instance().recipe_implementation
+    session_info = await recipe_impl.get_session_information(
+        session_handle, user_context
+    )
+
+    if session_info is None:
+        return SessionDoesNotExistError()
+
+    claim_validators_added_by_other_recipes = (
+        SessionRecipe.get_instance().get_claim_validators_added_by_other_recipes()
+    )
+    global_claim_validators = await resolve(
+        recipe_impl.get_global_claim_validators(
+            session_info.tenant_id,
+            session_info.user_id,
+            claim_validators_added_by_other_recipes,
+            user_context,
+        )
+    )
+
+    if override_global_claim_validators is not None:
+        claim_validators = await resolve(
+            override_global_claim_validators(
+                global_claim_validators, session_info, user_context
+            )
+        )
+    else:
+        claim_validators = global_claim_validators
+
+    claim_validation_res = await recipe_impl.validate_claims(
+        session_info.user_id,
+        session_info.custom_claims_in_access_token_payload,
+        claim_validators,
+        user_context,
+    )
+
+    if claim_validation_res.access_token_payload_update is not None:
+        updated = await recipe_impl.merge_into_access_token_payload(
+            session_handle,
+            claim_validation_res.access_token_payload_update,
+            user_context,
+        )
+        if not updated:
+            return SessionDoesNotExistError()
+
+    return ClaimsValidationResult(claim_validation_res.invalid_claims)
+
+
+async def validate_claims_in_jwt_payload(
+    tenant_id: str,
+    user_id: str,
+    jwt_payload: JSONObject,
+    override_global_claim_validators: Optional[
+        Callable[
+            [
+                List[SessionClaimValidator],
+                str,
+                Dict[str, Any],
+            ],
+            MaybeAwaitable[List[SessionClaimValidator]],
+        ]
+    ] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+):
+    if user_context is None:
+        user_context = {}
+
+    recipe_impl = SessionRecipe.get_instance().recipe_implementation
+
+    claim_validators_added_by_other_recipes = (
+        SessionRecipe.get_instance().get_claim_validators_added_by_other_recipes()
+    )
+    global_claim_validators = await resolve(
+        recipe_impl.get_global_claim_validators(
+            tenant_id,
+            user_id,
+            claim_validators_added_by_other_recipes,
+            user_context,
+        )
+    )
+
+    if override_global_claim_validators is not None:
+        claim_validators = await resolve(
+            override_global_claim_validators(
+                global_claim_validators, user_id, user_context
+            )
+        )
+    else:
+        claim_validators = global_claim_validators
+
+    return await recipe_impl.validate_claims_in_jwt_payload(
+        user_id, jwt_payload, claim_validators, user_context
+    )
+
+
+async def fetch_and_set_claim(
+    session_handle: str,
+    claim: SessionClaim[Any],
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> bool:
+    if user_context is None:
+        user_context = {}
+    return await SessionRecipe.get_instance().recipe_implementation.fetch_and_set_claim(
+        session_handle, claim, user_context
+    )
+
+
+async def get_claim_value(
+    session_handle: str,
+    claim: SessionClaim[_T],
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> Union[SessionDoesNotExistError, GetClaimValueOkResult[_T]]:
+    if user_context is None:
+        user_context = {}
+    return await SessionRecipe.get_instance().recipe_implementation.get_claim_value(
+        session_handle, claim, user_context
+    )
+
+
+async def set_claim_value(
+    session_handle: str,
+    claim: SessionClaim[_T],
+    value: _T,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> bool:
+    if user_context is None:
+        user_context = {}
+    return await SessionRecipe.get_instance().recipe_implementation.set_claim_value(
+        session_handle, claim, value, user_context
+    )
+
+
+async def remove_claim(
+    session_handle: str,
+    claim: SessionClaim[Any],
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> bool:
+    if user_context is None:
+        user_context = {}
+    return await SessionRecipe.get_instance().recipe_implementation.remove_claim(
+        session_handle, claim, user_context
+    )
+
+
+async def get_session(
+    request: Any,
+    session_required: Optional[bool] = None,
+    anti_csrf_check: Optional[bool] = None,
+    check_database: Optional[bool] = None,
+    override_global_claim_validators: Optional[
+        Callable[
+            [List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
+            MaybeAwaitable[List[SessionClaimValidator]],
+        ]
+    ] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> Union[SessionContainer, None]:
+    if user_context is None:
+        user_context = {}
+
+    if session_required is None:
+        session_required = True
+
+    recipe_instance = SessionRecipe.get_instance()
+    recipe_interface_impl = recipe_instance.recipe_implementation
+    config = recipe_instance.config
+
+    return await get_session_from_request(
+        request,
+        config,
+        recipe_interface_impl,
+        session_required=session_required,
+        anti_csrf_check=anti_csrf_check,
+        check_database=check_database,
+        override_global_claim_validators=override_global_claim_validators,
+        user_context=user_context,
+    )
+
+
+async def get_session_without_request_response(
+    access_token: str,
+    anti_csrf_token: Optional[str] = None,
+    anti_csrf_check: Optional[bool] = None,
+    session_required: Optional[bool] = None,
+    check_database: Optional[bool] = None,
+    override_global_claim_validators: Optional[
+        Callable[
+            [List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
+            MaybeAwaitable[List[SessionClaimValidator]],
+        ]
+    ] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> Optional[SessionContainer]:
+    """Tries to validate an access token and build a Session object from it.
+
+    Notes about anti-csrf checking:
+    - if the `antiCsrf` is set to VIA_HEADER in the Session recipe config you have to handle anti-csrf checking before calling this function and set antiCsrfCheck to false in the options.
+    - you can disable anti-csrf checks by setting antiCsrf to NONE in the Session recipe config. We only recommend this if you are always getting the access-token from the Authorization header.
+    - if the antiCsrf check fails the returned status will be TRY_REFRESH_TOKEN_ERROR
+
+    Args:
+    - access_token: The access token extracted from the authorization header or cookies
+    - anti_csrf_token: The anti-csrf token extracted from the authorization header or cookies. Can be undefined if antiCsrfCheck is false
+    - anti_csrf_check: If true, anti-csrf checking will be done. If false, it will be skipped. Default behaviour is to check.
+    - session_required: If true, throws an error if the session does not exist. Default is True.
+    - check_database: If true, the session will be checked in the database. If false, it will be skipped. Default behaviour is to skip.
+    - override_global_claim_validators: Alter the
+    - user_context: user context
+
+    Results:
+    - OK: The session was successfully validated, including claim validation
+    - CLAIM_VALIDATION_ERROR: While the access token is valid, one or more claim validators have failed. Our frontend SDKs expect a 403 response the contents matching the value returned from this function.
+    - TRY_REFRESH_TOKEN_ERROR: This means, that the access token structure was valid, but it didn't pass validation for some reason and the user should call the refresh API.
+        You can send a 401 response to trigger this behaviour if you are using our frontend SDKs
+    - UNAUTHORISED: This means that the access token likely doesn't belong to a SuperTokens session. If this is unexpected, it's best handled by sending a 401 response.
+    """
+    if user_context is None:
+        user_context = {}
+
+    if session_required is None:
+        session_required = True
+
+    recipe_interface_impl = SessionRecipe.get_instance().recipe_implementation
+
+    session = await recipe_interface_impl.get_session(
+        access_token,
+        anti_csrf_token,
+        anti_csrf_check,
+        session_required,
+        check_database,
+        override_global_claim_validators,
+        user_context,
+    )
+
+    if session is not None:
+        claim_validators = await get_required_claim_validators(
+            session, override_global_claim_validators, user_context
+        )
+        await session.assert_claims(claim_validators, user_context)
+
+    return session
+
+
+async def refresh_session(
+    request: Any,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> SessionContainer:
+    if user_context is None:
+        user_context = {}
+
+    if not hasattr(request, "wrapper_used") or not request.wrapper_used:
+        request = FRAMEWORKS[
+            SessionRecipe.get_instance().app_info.framework
+        ].wrap_request(request)
+
+    recipe_instance = SessionRecipe.get_instance()
+    config = recipe_instance.config
+    recipe_interface_impl = recipe_instance.recipe_implementation
+
+    return await refresh_session_in_request(
+        request,
+        user_context,
+        config,
+        recipe_interface_impl,
+    )
+
+
+async def refresh_session_without_request_response(
+    refresh_token: str,
+    disable_anti_csrf: bool = False,
+    anti_csrf_token: Optional[str] = None,
+    user_context: Optional[Dict[str, Any]] = None,
+) -> SessionContainer:
+    if user_context is None:
+        user_context = {}
+
+    return await SessionRecipe.get_instance().recipe_implementation.refresh_session(
+        refresh_token, anti_csrf_token, disable_anti_csrf, user_context
+    )
+
+
+async def revoke_session(
+    session_handle: str, user_context: Union[None, Dict[str, Any]] = None
+) -> bool:
+    if user_context is None:
+        user_context = {}
+    return await SessionRecipe.get_instance().recipe_implementation.revoke_session(
+        session_handle, user_context
+    )
+
+
+async def revoke_all_sessions_for_user(
+    user_id: str,
+    tenant_id: Optional[str] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> List[str]:
+    if user_context is None:
+        user_context = {}
+    return await SessionRecipe.get_instance().recipe_implementation.revoke_all_sessions_for_user(
+        user_id, tenant_id or DEFAULT_TENANT_ID, tenant_id is None, user_context
+    )
+
+
+async def get_all_session_handles_for_user(
+    user_id: str,
+    tenant_id: Optional[str] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> List[str]:
+    if user_context is None:
+        user_context = {}
+    return await SessionRecipe.get_instance().recipe_implementation.get_all_session_handles_for_user(
+        user_id, tenant_id or DEFAULT_TENANT_ID, tenant_id is None, user_context
+    )
+
+
+async def revoke_multiple_sessions(
+    session_handles: List[str], user_context: Union[None, Dict[str, Any]] = None
+) -> List[str]:
+    if user_context is None:
+        user_context = {}
+    return await SessionRecipe.get_instance().recipe_implementation.revoke_multiple_sessions(
+        session_handles, user_context
+    )
+
+
+async def get_session_information(
+    session_handle: str, user_context: Union[None, Dict[str, Any]] = None
+) -> Union[SessionInformationResult, None]:
+    if user_context is None:
+        user_context = {}
+    return await SessionRecipe.get_instance().recipe_implementation.get_session_information(
+        session_handle, user_context
+    )
+
+
+async def update_session_data_in_database(
+    session_handle: str,
+    new_session_data: Dict[str, Any],
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> bool:
+    if user_context is None:
+        user_context = {}
+    return await SessionRecipe.get_instance().recipe_implementation.update_session_data_in_database(
+        session_handle, new_session_data, user_context
+    )
+
+
+async def merge_into_access_token_payload(
+    session_handle: str,
+    new_access_token_payload: Dict[str, Any],
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> bool:
+    if user_context is None:
+        user_context = {}
+
+    return await SessionRecipe.get_instance().recipe_implementation.merge_into_access_token_payload(
+        session_handle, new_access_token_payload, user_context
+    )
+
+
+async def create_jwt(
+    payload: Dict[str, Any],
+    validity_seconds: Optional[int] = None,
+    use_static_signing_key: Optional[bool] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> Union[CreateJwtOkResult, CreateJwtResultUnsupportedAlgorithm]:
+    if user_context is None:
+        user_context = {}
+    openid_recipe = SessionRecipe.get_instance().openid_recipe
+
+    return await openid_recipe.recipe_implementation.create_jwt(
+        payload, validity_seconds, use_static_signing_key, user_context
+    )
+
+
+async def get_jwks(user_context: Union[None, Dict[str, Any]] = None) -> GetJWKSResult:
+    if user_context is None:
+        user_context = {}
+    openid_recipe = SessionRecipe.get_instance().openid_recipe
+    return await openid_recipe.recipe_implementation.get_jwks(user_context)
+
+
+async def get_open_id_discovery_configuration(
+    user_context: Union[None, Dict[str, Any]] = None
+) -> GetOpenIdDiscoveryConfigurationResult:
+    if user_context is None:
+        user_context = {}
+    openid_recipe = SessionRecipe.get_instance().openid_recipe
+
+    return (
+        await openid_recipe.recipe_implementation.get_open_id_discovery_configuration(
+            user_context
+        )
+    )
+
+
+
+
+
+
+
+

Functions

+
+
+async def create_jwt(payload: Dict[str, Any], validity_seconds: Optional[int] = None, use_static_signing_key: Optional[bool] = None, user_context: Union[None, Dict[str, Any]] = None) ‑> Union[CreateJwtOkResultCreateJwtResultUnsupportedAlgorithm] +
+
+
+
+ +Expand source code + +
async def create_jwt(
+    payload: Dict[str, Any],
+    validity_seconds: Optional[int] = None,
+    use_static_signing_key: Optional[bool] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> Union[CreateJwtOkResult, CreateJwtResultUnsupportedAlgorithm]:
+    if user_context is None:
+        user_context = {}
+    openid_recipe = SessionRecipe.get_instance().openid_recipe
+
+    return await openid_recipe.recipe_implementation.create_jwt(
+        payload, validity_seconds, use_static_signing_key, user_context
+    )
+
+
+
+async def create_new_session(request: Any, tenant_id: str, user_id: str, access_token_payload: Union[Dict[str, Any], None] = None, session_data_in_database: Union[Dict[str, Any], None] = None, user_context: Union[None, Dict[str, Any]] = None) ‑> SessionContainer +
+
+
+
+ +Expand source code + +
async def create_new_session(
+    request: Any,
+    tenant_id: str,
+    user_id: str,
+    access_token_payload: Union[Dict[str, Any], None] = None,
+    session_data_in_database: Union[Dict[str, Any], None] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> SessionContainer:
+    if user_context is None:
+        user_context = {}
+    if session_data_in_database is None:
+        session_data_in_database = {}
+    if access_token_payload is None:
+        access_token_payload = {}
+
+    recipe_instance = SessionRecipe.get_instance()
+    config = recipe_instance.config
+    app_info = recipe_instance.app_info
+
+    return await create_new_session_in_request(
+        request,
+        user_context,
+        recipe_instance,
+        access_token_payload,
+        user_id,
+        config,
+        app_info,
+        session_data_in_database,
+        tenant_id,
+    )
+
+
+
+async def create_new_session_without_request_response(tenant_id: str, user_id: str, access_token_payload: Union[Dict[str, Any], None] = None, session_data_in_database: Union[Dict[str, Any], None] = None, disable_anti_csrf: bool = False, user_context: Union[None, Dict[str, Any]] = None) ‑> SessionContainer +
+
+
+
+ +Expand source code + +
async def create_new_session_without_request_response(
+    tenant_id: str,
+    user_id: str,
+    access_token_payload: Union[Dict[str, Any], None] = None,
+    session_data_in_database: Union[Dict[str, Any], None] = None,
+    disable_anti_csrf: bool = False,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> SessionContainer:
+    if user_context is None:
+        user_context = {}
+    if session_data_in_database is None:
+        session_data_in_database = {}
+    if access_token_payload is None:
+        access_token_payload = {}
+
+    claims_added_by_other_recipes = (
+        SessionRecipe.get_instance().get_claims_added_by_other_recipes()
+    )
+    app_info = SessionRecipe.get_instance().app_info
+    issuer = (
+        app_info.api_domain.get_as_string_dangerous()
+        + app_info.api_base_path.get_as_string_dangerous()
+    )
+
+    final_access_token_payload = {**access_token_payload, "iss": issuer}
+
+    for prop in protected_props:
+        if prop in final_access_token_payload:
+            del final_access_token_payload[prop]
+
+    for claim in claims_added_by_other_recipes:
+        update = await claim.build(user_id, tenant_id, user_context)
+        final_access_token_payload = {**final_access_token_payload, **update}
+
+    return await SessionRecipe.get_instance().recipe_implementation.create_new_session(
+        user_id,
+        final_access_token_payload,
+        session_data_in_database,
+        disable_anti_csrf,
+        tenant_id,
+        user_context=user_context,
+    )
+
+
+
+async def fetch_and_set_claim(session_handle: str, claim: SessionClaim[Any], user_context: Union[None, Dict[str, Any]] = None) ‑> bool +
+
+
+
+ +Expand source code + +
async def fetch_and_set_claim(
+    session_handle: str,
+    claim: SessionClaim[Any],
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> bool:
+    if user_context is None:
+        user_context = {}
+    return await SessionRecipe.get_instance().recipe_implementation.fetch_and_set_claim(
+        session_handle, claim, user_context
+    )
+
+
+
+async def get_all_session_handles_for_user(user_id: str, tenant_id: Optional[str] = None, user_context: Union[None, Dict[str, Any]] = None) ‑> List[str] +
+
+
+
+ +Expand source code + +
async def get_all_session_handles_for_user(
+    user_id: str,
+    tenant_id: Optional[str] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> List[str]:
+    if user_context is None:
+        user_context = {}
+    return await SessionRecipe.get_instance().recipe_implementation.get_all_session_handles_for_user(
+        user_id, tenant_id or DEFAULT_TENANT_ID, tenant_id is None, user_context
+    )
+
+
+
+async def get_claim_value(session_handle: str, claim: SessionClaim[_T], user_context: Union[None, Dict[str, Any]] = None) ‑> Union[SessionDoesNotExistErrorGetClaimValueOkResult[~_T]] +
+
+
+
+ +Expand source code + +
async def get_claim_value(
+    session_handle: str,
+    claim: SessionClaim[_T],
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> Union[SessionDoesNotExistError, GetClaimValueOkResult[_T]]:
+    if user_context is None:
+        user_context = {}
+    return await SessionRecipe.get_instance().recipe_implementation.get_claim_value(
+        session_handle, claim, user_context
+    )
+
+
+
+async def get_jwks(user_context: Union[None, Dict[str, Any]] = None) ‑> GetJWKSResult +
+
+
+
+ +Expand source code + +
async def get_jwks(user_context: Union[None, Dict[str, Any]] = None) -> GetJWKSResult:
+    if user_context is None:
+        user_context = {}
+    openid_recipe = SessionRecipe.get_instance().openid_recipe
+    return await openid_recipe.recipe_implementation.get_jwks(user_context)
+
+
+
+async def get_open_id_discovery_configuration(user_context: Union[None, Dict[str, Any]] = None) ‑> GetOpenIdDiscoveryConfigurationResult +
+
+
+
+ +Expand source code + +
async def get_open_id_discovery_configuration(
+    user_context: Union[None, Dict[str, Any]] = None
+) -> GetOpenIdDiscoveryConfigurationResult:
+    if user_context is None:
+        user_context = {}
+    openid_recipe = SessionRecipe.get_instance().openid_recipe
+
+    return (
+        await openid_recipe.recipe_implementation.get_open_id_discovery_configuration(
+            user_context
+        )
+    )
+
+
+
+async def get_session(request: Any, session_required: Optional[bool] = None, anti_csrf_check: Optional[bool] = None, check_database: Optional[bool] = None, override_global_claim_validators: Optional[Callable[[List[SessionClaimValidator], SessionContainer, Dict[str, Any]], MaybeAwaitable[List[SessionClaimValidator]]]] = None, user_context: Union[None, Dict[str, Any]] = None) ‑> Optional[SessionContainer] +
+
+
+
+ +Expand source code + +
async def get_session(
+    request: Any,
+    session_required: Optional[bool] = None,
+    anti_csrf_check: Optional[bool] = None,
+    check_database: Optional[bool] = None,
+    override_global_claim_validators: Optional[
+        Callable[
+            [List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
+            MaybeAwaitable[List[SessionClaimValidator]],
+        ]
+    ] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> Union[SessionContainer, None]:
+    if user_context is None:
+        user_context = {}
+
+    if session_required is None:
+        session_required = True
+
+    recipe_instance = SessionRecipe.get_instance()
+    recipe_interface_impl = recipe_instance.recipe_implementation
+    config = recipe_instance.config
+
+    return await get_session_from_request(
+        request,
+        config,
+        recipe_interface_impl,
+        session_required=session_required,
+        anti_csrf_check=anti_csrf_check,
+        check_database=check_database,
+        override_global_claim_validators=override_global_claim_validators,
+        user_context=user_context,
+    )
+
+
+
+async def get_session_information(session_handle: str, user_context: Union[None, Dict[str, Any]] = None) ‑> Optional[SessionInformationResult] +
+
+
+
+ +Expand source code + +
async def get_session_information(
+    session_handle: str, user_context: Union[None, Dict[str, Any]] = None
+) -> Union[SessionInformationResult, None]:
+    if user_context is None:
+        user_context = {}
+    return await SessionRecipe.get_instance().recipe_implementation.get_session_information(
+        session_handle, user_context
+    )
+
+
+
+async def get_session_without_request_response(access_token: str, anti_csrf_token: Optional[str] = None, anti_csrf_check: Optional[bool] = None, session_required: Optional[bool] = None, check_database: Optional[bool] = None, override_global_claim_validators: Optional[Callable[[List[SessionClaimValidator], SessionContainer, Dict[str, Any]], MaybeAwaitable[List[SessionClaimValidator]]]] = None, user_context: Union[None, Dict[str, Any]] = None) ‑> Optional[SessionContainer] +
+
+

Tries to validate an access token and build a Session object from it.

+

Notes about anti-csrf checking: +- if the antiCsrf is set to VIA_HEADER in the Session recipe config you have to handle anti-csrf checking before calling this function and set antiCsrfCheck to false in the options. +- you can disable anti-csrf checks by setting antiCsrf to NONE in the Session recipe config. We only recommend this if you are always getting the access-token from the Authorization header. +- if the antiCsrf check fails the returned status will be TRY_REFRESH_TOKEN_ERROR

+

Args: +- access_token: The access token extracted from the authorization header or cookies +- anti_csrf_token: The anti-csrf token extracted from the authorization header or cookies. Can be undefined if antiCsrfCheck is false +- anti_csrf_check: If true, anti-csrf checking will be done. If false, it will be skipped. Default behaviour is to check. +- session_required: If true, throws an error if the session does not exist. Default is True. +- check_database: If true, the session will be checked in the database. If false, it will be skipped. Default behaviour is to skip. +- override_global_claim_validators: Alter the +- user_context: user context

+

Results: +- OK: The session was successfully validated, including claim validation +- CLAIM_VALIDATION_ERROR: While the access token is valid, one or more claim validators have failed. Our frontend SDKs expect a 403 response the contents matching the value returned from this function. +- TRY_REFRESH_TOKEN_ERROR: This means, that the access token structure was valid, but it didn't pass validation for some reason and the user should call the refresh API. +You can send a 401 response to trigger this behaviour if you are using our frontend SDKs +- UNAUTHORISED: This means that the access token likely doesn't belong to a SuperTokens session. If this is unexpected, it's best handled by sending a 401 response.

+
+ +Expand source code + +
async def get_session_without_request_response(
+    access_token: str,
+    anti_csrf_token: Optional[str] = None,
+    anti_csrf_check: Optional[bool] = None,
+    session_required: Optional[bool] = None,
+    check_database: Optional[bool] = None,
+    override_global_claim_validators: Optional[
+        Callable[
+            [List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
+            MaybeAwaitable[List[SessionClaimValidator]],
+        ]
+    ] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> Optional[SessionContainer]:
+    """Tries to validate an access token and build a Session object from it.
+
+    Notes about anti-csrf checking:
+    - if the `antiCsrf` is set to VIA_HEADER in the Session recipe config you have to handle anti-csrf checking before calling this function and set antiCsrfCheck to false in the options.
+    - you can disable anti-csrf checks by setting antiCsrf to NONE in the Session recipe config. We only recommend this if you are always getting the access-token from the Authorization header.
+    - if the antiCsrf check fails the returned status will be TRY_REFRESH_TOKEN_ERROR
+
+    Args:
+    - access_token: The access token extracted from the authorization header or cookies
+    - anti_csrf_token: The anti-csrf token extracted from the authorization header or cookies. Can be undefined if antiCsrfCheck is false
+    - anti_csrf_check: If true, anti-csrf checking will be done. If false, it will be skipped. Default behaviour is to check.
+    - session_required: If true, throws an error if the session does not exist. Default is True.
+    - check_database: If true, the session will be checked in the database. If false, it will be skipped. Default behaviour is to skip.
+    - override_global_claim_validators: Alter the
+    - user_context: user context
+
+    Results:
+    - OK: The session was successfully validated, including claim validation
+    - CLAIM_VALIDATION_ERROR: While the access token is valid, one or more claim validators have failed. Our frontend SDKs expect a 403 response the contents matching the value returned from this function.
+    - TRY_REFRESH_TOKEN_ERROR: This means, that the access token structure was valid, but it didn't pass validation for some reason and the user should call the refresh API.
+        You can send a 401 response to trigger this behaviour if you are using our frontend SDKs
+    - UNAUTHORISED: This means that the access token likely doesn't belong to a SuperTokens session. If this is unexpected, it's best handled by sending a 401 response.
+    """
+    if user_context is None:
+        user_context = {}
+
+    if session_required is None:
+        session_required = True
+
+    recipe_interface_impl = SessionRecipe.get_instance().recipe_implementation
+
+    session = await recipe_interface_impl.get_session(
+        access_token,
+        anti_csrf_token,
+        anti_csrf_check,
+        session_required,
+        check_database,
+        override_global_claim_validators,
+        user_context,
+    )
+
+    if session is not None:
+        claim_validators = await get_required_claim_validators(
+            session, override_global_claim_validators, user_context
+        )
+        await session.assert_claims(claim_validators, user_context)
+
+    return session
+
+
+
+async def merge_into_access_token_payload(session_handle: str, new_access_token_payload: Dict[str, Any], user_context: Union[None, Dict[str, Any]] = None) ‑> bool +
+
+
+
+ +Expand source code + +
async def merge_into_access_token_payload(
+    session_handle: str,
+    new_access_token_payload: Dict[str, Any],
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> bool:
+    if user_context is None:
+        user_context = {}
+
+    return await SessionRecipe.get_instance().recipe_implementation.merge_into_access_token_payload(
+        session_handle, new_access_token_payload, user_context
+    )
+
+
+
+async def refresh_session(request: Any, user_context: Union[None, Dict[str, Any]] = None) ‑> SessionContainer +
+
+
+
+ +Expand source code + +
async def refresh_session(
+    request: Any,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> SessionContainer:
+    if user_context is None:
+        user_context = {}
+
+    if not hasattr(request, "wrapper_used") or not request.wrapper_used:
+        request = FRAMEWORKS[
+            SessionRecipe.get_instance().app_info.framework
+        ].wrap_request(request)
+
+    recipe_instance = SessionRecipe.get_instance()
+    config = recipe_instance.config
+    recipe_interface_impl = recipe_instance.recipe_implementation
+
+    return await refresh_session_in_request(
+        request,
+        user_context,
+        config,
+        recipe_interface_impl,
+    )
+
+
+
+async def refresh_session_without_request_response(refresh_token: str, disable_anti_csrf: bool = False, anti_csrf_token: Optional[str] = None, user_context: Optional[Dict[str, Any]] = None) ‑> SessionContainer +
+
+
+
+ +Expand source code + +
async def refresh_session_without_request_response(
+    refresh_token: str,
+    disable_anti_csrf: bool = False,
+    anti_csrf_token: Optional[str] = None,
+    user_context: Optional[Dict[str, Any]] = None,
+) -> SessionContainer:
+    if user_context is None:
+        user_context = {}
+
+    return await SessionRecipe.get_instance().recipe_implementation.refresh_session(
+        refresh_token, anti_csrf_token, disable_anti_csrf, user_context
+    )
+
+
+
+async def remove_claim(session_handle: str, claim: SessionClaim[Any], user_context: Union[None, Dict[str, Any]] = None) ‑> bool +
+
+
+
+ +Expand source code + +
async def remove_claim(
+    session_handle: str,
+    claim: SessionClaim[Any],
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> bool:
+    if user_context is None:
+        user_context = {}
+    return await SessionRecipe.get_instance().recipe_implementation.remove_claim(
+        session_handle, claim, user_context
+    )
+
+
+
+async def revoke_all_sessions_for_user(user_id: str, tenant_id: Optional[str] = None, user_context: Union[None, Dict[str, Any]] = None) ‑> List[str] +
+
+
+
+ +Expand source code + +
async def revoke_all_sessions_for_user(
+    user_id: str,
+    tenant_id: Optional[str] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> List[str]:
+    if user_context is None:
+        user_context = {}
+    return await SessionRecipe.get_instance().recipe_implementation.revoke_all_sessions_for_user(
+        user_id, tenant_id or DEFAULT_TENANT_ID, tenant_id is None, user_context
+    )
+
+
+
+async def revoke_multiple_sessions(session_handles: List[str], user_context: Union[None, Dict[str, Any]] = None) ‑> List[str] +
+
+
+
+ +Expand source code + +
async def revoke_multiple_sessions(
+    session_handles: List[str], user_context: Union[None, Dict[str, Any]] = None
+) -> List[str]:
+    if user_context is None:
+        user_context = {}
+    return await SessionRecipe.get_instance().recipe_implementation.revoke_multiple_sessions(
+        session_handles, user_context
+    )
+
+
+
+async def revoke_session(session_handle: str, user_context: Union[None, Dict[str, Any]] = None) ‑> bool +
+
+
+
+ +Expand source code + +
async def revoke_session(
+    session_handle: str, user_context: Union[None, Dict[str, Any]] = None
+) -> bool:
+    if user_context is None:
+        user_context = {}
+    return await SessionRecipe.get_instance().recipe_implementation.revoke_session(
+        session_handle, user_context
+    )
+
+
+
+async def set_claim_value(session_handle: str, claim: SessionClaim[_T], value: _T, user_context: Union[None, Dict[str, Any]] = None) ‑> bool +
+
+
+
+ +Expand source code + +
async def set_claim_value(
+    session_handle: str,
+    claim: SessionClaim[_T],
+    value: _T,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> bool:
+    if user_context is None:
+        user_context = {}
+    return await SessionRecipe.get_instance().recipe_implementation.set_claim_value(
+        session_handle, claim, value, user_context
+    )
+
+
+
+async def update_session_data_in_database(session_handle: str, new_session_data: Dict[str, Any], user_context: Union[None, Dict[str, Any]] = None) ‑> bool +
+
+
+
+ +Expand source code + +
async def update_session_data_in_database(
+    session_handle: str,
+    new_session_data: Dict[str, Any],
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> bool:
+    if user_context is None:
+        user_context = {}
+    return await SessionRecipe.get_instance().recipe_implementation.update_session_data_in_database(
+        session_handle, new_session_data, user_context
+    )
+
+
+
+async def validate_claims_for_session_handle(session_handle: str, override_global_claim_validators: Optional[Callable[[List[SessionClaimValidator], SessionInformationResult, Dict[str, Any]], MaybeAwaitable[List[SessionClaimValidator]]]] = None, user_context: Union[None, Dict[str, Any]] = None) ‑> Union[SessionDoesNotExistErrorClaimsValidationResult] +
+
+
+
+ +Expand source code + +
async def validate_claims_for_session_handle(
+    session_handle: str,
+    override_global_claim_validators: Optional[
+        Callable[
+            [
+                List[SessionClaimValidator],
+                SessionInformationResult,
+                Dict[str, Any],
+            ],
+            MaybeAwaitable[List[SessionClaimValidator]],
+        ]
+    ] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> Union[SessionDoesNotExistError, ClaimsValidationResult]:
+    if user_context is None:
+        user_context = {}
+
+    recipe_impl = SessionRecipe.get_instance().recipe_implementation
+    session_info = await recipe_impl.get_session_information(
+        session_handle, user_context
+    )
+
+    if session_info is None:
+        return SessionDoesNotExistError()
+
+    claim_validators_added_by_other_recipes = (
+        SessionRecipe.get_instance().get_claim_validators_added_by_other_recipes()
+    )
+    global_claim_validators = await resolve(
+        recipe_impl.get_global_claim_validators(
+            session_info.tenant_id,
+            session_info.user_id,
+            claim_validators_added_by_other_recipes,
+            user_context,
+        )
+    )
+
+    if override_global_claim_validators is not None:
+        claim_validators = await resolve(
+            override_global_claim_validators(
+                global_claim_validators, session_info, user_context
+            )
+        )
+    else:
+        claim_validators = global_claim_validators
+
+    claim_validation_res = await recipe_impl.validate_claims(
+        session_info.user_id,
+        session_info.custom_claims_in_access_token_payload,
+        claim_validators,
+        user_context,
+    )
+
+    if claim_validation_res.access_token_payload_update is not None:
+        updated = await recipe_impl.merge_into_access_token_payload(
+            session_handle,
+            claim_validation_res.access_token_payload_update,
+            user_context,
+        )
+        if not updated:
+            return SessionDoesNotExistError()
+
+    return ClaimsValidationResult(claim_validation_res.invalid_claims)
+
+
+
+async def validate_claims_in_jwt_payload(tenant_id: str, user_id: str, jwt_payload: JSONObject, override_global_claim_validators: Optional[Callable[[List[SessionClaimValidator], str, Dict[str, Any]], MaybeAwaitable[List[SessionClaimValidator]]]] = None, user_context: Union[None, Dict[str, Any]] = None) +
+
+
+
+ +Expand source code + +
async def validate_claims_in_jwt_payload(
+    tenant_id: str,
+    user_id: str,
+    jwt_payload: JSONObject,
+    override_global_claim_validators: Optional[
+        Callable[
+            [
+                List[SessionClaimValidator],
+                str,
+                Dict[str, Any],
+            ],
+            MaybeAwaitable[List[SessionClaimValidator]],
+        ]
+    ] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+):
+    if user_context is None:
+        user_context = {}
+
+    recipe_impl = SessionRecipe.get_instance().recipe_implementation
+
+    claim_validators_added_by_other_recipes = (
+        SessionRecipe.get_instance().get_claim_validators_added_by_other_recipes()
+    )
+    global_claim_validators = await resolve(
+        recipe_impl.get_global_claim_validators(
+            tenant_id,
+            user_id,
+            claim_validators_added_by_other_recipes,
+            user_context,
+        )
+    )
+
+    if override_global_claim_validators is not None:
+        claim_validators = await resolve(
+            override_global_claim_validators(
+                global_claim_validators, user_id, user_context
+            )
+        )
+    else:
+        claim_validators = global_claim_validators
+
+    return await recipe_impl.validate_claims_in_jwt_payload(
+        user_id, jwt_payload, claim_validators, user_context
+    )
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/session/claim_base_classes/boolean_claim.html b/html/supertokens_python/recipe/session/claim_base_classes/boolean_claim.html new file mode 100644 index 000000000..d82c5e358 --- /dev/null +++ b/html/supertokens_python/recipe/session/claim_base_classes/boolean_claim.html @@ -0,0 +1,245 @@ + + + + + + +supertokens_python.recipe.session.claim_base_classes.boolean_claim API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.session.claim_base_classes.boolean_claim

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from typing import Any, Callable, Dict, Optional
+
+from supertokens_python.types import MaybeAwaitable
+
+from .primitive_claim import PrimitiveClaim, PrimitiveClaimValidators
+
+
+class BooleanClaimValidators(PrimitiveClaimValidators[bool]):
+    def is_true(self, max_age: Optional[int], id_: Optional[str] = None):
+        return self.has_value(True, max_age, id_)
+
+    def is_false(self, max_age: Optional[int], id_: Optional[str] = None):
+        return self.has_value(False, max_age, id_)
+
+
+class BooleanClaim(PrimitiveClaim[bool]):
+    def __init__(
+        self,
+        key: str,
+        fetch_value: Callable[
+            [str, str, Dict[str, Any]],
+            MaybeAwaitable[Optional[bool]],
+        ],
+        default_max_age_in_sec: Optional[int] = None,
+    ):
+        super().__init__(key, fetch_value, default_max_age_in_sec)
+        self.validators = BooleanClaimValidators(
+            claim=self, default_max_age_in_sec=default_max_age_in_sec
+        )
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class BooleanClaim +(key: str, fetch_value: Callable[[str, str, Dict[str, Any]], Union[Awaitable[Optional[bool]], bool, None]], default_max_age_in_sec: Optional[int] = None) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+

Args

+
+
key
+
The key to use when storing the claim in the payload.
+
fetch_value
+
a method that fetches the current value of this claim for the user. +A None return value signifies that we don't want to update the claim payload and or the claim value is +not present in the database. For example, this can happen with a second factor auth claim, where we +don't want to add the claim to the session automatically
+
+
+ +Expand source code + +
class BooleanClaim(PrimitiveClaim[bool]):
+    def __init__(
+        self,
+        key: str,
+        fetch_value: Callable[
+            [str, str, Dict[str, Any]],
+            MaybeAwaitable[Optional[bool]],
+        ],
+        default_max_age_in_sec: Optional[int] = None,
+    ):
+        super().__init__(key, fetch_value, default_max_age_in_sec)
+        self.validators = BooleanClaimValidators(
+            claim=self, default_max_age_in_sec=default_max_age_in_sec
+        )
+
+

Ancestors

+ +

Subclasses

+ +

Inherited members

+ +
+
+class BooleanClaimValidators +(claim: SessionClaim[~Primitive], default_max_age_in_sec: Optional[int]) +
+
+

Abstract base class for generic types.

+

A generic type is typically declared by inheriting from +this class parameterized with one or more type variables. +For example, a generic mapping type might be defined as::

+

class Mapping(Generic[KT, VT]): +def getitem(self, key: KT) -> VT: +… +# Etc.

+

This class can then be used as follows::

+

def lookup_name(mapping: Mapping[KT, VT], key: KT, default: VT) -> VT: +try: +return mapping[key] +except KeyError: +return default

+
+ +Expand source code + +
class BooleanClaimValidators(PrimitiveClaimValidators[bool]):
+    def is_true(self, max_age: Optional[int], id_: Optional[str] = None):
+        return self.has_value(True, max_age, id_)
+
+    def is_false(self, max_age: Optional[int], id_: Optional[str] = None):
+        return self.has_value(False, max_age, id_)
+
+

Ancestors

+ +

Subclasses

+ +

Methods

+
+
+def is_false(self, max_age: Optional[int], id_: Optional[str] = None) +
+
+
+
+ +Expand source code + +
def is_false(self, max_age: Optional[int], id_: Optional[str] = None):
+    return self.has_value(False, max_age, id_)
+
+
+
+def is_true(self, max_age: Optional[int], id_: Optional[str] = None) +
+
+
+
+ +Expand source code + +
def is_true(self, max_age: Optional[int], id_: Optional[str] = None):
+    return self.has_value(True, max_age, id_)
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/session/claim_base_classes/index.html b/html/supertokens_python/recipe/session/claim_base_classes/index.html new file mode 100644 index 000000000..71e9251c6 --- /dev/null +++ b/html/supertokens_python/recipe/session/claim_base_classes/index.html @@ -0,0 +1,93 @@ + + + + + + +supertokens_python.recipe.session.claim_base_classes API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.session.claim_base_classes

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+
+
+

Sub-modules

+
+
supertokens_python.recipe.session.claim_base_classes.boolean_claim
+
+
+
+
supertokens_python.recipe.session.claim_base_classes.primitive_array_claim
+
+
+
+
supertokens_python.recipe.session.claim_base_classes.primitive_claim
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/session/claim_base_classes/primitive_array_claim.html b/html/supertokens_python/recipe/session/claim_base_classes/primitive_array_claim.html new file mode 100644 index 000000000..0a86d37c1 --- /dev/null +++ b/html/supertokens_python/recipe/session/claim_base_classes/primitive_array_claim.html @@ -0,0 +1,1158 @@ + + + + + + +supertokens_python.recipe.session.claim_base_classes.primitive_array_claim API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.session.claim_base_classes.primitive_array_claim

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from typing import Any, Callable, Dict, Optional, TypeVar, Union, Generic, List
+
+from supertokens_python.types import MaybeAwaitable
+from supertokens_python.utils import get_timestamp_ms
+
+from ..interfaces import (
+    JSONObject,
+    JSONPrimitive,
+    SessionClaim,
+    SessionClaimValidator,
+    ClaimValidationResult,
+    JSONPrimitiveList,
+)
+
+
+Primitive = TypeVar("Primitive", bound=JSONPrimitive)
+PrimitiveList = TypeVar("PrimitiveList", bound=JSONPrimitiveList)
+
+_T = TypeVar("_T")
+
+
+class SCVMixin(SessionClaimValidator, Generic[_T]):
+    def __init__(
+        self,
+        id_: str,
+        claim: SessionClaim[PrimitiveList],
+        val: _T,
+        max_age_in_sec: Optional[int] = None,
+    ):
+        super().__init__(id_)
+        self.claim: SessionClaim[PrimitiveList] = claim  # TODO:PrimitiveArrayClaim
+        self.val = val
+        self.max_age_in_sec = max_age_in_sec
+
+    def should_refetch(
+        self,
+        payload: JSONObject,
+        user_context: Dict[str, Any],
+    ) -> bool:
+        claim = self.claim
+
+        return (claim.get_value_from_payload(payload, user_context) is None) or (
+            self.max_age_in_sec is not None
+            and (
+                payload[claim.key]["t"]
+                < get_timestamp_ms() - self.max_age_in_sec * 1000
+            )
+        )
+
+    async def _validate(
+        self,
+        payload: JSONObject,
+        user_context: Dict[str, Any],
+        is_include: bool,
+        is_include_any: bool = False,
+    ):
+        val = self.val
+        max_age_in_sec = self.max_age_in_sec
+
+        expected_key = "expectedToInclude" if is_include else "expectedToNotInclude"
+        if is_include_any:
+            expected_key = "expectedToIncludeAtLeastOneOf"
+
+        assert isinstance(self.claim, PrimitiveArrayClaim)
+        claim_val = self.claim.get_value_from_payload(payload, user_context)
+
+        if claim_val is None:
+            return ClaimValidationResult(
+                is_valid=False,
+                reason={
+                    "message": "value does not exist",
+                    expected_key: val,
+                    "actualValue": claim_val,
+                },
+            )
+
+        last_refetch_time = self.claim.get_last_refetch_time(payload, user_context)
+        assert last_refetch_time is not None
+        age_in_sec = (get_timestamp_ms() - last_refetch_time) / 1000
+        if max_age_in_sec is not None and age_in_sec > max_age_in_sec:
+            return ClaimValidationResult(
+                is_valid=False,
+                reason={
+                    "message": "expired",
+                    "ageInSeconds": age_in_sec,
+                    "maxAgeInSeconds": max_age_in_sec,
+                },
+            )
+
+        # Doing this to ensure same code in the upcoming steps irrespective of
+        # whether self.val is Primitive or PrimitiveList
+        vals: List[JSONPrimitive] = (
+            val if isinstance(val, list) else [val]
+        )  # pyright: reportGeneralTypeIssues=false
+
+        claim_val_set = set(claim_val)
+        if is_include and not is_include_any:
+            for v in vals:
+                if v not in claim_val_set:
+                    return ClaimValidationResult(
+                        is_valid=False,
+                        reason={
+                            "message": "wrong value",
+                            expected_key: val,
+                            # other SDKs return the item itself
+                            "actualValue": claim_val,
+                        },
+                    )
+        else:
+            for v in vals:
+                if v in claim_val_set:
+                    if is_include_any:
+                        return ClaimValidationResult(is_valid=True)
+
+                    return ClaimValidationResult(
+                        is_valid=False,
+                        reason={
+                            "message": "wrong value",
+                            expected_key: val,
+                            # other SDKs return the item itself
+                            "actualValue": claim_val,
+                        },
+                    )
+
+            if is_include_any:
+                return ClaimValidationResult(
+                    is_valid=False,
+                    reason={
+                        "message": "wrong value",
+                        expected_key: val,
+                        # other SDKs return the item itself
+                        "actualValue": claim_val,
+                    },
+                )
+
+        return ClaimValidationResult(is_valid=True)
+
+
+class IncludesSCV(SCVMixin[Primitive]):
+    async def validate(
+        self,
+        payload: JSONObject,
+        user_context: Dict[str, Any],
+    ):
+        return await self._validate(payload, user_context, is_include=True)
+
+
+class ExcludesSCV(SCVMixin[Primitive]):
+    async def validate(
+        self,
+        payload: JSONObject,
+        user_context: Dict[str, Any],
+    ):
+        return await self._validate(payload, user_context, is_include=False)
+
+
+class IncludesAllSCV(SCVMixin[PrimitiveList]):
+    async def validate(
+        self,
+        payload: JSONObject,
+        user_context: Dict[str, Any],
+    ):
+        return await self._validate(payload, user_context, is_include=True)
+
+
+class IncludesAnySCV(SCVMixin[PrimitiveList]):
+    async def validate(
+        self,
+        payload: JSONObject,
+        user_context: Dict[str, Any],
+    ):
+        return await self._validate(
+            payload, user_context, is_include=True, is_include_any=True
+        )
+
+
+class ExcludesAllSCV(SCVMixin[PrimitiveList]):
+    async def validate(
+        self,
+        payload: JSONObject,
+        user_context: Dict[str, Any],
+    ):
+        return await self._validate(payload, user_context, is_include=False)
+
+
+class PrimitiveArrayClaimValidators(Generic[PrimitiveList]):
+    def __init__(
+        self,
+        claim: SessionClaim[PrimitiveList],
+        default_max_age_in_sec: Optional[int] = None,
+    ) -> None:
+        self.claim = claim
+        self.default_max_age_in_sec = default_max_age_in_sec
+
+    def includes(  # pyright: ignore[reportInvalidTypeVarUse]
+        self,
+        val: Primitive,  # pyright: ignore[reportInvalidTypeVarUse]
+        max_age_in_seconds: Optional[int] = None,
+        id_: Union[str, None] = None,
+    ) -> SessionClaimValidator:
+        max_age_in_sec = max_age_in_seconds or self.default_max_age_in_sec
+        return IncludesSCV(
+            (id_ or self.claim.key), self.claim, val=val, max_age_in_sec=max_age_in_sec
+        )
+
+    def excludes(  # pyright: ignore[reportInvalidTypeVarUse]
+        self,
+        val: Primitive,  # pyright: ignore[reportInvalidTypeVarUse]
+        max_age_in_seconds: Optional[int] = None,
+        id_: Union[str, None] = None,
+    ) -> SessionClaimValidator:
+        max_age_in_sec = max_age_in_seconds or self.default_max_age_in_sec
+        return ExcludesSCV(
+            (id_ or self.claim.key), self.claim, val=val, max_age_in_sec=max_age_in_sec
+        )
+
+    def includes_all(
+        self,
+        val: PrimitiveList,
+        max_age_in_seconds: Optional[int] = None,
+        id_: Union[str, None] = None,
+    ) -> SessionClaimValidator:
+        max_age_in_sec = max_age_in_seconds or self.default_max_age_in_sec
+        return IncludesAllSCV(
+            (id_ or self.claim.key), self.claim, val=val, max_age_in_sec=max_age_in_sec
+        )
+
+    def includes_any(
+        self,
+        val: PrimitiveList,
+        max_age_in_seconds: Optional[int] = None,
+        id_: Union[str, None] = None,
+    ) -> SessionClaimValidator:
+        max_age_in_sec = max_age_in_seconds or self.default_max_age_in_sec
+        return IncludesAnySCV(
+            (id_ or self.claim.key), self.claim, val=val, max_age_in_sec=max_age_in_sec
+        )
+
+    def excludes_all(
+        self,
+        val: PrimitiveList,
+        max_age_in_seconds: Optional[int] = None,
+        id_: Union[str, None] = None,
+    ) -> SessionClaimValidator:
+        max_age_in_sec = max_age_in_seconds or self.default_max_age_in_sec
+        return ExcludesAllSCV(
+            (id_ or self.claim.key), self.claim, val=val, max_age_in_sec=max_age_in_sec
+        )
+
+
+class PrimitiveArrayClaim(SessionClaim[PrimitiveList], Generic[PrimitiveList]):
+    def __init__(
+        self,
+        key: str,
+        fetch_value: Callable[
+            [str, str, Dict[str, Any]],
+            MaybeAwaitable[Optional[PrimitiveList]],
+        ],
+        default_max_age_in_sec: Optional[int] = None,
+    ) -> None:
+        super().__init__(key, fetch_value)
+
+        claim = self
+        self.validators = PrimitiveArrayClaimValidators(claim, default_max_age_in_sec)
+
+    def add_to_payload_(
+        self,
+        payload: Dict[str, Any],
+        value: PrimitiveList,
+        user_context: Union[Dict[str, Any], None] = None,
+    ) -> JSONObject:
+        payload[self.key] = {"v": value, "t": get_timestamp_ms()}
+        _ = user_context
+
+        return payload
+
+    def remove_from_payload_by_merge_(
+        self, payload: JSONObject, user_context: Optional[Dict[str, Any]] = None
+    ) -> JSONObject:
+        payload[self.key] = None
+        return payload
+
+    def remove_from_payload(
+        self, payload: JSONObject, user_context: Optional[Dict[str, Any]] = None
+    ) -> JSONObject:
+        del payload[self.key]
+        return payload
+
+    def get_value_from_payload(
+        self, payload: JSONObject, user_context: Union[Dict[str, Any], None] = None
+    ) -> Union[PrimitiveList, None]:
+        _ = user_context
+
+        return payload.get(self.key, {}).get("v")
+
+    def get_last_refetch_time(
+        self, payload: JSONObject, user_context: Union[Dict[str, Any], None] = None
+    ) -> Union[int, None]:
+        _ = user_context
+
+        return payload.get(self.key, {}).get("t")
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class ExcludesAllSCV +(id_: str, claim: SessionClaim[~PrimitiveList], val: ~_T, max_age_in_sec: Optional[int] = None) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class ExcludesAllSCV(SCVMixin[PrimitiveList]):
+    async def validate(
+        self,
+        payload: JSONObject,
+        user_context: Dict[str, Any],
+    ):
+        return await self._validate(payload, user_context, is_include=False)
+
+

Ancestors

+ +

Methods

+
+
+async def validate(self, payload: Dict[str, Any], user_context: Dict[str, Any]) +
+
+
+
+ +Expand source code + +
async def validate(
+    self,
+    payload: JSONObject,
+    user_context: Dict[str, Any],
+):
+    return await self._validate(payload, user_context, is_include=False)
+
+
+
+
+
+class ExcludesSCV +(id_: str, claim: SessionClaim[~PrimitiveList], val: ~_T, max_age_in_sec: Optional[int] = None) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class ExcludesSCV(SCVMixin[Primitive]):
+    async def validate(
+        self,
+        payload: JSONObject,
+        user_context: Dict[str, Any],
+    ):
+        return await self._validate(payload, user_context, is_include=False)
+
+

Ancestors

+ +

Methods

+
+
+async def validate(self, payload: Dict[str, Any], user_context: Dict[str, Any]) +
+
+
+
+ +Expand source code + +
async def validate(
+    self,
+    payload: JSONObject,
+    user_context: Dict[str, Any],
+):
+    return await self._validate(payload, user_context, is_include=False)
+
+
+
+
+
+class IncludesAllSCV +(id_: str, claim: SessionClaim[~PrimitiveList], val: ~_T, max_age_in_sec: Optional[int] = None) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class IncludesAllSCV(SCVMixin[PrimitiveList]):
+    async def validate(
+        self,
+        payload: JSONObject,
+        user_context: Dict[str, Any],
+    ):
+        return await self._validate(payload, user_context, is_include=True)
+
+

Ancestors

+ +

Methods

+
+
+async def validate(self, payload: Dict[str, Any], user_context: Dict[str, Any]) +
+
+
+
+ +Expand source code + +
async def validate(
+    self,
+    payload: JSONObject,
+    user_context: Dict[str, Any],
+):
+    return await self._validate(payload, user_context, is_include=True)
+
+
+
+
+
+class IncludesAnySCV +(id_: str, claim: SessionClaim[~PrimitiveList], val: ~_T, max_age_in_sec: Optional[int] = None) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class IncludesAnySCV(SCVMixin[PrimitiveList]):
+    async def validate(
+        self,
+        payload: JSONObject,
+        user_context: Dict[str, Any],
+    ):
+        return await self._validate(
+            payload, user_context, is_include=True, is_include_any=True
+        )
+
+

Ancestors

+ +

Methods

+
+
+async def validate(self, payload: Dict[str, Any], user_context: Dict[str, Any]) +
+
+
+
+ +Expand source code + +
async def validate(
+    self,
+    payload: JSONObject,
+    user_context: Dict[str, Any],
+):
+    return await self._validate(
+        payload, user_context, is_include=True, is_include_any=True
+    )
+
+
+
+
+
+class IncludesSCV +(id_: str, claim: SessionClaim[~PrimitiveList], val: ~_T, max_age_in_sec: Optional[int] = None) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class IncludesSCV(SCVMixin[Primitive]):
+    async def validate(
+        self,
+        payload: JSONObject,
+        user_context: Dict[str, Any],
+    ):
+        return await self._validate(payload, user_context, is_include=True)
+
+

Ancestors

+ +

Methods

+
+
+async def validate(self, payload: Dict[str, Any], user_context: Dict[str, Any]) +
+
+
+
+ +Expand source code + +
async def validate(
+    self,
+    payload: JSONObject,
+    user_context: Dict[str, Any],
+):
+    return await self._validate(payload, user_context, is_include=True)
+
+
+
+
+
+class PrimitiveArrayClaim +(key: str, fetch_value: Callable[[str, str, Dict[str, Any]], Union[Awaitable[Optional[~PrimitiveList]], ~PrimitiveList, None]], default_max_age_in_sec: Optional[int] = None) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+

Args

+
+
key
+
The key to use when storing the claim in the payload.
+
fetch_value
+
a method that fetches the current value of this claim for the user. +A None return value signifies that we don't want to update the claim payload and or the claim value is +not present in the database. For example, this can happen with a second factor auth claim, where we +don't want to add the claim to the session automatically
+
+
+ +Expand source code + +
class PrimitiveArrayClaim(SessionClaim[PrimitiveList], Generic[PrimitiveList]):
+    def __init__(
+        self,
+        key: str,
+        fetch_value: Callable[
+            [str, str, Dict[str, Any]],
+            MaybeAwaitable[Optional[PrimitiveList]],
+        ],
+        default_max_age_in_sec: Optional[int] = None,
+    ) -> None:
+        super().__init__(key, fetch_value)
+
+        claim = self
+        self.validators = PrimitiveArrayClaimValidators(claim, default_max_age_in_sec)
+
+    def add_to_payload_(
+        self,
+        payload: Dict[str, Any],
+        value: PrimitiveList,
+        user_context: Union[Dict[str, Any], None] = None,
+    ) -> JSONObject:
+        payload[self.key] = {"v": value, "t": get_timestamp_ms()}
+        _ = user_context
+
+        return payload
+
+    def remove_from_payload_by_merge_(
+        self, payload: JSONObject, user_context: Optional[Dict[str, Any]] = None
+    ) -> JSONObject:
+        payload[self.key] = None
+        return payload
+
+    def remove_from_payload(
+        self, payload: JSONObject, user_context: Optional[Dict[str, Any]] = None
+    ) -> JSONObject:
+        del payload[self.key]
+        return payload
+
+    def get_value_from_payload(
+        self, payload: JSONObject, user_context: Union[Dict[str, Any], None] = None
+    ) -> Union[PrimitiveList, None]:
+        _ = user_context
+
+        return payload.get(self.key, {}).get("v")
+
+    def get_last_refetch_time(
+        self, payload: JSONObject, user_context: Union[Dict[str, Any], None] = None
+    ) -> Union[int, None]:
+        _ = user_context
+
+        return payload.get(self.key, {}).get("t")
+
+

Ancestors

+ +

Subclasses

+ +

Methods

+
+
+def get_last_refetch_time(self, payload: Dict[str, Any], user_context: Optional[Dict[str, Any]] = None) ‑> Optional[int] +
+
+
+
+ +Expand source code + +
def get_last_refetch_time(
+    self, payload: JSONObject, user_context: Union[Dict[str, Any], None] = None
+) -> Union[int, None]:
+    _ = user_context
+
+    return payload.get(self.key, {}).get("t")
+
+
+
+

Inherited members

+ +
+
+class PrimitiveArrayClaimValidators +(claim: SessionClaim[~PrimitiveList], default_max_age_in_sec: Optional[int] = None) +
+
+

Abstract base class for generic types.

+

A generic type is typically declared by inheriting from +this class parameterized with one or more type variables. +For example, a generic mapping type might be defined as::

+

class Mapping(Generic[KT, VT]): +def getitem(self, key: KT) -> VT: +… +# Etc.

+

This class can then be used as follows::

+

def lookup_name(mapping: Mapping[KT, VT], key: KT, default: VT) -> VT: +try: +return mapping[key] +except KeyError: +return default

+
+ +Expand source code + +
class PrimitiveArrayClaimValidators(Generic[PrimitiveList]):
+    def __init__(
+        self,
+        claim: SessionClaim[PrimitiveList],
+        default_max_age_in_sec: Optional[int] = None,
+    ) -> None:
+        self.claim = claim
+        self.default_max_age_in_sec = default_max_age_in_sec
+
+    def includes(  # pyright: ignore[reportInvalidTypeVarUse]
+        self,
+        val: Primitive,  # pyright: ignore[reportInvalidTypeVarUse]
+        max_age_in_seconds: Optional[int] = None,
+        id_: Union[str, None] = None,
+    ) -> SessionClaimValidator:
+        max_age_in_sec = max_age_in_seconds or self.default_max_age_in_sec
+        return IncludesSCV(
+            (id_ or self.claim.key), self.claim, val=val, max_age_in_sec=max_age_in_sec
+        )
+
+    def excludes(  # pyright: ignore[reportInvalidTypeVarUse]
+        self,
+        val: Primitive,  # pyright: ignore[reportInvalidTypeVarUse]
+        max_age_in_seconds: Optional[int] = None,
+        id_: Union[str, None] = None,
+    ) -> SessionClaimValidator:
+        max_age_in_sec = max_age_in_seconds or self.default_max_age_in_sec
+        return ExcludesSCV(
+            (id_ or self.claim.key), self.claim, val=val, max_age_in_sec=max_age_in_sec
+        )
+
+    def includes_all(
+        self,
+        val: PrimitiveList,
+        max_age_in_seconds: Optional[int] = None,
+        id_: Union[str, None] = None,
+    ) -> SessionClaimValidator:
+        max_age_in_sec = max_age_in_seconds or self.default_max_age_in_sec
+        return IncludesAllSCV(
+            (id_ or self.claim.key), self.claim, val=val, max_age_in_sec=max_age_in_sec
+        )
+
+    def includes_any(
+        self,
+        val: PrimitiveList,
+        max_age_in_seconds: Optional[int] = None,
+        id_: Union[str, None] = None,
+    ) -> SessionClaimValidator:
+        max_age_in_sec = max_age_in_seconds or self.default_max_age_in_sec
+        return IncludesAnySCV(
+            (id_ or self.claim.key), self.claim, val=val, max_age_in_sec=max_age_in_sec
+        )
+
+    def excludes_all(
+        self,
+        val: PrimitiveList,
+        max_age_in_seconds: Optional[int] = None,
+        id_: Union[str, None] = None,
+    ) -> SessionClaimValidator:
+        max_age_in_sec = max_age_in_seconds or self.default_max_age_in_sec
+        return ExcludesAllSCV(
+            (id_ or self.claim.key), self.claim, val=val, max_age_in_sec=max_age_in_sec
+        )
+
+

Ancestors

+
    +
  • typing.Generic
  • +
+

Methods

+
+
+def excludes(self, val: ~Primitive, max_age_in_seconds: Optional[int] = None, id_: Optional[str] = None) ‑> SessionClaimValidator +
+
+
+
+ +Expand source code + +
def excludes(  # pyright: ignore[reportInvalidTypeVarUse]
+    self,
+    val: Primitive,  # pyright: ignore[reportInvalidTypeVarUse]
+    max_age_in_seconds: Optional[int] = None,
+    id_: Union[str, None] = None,
+) -> SessionClaimValidator:
+    max_age_in_sec = max_age_in_seconds or self.default_max_age_in_sec
+    return ExcludesSCV(
+        (id_ or self.claim.key), self.claim, val=val, max_age_in_sec=max_age_in_sec
+    )
+
+
+
+def excludes_all(self, val: ~PrimitiveList, max_age_in_seconds: Optional[int] = None, id_: Optional[str] = None) ‑> SessionClaimValidator +
+
+
+
+ +Expand source code + +
def excludes_all(
+    self,
+    val: PrimitiveList,
+    max_age_in_seconds: Optional[int] = None,
+    id_: Union[str, None] = None,
+) -> SessionClaimValidator:
+    max_age_in_sec = max_age_in_seconds or self.default_max_age_in_sec
+    return ExcludesAllSCV(
+        (id_ or self.claim.key), self.claim, val=val, max_age_in_sec=max_age_in_sec
+    )
+
+
+
+def includes(self, val: ~Primitive, max_age_in_seconds: Optional[int] = None, id_: Optional[str] = None) ‑> SessionClaimValidator +
+
+
+
+ +Expand source code + +
def includes(  # pyright: ignore[reportInvalidTypeVarUse]
+    self,
+    val: Primitive,  # pyright: ignore[reportInvalidTypeVarUse]
+    max_age_in_seconds: Optional[int] = None,
+    id_: Union[str, None] = None,
+) -> SessionClaimValidator:
+    max_age_in_sec = max_age_in_seconds or self.default_max_age_in_sec
+    return IncludesSCV(
+        (id_ or self.claim.key), self.claim, val=val, max_age_in_sec=max_age_in_sec
+    )
+
+
+
+def includes_all(self, val: ~PrimitiveList, max_age_in_seconds: Optional[int] = None, id_: Optional[str] = None) ‑> SessionClaimValidator +
+
+
+
+ +Expand source code + +
def includes_all(
+    self,
+    val: PrimitiveList,
+    max_age_in_seconds: Optional[int] = None,
+    id_: Union[str, None] = None,
+) -> SessionClaimValidator:
+    max_age_in_sec = max_age_in_seconds or self.default_max_age_in_sec
+    return IncludesAllSCV(
+        (id_ or self.claim.key), self.claim, val=val, max_age_in_sec=max_age_in_sec
+    )
+
+
+
+def includes_any(self, val: ~PrimitiveList, max_age_in_seconds: Optional[int] = None, id_: Optional[str] = None) ‑> SessionClaimValidator +
+
+
+
+ +Expand source code + +
def includes_any(
+    self,
+    val: PrimitiveList,
+    max_age_in_seconds: Optional[int] = None,
+    id_: Union[str, None] = None,
+) -> SessionClaimValidator:
+    max_age_in_sec = max_age_in_seconds or self.default_max_age_in_sec
+    return IncludesAnySCV(
+        (id_ or self.claim.key), self.claim, val=val, max_age_in_sec=max_age_in_sec
+    )
+
+
+
+
+
+class SCVMixin +(id_: str, claim: SessionClaim[~PrimitiveList], val: ~_T, max_age_in_sec: Optional[int] = None) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class SCVMixin(SessionClaimValidator, Generic[_T]):
+    def __init__(
+        self,
+        id_: str,
+        claim: SessionClaim[PrimitiveList],
+        val: _T,
+        max_age_in_sec: Optional[int] = None,
+    ):
+        super().__init__(id_)
+        self.claim: SessionClaim[PrimitiveList] = claim  # TODO:PrimitiveArrayClaim
+        self.val = val
+        self.max_age_in_sec = max_age_in_sec
+
+    def should_refetch(
+        self,
+        payload: JSONObject,
+        user_context: Dict[str, Any],
+    ) -> bool:
+        claim = self.claim
+
+        return (claim.get_value_from_payload(payload, user_context) is None) or (
+            self.max_age_in_sec is not None
+            and (
+                payload[claim.key]["t"]
+                < get_timestamp_ms() - self.max_age_in_sec * 1000
+            )
+        )
+
+    async def _validate(
+        self,
+        payload: JSONObject,
+        user_context: Dict[str, Any],
+        is_include: bool,
+        is_include_any: bool = False,
+    ):
+        val = self.val
+        max_age_in_sec = self.max_age_in_sec
+
+        expected_key = "expectedToInclude" if is_include else "expectedToNotInclude"
+        if is_include_any:
+            expected_key = "expectedToIncludeAtLeastOneOf"
+
+        assert isinstance(self.claim, PrimitiveArrayClaim)
+        claim_val = self.claim.get_value_from_payload(payload, user_context)
+
+        if claim_val is None:
+            return ClaimValidationResult(
+                is_valid=False,
+                reason={
+                    "message": "value does not exist",
+                    expected_key: val,
+                    "actualValue": claim_val,
+                },
+            )
+
+        last_refetch_time = self.claim.get_last_refetch_time(payload, user_context)
+        assert last_refetch_time is not None
+        age_in_sec = (get_timestamp_ms() - last_refetch_time) / 1000
+        if max_age_in_sec is not None and age_in_sec > max_age_in_sec:
+            return ClaimValidationResult(
+                is_valid=False,
+                reason={
+                    "message": "expired",
+                    "ageInSeconds": age_in_sec,
+                    "maxAgeInSeconds": max_age_in_sec,
+                },
+            )
+
+        # Doing this to ensure same code in the upcoming steps irrespective of
+        # whether self.val is Primitive or PrimitiveList
+        vals: List[JSONPrimitive] = (
+            val if isinstance(val, list) else [val]
+        )  # pyright: reportGeneralTypeIssues=false
+
+        claim_val_set = set(claim_val)
+        if is_include and not is_include_any:
+            for v in vals:
+                if v not in claim_val_set:
+                    return ClaimValidationResult(
+                        is_valid=False,
+                        reason={
+                            "message": "wrong value",
+                            expected_key: val,
+                            # other SDKs return the item itself
+                            "actualValue": claim_val,
+                        },
+                    )
+        else:
+            for v in vals:
+                if v in claim_val_set:
+                    if is_include_any:
+                        return ClaimValidationResult(is_valid=True)
+
+                    return ClaimValidationResult(
+                        is_valid=False,
+                        reason={
+                            "message": "wrong value",
+                            expected_key: val,
+                            # other SDKs return the item itself
+                            "actualValue": claim_val,
+                        },
+                    )
+
+            if is_include_any:
+                return ClaimValidationResult(
+                    is_valid=False,
+                    reason={
+                        "message": "wrong value",
+                        expected_key: val,
+                        # other SDKs return the item itself
+                        "actualValue": claim_val,
+                    },
+                )
+
+        return ClaimValidationResult(is_valid=True)
+
+

Ancestors

+ +

Subclasses

+ +

Methods

+
+
+def should_refetch(self, payload: Dict[str, Any], user_context: Dict[str, Any]) ‑> bool +
+
+
+
+ +Expand source code + +
def should_refetch(
+    self,
+    payload: JSONObject,
+    user_context: Dict[str, Any],
+) -> bool:
+    claim = self.claim
+
+    return (claim.get_value_from_payload(payload, user_context) is None) or (
+        self.max_age_in_sec is not None
+        and (
+            payload[claim.key]["t"]
+            < get_timestamp_ms() - self.max_age_in_sec * 1000
+        )
+    )
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/session/claim_base_classes/primitive_claim.html b/html/supertokens_python/recipe/session/claim_base_classes/primitive_claim.html new file mode 100644 index 000000000..0d8d4593f --- /dev/null +++ b/html/supertokens_python/recipe/session/claim_base_classes/primitive_claim.html @@ -0,0 +1,636 @@ + + + + + + +supertokens_python.recipe.session.claim_base_classes.primitive_claim API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.session.claim_base_classes.primitive_claim

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from typing import Any, Callable, Dict, Generic, Optional, TypeVar, Union
+
+from supertokens_python.types import MaybeAwaitable
+from supertokens_python.utils import get_timestamp_ms
+
+from ..interfaces import (
+    ClaimValidationResult,
+    JSONObject,
+    JSONPrimitive,
+    SessionClaim,
+    SessionClaimValidator,
+)
+
+Primitive = TypeVar("Primitive", bound=JSONPrimitive)
+
+
+class HasValueSCV(SessionClaimValidator):
+    def __init__(
+        self,
+        id_: str,
+        claim: SessionClaim[Primitive],
+        val: Primitive,
+        max_age_in_sec: Optional[int] = None,
+    ):
+        super().__init__(id_)
+        self.claim: SessionClaim[Primitive] = claim  # to fix the type for pyright
+        self.val = val
+        self.max_age_in_sec = max_age_in_sec
+
+    def should_refetch(
+        self,
+        payload: JSONObject,
+        user_context: Dict[str, Any],
+    ) -> bool:
+        max_age_in_sec = self.max_age_in_sec
+
+        # (claim value is None) OR (value has expired)
+        return (self.claim.get_value_from_payload(payload, user_context) is None) or (
+            (max_age_in_sec is not None)
+            and (
+                payload[self.claim.key]["t"]
+                < (get_timestamp_ms() - max_age_in_sec * 1000)
+            )
+        )
+
+    async def validate(
+        self,
+        payload: JSONObject,
+        user_context: Dict[str, Any],
+    ):
+        val = self.val
+        max_age_in_sec = self.max_age_in_sec
+
+        claim_val: JSONPrimitive = self.claim.get_value_from_payload(
+            payload, user_context
+        )
+        if claim_val is None:
+            return ClaimValidationResult(
+                is_valid=False,
+                reason={
+                    "message": "value does not exist",
+                    "expectedValue": val,
+                    "actualValue": claim_val,
+                },
+            )
+
+        if max_age_in_sec is not None:
+            assert isinstance(self.claim, PrimitiveClaim)
+            last_refetch_time = self.claim.get_last_refetch_time(payload, user_context)
+            assert last_refetch_time is not None
+            age_in_sec = (get_timestamp_ms() - last_refetch_time) / 1000
+            if age_in_sec > max_age_in_sec:
+                return ClaimValidationResult(
+                    is_valid=False,
+                    reason={
+                        "message": "expired",
+                        "ageInSeconds": age_in_sec,
+                        "maxAgeInSeconds": max_age_in_sec,
+                    },
+                )
+
+        if claim_val != val:
+            return ClaimValidationResult(
+                is_valid=False,
+                reason={
+                    "message": "wrong value",
+                    "expectedValue": val,
+                    "actualValue": claim_val,
+                },
+            )
+
+        return ClaimValidationResult(is_valid=True)
+
+
+class PrimitiveClaimValidators(Generic[Primitive]):
+    def __init__(
+        self,
+        claim: SessionClaim[Primitive],
+        default_max_age_in_sec: Optional[int],
+    ) -> None:
+        self.claim = claim
+        self.default_max_age_in_sec = default_max_age_in_sec
+
+    def has_value(
+        self,
+        val: Primitive,
+        max_age_in_sec: Optional[int] = None,
+        id_: Optional[str] = None,
+    ) -> SessionClaimValidator:
+        max_age_in_sec = max_age_in_sec or self.default_max_age_in_sec
+        return HasValueSCV(
+            (id_ or self.claim.key), self.claim, val=val, max_age_in_sec=max_age_in_sec
+        )
+
+
+class PrimitiveClaim(SessionClaim[Primitive]):
+    def __init__(
+        self,
+        key: str,
+        fetch_value: Callable[
+            [str, str, Dict[str, Any]],
+            MaybeAwaitable[Optional[Primitive]],
+        ],
+        default_max_age_in_sec: Optional[int] = None,
+    ) -> None:
+        super().__init__(key, fetch_value)
+
+        claim = self
+        self.validators = PrimitiveClaimValidators(claim, default_max_age_in_sec)
+
+    def add_to_payload_(
+        self,
+        payload: Dict[str, Any],
+        value: Primitive,
+        user_context: Union[Dict[str, Any], None] = None,
+    ) -> JSONObject:
+        payload[self.key] = {"v": value, "t": get_timestamp_ms()}
+        _ = user_context
+
+        return payload
+
+    def remove_from_payload_by_merge_(
+        self, payload: JSONObject, user_context: Optional[Dict[str, Any]] = None
+    ) -> JSONObject:
+        payload[self.key] = None
+        return payload
+
+    def remove_from_payload(
+        self, payload: JSONObject, user_context: Optional[Dict[str, Any]] = None
+    ) -> JSONObject:
+        del payload[self.key]
+        return payload
+
+    def get_value_from_payload(
+        self, payload: JSONObject, user_context: Union[Dict[str, Any], None] = None
+    ) -> Union[Primitive, None]:
+        _ = user_context
+
+        return payload.get(self.key, {}).get("v")
+
+    def get_last_refetch_time(
+        self, payload: JSONObject, user_context: Union[Dict[str, Any], None] = None
+    ) -> Union[int, None]:
+        _ = user_context
+
+        return payload.get(self.key, {}).get("t")
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class HasValueSCV +(id_: str, claim: SessionClaim[~Primitive], val: ~Primitive, max_age_in_sec: Optional[int] = None) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class HasValueSCV(SessionClaimValidator):
+    def __init__(
+        self,
+        id_: str,
+        claim: SessionClaim[Primitive],
+        val: Primitive,
+        max_age_in_sec: Optional[int] = None,
+    ):
+        super().__init__(id_)
+        self.claim: SessionClaim[Primitive] = claim  # to fix the type for pyright
+        self.val = val
+        self.max_age_in_sec = max_age_in_sec
+
+    def should_refetch(
+        self,
+        payload: JSONObject,
+        user_context: Dict[str, Any],
+    ) -> bool:
+        max_age_in_sec = self.max_age_in_sec
+
+        # (claim value is None) OR (value has expired)
+        return (self.claim.get_value_from_payload(payload, user_context) is None) or (
+            (max_age_in_sec is not None)
+            and (
+                payload[self.claim.key]["t"]
+                < (get_timestamp_ms() - max_age_in_sec * 1000)
+            )
+        )
+
+    async def validate(
+        self,
+        payload: JSONObject,
+        user_context: Dict[str, Any],
+    ):
+        val = self.val
+        max_age_in_sec = self.max_age_in_sec
+
+        claim_val: JSONPrimitive = self.claim.get_value_from_payload(
+            payload, user_context
+        )
+        if claim_val is None:
+            return ClaimValidationResult(
+                is_valid=False,
+                reason={
+                    "message": "value does not exist",
+                    "expectedValue": val,
+                    "actualValue": claim_val,
+                },
+            )
+
+        if max_age_in_sec is not None:
+            assert isinstance(self.claim, PrimitiveClaim)
+            last_refetch_time = self.claim.get_last_refetch_time(payload, user_context)
+            assert last_refetch_time is not None
+            age_in_sec = (get_timestamp_ms() - last_refetch_time) / 1000
+            if age_in_sec > max_age_in_sec:
+                return ClaimValidationResult(
+                    is_valid=False,
+                    reason={
+                        "message": "expired",
+                        "ageInSeconds": age_in_sec,
+                        "maxAgeInSeconds": max_age_in_sec,
+                    },
+                )
+
+        if claim_val != val:
+            return ClaimValidationResult(
+                is_valid=False,
+                reason={
+                    "message": "wrong value",
+                    "expectedValue": val,
+                    "actualValue": claim_val,
+                },
+            )
+
+        return ClaimValidationResult(is_valid=True)
+
+

Ancestors

+ +

Methods

+
+
+def should_refetch(self, payload: Dict[str, Any], user_context: Dict[str, Any]) ‑> bool +
+
+
+
+ +Expand source code + +
def should_refetch(
+    self,
+    payload: JSONObject,
+    user_context: Dict[str, Any],
+) -> bool:
+    max_age_in_sec = self.max_age_in_sec
+
+    # (claim value is None) OR (value has expired)
+    return (self.claim.get_value_from_payload(payload, user_context) is None) or (
+        (max_age_in_sec is not None)
+        and (
+            payload[self.claim.key]["t"]
+            < (get_timestamp_ms() - max_age_in_sec * 1000)
+        )
+    )
+
+
+
+async def validate(self, payload: Dict[str, Any], user_context: Dict[str, Any]) +
+
+
+
+ +Expand source code + +
async def validate(
+    self,
+    payload: JSONObject,
+    user_context: Dict[str, Any],
+):
+    val = self.val
+    max_age_in_sec = self.max_age_in_sec
+
+    claim_val: JSONPrimitive = self.claim.get_value_from_payload(
+        payload, user_context
+    )
+    if claim_val is None:
+        return ClaimValidationResult(
+            is_valid=False,
+            reason={
+                "message": "value does not exist",
+                "expectedValue": val,
+                "actualValue": claim_val,
+            },
+        )
+
+    if max_age_in_sec is not None:
+        assert isinstance(self.claim, PrimitiveClaim)
+        last_refetch_time = self.claim.get_last_refetch_time(payload, user_context)
+        assert last_refetch_time is not None
+        age_in_sec = (get_timestamp_ms() - last_refetch_time) / 1000
+        if age_in_sec > max_age_in_sec:
+            return ClaimValidationResult(
+                is_valid=False,
+                reason={
+                    "message": "expired",
+                    "ageInSeconds": age_in_sec,
+                    "maxAgeInSeconds": max_age_in_sec,
+                },
+            )
+
+    if claim_val != val:
+        return ClaimValidationResult(
+            is_valid=False,
+            reason={
+                "message": "wrong value",
+                "expectedValue": val,
+                "actualValue": claim_val,
+            },
+        )
+
+    return ClaimValidationResult(is_valid=True)
+
+
+
+
+
+class PrimitiveClaim +(key: str, fetch_value: Callable[[str, str, Dict[str, Any]], Union[Awaitable[Optional[~Primitive]], ~Primitive, None]], default_max_age_in_sec: Optional[int] = None) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+

Args

+
+
key
+
The key to use when storing the claim in the payload.
+
fetch_value
+
a method that fetches the current value of this claim for the user. +A None return value signifies that we don't want to update the claim payload and or the claim value is +not present in the database. For example, this can happen with a second factor auth claim, where we +don't want to add the claim to the session automatically
+
+
+ +Expand source code + +
class PrimitiveClaim(SessionClaim[Primitive]):
+    def __init__(
+        self,
+        key: str,
+        fetch_value: Callable[
+            [str, str, Dict[str, Any]],
+            MaybeAwaitable[Optional[Primitive]],
+        ],
+        default_max_age_in_sec: Optional[int] = None,
+    ) -> None:
+        super().__init__(key, fetch_value)
+
+        claim = self
+        self.validators = PrimitiveClaimValidators(claim, default_max_age_in_sec)
+
+    def add_to_payload_(
+        self,
+        payload: Dict[str, Any],
+        value: Primitive,
+        user_context: Union[Dict[str, Any], None] = None,
+    ) -> JSONObject:
+        payload[self.key] = {"v": value, "t": get_timestamp_ms()}
+        _ = user_context
+
+        return payload
+
+    def remove_from_payload_by_merge_(
+        self, payload: JSONObject, user_context: Optional[Dict[str, Any]] = None
+    ) -> JSONObject:
+        payload[self.key] = None
+        return payload
+
+    def remove_from_payload(
+        self, payload: JSONObject, user_context: Optional[Dict[str, Any]] = None
+    ) -> JSONObject:
+        del payload[self.key]
+        return payload
+
+    def get_value_from_payload(
+        self, payload: JSONObject, user_context: Union[Dict[str, Any], None] = None
+    ) -> Union[Primitive, None]:
+        _ = user_context
+
+        return payload.get(self.key, {}).get("v")
+
+    def get_last_refetch_time(
+        self, payload: JSONObject, user_context: Union[Dict[str, Any], None] = None
+    ) -> Union[int, None]:
+        _ = user_context
+
+        return payload.get(self.key, {}).get("t")
+
+

Ancestors

+ +

Subclasses

+ +

Methods

+
+
+def get_last_refetch_time(self, payload: Dict[str, Any], user_context: Optional[Dict[str, Any]] = None) ‑> Optional[int] +
+
+
+
+ +Expand source code + +
def get_last_refetch_time(
+    self, payload: JSONObject, user_context: Union[Dict[str, Any], None] = None
+) -> Union[int, None]:
+    _ = user_context
+
+    return payload.get(self.key, {}).get("t")
+
+
+
+

Inherited members

+ +
+
+class PrimitiveClaimValidators +(claim: SessionClaim[~Primitive], default_max_age_in_sec: Optional[int]) +
+
+

Abstract base class for generic types.

+

A generic type is typically declared by inheriting from +this class parameterized with one or more type variables. +For example, a generic mapping type might be defined as::

+

class Mapping(Generic[KT, VT]): +def getitem(self, key: KT) -> VT: +… +# Etc.

+

This class can then be used as follows::

+

def lookup_name(mapping: Mapping[KT, VT], key: KT, default: VT) -> VT: +try: +return mapping[key] +except KeyError: +return default

+
+ +Expand source code + +
class PrimitiveClaimValidators(Generic[Primitive]):
+    def __init__(
+        self,
+        claim: SessionClaim[Primitive],
+        default_max_age_in_sec: Optional[int],
+    ) -> None:
+        self.claim = claim
+        self.default_max_age_in_sec = default_max_age_in_sec
+
+    def has_value(
+        self,
+        val: Primitive,
+        max_age_in_sec: Optional[int] = None,
+        id_: Optional[str] = None,
+    ) -> SessionClaimValidator:
+        max_age_in_sec = max_age_in_sec or self.default_max_age_in_sec
+        return HasValueSCV(
+            (id_ or self.claim.key), self.claim, val=val, max_age_in_sec=max_age_in_sec
+        )
+
+

Ancestors

+
    +
  • typing.Generic
  • +
+

Subclasses

+ +

Methods

+
+
+def has_value(self, val: ~Primitive, max_age_in_sec: Optional[int] = None, id_: Optional[str] = None) ‑> SessionClaimValidator +
+
+
+
+ +Expand source code + +
def has_value(
+    self,
+    val: Primitive,
+    max_age_in_sec: Optional[int] = None,
+    id_: Optional[str] = None,
+) -> SessionClaimValidator:
+    max_age_in_sec = max_age_in_sec or self.default_max_age_in_sec
+    return HasValueSCV(
+        (id_ or self.claim.key), self.claim, val=val, max_age_in_sec=max_age_in_sec
+    )
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/session/claims.html b/html/supertokens_python/recipe/session/claims.html new file mode 100644 index 000000000..7d732a9a4 --- /dev/null +++ b/html/supertokens_python/recipe/session/claims.html @@ -0,0 +1,79 @@ + + + + + + +supertokens_python.recipe.session.claims API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.session.claims

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from . import interfaces
+from .claim_base_classes import boolean_claim, primitive_claim, primitive_array_claim
+
+SessionClaim = interfaces.SessionClaim
+BooleanClaim = boolean_claim.BooleanClaim
+PrimitiveClaim = primitive_claim.PrimitiveClaim
+PrimitiveArrayClaim = primitive_array_claim.PrimitiveArrayClaim
+
+
+
+
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/session/constants.html b/html/supertokens_python/recipe/session/constants.html new file mode 100644 index 000000000..283484403 --- /dev/null +++ b/html/supertokens_python/recipe/session/constants.html @@ -0,0 +1,104 @@ + + + + + + +supertokens_python.recipe.session.constants API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.session.constants

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, List
+
+if TYPE_CHECKING:
+    from .utils import TokenTransferMethod
+
+SESSION_REFRESH = "/session/refresh"
+SIGNOUT = "/signout"
+ACCESS_TOKEN_COOKIE_KEY = "sAccessToken"
+REFRESH_TOKEN_COOKIE_KEY = "sRefreshToken"
+FRONT_TOKEN_HEADER_SET_KEY = "front-token"
+ANTI_CSRF_HEADER_KEY = "anti-csrf"
+RID_HEADER_KEY = "rid"
+AUTH_MODE_HEADER_KEY = "st-auth-mode"
+AUTHORIZATION_HEADER_KEY = "authorization"
+ACCESS_TOKEN_HEADER_KEY = "st-access-token"
+REFRESH_TOKEN_HEADER_KEY = "st-refresh-token"
+ACCESS_CONTROL_EXPOSE_HEADERS = "Access-Control-Expose-Headers"
+
+available_token_transfer_methods: List[TokenTransferMethod] = ["cookie", "header"]
+
+protected_props = [
+    "sub",
+    "iat",
+    "exp",
+    "sessionHandle",
+    "parentRefreshTokenHash1",
+    "refreshTokenHash1",
+    "antiCsrfToken",
+    "rsub",
+    "tId",
+]
+
+
+
+
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/session/cookie_and_header.html b/html/supertokens_python/recipe/session/cookie_and_header.html new file mode 100644 index 000000000..1053c9b83 --- /dev/null +++ b/html/supertokens_python/recipe/session/cookie_and_header.html @@ -0,0 +1,1061 @@ + + + + + + +supertokens_python.recipe.session.cookie_and_header API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.session.cookie_and_header

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, List, Optional
+from urllib.parse import quote, unquote
+
+from typing_extensions import Literal
+
+from supertokens_python.recipe.session.exceptions import (
+    raise_clear_duplicate_session_cookies_exception,
+)
+from supertokens_python.recipe.session.interfaces import ResponseMutator
+
+from .constants import (
+    ACCESS_CONTROL_EXPOSE_HEADERS,
+    ACCESS_TOKEN_COOKIE_KEY,
+    ACCESS_TOKEN_HEADER_KEY,
+    ANTI_CSRF_HEADER_KEY,
+    AUTH_MODE_HEADER_KEY,
+    AUTHORIZATION_HEADER_KEY,
+    FRONT_TOKEN_HEADER_SET_KEY,
+    REFRESH_TOKEN_COOKIE_KEY,
+    REFRESH_TOKEN_HEADER_KEY,
+    RID_HEADER_KEY,
+    available_token_transfer_methods,
+)
+from ...logger import log_debug_message
+from supertokens_python.constants import ONE_YEAR_IN_MS
+
+if TYPE_CHECKING:
+    from supertokens_python.framework.request import BaseRequest
+    from supertokens_python.framework.response import BaseResponse
+    from .recipe import SessionRecipe
+    from .utils import (
+        TokenTransferMethod,
+        TokenType,
+        SessionConfig,
+    )
+
+from json import dumps
+from typing import Any, Dict
+
+from supertokens_python.utils import get_header, utf_base64encode, get_timestamp_ms
+
+
+def build_front_token(
+    user_id: str, at_expiry: int, access_token_payload: Optional[Dict[str, Any]] = None
+):
+    if access_token_payload is None:
+        access_token_payload = {}
+    token_info = {"uid": user_id, "ate": at_expiry, "up": access_token_payload}
+    return utf_base64encode(
+        dumps(token_info, separators=(",", ":"), sort_keys=True), urlsafe=False
+    )
+
+
+def _set_front_token_in_headers(
+    response: BaseResponse,
+    front_token: str,
+):
+    set_header(response, FRONT_TOKEN_HEADER_SET_KEY, front_token, False)
+    set_header(
+        response, ACCESS_CONTROL_EXPOSE_HEADERS, FRONT_TOKEN_HEADER_SET_KEY, True
+    )
+
+
+def get_cors_allowed_headers():
+    return [
+        ANTI_CSRF_HEADER_KEY,
+        RID_HEADER_KEY,
+        AUTHORIZATION_HEADER_KEY,
+        AUTH_MODE_HEADER_KEY,
+    ]
+
+
+def set_header(response: BaseResponse, key: str, value: str, allow_duplicate: bool):
+    if allow_duplicate:
+        old_value = response.get_header(key)
+        if old_value is None:
+            response.set_header(key, value)
+        else:
+            response.set_header(key, old_value + "," + value)
+    else:
+        response.set_header(key, value)
+
+
+def remove_header(response: BaseResponse, key: str):
+    if response.get_header(key) is not None:
+        response.remove_header(key)
+
+
+def get_cookie(request: BaseRequest, key: str):
+    cookie_val = request.get_cookie(key)
+    if cookie_val is None:
+        return None
+    return unquote(cookie_val)
+
+
+def _set_cookie(
+    response: BaseResponse,
+    config: SessionConfig,
+    key: str,
+    value: str,
+    expires: int,
+    path_type: Literal["refresh_token_path", "access_token_path"],
+    request: BaseRequest,
+    domain: Optional[str],
+    user_context: Dict[str, Any],
+):
+    secure = config.cookie_secure
+    same_site = config.get_cookie_same_site(request, user_context)
+    path = ""
+    if path_type == "refresh_token_path":
+        path = config.refresh_token_path.get_as_string_dangerous()
+    elif path_type == "access_token_path":
+        path = "/"
+    http_only = True
+    response.set_cookie(
+        key=key,
+        value=quote(value, encoding="utf-8"),
+        expires=expires,
+        path=path,
+        domain=domain,
+        secure=secure,
+        httponly=http_only,
+        samesite=same_site,
+    )
+
+
+def set_cookie_response_mutator(
+    config: SessionConfig,
+    key: str,
+    value: str,
+    expires: int,
+    path_type: Literal["refresh_token_path", "access_token_path"],
+    request: BaseRequest,
+    domain: Optional[str] = None,
+):
+    domain = domain if domain is not None else config.cookie_domain
+
+    def mutator(response: BaseResponse, user_context: Dict[str, Any]):
+        return _set_cookie(
+            response,
+            config,
+            key,
+            value,
+            expires,
+            path_type,
+            request,
+            domain,
+            user_context,
+        )
+
+    return mutator
+
+
+def _attach_anti_csrf_header(response: BaseResponse, value: str):
+    set_header(response, ANTI_CSRF_HEADER_KEY, value, False)
+    set_header(response, ACCESS_CONTROL_EXPOSE_HEADERS, ANTI_CSRF_HEADER_KEY, True)
+
+
+def anti_csrf_response_mutator(value: str):
+    def mutator(
+        response: BaseResponse,
+        _: Dict[str, Any],
+    ):
+        return _attach_anti_csrf_header(response, value)
+
+    return mutator
+
+
+def get_anti_csrf_header(request: BaseRequest):
+    return get_header(request, ANTI_CSRF_HEADER_KEY)
+
+
+def get_rid_header(request: BaseRequest):
+    return get_header(request, RID_HEADER_KEY)
+
+
+def clear_session_from_all_token_transfer_methods(
+    response: BaseResponse,
+    recipe: SessionRecipe,
+    request: BaseRequest,
+    user_context: Dict[str, Any],
+):
+    # We are clearing the session in all transfermethods to be sure to override cookies in case they have been already added to the response.
+    # This is done to handle the following use-case:
+    # If the app overrides signInPOST to check the ban status of the user after the original implementation and throwing an UNAUTHORISED error
+    # In this case: the SDK has attached cookies to the response, but none was sent with the request
+    # We can't know which to clear since we can't reliably query or remove the set-cookie header added to the response (causes issues in some frameworks, i.e.: hapi)
+    # The safe solution in this case is to overwrite all the response cookies/headers with an empty value, which is what we are doing here.
+    for transfer_method in available_token_transfer_methods:
+        _clear_session(response, recipe.config, transfer_method, request, user_context)
+
+
+def clear_session_mutator(
+    config: SessionConfig,
+    transfer_method: TokenTransferMethod,
+    request: BaseRequest,
+):
+    def mutator(
+        response: BaseResponse,
+        user_context: Dict[str, Any],
+    ):
+        return _clear_session(response, config, transfer_method, request, user_context)
+
+    return mutator
+
+
+def _clear_session(
+    response: BaseResponse,
+    config: SessionConfig,
+    transfer_method: TokenTransferMethod,
+    request: BaseRequest,
+    user_context: Dict[str, Any],
+):
+    # If we can be specific about which transferMethod we want to clear, there is no reason to clear the other ones
+    token_types: List[TokenType] = ["access", "refresh"]
+    for token_type in token_types:
+        _set_token(
+            response, config, token_type, "", 0, transfer_method, request, user_context
+        )
+
+    remove_header(
+        response, ANTI_CSRF_HEADER_KEY
+    )  # This can be added multiple times in some cases, but that should be OK
+    set_header(response, FRONT_TOKEN_HEADER_SET_KEY, "remove", False)
+    set_header(
+        response, ACCESS_CONTROL_EXPOSE_HEADERS, FRONT_TOKEN_HEADER_SET_KEY, True
+    )
+
+
+def clear_session_response_mutator(
+    config: SessionConfig,
+    transfer_method: TokenTransferMethod,
+    request: BaseRequest,
+):
+    def mutator(
+        response: BaseResponse,
+        user_context: Dict[str, Any],
+    ):
+        return _clear_session(response, config, transfer_method, request, user_context)
+
+    return mutator
+
+
+def get_cookie_name_from_token_type(token_type: TokenType):
+    if token_type == "access":
+        return ACCESS_TOKEN_COOKIE_KEY
+    if token_type == "refresh":
+        return REFRESH_TOKEN_COOKIE_KEY
+    raise Exception("Unknown token type, should never happen")
+
+
+def get_response_header_name_for_token_type(token_type: TokenType):
+    if token_type == "access":
+        return ACCESS_TOKEN_HEADER_KEY
+    if token_type == "refresh":
+        return REFRESH_TOKEN_HEADER_KEY
+    raise Exception("Unknown token type, should never happen")
+
+
+def get_token(
+    request: BaseRequest,
+    token_type: TokenType,
+    transfer_method: TokenTransferMethod,
+) -> Optional[str]:
+    if transfer_method == "cookie":
+        # Note: Don't use request.get_cookie() as it won't apply unquote() func
+        return get_cookie(request, get_cookie_name_from_token_type(token_type))
+    if transfer_method == "header":
+        value = request.get_header(AUTHORIZATION_HEADER_KEY)
+        if value is None or not value.startswith("Bearer "):
+            return None
+
+        return value[len("Bearer ") :].strip()
+
+    raise Exception("Should never happen: Unknown transferMethod: " + transfer_method)
+
+
+def _set_token(
+    response: BaseResponse,
+    config: SessionConfig,
+    token_type: TokenType,
+    value: str,
+    expires: int,
+    transfer_method: TokenTransferMethod,
+    request: BaseRequest,
+    user_context: Dict[str, Any],
+):
+    log_debug_message("Setting %s token as %s", token_type, transfer_method)
+    if transfer_method == "cookie":
+        _set_cookie(
+            response,
+            config,
+            get_cookie_name_from_token_type(token_type),
+            value,
+            expires,
+            "refresh_token_path" if token_type == "refresh" else "access_token_path",
+            request,
+            config.cookie_domain,
+            user_context,
+        )
+    elif transfer_method == "header":
+        set_token_in_header(
+            response,
+            get_response_header_name_for_token_type(token_type),
+            value,
+        )
+
+
+def token_response_mutator(
+    config: SessionConfig,
+    token_type: TokenType,
+    value: str,
+    expires: int,
+    transfer_method: TokenTransferMethod,
+    request: BaseRequest,
+):
+    def mutator(
+        response: BaseResponse,
+        user_context: Dict[str, Any],
+    ):
+        _set_token(
+            response,
+            config,
+            token_type,
+            value,
+            expires,
+            transfer_method,
+            request,
+            user_context,
+        )
+
+    return mutator
+
+
+def set_token_in_header(response: BaseResponse, name: str, value: str):
+    set_header(response, name, value, allow_duplicate=False)
+    set_header(response, ACCESS_CONTROL_EXPOSE_HEADERS, name, allow_duplicate=True)
+
+
+def access_token_mutator(
+    access_token: str,
+    front_token: str,
+    config: SessionConfig,
+    transfer_method: TokenTransferMethod,
+    request: BaseRequest,
+):
+    def mutator(
+        response: BaseResponse,
+        user_context: Dict[str, Any],
+    ):
+        _set_access_token_in_response(
+            response,
+            access_token,
+            front_token,
+            config,
+            transfer_method,
+            request,
+            user_context,
+        )
+
+    return mutator
+
+
+def _set_access_token_in_response(
+    res: BaseResponse,
+    access_token: str,
+    front_token: str,
+    config: SessionConfig,
+    transfer_method: TokenTransferMethod,
+    request: BaseRequest,
+    user_context: Dict[str, Any],
+):
+    _set_front_token_in_headers(res, front_token)
+    _set_token(
+        res,
+        config,
+        "access",
+        access_token,
+        # We set the expiration to 1 year, because we can't really access the expiration of the refresh token everywhere we are setting it.
+        # This should be safe to do, since this is only the validity of the cookie (set here or on the frontend) but we check the expiration of the JWT anyway.
+        # Even if the token is expired the presence of the token indicates that the user could have a valid refresh
+        # Some browsers now cap the maximum expiry at 400 days, so we set it to 1 year, which should suffice.
+        get_timestamp_ms() + ONE_YEAR_IN_MS,
+        transfer_method,
+        request,
+        user_context,
+    )
+
+    if (
+        config.expose_access_token_to_frontend_in_cookie_based_auth
+        and transfer_method == "cookie"
+    ):
+        _set_token(
+            res,
+            config,
+            "access",
+            access_token,
+            get_timestamp_ms() + ONE_YEAR_IN_MS,
+            "header",
+            request,
+            user_context,
+        )
+
+
+# This function addresses an edge case where changing the cookie_domain config on the server can
+# lead to session integrity issues. For instance, if the API server URL is 'api.example.com'
+# with a cookie domain of '.example.com', and the server updates the cookie domain to 'api.example.com',
+# the client may retain cookies with both '.example.com' and 'api.example.com' domains.
+
+# Consequently, if the server chooses the older cookie, session invalidation occurs, potentially
+# resulting in an infinite refresh loop. To fix this, users are asked to specify "older_cookie_domain" in
+# the config.
+
+
+# This function checks for multiple cookies with the same name and clears the cookies for the older domain.
+def clear_session_cookies_from_older_cookie_domain(
+    request: BaseRequest, config: SessionConfig, user_context: Dict[str, Any]
+):
+    allowed_transfer_method = config.get_token_transfer_method(
+        request, False, user_context
+    )
+    # If the transfer method is 'header', there's no need to clear cookies immediately, even if there are multiple in the request.
+    if allowed_transfer_method == "header":
+        return
+
+    did_clear_cookies = False
+    response_mutators: List[ResponseMutator] = []
+
+    token_types: List[TokenType] = ["access", "refresh"]
+    for token_type in token_types:
+        if has_multiple_cookies_for_token_type(request, token_type):
+            # If a request has multiple session cookies and 'older_cookie_domain' is
+            # unset, we can't identify the correct cookie for refreshing the session.
+            # Using the wrong cookie can cause an infinite refresh loop. To avoid this,
+            # we throw a 500 error asking the user to set 'older_cookie_domain''.
+            if config.older_cookie_domain is None:
+                raise Exception(
+                    "The request contains multiple session cookies. This may happen if you've changed the 'cookie_domain' setting in your configuration. To clear tokens from the previous domain, set 'older_cookie_domain' in your config."
+                )
+
+            log_debug_message(
+                "Clearing duplicate %s cookie with domain %s",
+                token_type,
+                config.cookie_domain,
+            )
+            response_mutators.append(
+                set_cookie_response_mutator(
+                    config,
+                    get_cookie_name_from_token_type(token_type),
+                    "",
+                    0,
+                    (
+                        "refresh_token_path"
+                        if token_type == "refresh"
+                        else "access_token_path"
+                    ),
+                    request,
+                    domain=config.older_cookie_domain,
+                )
+            )
+            did_clear_cookies = True
+    if did_clear_cookies:
+        raise_clear_duplicate_session_cookies_exception(
+            "The request contains multiple session cookies. We are clearing the cookie from older_cookie_domain. Session will be refreshed in the next refresh call.",
+            response_mutators=response_mutators,
+        )
+
+
+def has_multiple_cookies_for_token_type(
+    request: BaseRequest, token_type: TokenType
+) -> bool:
+    cookie_string = request.get_header("cookie")
+    if cookie_string is None:
+        return False
+
+    cookies = _parse_cookie_string_from_request_header_allow_duplicates(cookie_string)
+    cookie_name = get_cookie_name_from_token_type(token_type)
+    return cookie_name in cookies and len(cookies[cookie_name]) > 1
+
+
+def _parse_cookie_string_from_request_header_allow_duplicates(
+    cookie_string: str,
+) -> Dict[str, List[str]]:
+    cookies: Dict[str, List[str]] = {}
+    cookie_pairs = cookie_string.split(";")
+    for cookie_pair in cookie_pairs:
+        name_value = cookie_pair.split("=")
+        if len(name_value) != 2:
+            continue
+        name, value = unquote(name_value[0].strip()), unquote(name_value[1].strip())
+        if name in cookies:
+            cookies[name].append(value)
+        else:
+            cookies[name] = [value]
+    return cookies
+
+
+
+
+
+
+
+

Functions

+
+
+def access_token_mutator(access_token: str, front_token: str, config: SessionConfig, transfer_method: TokenTransferMethod, request: BaseRequest) +
+
+
+
+ +Expand source code + +
def access_token_mutator(
+    access_token: str,
+    front_token: str,
+    config: SessionConfig,
+    transfer_method: TokenTransferMethod,
+    request: BaseRequest,
+):
+    def mutator(
+        response: BaseResponse,
+        user_context: Dict[str, Any],
+    ):
+        _set_access_token_in_response(
+            response,
+            access_token,
+            front_token,
+            config,
+            transfer_method,
+            request,
+            user_context,
+        )
+
+    return mutator
+
+
+
+def anti_csrf_response_mutator(value: str) +
+
+
+
+ +Expand source code + +
def anti_csrf_response_mutator(value: str):
+    def mutator(
+        response: BaseResponse,
+        _: Dict[str, Any],
+    ):
+        return _attach_anti_csrf_header(response, value)
+
+    return mutator
+
+
+
+def build_front_token(user_id: str, at_expiry: int, access_token_payload: Optional[Dict[str, Any]] = None) +
+
+
+
+ +Expand source code + +
def build_front_token(
+    user_id: str, at_expiry: int, access_token_payload: Optional[Dict[str, Any]] = None
+):
+    if access_token_payload is None:
+        access_token_payload = {}
+    token_info = {"uid": user_id, "ate": at_expiry, "up": access_token_payload}
+    return utf_base64encode(
+        dumps(token_info, separators=(",", ":"), sort_keys=True), urlsafe=False
+    )
+
+
+ +
+
+
+ +Expand source code + +
def clear_session_cookies_from_older_cookie_domain(
+    request: BaseRequest, config: SessionConfig, user_context: Dict[str, Any]
+):
+    allowed_transfer_method = config.get_token_transfer_method(
+        request, False, user_context
+    )
+    # If the transfer method is 'header', there's no need to clear cookies immediately, even if there are multiple in the request.
+    if allowed_transfer_method == "header":
+        return
+
+    did_clear_cookies = False
+    response_mutators: List[ResponseMutator] = []
+
+    token_types: List[TokenType] = ["access", "refresh"]
+    for token_type in token_types:
+        if has_multiple_cookies_for_token_type(request, token_type):
+            # If a request has multiple session cookies and 'older_cookie_domain' is
+            # unset, we can't identify the correct cookie for refreshing the session.
+            # Using the wrong cookie can cause an infinite refresh loop. To avoid this,
+            # we throw a 500 error asking the user to set 'older_cookie_domain''.
+            if config.older_cookie_domain is None:
+                raise Exception(
+                    "The request contains multiple session cookies. This may happen if you've changed the 'cookie_domain' setting in your configuration. To clear tokens from the previous domain, set 'older_cookie_domain' in your config."
+                )
+
+            log_debug_message(
+                "Clearing duplicate %s cookie with domain %s",
+                token_type,
+                config.cookie_domain,
+            )
+            response_mutators.append(
+                set_cookie_response_mutator(
+                    config,
+                    get_cookie_name_from_token_type(token_type),
+                    "",
+                    0,
+                    (
+                        "refresh_token_path"
+                        if token_type == "refresh"
+                        else "access_token_path"
+                    ),
+                    request,
+                    domain=config.older_cookie_domain,
+                )
+            )
+            did_clear_cookies = True
+    if did_clear_cookies:
+        raise_clear_duplicate_session_cookies_exception(
+            "The request contains multiple session cookies. We are clearing the cookie from older_cookie_domain. Session will be refreshed in the next refresh call.",
+            response_mutators=response_mutators,
+        )
+
+
+
+def clear_session_from_all_token_transfer_methods(response: BaseResponse, recipe: SessionRecipe, request: BaseRequest, user_context: Dict[str, Any]) +
+
+
+
+ +Expand source code + +
def clear_session_from_all_token_transfer_methods(
+    response: BaseResponse,
+    recipe: SessionRecipe,
+    request: BaseRequest,
+    user_context: Dict[str, Any],
+):
+    # We are clearing the session in all transfermethods to be sure to override cookies in case they have been already added to the response.
+    # This is done to handle the following use-case:
+    # If the app overrides signInPOST to check the ban status of the user after the original implementation and throwing an UNAUTHORISED error
+    # In this case: the SDK has attached cookies to the response, but none was sent with the request
+    # We can't know which to clear since we can't reliably query or remove the set-cookie header added to the response (causes issues in some frameworks, i.e.: hapi)
+    # The safe solution in this case is to overwrite all the response cookies/headers with an empty value, which is what we are doing here.
+    for transfer_method in available_token_transfer_methods:
+        _clear_session(response, recipe.config, transfer_method, request, user_context)
+
+
+
+def clear_session_mutator(config: SessionConfig, transfer_method: TokenTransferMethod, request: BaseRequest) +
+
+
+
+ +Expand source code + +
def clear_session_mutator(
+    config: SessionConfig,
+    transfer_method: TokenTransferMethod,
+    request: BaseRequest,
+):
+    def mutator(
+        response: BaseResponse,
+        user_context: Dict[str, Any],
+    ):
+        return _clear_session(response, config, transfer_method, request, user_context)
+
+    return mutator
+
+
+
+def clear_session_response_mutator(config: SessionConfig, transfer_method: TokenTransferMethod, request: BaseRequest) +
+
+
+
+ +Expand source code + +
def clear_session_response_mutator(
+    config: SessionConfig,
+    transfer_method: TokenTransferMethod,
+    request: BaseRequest,
+):
+    def mutator(
+        response: BaseResponse,
+        user_context: Dict[str, Any],
+    ):
+        return _clear_session(response, config, transfer_method, request, user_context)
+
+    return mutator
+
+
+
+def get_anti_csrf_header(request: BaseRequest) +
+
+
+
+ +Expand source code + +
def get_anti_csrf_header(request: BaseRequest):
+    return get_header(request, ANTI_CSRF_HEADER_KEY)
+
+
+ +
+
+
+ +Expand source code + +
def get_cookie(request: BaseRequest, key: str):
+    cookie_val = request.get_cookie(key)
+    if cookie_val is None:
+        return None
+    return unquote(cookie_val)
+
+
+ +
+
+
+ +Expand source code + +
def get_cookie_name_from_token_type(token_type: TokenType):
+    if token_type == "access":
+        return ACCESS_TOKEN_COOKIE_KEY
+    if token_type == "refresh":
+        return REFRESH_TOKEN_COOKIE_KEY
+    raise Exception("Unknown token type, should never happen")
+
+
+
+def get_cors_allowed_headers() +
+
+
+
+ +Expand source code + +
def get_cors_allowed_headers():
+    return [
+        ANTI_CSRF_HEADER_KEY,
+        RID_HEADER_KEY,
+        AUTHORIZATION_HEADER_KEY,
+        AUTH_MODE_HEADER_KEY,
+    ]
+
+
+
+def get_response_header_name_for_token_type(token_type: TokenType) +
+
+
+
+ +Expand source code + +
def get_response_header_name_for_token_type(token_type: TokenType):
+    if token_type == "access":
+        return ACCESS_TOKEN_HEADER_KEY
+    if token_type == "refresh":
+        return REFRESH_TOKEN_HEADER_KEY
+    raise Exception("Unknown token type, should never happen")
+
+
+
+def get_rid_header(request: BaseRequest) +
+
+
+
+ +Expand source code + +
def get_rid_header(request: BaseRequest):
+    return get_header(request, RID_HEADER_KEY)
+
+
+
+def get_token(request: BaseRequest, token_type: TokenType, transfer_method: TokenTransferMethod) ‑> Optional[str] +
+
+
+
+ +Expand source code + +
def get_token(
+    request: BaseRequest,
+    token_type: TokenType,
+    transfer_method: TokenTransferMethod,
+) -> Optional[str]:
+    if transfer_method == "cookie":
+        # Note: Don't use request.get_cookie() as it won't apply unquote() func
+        return get_cookie(request, get_cookie_name_from_token_type(token_type))
+    if transfer_method == "header":
+        value = request.get_header(AUTHORIZATION_HEADER_KEY)
+        if value is None or not value.startswith("Bearer "):
+            return None
+
+        return value[len("Bearer ") :].strip()
+
+    raise Exception("Should never happen: Unknown transferMethod: " + transfer_method)
+
+
+
+def has_multiple_cookies_for_token_type(request: BaseRequest, token_type: TokenType) ‑> bool +
+
+
+
+ +Expand source code + +
def has_multiple_cookies_for_token_type(
+    request: BaseRequest, token_type: TokenType
+) -> bool:
+    cookie_string = request.get_header("cookie")
+    if cookie_string is None:
+        return False
+
+    cookies = _parse_cookie_string_from_request_header_allow_duplicates(cookie_string)
+    cookie_name = get_cookie_name_from_token_type(token_type)
+    return cookie_name in cookies and len(cookies[cookie_name]) > 1
+
+
+
+def remove_header(response: BaseResponse, key: str) +
+
+
+
+ +Expand source code + +
def remove_header(response: BaseResponse, key: str):
+    if response.get_header(key) is not None:
+        response.remove_header(key)
+
+
+ +
+
+
+ +Expand source code + +
def set_cookie_response_mutator(
+    config: SessionConfig,
+    key: str,
+    value: str,
+    expires: int,
+    path_type: Literal["refresh_token_path", "access_token_path"],
+    request: BaseRequest,
+    domain: Optional[str] = None,
+):
+    domain = domain if domain is not None else config.cookie_domain
+
+    def mutator(response: BaseResponse, user_context: Dict[str, Any]):
+        return _set_cookie(
+            response,
+            config,
+            key,
+            value,
+            expires,
+            path_type,
+            request,
+            domain,
+            user_context,
+        )
+
+    return mutator
+
+
+
+def set_header(response: BaseResponse, key: str, value: str, allow_duplicate: bool) +
+
+
+
+ +Expand source code + +
def set_header(response: BaseResponse, key: str, value: str, allow_duplicate: bool):
+    if allow_duplicate:
+        old_value = response.get_header(key)
+        if old_value is None:
+            response.set_header(key, value)
+        else:
+            response.set_header(key, old_value + "," + value)
+    else:
+        response.set_header(key, value)
+
+
+
+def set_token_in_header(response: BaseResponse, name: str, value: str) +
+
+
+
+ +Expand source code + +
def set_token_in_header(response: BaseResponse, name: str, value: str):
+    set_header(response, name, value, allow_duplicate=False)
+    set_header(response, ACCESS_CONTROL_EXPOSE_HEADERS, name, allow_duplicate=True)
+
+
+
+def token_response_mutator(config: SessionConfig, token_type: TokenType, value: str, expires: int, transfer_method: TokenTransferMethod, request: BaseRequest) +
+
+
+
+ +Expand source code + +
def token_response_mutator(
+    config: SessionConfig,
+    token_type: TokenType,
+    value: str,
+    expires: int,
+    transfer_method: TokenTransferMethod,
+    request: BaseRequest,
+):
+    def mutator(
+        response: BaseResponse,
+        user_context: Dict[str, Any],
+    ):
+        _set_token(
+            response,
+            config,
+            token_type,
+            value,
+            expires,
+            transfer_method,
+            request,
+            user_context,
+        )
+
+    return mutator
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/session/exceptions.html b/html/supertokens_python/recipe/session/exceptions.html new file mode 100644 index 000000000..83f9bfd56 --- /dev/null +++ b/html/supertokens_python/recipe/session/exceptions.html @@ -0,0 +1,470 @@ + + + + + + +supertokens_python.recipe.session.exceptions API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.session.exceptions

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Any, Dict, List, NoReturn, Optional, Union
+
+from supertokens_python.exceptions import SuperTokensError
+
+if TYPE_CHECKING:
+    from .interfaces import ResponseMutator
+
+
+def raise_token_theft_exception(user_id: str, session_handle: str) -> NoReturn:
+    raise TokenTheftError(user_id, session_handle)
+
+
+def raise_try_refresh_token_exception(ex: Union[str, Exception]) -> NoReturn:
+    if isinstance(ex, SuperTokensError):
+        raise ex
+
+    raise TryRefreshTokenError(ex) from None
+
+
+def raise_unauthorised_exception(
+    msg: str,
+    clear_tokens: bool = True,
+    response_mutators: Optional[List[ResponseMutator]] = None,
+) -> NoReturn:
+    err = UnauthorisedError(msg, clear_tokens)
+
+    if response_mutators is not None:
+        err.response_mutators.extend(response_mutators)
+
+    raise err
+
+
+def raise_clear_duplicate_session_cookies_exception(
+    msg: str, response_mutators: List[ResponseMutator]
+) -> NoReturn:
+    err = ClearDuplicateSessionCookiesError(msg)
+    err.response_mutators.extend(response_mutators)
+    raise err
+
+
+class SuperTokensSessionError(SuperTokensError):
+    def __init__(self, *args: Any, **kwargs: Any) -> None:
+        super().__init__(*args, **kwargs)
+        self.response_mutators: List[ResponseMutator] = []
+
+
+class TokenTheftError(SuperTokensSessionError):
+    def __init__(self, user_id: str, session_handle: str):
+        super().__init__("token theft detected")
+        self.user_id = user_id
+        self.session_handle = session_handle
+
+
+class UnauthorisedError(SuperTokensSessionError):
+    def __init__(self, msg: str, clear_tokens: bool = True):
+        super().__init__(msg)
+        self.clear_tokens = clear_tokens
+
+
+class TryRefreshTokenError(SuperTokensSessionError):
+    pass
+
+
+class InvalidClaimsError(SuperTokensSessionError):
+    def __init__(self, msg: str, payload: List[ClaimValidationError]):
+        super().__init__(msg)
+        self.payload = payload
+
+
+class ClaimValidationError:
+    def __init__(self, id_: str, reason: Optional[Dict[str, Any]]):
+        self.id = id_
+        self.reason = reason
+
+    def to_json(self):
+        result: Dict[str, Any] = {"id": self.id}
+        if self.reason is not None:
+            result["reason"] = self.reason
+
+        return result
+
+
+def raise_invalid_claims_exception(msg: str, payload: List[ClaimValidationError]):
+    raise InvalidClaimsError(msg, payload)
+
+
+class ClearDuplicateSessionCookiesError(SuperTokensSessionError):
+    pass
+
+
+
+
+
+
+
+

Functions

+
+
+def raise_clear_duplicate_session_cookies_exception(msg: str, response_mutators: List[ResponseMutator]) ‑> NoReturn +
+
+
+
+ +Expand source code + +
def raise_clear_duplicate_session_cookies_exception(
+    msg: str, response_mutators: List[ResponseMutator]
+) -> NoReturn:
+    err = ClearDuplicateSessionCookiesError(msg)
+    err.response_mutators.extend(response_mutators)
+    raise err
+
+
+
+def raise_invalid_claims_exception(msg: str, payload: List[ClaimValidationError]) +
+
+
+
+ +Expand source code + +
def raise_invalid_claims_exception(msg: str, payload: List[ClaimValidationError]):
+    raise InvalidClaimsError(msg, payload)
+
+
+
+def raise_token_theft_exception(user_id: str, session_handle: str) ‑> NoReturn +
+
+
+
+ +Expand source code + +
def raise_token_theft_exception(user_id: str, session_handle: str) -> NoReturn:
+    raise TokenTheftError(user_id, session_handle)
+
+
+
+def raise_try_refresh_token_exception(ex: Union[str, Exception]) ‑> NoReturn +
+
+
+
+ +Expand source code + +
def raise_try_refresh_token_exception(ex: Union[str, Exception]) -> NoReturn:
+    if isinstance(ex, SuperTokensError):
+        raise ex
+
+    raise TryRefreshTokenError(ex) from None
+
+
+
+def raise_unauthorised_exception(msg: str, clear_tokens: bool = True, response_mutators: Optional[List[ResponseMutator]] = None) ‑> NoReturn +
+
+
+
+ +Expand source code + +
def raise_unauthorised_exception(
+    msg: str,
+    clear_tokens: bool = True,
+    response_mutators: Optional[List[ResponseMutator]] = None,
+) -> NoReturn:
+    err = UnauthorisedError(msg, clear_tokens)
+
+    if response_mutators is not None:
+        err.response_mutators.extend(response_mutators)
+
+    raise err
+
+
+
+
+
+

Classes

+
+
+class ClaimValidationError +(id_: str, reason: Optional[Dict[str, Any]]) +
+
+
+
+ +Expand source code + +
class ClaimValidationError:
+    def __init__(self, id_: str, reason: Optional[Dict[str, Any]]):
+        self.id = id_
+        self.reason = reason
+
+    def to_json(self):
+        result: Dict[str, Any] = {"id": self.id}
+        if self.reason is not None:
+            result["reason"] = self.reason
+
+        return result
+
+

Methods

+
+
+def to_json(self) +
+
+
+
+ +Expand source code + +
def to_json(self):
+    result: Dict[str, Any] = {"id": self.id}
+    if self.reason is not None:
+        result["reason"] = self.reason
+
+    return result
+
+
+
+
+
+class ClearDuplicateSessionCookiesError +(*args: Any, **kwargs: Any) +
+
+

Common base class for all non-exit exceptions.

+
+ +Expand source code + +
class ClearDuplicateSessionCookiesError(SuperTokensSessionError):
+    pass
+
+

Ancestors

+ +
+
+class InvalidClaimsError +(msg: str, payload: List[ClaimValidationError]) +
+
+

Common base class for all non-exit exceptions.

+
+ +Expand source code + +
class InvalidClaimsError(SuperTokensSessionError):
+    def __init__(self, msg: str, payload: List[ClaimValidationError]):
+        super().__init__(msg)
+        self.payload = payload
+
+

Ancestors

+ +
+
+class SuperTokensSessionError +(*args: Any, **kwargs: Any) +
+
+

Common base class for all non-exit exceptions.

+
+ +Expand source code + +
class SuperTokensSessionError(SuperTokensError):
+    def __init__(self, *args: Any, **kwargs: Any) -> None:
+        super().__init__(*args, **kwargs)
+        self.response_mutators: List[ResponseMutator] = []
+
+

Ancestors

+ +

Subclasses

+ +
+
+class TokenTheftError +(user_id: str, session_handle: str) +
+
+

Common base class for all non-exit exceptions.

+
+ +Expand source code + +
class TokenTheftError(SuperTokensSessionError):
+    def __init__(self, user_id: str, session_handle: str):
+        super().__init__("token theft detected")
+        self.user_id = user_id
+        self.session_handle = session_handle
+
+

Ancestors

+ +
+
+class TryRefreshTokenError +(*args: Any, **kwargs: Any) +
+
+

Common base class for all non-exit exceptions.

+
+ +Expand source code + +
class TryRefreshTokenError(SuperTokensSessionError):
+    pass
+
+

Ancestors

+ +
+
+class UnauthorisedError +(msg: str, clear_tokens: bool = True) +
+
+

Common base class for all non-exit exceptions.

+
+ +Expand source code + +
class UnauthorisedError(SuperTokensSessionError):
+    def __init__(self, msg: str, clear_tokens: bool = True):
+        super().__init__(msg)
+        self.clear_tokens = clear_tokens
+
+

Ancestors

+ +
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/session/framework/django/asyncio/index.html b/html/supertokens_python/recipe/session/framework/django/asyncio/index.html new file mode 100644 index 000000000..394e376b1 --- /dev/null +++ b/html/supertokens_python/recipe/session/framework/django/asyncio/index.html @@ -0,0 +1,223 @@ + + + + + + +supertokens_python.recipe.session.framework.django.asyncio API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.session.framework.django.asyncio

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from functools import wraps
+from typing import Any, Callable, Dict, List, Optional, TypeVar, Union, cast
+
+from supertokens_python import Supertokens
+from supertokens_python.exceptions import SuperTokensError
+from supertokens_python.framework.django.django_request import DjangoRequest
+from supertokens_python.framework.django.django_response import DjangoResponse
+from supertokens_python.recipe.session import SessionContainer, SessionRecipe
+from supertokens_python.recipe.session.interfaces import SessionClaimValidator
+from supertokens_python.utils import set_request_in_user_context_if_not_defined
+from supertokens_python.types import MaybeAwaitable
+
+_T = TypeVar("_T", bound=Callable[..., Any])
+
+
+def verify_session(
+    anti_csrf_check: Union[bool, None] = None,
+    session_required: bool = True,
+    check_database: bool = False,
+    override_global_claim_validators: Optional[
+        Callable[
+            [List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
+            MaybeAwaitable[List[SessionClaimValidator]],
+        ]
+    ] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> Callable[[_T], _T]:
+    _ = user_context
+
+    def session_verify(f: _T) -> _T:
+        from django.http import HttpRequest
+
+        @wraps(f)
+        async def wrapped_function(request: HttpRequest, *args: Any, **kwargs: Any):
+            nonlocal user_context
+            from django.http import JsonResponse
+
+            baseRequest = DjangoRequest(request)
+            try:
+                user_context = set_request_in_user_context_if_not_defined(
+                    user_context, baseRequest
+                )
+
+                recipe = SessionRecipe.get_instance()
+                session = await recipe.verify_session(
+                    baseRequest,
+                    anti_csrf_check,
+                    session_required,
+                    check_database,
+                    override_global_claim_validators,
+                    user_context,
+                )
+                if session is None:
+                    if session_required:
+                        raise Exception("Should never come here")
+                    baseRequest.set_session_as_none()
+                else:
+                    baseRequest.set_session(session)
+                return await f(baseRequest.request, *args, **kwargs)
+            except SuperTokensError as e:
+                response = DjangoResponse(JsonResponse({}))
+                user_context = set_request_in_user_context_if_not_defined(
+                    user_context, baseRequest
+                )
+                result = await Supertokens.get_instance().handle_supertokens_error(
+                    DjangoRequest(request), e, response, user_context
+                )
+                if isinstance(result, DjangoResponse):
+                    return result.response
+                raise Exception("Should never come here")
+
+        return cast(_T, wrapped_function)
+
+    return session_verify
+
+
+
+
+
+
+
+

Functions

+
+
+def verify_session(anti_csrf_check: Optional[bool] = None, session_required: bool = True, check_database: bool = False, override_global_claim_validators: Optional[Callable[[List[SessionClaimValidator], SessionContainer, Dict[str, Any]], Union[Awaitable[List[SessionClaimValidator]], List[SessionClaimValidator]]]] = None, user_context: Optional[Dict[str, Any]] = None) ‑> Callable[[~_T], ~_T] +
+
+
+
+ +Expand source code + +
def verify_session(
+    anti_csrf_check: Union[bool, None] = None,
+    session_required: bool = True,
+    check_database: bool = False,
+    override_global_claim_validators: Optional[
+        Callable[
+            [List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
+            MaybeAwaitable[List[SessionClaimValidator]],
+        ]
+    ] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> Callable[[_T], _T]:
+    _ = user_context
+
+    def session_verify(f: _T) -> _T:
+        from django.http import HttpRequest
+
+        @wraps(f)
+        async def wrapped_function(request: HttpRequest, *args: Any, **kwargs: Any):
+            nonlocal user_context
+            from django.http import JsonResponse
+
+            baseRequest = DjangoRequest(request)
+            try:
+                user_context = set_request_in_user_context_if_not_defined(
+                    user_context, baseRequest
+                )
+
+                recipe = SessionRecipe.get_instance()
+                session = await recipe.verify_session(
+                    baseRequest,
+                    anti_csrf_check,
+                    session_required,
+                    check_database,
+                    override_global_claim_validators,
+                    user_context,
+                )
+                if session is None:
+                    if session_required:
+                        raise Exception("Should never come here")
+                    baseRequest.set_session_as_none()
+                else:
+                    baseRequest.set_session(session)
+                return await f(baseRequest.request, *args, **kwargs)
+            except SuperTokensError as e:
+                response = DjangoResponse(JsonResponse({}))
+                user_context = set_request_in_user_context_if_not_defined(
+                    user_context, baseRequest
+                )
+                result = await Supertokens.get_instance().handle_supertokens_error(
+                    DjangoRequest(request), e, response, user_context
+                )
+                if isinstance(result, DjangoResponse):
+                    return result.response
+                raise Exception("Should never come here")
+
+        return cast(_T, wrapped_function)
+
+    return session_verify
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/session/framework/django/index.html b/html/supertokens_python/recipe/session/framework/django/index.html new file mode 100644 index 000000000..e83af2d25 --- /dev/null +++ b/html/supertokens_python/recipe/session/framework/django/index.html @@ -0,0 +1,88 @@ + + + + + + +supertokens_python.recipe.session.framework.django API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.session.framework.django

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+
+
+

Sub-modules

+
+
supertokens_python.recipe.session.framework.django.asyncio
+
+
+
+
supertokens_python.recipe.session.framework.django.syncio
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/session/framework/django/syncio/index.html b/html/supertokens_python/recipe/session/framework/django/syncio/index.html new file mode 100644 index 000000000..978a855d5 --- /dev/null +++ b/html/supertokens_python/recipe/session/framework/django/syncio/index.html @@ -0,0 +1,232 @@ + + + + + + +supertokens_python.recipe.session.framework.django.syncio API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.session.framework.django.syncio

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from functools import wraps
+from typing import Any, Callable, Dict, TypeVar, Union, cast, List, Optional
+
+from supertokens_python import Supertokens
+from supertokens_python.async_to_sync_wrapper import sync
+from supertokens_python.exceptions import SuperTokensError
+from supertokens_python.framework.django.django_request import DjangoRequest
+from supertokens_python.framework.django.django_response import DjangoResponse
+from supertokens_python.recipe.session import SessionRecipe, SessionContainer
+from supertokens_python.recipe.session.interfaces import SessionClaimValidator
+from supertokens_python.utils import set_request_in_user_context_if_not_defined
+from supertokens_python.types import MaybeAwaitable
+
+_T = TypeVar("_T", bound=Callable[..., Any])
+
+
+def verify_session(
+    anti_csrf_check: Union[bool, None] = None,
+    session_required: bool = True,
+    check_database: bool = False,
+    override_global_claim_validators: Optional[
+        Callable[
+            [List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
+            MaybeAwaitable[List[SessionClaimValidator]],
+        ]
+    ] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> Callable[[_T], _T]:
+    _ = user_context
+
+    def session_verify(f: _T) -> _T:
+        from django.http import HttpRequest
+
+        @wraps(f)
+        def wrapped_function(request: HttpRequest, *args: Any, **kwargs: Any):
+            nonlocal user_context
+            from django.http import JsonResponse
+
+            baseRequest = DjangoRequest(request)
+            try:
+                user_context = set_request_in_user_context_if_not_defined(
+                    user_context, baseRequest
+                )
+
+                recipe = SessionRecipe.get_instance()
+                session = sync(
+                    recipe.verify_session(
+                        baseRequest,
+                        anti_csrf_check,
+                        session_required,
+                        check_database,
+                        override_global_claim_validators,
+                        user_context,
+                    )
+                )
+                if session is None:
+                    if session_required:
+                        raise Exception("Should never come here")
+                    baseRequest.set_session_as_none()
+                else:
+                    baseRequest.set_session(session)
+                return f(baseRequest.request, *args, **kwargs)
+            except SuperTokensError as e:
+                response = DjangoResponse(JsonResponse({}))
+                user_context = set_request_in_user_context_if_not_defined(
+                    user_context, baseRequest
+                )
+                result = sync(
+                    Supertokens.get_instance().handle_supertokens_error(
+                        DjangoRequest(request), e, response, user_context
+                    )
+                )
+                if isinstance(result, DjangoResponse):
+                    return result.response
+                raise Exception("Should never come here")
+
+        return cast(_T, wrapped_function)
+
+    return session_verify
+
+
+
+
+
+
+
+

Functions

+
+
+def verify_session(anti_csrf_check: Optional[bool] = None, session_required: bool = True, check_database: bool = False, override_global_claim_validators: Optional[Callable[[List[SessionClaimValidator], SessionContainer, Dict[str, Any]], Union[Awaitable[List[SessionClaimValidator]], List[SessionClaimValidator]]]] = None, user_context: Optional[Dict[str, Any]] = None) ‑> Callable[[~_T], ~_T] +
+
+
+
+ +Expand source code + +
def verify_session(
+    anti_csrf_check: Union[bool, None] = None,
+    session_required: bool = True,
+    check_database: bool = False,
+    override_global_claim_validators: Optional[
+        Callable[
+            [List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
+            MaybeAwaitable[List[SessionClaimValidator]],
+        ]
+    ] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> Callable[[_T], _T]:
+    _ = user_context
+
+    def session_verify(f: _T) -> _T:
+        from django.http import HttpRequest
+
+        @wraps(f)
+        def wrapped_function(request: HttpRequest, *args: Any, **kwargs: Any):
+            nonlocal user_context
+            from django.http import JsonResponse
+
+            baseRequest = DjangoRequest(request)
+            try:
+                user_context = set_request_in_user_context_if_not_defined(
+                    user_context, baseRequest
+                )
+
+                recipe = SessionRecipe.get_instance()
+                session = sync(
+                    recipe.verify_session(
+                        baseRequest,
+                        anti_csrf_check,
+                        session_required,
+                        check_database,
+                        override_global_claim_validators,
+                        user_context,
+                    )
+                )
+                if session is None:
+                    if session_required:
+                        raise Exception("Should never come here")
+                    baseRequest.set_session_as_none()
+                else:
+                    baseRequest.set_session(session)
+                return f(baseRequest.request, *args, **kwargs)
+            except SuperTokensError as e:
+                response = DjangoResponse(JsonResponse({}))
+                user_context = set_request_in_user_context_if_not_defined(
+                    user_context, baseRequest
+                )
+                result = sync(
+                    Supertokens.get_instance().handle_supertokens_error(
+                        DjangoRequest(request), e, response, user_context
+                    )
+                )
+                if isinstance(result, DjangoResponse):
+                    return result.response
+                raise Exception("Should never come here")
+
+        return cast(_T, wrapped_function)
+
+    return session_verify
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/session/framework/fastapi/index.html b/html/supertokens_python/recipe/session/framework/fastapi/index.html new file mode 100644 index 000000000..f2453f7c2 --- /dev/null +++ b/html/supertokens_python/recipe/session/framework/fastapi/index.html @@ -0,0 +1,240 @@ + + + + + + +supertokens_python.recipe.session.framework.fastapi API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.session.framework.fastapi

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+import json
+
+from typing import Any, Callable, Coroutine, Dict, Union, List, Optional
+
+from supertokens_python import Supertokens
+from supertokens_python.framework.fastapi.fastapi_request import FastApiRequest
+from supertokens_python.framework.fastapi.fastapi_response import FastApiResponse
+from supertokens_python.recipe.session import SessionRecipe
+from supertokens_python.exceptions import SuperTokensError
+from supertokens_python.types import MaybeAwaitable
+from fastapi.responses import JSONResponse
+
+from ...interfaces import SessionContainer, SessionClaimValidator
+from supertokens_python.utils import (
+    set_request_in_user_context_if_not_defined,
+    default_user_context,
+)
+
+from fastapi import Request
+
+
+def verify_session(
+    anti_csrf_check: Union[bool, None] = None,
+    session_required: bool = True,
+    check_database: bool = False,
+    override_global_claim_validators: Optional[
+        Callable[
+            [List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
+            MaybeAwaitable[List[SessionClaimValidator]],
+        ]
+    ] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> Callable[..., Coroutine[Any, Any, Union[SessionContainer, None]]]:
+    _ = user_context
+
+    async def func(request: Request) -> Union[SessionContainer, None]:
+        nonlocal user_context
+        base_req = FastApiRequest(request)
+        user_context = set_request_in_user_context_if_not_defined(
+            user_context, base_req
+        )
+
+        recipe = SessionRecipe.get_instance()
+        session = await recipe.verify_session(
+            base_req,
+            anti_csrf_check,
+            session_required,
+            check_database,
+            override_global_claim_validators,
+            user_context,
+        )
+        if session is None:
+            if session_required:
+                raise Exception("Should never come here")
+            base_req.set_session_as_none()
+        else:
+            base_req.set_session(session)
+        return base_req.get_session()
+
+    return func
+
+
+async def session_exception_handler(
+    request: Request, exc: SuperTokensError
+) -> JSONResponse:
+    """FastAPI exceptional handler for errors raised by Supertokens SDK when not using middleware
+
+    Usage: `app.add_exception_handler(SuperTokensError, st_exception_handler)`
+    """
+    base_req = FastApiRequest(request)
+    base_res = FastApiResponse(JSONResponse())
+    user_context = default_user_context(base_req)
+    result = await Supertokens.get_instance().handle_supertokens_error(
+        base_req, exc, base_res, user_context
+    )
+    if isinstance(result, FastApiResponse):
+        body = json.loads(result.response.body)
+        return JSONResponse(body, status_code=result.response.status_code)
+
+    raise Exception("Should never come here")
+
+
+
+
+
+
+
+

Functions

+
+
+async def session_exception_handler(request: starlette.requests.Request, exc: SuperTokensError) ‑> starlette.responses.JSONResponse +
+
+

FastAPI exceptional handler for errors raised by Supertokens SDK when not using middleware

+

Usage: app.add_exception_handler(SuperTokensError, st_exception_handler)

+
+ +Expand source code + +
async def session_exception_handler(
+    request: Request, exc: SuperTokensError
+) -> JSONResponse:
+    """FastAPI exceptional handler for errors raised by Supertokens SDK when not using middleware
+
+    Usage: `app.add_exception_handler(SuperTokensError, st_exception_handler)`
+    """
+    base_req = FastApiRequest(request)
+    base_res = FastApiResponse(JSONResponse())
+    user_context = default_user_context(base_req)
+    result = await Supertokens.get_instance().handle_supertokens_error(
+        base_req, exc, base_res, user_context
+    )
+    if isinstance(result, FastApiResponse):
+        body = json.loads(result.response.body)
+        return JSONResponse(body, status_code=result.response.status_code)
+
+    raise Exception("Should never come here")
+
+
+
+def verify_session(anti_csrf_check: Optional[bool] = None, session_required: bool = True, check_database: bool = False, override_global_claim_validators: Optional[Callable[[List[SessionClaimValidator], SessionContainer, Dict[str, Any]], Union[Awaitable[List[SessionClaimValidator]], List[SessionClaimValidator]]]] = None, user_context: Optional[Dict[str, Any]] = None) ‑> Callable[..., Coroutine[Any, Any, Optional[SessionContainer]]] +
+
+
+
+ +Expand source code + +
def verify_session(
+    anti_csrf_check: Union[bool, None] = None,
+    session_required: bool = True,
+    check_database: bool = False,
+    override_global_claim_validators: Optional[
+        Callable[
+            [List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
+            MaybeAwaitable[List[SessionClaimValidator]],
+        ]
+    ] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> Callable[..., Coroutine[Any, Any, Union[SessionContainer, None]]]:
+    _ = user_context
+
+    async def func(request: Request) -> Union[SessionContainer, None]:
+        nonlocal user_context
+        base_req = FastApiRequest(request)
+        user_context = set_request_in_user_context_if_not_defined(
+            user_context, base_req
+        )
+
+        recipe = SessionRecipe.get_instance()
+        session = await recipe.verify_session(
+            base_req,
+            anti_csrf_check,
+            session_required,
+            check_database,
+            override_global_claim_validators,
+            user_context,
+        )
+        if session is None:
+            if session_required:
+                raise Exception("Should never come here")
+            base_req.set_session_as_none()
+        else:
+            base_req.set_session(session)
+        return base_req.get_session()
+
+    return func
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/session/framework/flask/index.html b/html/supertokens_python/recipe/session/framework/flask/index.html new file mode 100644 index 000000000..6c578e6d5 --- /dev/null +++ b/html/supertokens_python/recipe/session/framework/flask/index.html @@ -0,0 +1,228 @@ + + + + + + +supertokens_python.recipe.session.framework.flask API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.session.framework.flask

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from functools import wraps
+from typing import Any, Callable, Dict, TypeVar, Union, cast, List, Optional
+
+from supertokens_python import Supertokens
+from supertokens_python.async_to_sync_wrapper import sync
+from supertokens_python.framework.flask.flask_request import FlaskRequest
+from supertokens_python.framework.flask.flask_response import FlaskResponse
+from supertokens_python.recipe.session import SessionRecipe, SessionContainer
+from supertokens_python.exceptions import SuperTokensError
+from supertokens_python.recipe.session.interfaces import SessionClaimValidator
+from supertokens_python.utils import set_request_in_user_context_if_not_defined
+from supertokens_python.types import MaybeAwaitable
+
+_T = TypeVar("_T", bound=Callable[..., Any])
+
+
+def verify_session(
+    anti_csrf_check: Union[bool, None] = None,
+    session_required: bool = True,
+    check_database: bool = False,
+    override_global_claim_validators: Optional[
+        Callable[
+            [List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
+            MaybeAwaitable[List[SessionClaimValidator]],
+        ]
+    ] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> Callable[[_T], _T]:
+    _ = user_context
+
+    def session_verify(f: _T) -> _T:
+        @wraps(f)
+        def wrapped_function(*args: Any, **kwargs: Any):
+            nonlocal user_context
+            from flask import make_response, request
+
+            base_req = FlaskRequest(request)
+            user_context = set_request_in_user_context_if_not_defined(
+                user_context, base_req
+            )
+
+            recipe = SessionRecipe.get_instance()
+
+            try:
+                session = sync(
+                    recipe.verify_session(
+                        base_req,
+                        anti_csrf_check,
+                        session_required,
+                        check_database,
+                        override_global_claim_validators,
+                        user_context,
+                    )
+                )
+                if session is None:
+                    if session_required:
+                        raise Exception("Should never come here")
+                    base_req.set_session_as_none()
+                else:
+                    base_req.set_session(session)
+
+                response = f(*args, **kwargs)
+                return make_response(response) if response is not None else None
+            except SuperTokensError as e:
+                response = FlaskResponse(make_response())
+                result = sync(
+                    Supertokens.get_instance().handle_supertokens_error(
+                        base_req, e, response, user_context
+                    )
+                )
+                if isinstance(result, FlaskResponse):
+                    return result.response
+                raise Exception("Should never come here")
+
+        return cast(_T, wrapped_function)
+
+    return session_verify
+
+
+
+
+
+
+
+

Functions

+
+
+def verify_session(anti_csrf_check: Optional[bool] = None, session_required: bool = True, check_database: bool = False, override_global_claim_validators: Optional[Callable[[List[SessionClaimValidator], SessionContainer, Dict[str, Any]], Union[Awaitable[List[SessionClaimValidator]], List[SessionClaimValidator]]]] = None, user_context: Optional[Dict[str, Any]] = None) ‑> Callable[[~_T], ~_T] +
+
+
+
+ +Expand source code + +
def verify_session(
+    anti_csrf_check: Union[bool, None] = None,
+    session_required: bool = True,
+    check_database: bool = False,
+    override_global_claim_validators: Optional[
+        Callable[
+            [List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
+            MaybeAwaitable[List[SessionClaimValidator]],
+        ]
+    ] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> Callable[[_T], _T]:
+    _ = user_context
+
+    def session_verify(f: _T) -> _T:
+        @wraps(f)
+        def wrapped_function(*args: Any, **kwargs: Any):
+            nonlocal user_context
+            from flask import make_response, request
+
+            base_req = FlaskRequest(request)
+            user_context = set_request_in_user_context_if_not_defined(
+                user_context, base_req
+            )
+
+            recipe = SessionRecipe.get_instance()
+
+            try:
+                session = sync(
+                    recipe.verify_session(
+                        base_req,
+                        anti_csrf_check,
+                        session_required,
+                        check_database,
+                        override_global_claim_validators,
+                        user_context,
+                    )
+                )
+                if session is None:
+                    if session_required:
+                        raise Exception("Should never come here")
+                    base_req.set_session_as_none()
+                else:
+                    base_req.set_session(session)
+
+                response = f(*args, **kwargs)
+                return make_response(response) if response is not None else None
+            except SuperTokensError as e:
+                response = FlaskResponse(make_response())
+                result = sync(
+                    Supertokens.get_instance().handle_supertokens_error(
+                        base_req, e, response, user_context
+                    )
+                )
+                if isinstance(result, FlaskResponse):
+                    return result.response
+                raise Exception("Should never come here")
+
+        return cast(_T, wrapped_function)
+
+    return session_verify
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/session/framework/index.html b/html/supertokens_python/recipe/session/framework/index.html new file mode 100644 index 000000000..5f690181d --- /dev/null +++ b/html/supertokens_python/recipe/session/framework/index.html @@ -0,0 +1,93 @@ + + + + + + +supertokens_python.recipe.session.framework API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.session.framework

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+
+
+

Sub-modules

+
+
supertokens_python.recipe.session.framework.django
+
+
+
+
supertokens_python.recipe.session.framework.fastapi
+
+
+
+
supertokens_python.recipe.session.framework.flask
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/session/index.html b/html/supertokens_python/recipe/session/index.html new file mode 100644 index 000000000..f00f4525a --- /dev/null +++ b/html/supertokens_python/recipe/session/index.html @@ -0,0 +1,285 @@ + + + + + + +supertokens_python.recipe.session API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.session

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Any, Callable, Dict, Union
+
+from typing_extensions import Literal
+
+if TYPE_CHECKING:
+    from ...recipe_module import RecipeModule
+    from supertokens_python.supertokens import AppInfo, BaseRequest
+    from .utils import TokenTransferMethod
+
+from . import exceptions as ex
+from . import interfaces, utils
+from .recipe import SessionRecipe
+
+InputErrorHandlers = utils.InputErrorHandlers
+InputOverrideConfig = utils.InputOverrideConfig
+SessionContainer = interfaces.SessionContainer
+exceptions = ex
+
+
+def init(
+    cookie_domain: Union[str, None] = None,
+    older_cookie_domain: Union[str, None] = None,
+    cookie_secure: Union[bool, None] = None,
+    cookie_same_site: Union[Literal["lax", "none", "strict"], None] = None,
+    session_expired_status_code: Union[int, None] = None,
+    anti_csrf: Union[Literal["VIA_TOKEN", "VIA_CUSTOM_HEADER", "NONE"], None] = None,
+    get_token_transfer_method: Union[
+        Callable[
+            [BaseRequest, bool, Dict[str, Any]],
+            Union[TokenTransferMethod, Literal["any"]],
+        ],
+        None,
+    ] = None,
+    error_handlers: Union[InputErrorHandlers, None] = None,
+    override: Union[InputOverrideConfig, None] = None,
+    invalid_claim_status_code: Union[int, None] = None,
+    use_dynamic_access_token_signing_key: Union[bool, None] = None,
+    expose_access_token_to_frontend_in_cookie_based_auth: Union[bool, None] = None,
+    jwks_refresh_interval_sec: Union[int, None] = None,
+) -> Callable[[AppInfo], RecipeModule]:
+    return SessionRecipe.init(
+        cookie_domain,
+        older_cookie_domain,
+        cookie_secure,
+        cookie_same_site,
+        session_expired_status_code,
+        anti_csrf,
+        get_token_transfer_method,
+        error_handlers,
+        override,
+        invalid_claim_status_code,
+        use_dynamic_access_token_signing_key,
+        expose_access_token_to_frontend_in_cookie_based_auth,
+        jwks_refresh_interval_sec,
+    )
+
+
+
+

Sub-modules

+
+
supertokens_python.recipe.session.access_token
+
+
+
+
supertokens_python.recipe.session.api
+
+
+
+
supertokens_python.recipe.session.asyncio
+
+
+
+
supertokens_python.recipe.session.claim_base_classes
+
+
+
+
supertokens_python.recipe.session.claims
+
+
+
+
supertokens_python.recipe.session.constants
+
+
+
+
supertokens_python.recipe.session.cookie_and_header
+
+
+
+
supertokens_python.recipe.session.exceptions
+
+
+
+
supertokens_python.recipe.session.framework
+
+
+
+
supertokens_python.recipe.session.interfaces
+
+
+
+
supertokens_python.recipe.session.jwks
+
+
+
+
supertokens_python.recipe.session.jwt
+
+
+
+
supertokens_python.recipe.session.recipe
+
+
+
+
supertokens_python.recipe.session.recipe_implementation
+
+
+
+
supertokens_python.recipe.session.session_class
+
+
+
+
supertokens_python.recipe.session.session_functions
+
+
+
+
supertokens_python.recipe.session.session_request_functions
+
+
+
+
supertokens_python.recipe.session.syncio
+
+
+
+
supertokens_python.recipe.session.utils
+
+
+
+
+
+
+
+
+

Functions

+
+
+def init(cookie_domain: Union[str, None] = None, older_cookie_domain: Union[str, None] = None, cookie_secure: Union[bool, None] = None, cookie_same_site: "Union[Literal[('lax', 'none', 'strict')], None]" = None, session_expired_status_code: Union[int, None] = None, anti_csrf: "Union[Literal[('VIA_TOKEN', 'VIA_CUSTOM_HEADER', 'NONE')], None]" = None, get_token_transfer_method: "Union[Callable[[BaseRequest, bool, Dict[str, Any]], Union[TokenTransferMethod, Literal['any']]], None]" = None, error_handlers: Union[InputErrorHandlers, None] = None, override: Union[InputOverrideConfig, None] = None, invalid_claim_status_code: Union[int, None] = None, use_dynamic_access_token_signing_key: Union[bool, None] = None, expose_access_token_to_frontend_in_cookie_based_auth: Union[bool, None] = None, jwks_refresh_interval_sec: Union[int, None] = None) ‑> Callable[[AppInfo], RecipeModule] +
+
+
+
+ +Expand source code + +
def init(
+    cookie_domain: Union[str, None] = None,
+    older_cookie_domain: Union[str, None] = None,
+    cookie_secure: Union[bool, None] = None,
+    cookie_same_site: Union[Literal["lax", "none", "strict"], None] = None,
+    session_expired_status_code: Union[int, None] = None,
+    anti_csrf: Union[Literal["VIA_TOKEN", "VIA_CUSTOM_HEADER", "NONE"], None] = None,
+    get_token_transfer_method: Union[
+        Callable[
+            [BaseRequest, bool, Dict[str, Any]],
+            Union[TokenTransferMethod, Literal["any"]],
+        ],
+        None,
+    ] = None,
+    error_handlers: Union[InputErrorHandlers, None] = None,
+    override: Union[InputOverrideConfig, None] = None,
+    invalid_claim_status_code: Union[int, None] = None,
+    use_dynamic_access_token_signing_key: Union[bool, None] = None,
+    expose_access_token_to_frontend_in_cookie_based_auth: Union[bool, None] = None,
+    jwks_refresh_interval_sec: Union[int, None] = None,
+) -> Callable[[AppInfo], RecipeModule]:
+    return SessionRecipe.init(
+        cookie_domain,
+        older_cookie_domain,
+        cookie_secure,
+        cookie_same_site,
+        session_expired_status_code,
+        anti_csrf,
+        get_token_transfer_method,
+        error_handlers,
+        override,
+        invalid_claim_status_code,
+        use_dynamic_access_token_signing_key,
+        expose_access_token_to_frontend_in_cookie_based_auth,
+        jwks_refresh_interval_sec,
+    )
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/session/interfaces.html b/html/supertokens_python/recipe/session/interfaces.html new file mode 100644 index 000000000..f4d88a71a --- /dev/null +++ b/html/supertokens_python/recipe/session/interfaces.html @@ -0,0 +1,2903 @@ + + + + + + +supertokens_python.recipe.session.interfaces API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.session.interfaces

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from abc import ABC, abstractmethod
+from typing import (
+    TYPE_CHECKING,
+    Any,
+    Callable,
+    Dict,
+    Generic,
+    List,
+    Optional,
+    TypeVar,
+    Union,
+)
+from typing_extensions import TypedDict
+
+from supertokens_python.async_to_sync_wrapper import sync
+from supertokens_python.types import APIResponse, GeneralErrorResponse, MaybeAwaitable
+
+from ...utils import resolve
+from .exceptions import ClaimValidationError
+from .utils import SessionConfig, TokenTransferMethod
+
+if TYPE_CHECKING:
+    from supertokens_python.framework import BaseRequest
+
+from supertokens_python.framework import BaseResponse
+
+
+class SessionObj:
+    def __init__(
+        self,
+        handle: str,
+        user_id: str,
+        user_data_in_jwt: Dict[str, Any],
+        tenant_id: str,
+    ):
+        self.handle = handle
+        self.user_id = user_id
+        self.user_data_in_jwt = user_data_in_jwt
+        self.tenant_id = tenant_id
+
+
+class AccessTokenObj:
+    def __init__(self, token: str, expiry: int, created_time: int):
+        self.token = token
+        self.expiry = expiry
+        self.created_time = created_time
+
+
+class RegenerateAccessTokenOkResult:
+    def __init__(self, session: SessionObj, access_token: Union[AccessTokenObj, None]):
+        self.session = session
+        self.access_token = access_token
+
+
+class SessionInformationResult:
+    def __init__(
+        self,
+        session_handle: str,
+        user_id: str,
+        session_data_in_database: Dict[str, Any],
+        expiry: int,
+        custom_claims_in_access_token_payload: Dict[str, Any],
+        time_created: int,
+        tenant_id: str,
+    ):
+        self.session_handle = session_handle
+        self.user_id = user_id
+        self.session_data_in_database = session_data_in_database
+        self.expiry = expiry
+        self.custom_claims_in_access_token_payload = (
+            custom_claims_in_access_token_payload
+        )
+        self.time_created = time_created
+        self.tenant_id = tenant_id
+
+
+class ReqResInfo:
+    def __init__(
+        self,
+        request: BaseRequest,
+        transfer_method: TokenTransferMethod,
+    ):
+        self.request = request
+        self.transfer_method = transfer_method
+
+
+_T = TypeVar("_T")
+JSONObject = Dict[str, Any]
+
+JSONPrimitive = Union[str, int, bool, None, Dict[str, Any]]
+JSONPrimitiveList = Union[
+    List[str], List[int], List[bool], List[None], List[Dict[str, Any]]
+]
+
+
+class SessionDoesNotExistError:
+    pass
+
+
+class GetClaimValueOkResult(Generic[_T]):
+    def __init__(self, value: Optional[_T]):
+        self.value = value
+
+
+class ClaimsValidationResult:
+    def __init__(
+        self,
+        invalid_claims: List[ClaimValidationError],
+        access_token_payload_update: Optional[Dict[str, Any]] = None,
+    ):
+        self.invalid_claims = invalid_claims
+        self.access_token_payload_update = access_token_payload_update
+
+
+class GetSessionTokensDangerouslyDict(TypedDict):
+    accessToken: str
+    accessAndFrontTokenUpdated: bool
+    refreshToken: Optional[str]
+    frontToken: str
+    antiCsrfToken: Optional[str]
+
+
+class RecipeInterface(ABC):  # pylint: disable=too-many-public-methods
+    def __init__(self):
+        pass
+
+    @abstractmethod
+    async def create_new_session(
+        self,
+        user_id: str,
+        access_token_payload: Optional[Dict[str, Any]],
+        session_data_in_database: Optional[Dict[str, Any]],
+        disable_anti_csrf: Optional[bool],
+        tenant_id: str,
+        user_context: Dict[str, Any],
+    ) -> SessionContainer:
+        pass
+
+    @abstractmethod
+    def get_global_claim_validators(
+        self,
+        tenant_id: str,
+        user_id: str,
+        claim_validators_added_by_other_recipes: List[SessionClaimValidator],
+        user_context: Dict[str, Any],
+    ) -> MaybeAwaitable[List[SessionClaimValidator]]:
+        pass
+
+    @abstractmethod
+    async def get_session(
+        self,
+        access_token: Optional[str],
+        anti_csrf_token: Optional[str] = None,
+        anti_csrf_check: Optional[bool] = None,
+        session_required: Optional[bool] = None,
+        check_database: Optional[bool] = None,
+        override_global_claim_validators: Optional[
+            Callable[
+                [List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
+                MaybeAwaitable[List[SessionClaimValidator]],
+            ]
+        ] = None,
+        user_context: Optional[Dict[str, Any]] = None,
+    ) -> Optional[SessionContainer]:
+        pass
+
+    @abstractmethod
+    async def validate_claims(
+        self,
+        user_id: str,
+        access_token_payload: Dict[str, Any],
+        claim_validators: List[SessionClaimValidator],
+        user_context: Dict[str, Any],
+    ) -> ClaimsValidationResult:
+        pass
+
+    @abstractmethod
+    async def validate_claims_in_jwt_payload(
+        self,
+        user_id: str,
+        jwt_payload: JSONObject,
+        claim_validators: List[SessionClaimValidator],
+        user_context: Dict[str, Any],
+    ) -> ClaimsValidationResult:
+        pass
+
+    @abstractmethod
+    async def refresh_session(
+        self,
+        refresh_token: str,
+        anti_csrf_token: Optional[str],
+        disable_anti_csrf: bool,
+        user_context: Dict[str, Any],
+    ) -> SessionContainer:
+        pass
+
+    @abstractmethod
+    async def revoke_session(
+        self, session_handle: str, user_context: Dict[str, Any]
+    ) -> bool:
+        pass
+
+    @abstractmethod
+    async def revoke_all_sessions_for_user(
+        self,
+        user_id: str,
+        tenant_id: str,
+        revoke_across_all_tenants: bool,
+        user_context: Dict[str, Any],
+    ) -> List[str]:
+        pass
+
+    @abstractmethod
+    async def get_all_session_handles_for_user(
+        self,
+        user_id: str,
+        tenant_id: str,
+        fetch_across_all_tenants: bool,
+        user_context: Dict[str, Any],
+    ) -> List[str]:
+        pass
+
+    @abstractmethod
+    async def revoke_multiple_sessions(
+        self, session_handles: List[str], user_context: Dict[str, Any]
+    ) -> List[str]:
+        pass
+
+    @abstractmethod
+    async def get_session_information(
+        self, session_handle: str, user_context: Dict[str, Any]
+    ) -> Union[SessionInformationResult, None]:
+        pass
+
+    @abstractmethod
+    async def update_session_data_in_database(
+        self,
+        session_handle: str,
+        new_session_data: Dict[str, Any],
+        user_context: Dict[str, Any],
+    ) -> bool:
+        pass
+
+    @abstractmethod
+    async def merge_into_access_token_payload(
+        self,
+        session_handle: str,
+        access_token_payload_update: JSONObject,
+        user_context: Dict[str, Any],
+    ) -> bool:
+        pass
+
+    @abstractmethod
+    async def fetch_and_set_claim(
+        self,
+        session_handle: str,
+        claim: SessionClaim[Any],
+        user_context: Dict[str, Any],
+    ) -> bool:
+        pass
+
+    @abstractmethod
+    async def set_claim_value(
+        self,
+        session_handle: str,
+        claim: SessionClaim[_T],
+        value: _T,
+        user_context: Dict[str, Any],
+    ) -> bool:
+        pass
+
+    @abstractmethod
+    async def get_claim_value(
+        self,
+        session_handle: str,
+        claim: SessionClaim[Any],
+        user_context: Dict[str, Any],
+    ) -> Union[SessionDoesNotExistError, GetClaimValueOkResult[Any]]:
+        pass
+
+    @abstractmethod
+    async def remove_claim(
+        self,
+        session_handle: str,
+        claim: SessionClaim[Any],
+        user_context: Dict[str, Any],
+    ) -> bool:
+        pass
+
+    @abstractmethod
+    async def regenerate_access_token(
+        self,
+        access_token: str,
+        new_access_token_payload: Union[Dict[str, Any], None],
+        user_context: Dict[str, Any],
+    ) -> Union[RegenerateAccessTokenOkResult, None]:
+        pass
+
+
+class SignOutOkayResponse(APIResponse):
+    def __init__(self):
+        self.status = "OK"
+
+    def to_json(self) -> Dict[str, Any]:
+        return {"status": self.status}
+
+
+class APIOptions:
+    def __init__(
+        self,
+        request: BaseRequest,
+        response: Optional[BaseResponse],
+        recipe_id: str,
+        config: SessionConfig,
+        recipe_implementation: RecipeInterface,
+    ):
+        self.request = request
+        self.response = response
+        self.recipe_id = recipe_id
+        self.config = config
+        self.recipe_implementation = recipe_implementation
+
+
+class APIInterface(ABC):
+    def __init__(self):
+        self.disable_refresh_post = False
+        self.disable_signout_post = False
+
+    # We do not add a GeneralErrorResponse response to this API
+    # since it's not something that is directly called by the user on the
+    # frontend anyway
+
+    @abstractmethod
+    async def refresh_post(
+        self, api_options: APIOptions, user_context: Dict[str, Any]
+    ) -> SessionContainer:
+        pass
+
+    @abstractmethod
+    async def signout_post(
+        self,
+        session: Optional[SessionContainer],
+        api_options: APIOptions,
+        user_context: Dict[str, Any],
+    ) -> Union[SignOutOkayResponse, GeneralErrorResponse]:
+        pass
+
+    @abstractmethod
+    async def verify_session(
+        self,
+        api_options: APIOptions,
+        anti_csrf_check: Union[bool, None],
+        session_required: bool,
+        check_database: bool,
+        override_global_claim_validators: Optional[
+            Callable[
+                [List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
+                MaybeAwaitable[List[SessionClaimValidator]],
+            ]
+        ],
+        user_context: Dict[str, Any],
+    ) -> Union[SessionContainer, None]:
+        pass
+
+
+ResponseMutator = Callable[[BaseResponse, Dict[str, Any]], None]
+
+
+class TokenInfo:
+    def __init__(self, token: str, expiry: int, created_time: int):
+        self.token = token
+        self.expiry = expiry
+        self.created_time = created_time
+
+
+class SessionContainer(ABC):  # pylint: disable=too-many-public-methods
+    def __init__(
+        self,
+        recipe_implementation: RecipeInterface,
+        config: SessionConfig,
+        access_token: str,
+        front_token: str,
+        refresh_token: Optional[TokenInfo],
+        anti_csrf_token: Optional[str],
+        session_handle: str,
+        user_id: str,
+        user_data_in_access_token: Optional[Dict[str, Any]],
+        req_res_info: Optional[ReqResInfo],
+        access_token_updated: bool,
+        tenant_id: str,
+    ):
+        self.recipe_implementation = recipe_implementation
+        self.config = config
+        self.access_token = access_token
+        self.front_token = front_token
+        self.refresh_token = refresh_token
+        self.anti_csrf_token = anti_csrf_token
+        self.session_handle = session_handle
+        self.user_id = user_id
+        self.user_data_in_access_token = user_data_in_access_token
+        self.req_res_info: Optional[ReqResInfo] = req_res_info
+        self.access_token_updated = access_token_updated
+        self.tenant_id = tenant_id
+
+        self.response_mutators: List[ResponseMutator] = []
+
+    @abstractmethod
+    async def revoke_session(
+        self, user_context: Optional[Dict[str, Any]] = None
+    ) -> None:
+        pass
+
+    @abstractmethod
+    async def get_session_data_from_database(
+        self, user_context: Optional[Dict[str, Any]] = None
+    ) -> Dict[str, Any]:
+        pass
+
+    @abstractmethod
+    async def update_session_data_in_database(
+        self,
+        new_session_data: Dict[str, Any],
+        user_context: Optional[Dict[str, Any]] = None,
+    ) -> None:
+        pass
+
+    @abstractmethod
+    async def attach_to_request_response(
+        self,
+        request: BaseRequest,
+        transfer_method: TokenTransferMethod,
+        user_context: Dict[str, Any],
+    ):
+        pass
+
+    @abstractmethod
+    async def merge_into_access_token_payload(
+        self,
+        access_token_payload_update: JSONObject,
+        user_context: Optional[Dict[str, Any]] = None,
+    ) -> None:
+        pass
+
+    @abstractmethod
+    def get_user_id(self, user_context: Optional[Dict[str, Any]] = None) -> str:
+        pass
+
+    @abstractmethod
+    def get_tenant_id(self, user_context: Optional[Dict[str, Any]] = None) -> str:
+        pass
+
+    @abstractmethod
+    def get_access_token_payload(
+        self, user_context: Optional[Dict[str, Any]] = None
+    ) -> Dict[str, Any]:
+        pass
+
+    @abstractmethod
+    def get_handle(self, user_context: Optional[Dict[str, Any]] = None) -> str:
+        pass
+
+    @abstractmethod
+    def get_all_session_tokens_dangerously(self) -> GetSessionTokensDangerouslyDict:
+        pass
+
+    @abstractmethod
+    def get_access_token(self, user_context: Optional[Dict[str, Any]] = None) -> str:
+        pass
+
+    @abstractmethod
+    async def get_time_created(
+        self, user_context: Optional[Dict[str, Any]] = None
+    ) -> int:
+        pass
+
+    @abstractmethod
+    async def get_expiry(self, user_context: Optional[Dict[str, Any]] = None) -> int:
+        pass
+
+    @abstractmethod
+    async def assert_claims(
+        self,
+        claim_validators: List[SessionClaimValidator],
+        user_context: Optional[Dict[str, Any]] = None,
+    ) -> None:
+        pass
+
+    @abstractmethod
+    async def fetch_and_set_claim(
+        self, claim: SessionClaim[Any], user_context: Optional[Dict[str, Any]] = None
+    ) -> None:
+        pass
+
+    @abstractmethod
+    async def set_claim_value(
+        self,
+        claim: SessionClaim[_T],
+        value: _T,
+        user_context: Optional[Dict[str, Any]] = None,
+    ) -> None:
+        pass
+
+    @abstractmethod
+    async def get_claim_value(
+        self, claim: SessionClaim[_T], user_context: Optional[Dict[str, Any]] = None
+    ) -> Union[_T, None]:
+        pass
+
+    @abstractmethod
+    async def remove_claim(
+        self,
+        claim: SessionClaim[Any],
+        user_context: Optional[Dict[str, Any]] = None,
+    ) -> None:
+        pass
+
+    def sync_get_expiry(self, user_context: Optional[Dict[str, Any]] = None) -> int:
+        return sync(self.get_expiry(user_context))
+
+    def sync_revoke_session(
+        self, user_context: Optional[Dict[str, Any]] = None
+    ) -> None:
+        return sync(self.revoke_session(user_context=user_context))
+
+    def sync_get_session_data_from_database(
+        self, user_context: Union[Dict[str, Any], None] = None
+    ) -> Dict[str, Any]:
+        return sync(self.get_session_data_from_database(user_context))
+
+    def sync_get_time_created(
+        self, user_context: Optional[Dict[str, Any]] = None
+    ) -> int:
+        return sync(self.get_time_created(user_context))
+
+    def sync_merge_into_access_token_payload(
+        self,
+        access_token_payload_update: Dict[str, Any],
+        user_context: Optional[Dict[str, Any]] = None,
+    ) -> None:
+        return sync(
+            self.merge_into_access_token_payload(
+                access_token_payload_update, user_context
+            )
+        )
+
+    def sync_update_session_data_in_database(
+        self,
+        new_session_data: Dict[str, Any],
+        user_context: Optional[Dict[str, Any]] = None,
+    ) -> None:
+        return sync(
+            self.update_session_data_in_database(new_session_data, user_context)
+        )
+
+    # Session claims sync functions:
+    def sync_assert_claims(
+        self,
+        claim_validators: List[SessionClaimValidator],
+        user_context: Optional[Dict[str, Any]] = None,
+    ) -> None:
+        return sync(self.assert_claims(claim_validators, user_context))
+
+    def sync_fetch_and_set_claim(
+        self, claim: SessionClaim[Any], user_context: Optional[Dict[str, Any]] = None
+    ) -> None:
+        return sync(self.fetch_and_set_claim(claim, user_context))
+
+    def sync_set_claim_value(
+        self,
+        claim: SessionClaim[_T],
+        value: _T,
+        user_context: Optional[Dict[str, Any]] = None,
+    ) -> None:
+        return sync(self.set_claim_value(claim, value, user_context))
+
+    def sync_get_claim_value(
+        self, claim: SessionClaim[_T], user_context: Optional[Dict[str, Any]] = None
+    ) -> Union[_T, None]:
+        return sync(self.get_claim_value(claim, user_context))
+
+    def sync_remove_claim(
+        self, claim: SessionClaim[Any], user_context: Optional[Dict[str, Any]] = None
+    ) -> None:
+        return sync(self.remove_claim(claim, user_context))
+
+    def sync_attach_to_request_response(
+        self,
+        request: BaseRequest,
+        token_transfer: TokenTransferMethod,
+        user_context: Dict[str, Any],
+    ) -> None:
+        return sync(
+            self.attach_to_request_response(request, token_transfer, user_context)
+        )
+
+    # This is there so that we can do session["..."] to access some of the members of this class
+    def __getitem__(self, item: str):
+        return getattr(self, item)
+
+
+class SessionClaim(ABC, Generic[_T]):
+    def __init__(
+        self,
+        key: str,
+        fetch_value: Callable[
+            [str, str, Dict[str, Any]],
+            MaybeAwaitable[Optional[_T]],
+        ],
+    ) -> None:
+        """
+        Args:
+            key: The key to use when storing the claim in the payload.
+            fetch_value: a method that fetches the current value of this claim for the user.
+                A None return value signifies that we don't want to update the claim payload and or the claim value is
+                not present in the database. For example, this can happen with a second factor auth claim, where we
+                don't want to add the claim to the session automatically
+        """
+        self.key = key
+        self.fetch_value = fetch_value
+
+    @abstractmethod
+    def add_to_payload_(
+        self,
+        payload: JSONObject,
+        value: _T,
+        user_context: Union[Dict[str, Any], None] = None,
+    ) -> JSONObject:
+        """Saves the provided value into the payload, by cloning and updating the entire object"""
+
+    @abstractmethod
+    def remove_from_payload_by_merge_(
+        self, payload: JSONObject, user_context: Optional[Dict[str, Any]] = None
+    ) -> JSONObject:
+        """Removes the claim from the payload by setting it to None, so merge_into_access_token_payload can clear it"""
+
+    @abstractmethod
+    def remove_from_payload(
+        self, payload: JSONObject, user_context: Optional[Dict[str, Any]] = None
+    ) -> JSONObject:
+        """Removes the claim from the payload, by cloning and updating the entire object."""
+
+    @abstractmethod
+    def get_value_from_payload(
+        self, payload: JSONObject, user_context: Union[Dict[str, Any], None] = None
+    ) -> Union[_T, None]:
+        """Gets the value of the claim stored in the payload"""
+
+    async def build(
+        self,
+        user_id: str,
+        tenant_id: str,
+        user_context: Optional[Dict[str, Any]] = None,
+    ) -> JSONObject:
+        if user_context is None:
+            user_context = {}
+
+        value = await resolve(self.fetch_value(user_id, tenant_id, user_context))
+
+        if value is None:
+            return {}
+
+        return self.add_to_payload_({}, value, user_context)
+
+
+class ClaimValidationResult:
+    def __init__(self, is_valid: bool, reason: Optional[Dict[str, Any]] = None):
+        self.is_valid = is_valid
+        self.reason = {} if is_valid else reason
+
+
+class SessionClaimValidator(ABC):
+    def __init__(
+        self,
+        id_: str,
+    ) -> None:
+        self.id = id_
+        self.claim: Optional[SessionClaim[Any]] = None
+
+    @abstractmethod
+    async def validate(
+        self, payload: JSONObject, user_context: Dict[str, Any]
+    ) -> ClaimValidationResult:
+        pass
+
+    def should_refetch(
+        self, payload: JSONObject, user_context: Dict[str, Any]
+    ) -> MaybeAwaitable[bool]:
+        raise NotImplementedError()
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class APIInterface +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class APIInterface(ABC):
+    def __init__(self):
+        self.disable_refresh_post = False
+        self.disable_signout_post = False
+
+    # We do not add a GeneralErrorResponse response to this API
+    # since it's not something that is directly called by the user on the
+    # frontend anyway
+
+    @abstractmethod
+    async def refresh_post(
+        self, api_options: APIOptions, user_context: Dict[str, Any]
+    ) -> SessionContainer:
+        pass
+
+    @abstractmethod
+    async def signout_post(
+        self,
+        session: Optional[SessionContainer],
+        api_options: APIOptions,
+        user_context: Dict[str, Any],
+    ) -> Union[SignOutOkayResponse, GeneralErrorResponse]:
+        pass
+
+    @abstractmethod
+    async def verify_session(
+        self,
+        api_options: APIOptions,
+        anti_csrf_check: Union[bool, None],
+        session_required: bool,
+        check_database: bool,
+        override_global_claim_validators: Optional[
+            Callable[
+                [List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
+                MaybeAwaitable[List[SessionClaimValidator]],
+            ]
+        ],
+        user_context: Dict[str, Any],
+    ) -> Union[SessionContainer, None]:
+        pass
+
+

Ancestors

+
    +
  • abc.ABC
  • +
+

Subclasses

+ +

Methods

+
+
+async def refresh_post(self, api_options: APIOptions, user_context: Dict[str, Any]) ‑> SessionContainer +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def refresh_post(
+    self, api_options: APIOptions, user_context: Dict[str, Any]
+) -> SessionContainer:
+    pass
+
+
+
+async def signout_post(self, session: Optional[SessionContainer], api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[SignOutOkayResponseGeneralErrorResponse] +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def signout_post(
+    self,
+    session: Optional[SessionContainer],
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+) -> Union[SignOutOkayResponse, GeneralErrorResponse]:
+    pass
+
+
+
+async def verify_session(self, api_options: APIOptions, anti_csrf_check: Union[bool, None], session_required: bool, check_database: bool, override_global_claim_validators: Optional[Callable[[List[SessionClaimValidator], SessionContainer, Dict[str, Any]], MaybeAwaitable[List[SessionClaimValidator]]]], user_context: Dict[str, Any]) ‑> Optional[SessionContainer] +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def verify_session(
+    self,
+    api_options: APIOptions,
+    anti_csrf_check: Union[bool, None],
+    session_required: bool,
+    check_database: bool,
+    override_global_claim_validators: Optional[
+        Callable[
+            [List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
+            MaybeAwaitable[List[SessionClaimValidator]],
+        ]
+    ],
+    user_context: Dict[str, Any],
+) -> Union[SessionContainer, None]:
+    pass
+
+
+
+
+
+class APIOptions +(request: BaseRequest, response: Optional[BaseResponse], recipe_id: str, config: SessionConfig, recipe_implementation: RecipeInterface) +
+
+
+
+ +Expand source code + +
class APIOptions:
+    def __init__(
+        self,
+        request: BaseRequest,
+        response: Optional[BaseResponse],
+        recipe_id: str,
+        config: SessionConfig,
+        recipe_implementation: RecipeInterface,
+    ):
+        self.request = request
+        self.response = response
+        self.recipe_id = recipe_id
+        self.config = config
+        self.recipe_implementation = recipe_implementation
+
+
+
+class AccessTokenObj +(token: str, expiry: int, created_time: int) +
+
+
+
+ +Expand source code + +
class AccessTokenObj:
+    def __init__(self, token: str, expiry: int, created_time: int):
+        self.token = token
+        self.expiry = expiry
+        self.created_time = created_time
+
+
+
+class ClaimValidationResult +(is_valid: bool, reason: Optional[Dict[str, Any]] = None) +
+
+
+
+ +Expand source code + +
class ClaimValidationResult:
+    def __init__(self, is_valid: bool, reason: Optional[Dict[str, Any]] = None):
+        self.is_valid = is_valid
+        self.reason = {} if is_valid else reason
+
+
+
+class ClaimsValidationResult +(invalid_claims: List[ClaimValidationError], access_token_payload_update: Optional[Dict[str, Any]] = None) +
+
+
+
+ +Expand source code + +
class ClaimsValidationResult:
+    def __init__(
+        self,
+        invalid_claims: List[ClaimValidationError],
+        access_token_payload_update: Optional[Dict[str, Any]] = None,
+    ):
+        self.invalid_claims = invalid_claims
+        self.access_token_payload_update = access_token_payload_update
+
+
+
+class GetClaimValueOkResult +(value: Optional[_T]) +
+
+

Abstract base class for generic types.

+

A generic type is typically declared by inheriting from +this class parameterized with one or more type variables. +For example, a generic mapping type might be defined as::

+

class Mapping(Generic[KT, VT]): +def getitem(self, key: KT) -> VT: +… +# Etc.

+

This class can then be used as follows::

+

def lookup_name(mapping: Mapping[KT, VT], key: KT, default: VT) -> VT: +try: +return mapping[key] +except KeyError: +return default

+
+ +Expand source code + +
class GetClaimValueOkResult(Generic[_T]):
+    def __init__(self, value: Optional[_T]):
+        self.value = value
+
+

Ancestors

+
    +
  • typing.Generic
  • +
+
+
+class GetSessionTokensDangerouslyDict +(*args, **kwargs) +
+
+

dict() -> new empty dictionary +dict(mapping) -> new dictionary initialized from a mapping object's +(key, value) pairs +dict(iterable) -> new dictionary initialized as if via: +d = {} +for k, v in iterable: +d[k] = v +dict(**kwargs) -> new dictionary initialized with the name=value pairs +in the keyword argument list. +For example: +dict(one=1, two=2)

+
+ +Expand source code + +
class GetSessionTokensDangerouslyDict(TypedDict):
+    accessToken: str
+    accessAndFrontTokenUpdated: bool
+    refreshToken: Optional[str]
+    frontToken: str
+    antiCsrfToken: Optional[str]
+
+

Ancestors

+
    +
  • builtins.dict
  • +
+

Class variables

+
+
var accessAndFrontTokenUpdated : bool
+
+
+
+
var accessToken : str
+
+
+
+
var antiCsrfToken : Optional[str]
+
+
+
+
var frontToken : str
+
+
+
+
var refreshToken : Optional[str]
+
+
+
+
+
+
+class RecipeInterface +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class RecipeInterface(ABC):  # pylint: disable=too-many-public-methods
+    def __init__(self):
+        pass
+
+    @abstractmethod
+    async def create_new_session(
+        self,
+        user_id: str,
+        access_token_payload: Optional[Dict[str, Any]],
+        session_data_in_database: Optional[Dict[str, Any]],
+        disable_anti_csrf: Optional[bool],
+        tenant_id: str,
+        user_context: Dict[str, Any],
+    ) -> SessionContainer:
+        pass
+
+    @abstractmethod
+    def get_global_claim_validators(
+        self,
+        tenant_id: str,
+        user_id: str,
+        claim_validators_added_by_other_recipes: List[SessionClaimValidator],
+        user_context: Dict[str, Any],
+    ) -> MaybeAwaitable[List[SessionClaimValidator]]:
+        pass
+
+    @abstractmethod
+    async def get_session(
+        self,
+        access_token: Optional[str],
+        anti_csrf_token: Optional[str] = None,
+        anti_csrf_check: Optional[bool] = None,
+        session_required: Optional[bool] = None,
+        check_database: Optional[bool] = None,
+        override_global_claim_validators: Optional[
+            Callable[
+                [List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
+                MaybeAwaitable[List[SessionClaimValidator]],
+            ]
+        ] = None,
+        user_context: Optional[Dict[str, Any]] = None,
+    ) -> Optional[SessionContainer]:
+        pass
+
+    @abstractmethod
+    async def validate_claims(
+        self,
+        user_id: str,
+        access_token_payload: Dict[str, Any],
+        claim_validators: List[SessionClaimValidator],
+        user_context: Dict[str, Any],
+    ) -> ClaimsValidationResult:
+        pass
+
+    @abstractmethod
+    async def validate_claims_in_jwt_payload(
+        self,
+        user_id: str,
+        jwt_payload: JSONObject,
+        claim_validators: List[SessionClaimValidator],
+        user_context: Dict[str, Any],
+    ) -> ClaimsValidationResult:
+        pass
+
+    @abstractmethod
+    async def refresh_session(
+        self,
+        refresh_token: str,
+        anti_csrf_token: Optional[str],
+        disable_anti_csrf: bool,
+        user_context: Dict[str, Any],
+    ) -> SessionContainer:
+        pass
+
+    @abstractmethod
+    async def revoke_session(
+        self, session_handle: str, user_context: Dict[str, Any]
+    ) -> bool:
+        pass
+
+    @abstractmethod
+    async def revoke_all_sessions_for_user(
+        self,
+        user_id: str,
+        tenant_id: str,
+        revoke_across_all_tenants: bool,
+        user_context: Dict[str, Any],
+    ) -> List[str]:
+        pass
+
+    @abstractmethod
+    async def get_all_session_handles_for_user(
+        self,
+        user_id: str,
+        tenant_id: str,
+        fetch_across_all_tenants: bool,
+        user_context: Dict[str, Any],
+    ) -> List[str]:
+        pass
+
+    @abstractmethod
+    async def revoke_multiple_sessions(
+        self, session_handles: List[str], user_context: Dict[str, Any]
+    ) -> List[str]:
+        pass
+
+    @abstractmethod
+    async def get_session_information(
+        self, session_handle: str, user_context: Dict[str, Any]
+    ) -> Union[SessionInformationResult, None]:
+        pass
+
+    @abstractmethod
+    async def update_session_data_in_database(
+        self,
+        session_handle: str,
+        new_session_data: Dict[str, Any],
+        user_context: Dict[str, Any],
+    ) -> bool:
+        pass
+
+    @abstractmethod
+    async def merge_into_access_token_payload(
+        self,
+        session_handle: str,
+        access_token_payload_update: JSONObject,
+        user_context: Dict[str, Any],
+    ) -> bool:
+        pass
+
+    @abstractmethod
+    async def fetch_and_set_claim(
+        self,
+        session_handle: str,
+        claim: SessionClaim[Any],
+        user_context: Dict[str, Any],
+    ) -> bool:
+        pass
+
+    @abstractmethod
+    async def set_claim_value(
+        self,
+        session_handle: str,
+        claim: SessionClaim[_T],
+        value: _T,
+        user_context: Dict[str, Any],
+    ) -> bool:
+        pass
+
+    @abstractmethod
+    async def get_claim_value(
+        self,
+        session_handle: str,
+        claim: SessionClaim[Any],
+        user_context: Dict[str, Any],
+    ) -> Union[SessionDoesNotExistError, GetClaimValueOkResult[Any]]:
+        pass
+
+    @abstractmethod
+    async def remove_claim(
+        self,
+        session_handle: str,
+        claim: SessionClaim[Any],
+        user_context: Dict[str, Any],
+    ) -> bool:
+        pass
+
+    @abstractmethod
+    async def regenerate_access_token(
+        self,
+        access_token: str,
+        new_access_token_payload: Union[Dict[str, Any], None],
+        user_context: Dict[str, Any],
+    ) -> Union[RegenerateAccessTokenOkResult, None]:
+        pass
+
+

Ancestors

+
    +
  • abc.ABC
  • +
+

Subclasses

+ +

Methods

+
+
+async def create_new_session(self, user_id: str, access_token_payload: Optional[Dict[str, Any]], session_data_in_database: Optional[Dict[str, Any]], disable_anti_csrf: Optional[bool], tenant_id: str, user_context: Dict[str, Any]) ‑> SessionContainer +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def create_new_session(
+    self,
+    user_id: str,
+    access_token_payload: Optional[Dict[str, Any]],
+    session_data_in_database: Optional[Dict[str, Any]],
+    disable_anti_csrf: Optional[bool],
+    tenant_id: str,
+    user_context: Dict[str, Any],
+) -> SessionContainer:
+    pass
+
+
+
+async def fetch_and_set_claim(self, session_handle: str, claim: SessionClaim[Any], user_context: Dict[str, Any]) ‑> bool +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def fetch_and_set_claim(
+    self,
+    session_handle: str,
+    claim: SessionClaim[Any],
+    user_context: Dict[str, Any],
+) -> bool:
+    pass
+
+
+
+async def get_all_session_handles_for_user(self, user_id: str, tenant_id: str, fetch_across_all_tenants: bool, user_context: Dict[str, Any]) ‑> List[str] +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def get_all_session_handles_for_user(
+    self,
+    user_id: str,
+    tenant_id: str,
+    fetch_across_all_tenants: bool,
+    user_context: Dict[str, Any],
+) -> List[str]:
+    pass
+
+
+
+async def get_claim_value(self, session_handle: str, claim: SessionClaim[Any], user_context: Dict[str, Any]) ‑> Union[SessionDoesNotExistErrorGetClaimValueOkResult[Any]] +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def get_claim_value(
+    self,
+    session_handle: str,
+    claim: SessionClaim[Any],
+    user_context: Dict[str, Any],
+) -> Union[SessionDoesNotExistError, GetClaimValueOkResult[Any]]:
+    pass
+
+
+
+def get_global_claim_validators(self, tenant_id: str, user_id: str, claim_validators_added_by_other_recipes: List[SessionClaimValidator], user_context: Dict[str, Any]) ‑> Union[Awaitable[List[SessionClaimValidator]], List[SessionClaimValidator]] +
+
+
+
+ +Expand source code + +
@abstractmethod
+def get_global_claim_validators(
+    self,
+    tenant_id: str,
+    user_id: str,
+    claim_validators_added_by_other_recipes: List[SessionClaimValidator],
+    user_context: Dict[str, Any],
+) -> MaybeAwaitable[List[SessionClaimValidator]]:
+    pass
+
+
+
+async def get_session(self, access_token: Optional[str], anti_csrf_token: Optional[str] = None, anti_csrf_check: Optional[bool] = None, session_required: Optional[bool] = None, check_database: Optional[bool] = None, override_global_claim_validators: Optional[Callable[[List[SessionClaimValidator], SessionContainer, Dict[str, Any]], MaybeAwaitable[List[SessionClaimValidator]]]] = None, user_context: Optional[Dict[str, Any]] = None) ‑> Optional[SessionContainer] +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def get_session(
+    self,
+    access_token: Optional[str],
+    anti_csrf_token: Optional[str] = None,
+    anti_csrf_check: Optional[bool] = None,
+    session_required: Optional[bool] = None,
+    check_database: Optional[bool] = None,
+    override_global_claim_validators: Optional[
+        Callable[
+            [List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
+            MaybeAwaitable[List[SessionClaimValidator]],
+        ]
+    ] = None,
+    user_context: Optional[Dict[str, Any]] = None,
+) -> Optional[SessionContainer]:
+    pass
+
+
+
+async def get_session_information(self, session_handle: str, user_context: Dict[str, Any]) ‑> Optional[SessionInformationResult] +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def get_session_information(
+    self, session_handle: str, user_context: Dict[str, Any]
+) -> Union[SessionInformationResult, None]:
+    pass
+
+
+
+async def merge_into_access_token_payload(self, session_handle: str, access_token_payload_update: JSONObject, user_context: Dict[str, Any]) ‑> bool +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def merge_into_access_token_payload(
+    self,
+    session_handle: str,
+    access_token_payload_update: JSONObject,
+    user_context: Dict[str, Any],
+) -> bool:
+    pass
+
+
+
+async def refresh_session(self, refresh_token: str, anti_csrf_token: Optional[str], disable_anti_csrf: bool, user_context: Dict[str, Any]) ‑> SessionContainer +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def refresh_session(
+    self,
+    refresh_token: str,
+    anti_csrf_token: Optional[str],
+    disable_anti_csrf: bool,
+    user_context: Dict[str, Any],
+) -> SessionContainer:
+    pass
+
+
+
+async def regenerate_access_token(self, access_token: str, new_access_token_payload: Union[Dict[str, Any], None], user_context: Dict[str, Any]) ‑> Optional[RegenerateAccessTokenOkResult] +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def regenerate_access_token(
+    self,
+    access_token: str,
+    new_access_token_payload: Union[Dict[str, Any], None],
+    user_context: Dict[str, Any],
+) -> Union[RegenerateAccessTokenOkResult, None]:
+    pass
+
+
+
+async def remove_claim(self, session_handle: str, claim: SessionClaim[Any], user_context: Dict[str, Any]) ‑> bool +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def remove_claim(
+    self,
+    session_handle: str,
+    claim: SessionClaim[Any],
+    user_context: Dict[str, Any],
+) -> bool:
+    pass
+
+
+
+async def revoke_all_sessions_for_user(self, user_id: str, tenant_id: str, revoke_across_all_tenants: bool, user_context: Dict[str, Any]) ‑> List[str] +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def revoke_all_sessions_for_user(
+    self,
+    user_id: str,
+    tenant_id: str,
+    revoke_across_all_tenants: bool,
+    user_context: Dict[str, Any],
+) -> List[str]:
+    pass
+
+
+
+async def revoke_multiple_sessions(self, session_handles: List[str], user_context: Dict[str, Any]) ‑> List[str] +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def revoke_multiple_sessions(
+    self, session_handles: List[str], user_context: Dict[str, Any]
+) -> List[str]:
+    pass
+
+
+
+async def revoke_session(self, session_handle: str, user_context: Dict[str, Any]) ‑> bool +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def revoke_session(
+    self, session_handle: str, user_context: Dict[str, Any]
+) -> bool:
+    pass
+
+
+
+async def set_claim_value(self, session_handle: str, claim: SessionClaim[_T], value: _T, user_context: Dict[str, Any]) ‑> bool +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def set_claim_value(
+    self,
+    session_handle: str,
+    claim: SessionClaim[_T],
+    value: _T,
+    user_context: Dict[str, Any],
+) -> bool:
+    pass
+
+
+
+async def update_session_data_in_database(self, session_handle: str, new_session_data: Dict[str, Any], user_context: Dict[str, Any]) ‑> bool +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def update_session_data_in_database(
+    self,
+    session_handle: str,
+    new_session_data: Dict[str, Any],
+    user_context: Dict[str, Any],
+) -> bool:
+    pass
+
+
+
+async def validate_claims(self, user_id: str, access_token_payload: Dict[str, Any], claim_validators: List[SessionClaimValidator], user_context: Dict[str, Any]) ‑> ClaimsValidationResult +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def validate_claims(
+    self,
+    user_id: str,
+    access_token_payload: Dict[str, Any],
+    claim_validators: List[SessionClaimValidator],
+    user_context: Dict[str, Any],
+) -> ClaimsValidationResult:
+    pass
+
+
+
+async def validate_claims_in_jwt_payload(self, user_id: str, jwt_payload: JSONObject, claim_validators: List[SessionClaimValidator], user_context: Dict[str, Any]) ‑> ClaimsValidationResult +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def validate_claims_in_jwt_payload(
+    self,
+    user_id: str,
+    jwt_payload: JSONObject,
+    claim_validators: List[SessionClaimValidator],
+    user_context: Dict[str, Any],
+) -> ClaimsValidationResult:
+    pass
+
+
+
+
+
+class RegenerateAccessTokenOkResult +(session: SessionObj, access_token: Union[AccessTokenObj, None]) +
+
+
+
+ +Expand source code + +
class RegenerateAccessTokenOkResult:
+    def __init__(self, session: SessionObj, access_token: Union[AccessTokenObj, None]):
+        self.session = session
+        self.access_token = access_token
+
+
+
+class ReqResInfo +(request: BaseRequest, transfer_method: TokenTransferMethod) +
+
+
+
+ +Expand source code + +
class ReqResInfo:
+    def __init__(
+        self,
+        request: BaseRequest,
+        transfer_method: TokenTransferMethod,
+    ):
+        self.request = request
+        self.transfer_method = transfer_method
+
+
+
+class SessionClaim +(key: str, fetch_value: Callable[[str, str, Dict[str, Any]], MaybeAwaitable[Optional[_T]]]) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+

Args

+
+
key
+
The key to use when storing the claim in the payload.
+
fetch_value
+
a method that fetches the current value of this claim for the user. +A None return value signifies that we don't want to update the claim payload and or the claim value is +not present in the database. For example, this can happen with a second factor auth claim, where we +don't want to add the claim to the session automatically
+
+
+ +Expand source code + +
class SessionClaim(ABC, Generic[_T]):
+    def __init__(
+        self,
+        key: str,
+        fetch_value: Callable[
+            [str, str, Dict[str, Any]],
+            MaybeAwaitable[Optional[_T]],
+        ],
+    ) -> None:
+        """
+        Args:
+            key: The key to use when storing the claim in the payload.
+            fetch_value: a method that fetches the current value of this claim for the user.
+                A None return value signifies that we don't want to update the claim payload and or the claim value is
+                not present in the database. For example, this can happen with a second factor auth claim, where we
+                don't want to add the claim to the session automatically
+        """
+        self.key = key
+        self.fetch_value = fetch_value
+
+    @abstractmethod
+    def add_to_payload_(
+        self,
+        payload: JSONObject,
+        value: _T,
+        user_context: Union[Dict[str, Any], None] = None,
+    ) -> JSONObject:
+        """Saves the provided value into the payload, by cloning and updating the entire object"""
+
+    @abstractmethod
+    def remove_from_payload_by_merge_(
+        self, payload: JSONObject, user_context: Optional[Dict[str, Any]] = None
+    ) -> JSONObject:
+        """Removes the claim from the payload by setting it to None, so merge_into_access_token_payload can clear it"""
+
+    @abstractmethod
+    def remove_from_payload(
+        self, payload: JSONObject, user_context: Optional[Dict[str, Any]] = None
+    ) -> JSONObject:
+        """Removes the claim from the payload, by cloning and updating the entire object."""
+
+    @abstractmethod
+    def get_value_from_payload(
+        self, payload: JSONObject, user_context: Union[Dict[str, Any], None] = None
+    ) -> Union[_T, None]:
+        """Gets the value of the claim stored in the payload"""
+
+    async def build(
+        self,
+        user_id: str,
+        tenant_id: str,
+        user_context: Optional[Dict[str, Any]] = None,
+    ) -> JSONObject:
+        if user_context is None:
+            user_context = {}
+
+        value = await resolve(self.fetch_value(user_id, tenant_id, user_context))
+
+        if value is None:
+            return {}
+
+        return self.add_to_payload_({}, value, user_context)
+
+

Ancestors

+
    +
  • abc.ABC
  • +
  • typing.Generic
  • +
+

Subclasses

+ +

Methods

+
+
+def add_to_payload_(self, payload: JSONObject, value: _T, user_context: Union[Dict[str, Any], None] = None) ‑> Dict[str, Any] +
+
+

Saves the provided value into the payload, by cloning and updating the entire object

+
+ +Expand source code + +
@abstractmethod
+def add_to_payload_(
+    self,
+    payload: JSONObject,
+    value: _T,
+    user_context: Union[Dict[str, Any], None] = None,
+) -> JSONObject:
+    """Saves the provided value into the payload, by cloning and updating the entire object"""
+
+
+
+async def build(self, user_id: str, tenant_id: str, user_context: Optional[Dict[str, Any]] = None) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
async def build(
+    self,
+    user_id: str,
+    tenant_id: str,
+    user_context: Optional[Dict[str, Any]] = None,
+) -> JSONObject:
+    if user_context is None:
+        user_context = {}
+
+    value = await resolve(self.fetch_value(user_id, tenant_id, user_context))
+
+    if value is None:
+        return {}
+
+    return self.add_to_payload_({}, value, user_context)
+
+
+
+def get_value_from_payload(self, payload: JSONObject, user_context: Union[Dict[str, Any], None] = None) ‑> Optional[~_T] +
+
+

Gets the value of the claim stored in the payload

+
+ +Expand source code + +
@abstractmethod
+def get_value_from_payload(
+    self, payload: JSONObject, user_context: Union[Dict[str, Any], None] = None
+) -> Union[_T, None]:
+    """Gets the value of the claim stored in the payload"""
+
+
+
+def remove_from_payload(self, payload: JSONObject, user_context: Optional[Dict[str, Any]] = None) ‑> Dict[str, Any] +
+
+

Removes the claim from the payload, by cloning and updating the entire object.

+
+ +Expand source code + +
@abstractmethod
+def remove_from_payload(
+    self, payload: JSONObject, user_context: Optional[Dict[str, Any]] = None
+) -> JSONObject:
+    """Removes the claim from the payload, by cloning and updating the entire object."""
+
+
+
+def remove_from_payload_by_merge_(self, payload: JSONObject, user_context: Optional[Dict[str, Any]] = None) ‑> Dict[str, Any] +
+
+

Removes the claim from the payload by setting it to None, so merge_into_access_token_payload can clear it

+
+ +Expand source code + +
@abstractmethod
+def remove_from_payload_by_merge_(
+    self, payload: JSONObject, user_context: Optional[Dict[str, Any]] = None
+) -> JSONObject:
+    """Removes the claim from the payload by setting it to None, so merge_into_access_token_payload can clear it"""
+
+
+
+
+
+class SessionClaimValidator +(id_: str) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class SessionClaimValidator(ABC):
+    def __init__(
+        self,
+        id_: str,
+    ) -> None:
+        self.id = id_
+        self.claim: Optional[SessionClaim[Any]] = None
+
+    @abstractmethod
+    async def validate(
+        self, payload: JSONObject, user_context: Dict[str, Any]
+    ) -> ClaimValidationResult:
+        pass
+
+    def should_refetch(
+        self, payload: JSONObject, user_context: Dict[str, Any]
+    ) -> MaybeAwaitable[bool]:
+        raise NotImplementedError()
+
+

Ancestors

+
    +
  • abc.ABC
  • +
+

Subclasses

+ +

Methods

+
+
+def should_refetch(self, payload: JSONObject, user_context: Dict[str, Any]) ‑> Union[Awaitable[bool], bool] +
+
+
+
+ +Expand source code + +
def should_refetch(
+    self, payload: JSONObject, user_context: Dict[str, Any]
+) -> MaybeAwaitable[bool]:
+    raise NotImplementedError()
+
+
+
+async def validate(self, payload: JSONObject, user_context: Dict[str, Any]) ‑> ClaimValidationResult +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def validate(
+    self, payload: JSONObject, user_context: Dict[str, Any]
+) -> ClaimValidationResult:
+    pass
+
+
+
+
+
+class SessionContainer +(recipe_implementation: RecipeInterface, config: SessionConfig, access_token: str, front_token: str, refresh_token: Optional[TokenInfo], anti_csrf_token: Optional[str], session_handle: str, user_id: str, user_data_in_access_token: Optional[Dict[str, Any]], req_res_info: Optional[ReqResInfo], access_token_updated: bool, tenant_id: str) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class SessionContainer(ABC):  # pylint: disable=too-many-public-methods
+    def __init__(
+        self,
+        recipe_implementation: RecipeInterface,
+        config: SessionConfig,
+        access_token: str,
+        front_token: str,
+        refresh_token: Optional[TokenInfo],
+        anti_csrf_token: Optional[str],
+        session_handle: str,
+        user_id: str,
+        user_data_in_access_token: Optional[Dict[str, Any]],
+        req_res_info: Optional[ReqResInfo],
+        access_token_updated: bool,
+        tenant_id: str,
+    ):
+        self.recipe_implementation = recipe_implementation
+        self.config = config
+        self.access_token = access_token
+        self.front_token = front_token
+        self.refresh_token = refresh_token
+        self.anti_csrf_token = anti_csrf_token
+        self.session_handle = session_handle
+        self.user_id = user_id
+        self.user_data_in_access_token = user_data_in_access_token
+        self.req_res_info: Optional[ReqResInfo] = req_res_info
+        self.access_token_updated = access_token_updated
+        self.tenant_id = tenant_id
+
+        self.response_mutators: List[ResponseMutator] = []
+
+    @abstractmethod
+    async def revoke_session(
+        self, user_context: Optional[Dict[str, Any]] = None
+    ) -> None:
+        pass
+
+    @abstractmethod
+    async def get_session_data_from_database(
+        self, user_context: Optional[Dict[str, Any]] = None
+    ) -> Dict[str, Any]:
+        pass
+
+    @abstractmethod
+    async def update_session_data_in_database(
+        self,
+        new_session_data: Dict[str, Any],
+        user_context: Optional[Dict[str, Any]] = None,
+    ) -> None:
+        pass
+
+    @abstractmethod
+    async def attach_to_request_response(
+        self,
+        request: BaseRequest,
+        transfer_method: TokenTransferMethod,
+        user_context: Dict[str, Any],
+    ):
+        pass
+
+    @abstractmethod
+    async def merge_into_access_token_payload(
+        self,
+        access_token_payload_update: JSONObject,
+        user_context: Optional[Dict[str, Any]] = None,
+    ) -> None:
+        pass
+
+    @abstractmethod
+    def get_user_id(self, user_context: Optional[Dict[str, Any]] = None) -> str:
+        pass
+
+    @abstractmethod
+    def get_tenant_id(self, user_context: Optional[Dict[str, Any]] = None) -> str:
+        pass
+
+    @abstractmethod
+    def get_access_token_payload(
+        self, user_context: Optional[Dict[str, Any]] = None
+    ) -> Dict[str, Any]:
+        pass
+
+    @abstractmethod
+    def get_handle(self, user_context: Optional[Dict[str, Any]] = None) -> str:
+        pass
+
+    @abstractmethod
+    def get_all_session_tokens_dangerously(self) -> GetSessionTokensDangerouslyDict:
+        pass
+
+    @abstractmethod
+    def get_access_token(self, user_context: Optional[Dict[str, Any]] = None) -> str:
+        pass
+
+    @abstractmethod
+    async def get_time_created(
+        self, user_context: Optional[Dict[str, Any]] = None
+    ) -> int:
+        pass
+
+    @abstractmethod
+    async def get_expiry(self, user_context: Optional[Dict[str, Any]] = None) -> int:
+        pass
+
+    @abstractmethod
+    async def assert_claims(
+        self,
+        claim_validators: List[SessionClaimValidator],
+        user_context: Optional[Dict[str, Any]] = None,
+    ) -> None:
+        pass
+
+    @abstractmethod
+    async def fetch_and_set_claim(
+        self, claim: SessionClaim[Any], user_context: Optional[Dict[str, Any]] = None
+    ) -> None:
+        pass
+
+    @abstractmethod
+    async def set_claim_value(
+        self,
+        claim: SessionClaim[_T],
+        value: _T,
+        user_context: Optional[Dict[str, Any]] = None,
+    ) -> None:
+        pass
+
+    @abstractmethod
+    async def get_claim_value(
+        self, claim: SessionClaim[_T], user_context: Optional[Dict[str, Any]] = None
+    ) -> Union[_T, None]:
+        pass
+
+    @abstractmethod
+    async def remove_claim(
+        self,
+        claim: SessionClaim[Any],
+        user_context: Optional[Dict[str, Any]] = None,
+    ) -> None:
+        pass
+
+    def sync_get_expiry(self, user_context: Optional[Dict[str, Any]] = None) -> int:
+        return sync(self.get_expiry(user_context))
+
+    def sync_revoke_session(
+        self, user_context: Optional[Dict[str, Any]] = None
+    ) -> None:
+        return sync(self.revoke_session(user_context=user_context))
+
+    def sync_get_session_data_from_database(
+        self, user_context: Union[Dict[str, Any], None] = None
+    ) -> Dict[str, Any]:
+        return sync(self.get_session_data_from_database(user_context))
+
+    def sync_get_time_created(
+        self, user_context: Optional[Dict[str, Any]] = None
+    ) -> int:
+        return sync(self.get_time_created(user_context))
+
+    def sync_merge_into_access_token_payload(
+        self,
+        access_token_payload_update: Dict[str, Any],
+        user_context: Optional[Dict[str, Any]] = None,
+    ) -> None:
+        return sync(
+            self.merge_into_access_token_payload(
+                access_token_payload_update, user_context
+            )
+        )
+
+    def sync_update_session_data_in_database(
+        self,
+        new_session_data: Dict[str, Any],
+        user_context: Optional[Dict[str, Any]] = None,
+    ) -> None:
+        return sync(
+            self.update_session_data_in_database(new_session_data, user_context)
+        )
+
+    # Session claims sync functions:
+    def sync_assert_claims(
+        self,
+        claim_validators: List[SessionClaimValidator],
+        user_context: Optional[Dict[str, Any]] = None,
+    ) -> None:
+        return sync(self.assert_claims(claim_validators, user_context))
+
+    def sync_fetch_and_set_claim(
+        self, claim: SessionClaim[Any], user_context: Optional[Dict[str, Any]] = None
+    ) -> None:
+        return sync(self.fetch_and_set_claim(claim, user_context))
+
+    def sync_set_claim_value(
+        self,
+        claim: SessionClaim[_T],
+        value: _T,
+        user_context: Optional[Dict[str, Any]] = None,
+    ) -> None:
+        return sync(self.set_claim_value(claim, value, user_context))
+
+    def sync_get_claim_value(
+        self, claim: SessionClaim[_T], user_context: Optional[Dict[str, Any]] = None
+    ) -> Union[_T, None]:
+        return sync(self.get_claim_value(claim, user_context))
+
+    def sync_remove_claim(
+        self, claim: SessionClaim[Any], user_context: Optional[Dict[str, Any]] = None
+    ) -> None:
+        return sync(self.remove_claim(claim, user_context))
+
+    def sync_attach_to_request_response(
+        self,
+        request: BaseRequest,
+        token_transfer: TokenTransferMethod,
+        user_context: Dict[str, Any],
+    ) -> None:
+        return sync(
+            self.attach_to_request_response(request, token_transfer, user_context)
+        )
+
+    # This is there so that we can do session["..."] to access some of the members of this class
+    def __getitem__(self, item: str):
+        return getattr(self, item)
+
+

Ancestors

+
    +
  • abc.ABC
  • +
+

Subclasses

+ +

Methods

+
+
+async def assert_claims(self, claim_validators: List[SessionClaimValidator], user_context: Optional[Dict[str, Any]] = None) ‑> None +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def assert_claims(
+    self,
+    claim_validators: List[SessionClaimValidator],
+    user_context: Optional[Dict[str, Any]] = None,
+) -> None:
+    pass
+
+
+
+async def attach_to_request_response(self, request: BaseRequest, transfer_method: TokenTransferMethod, user_context: Dict[str, Any]) +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def attach_to_request_response(
+    self,
+    request: BaseRequest,
+    transfer_method: TokenTransferMethod,
+    user_context: Dict[str, Any],
+):
+    pass
+
+
+
+async def fetch_and_set_claim(self, claim: SessionClaim[Any], user_context: Optional[Dict[str, Any]] = None) ‑> None +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def fetch_and_set_claim(
+    self, claim: SessionClaim[Any], user_context: Optional[Dict[str, Any]] = None
+) -> None:
+    pass
+
+
+
+def get_access_token(self, user_context: Optional[Dict[str, Any]] = None) ‑> str +
+
+
+
+ +Expand source code + +
@abstractmethod
+def get_access_token(self, user_context: Optional[Dict[str, Any]] = None) -> str:
+    pass
+
+
+
+def get_access_token_payload(self, user_context: Optional[Dict[str, Any]] = None) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
@abstractmethod
+def get_access_token_payload(
+    self, user_context: Optional[Dict[str, Any]] = None
+) -> Dict[str, Any]:
+    pass
+
+
+
+def get_all_session_tokens_dangerously(self) ‑> GetSessionTokensDangerouslyDict +
+
+
+
+ +Expand source code + +
@abstractmethod
+def get_all_session_tokens_dangerously(self) -> GetSessionTokensDangerouslyDict:
+    pass
+
+
+
+async def get_claim_value(self, claim: SessionClaim[_T], user_context: Optional[Dict[str, Any]] = None) ‑> Optional[~_T] +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def get_claim_value(
+    self, claim: SessionClaim[_T], user_context: Optional[Dict[str, Any]] = None
+) -> Union[_T, None]:
+    pass
+
+
+
+async def get_expiry(self, user_context: Optional[Dict[str, Any]] = None) ‑> int +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def get_expiry(self, user_context: Optional[Dict[str, Any]] = None) -> int:
+    pass
+
+
+
+def get_handle(self, user_context: Optional[Dict[str, Any]] = None) ‑> str +
+
+
+
+ +Expand source code + +
@abstractmethod
+def get_handle(self, user_context: Optional[Dict[str, Any]] = None) -> str:
+    pass
+
+
+
+async def get_session_data_from_database(self, user_context: Optional[Dict[str, Any]] = None) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def get_session_data_from_database(
+    self, user_context: Optional[Dict[str, Any]] = None
+) -> Dict[str, Any]:
+    pass
+
+
+
+def get_tenant_id(self, user_context: Optional[Dict[str, Any]] = None) ‑> str +
+
+
+
+ +Expand source code + +
@abstractmethod
+def get_tenant_id(self, user_context: Optional[Dict[str, Any]] = None) -> str:
+    pass
+
+
+
+async def get_time_created(self, user_context: Optional[Dict[str, Any]] = None) ‑> int +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def get_time_created(
+    self, user_context: Optional[Dict[str, Any]] = None
+) -> int:
+    pass
+
+
+
+def get_user_id(self, user_context: Optional[Dict[str, Any]] = None) ‑> str +
+
+
+
+ +Expand source code + +
@abstractmethod
+def get_user_id(self, user_context: Optional[Dict[str, Any]] = None) -> str:
+    pass
+
+
+
+async def merge_into_access_token_payload(self, access_token_payload_update: JSONObject, user_context: Optional[Dict[str, Any]] = None) ‑> None +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def merge_into_access_token_payload(
+    self,
+    access_token_payload_update: JSONObject,
+    user_context: Optional[Dict[str, Any]] = None,
+) -> None:
+    pass
+
+
+
+async def remove_claim(self, claim: SessionClaim[Any], user_context: Optional[Dict[str, Any]] = None) ‑> None +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def remove_claim(
+    self,
+    claim: SessionClaim[Any],
+    user_context: Optional[Dict[str, Any]] = None,
+) -> None:
+    pass
+
+
+
+async def revoke_session(self, user_context: Optional[Dict[str, Any]] = None) ‑> None +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def revoke_session(
+    self, user_context: Optional[Dict[str, Any]] = None
+) -> None:
+    pass
+
+
+
+async def set_claim_value(self, claim: SessionClaim[_T], value: _T, user_context: Optional[Dict[str, Any]] = None) ‑> None +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def set_claim_value(
+    self,
+    claim: SessionClaim[_T],
+    value: _T,
+    user_context: Optional[Dict[str, Any]] = None,
+) -> None:
+    pass
+
+
+
+def sync_assert_claims(self, claim_validators: List[SessionClaimValidator], user_context: Optional[Dict[str, Any]] = None) ‑> None +
+
+
+
+ +Expand source code + +
def sync_assert_claims(
+    self,
+    claim_validators: List[SessionClaimValidator],
+    user_context: Optional[Dict[str, Any]] = None,
+) -> None:
+    return sync(self.assert_claims(claim_validators, user_context))
+
+
+
+def sync_attach_to_request_response(self, request: BaseRequest, token_transfer: TokenTransferMethod, user_context: Dict[str, Any]) ‑> None +
+
+
+
+ +Expand source code + +
def sync_attach_to_request_response(
+    self,
+    request: BaseRequest,
+    token_transfer: TokenTransferMethod,
+    user_context: Dict[str, Any],
+) -> None:
+    return sync(
+        self.attach_to_request_response(request, token_transfer, user_context)
+    )
+
+
+
+def sync_fetch_and_set_claim(self, claim: SessionClaim[Any], user_context: Optional[Dict[str, Any]] = None) ‑> None +
+
+
+
+ +Expand source code + +
def sync_fetch_and_set_claim(
+    self, claim: SessionClaim[Any], user_context: Optional[Dict[str, Any]] = None
+) -> None:
+    return sync(self.fetch_and_set_claim(claim, user_context))
+
+
+
+def sync_get_claim_value(self, claim: SessionClaim[_T], user_context: Optional[Dict[str, Any]] = None) ‑> Optional[~_T] +
+
+
+
+ +Expand source code + +
def sync_get_claim_value(
+    self, claim: SessionClaim[_T], user_context: Optional[Dict[str, Any]] = None
+) -> Union[_T, None]:
+    return sync(self.get_claim_value(claim, user_context))
+
+
+
+def sync_get_expiry(self, user_context: Optional[Dict[str, Any]] = None) ‑> int +
+
+
+
+ +Expand source code + +
def sync_get_expiry(self, user_context: Optional[Dict[str, Any]] = None) -> int:
+    return sync(self.get_expiry(user_context))
+
+
+
+def sync_get_session_data_from_database(self, user_context: Union[Dict[str, Any], None] = None) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
def sync_get_session_data_from_database(
+    self, user_context: Union[Dict[str, Any], None] = None
+) -> Dict[str, Any]:
+    return sync(self.get_session_data_from_database(user_context))
+
+
+
+def sync_get_time_created(self, user_context: Optional[Dict[str, Any]] = None) ‑> int +
+
+
+
+ +Expand source code + +
def sync_get_time_created(
+    self, user_context: Optional[Dict[str, Any]] = None
+) -> int:
+    return sync(self.get_time_created(user_context))
+
+
+
+def sync_merge_into_access_token_payload(self, access_token_payload_update: Dict[str, Any], user_context: Optional[Dict[str, Any]] = None) ‑> None +
+
+
+
+ +Expand source code + +
def sync_merge_into_access_token_payload(
+    self,
+    access_token_payload_update: Dict[str, Any],
+    user_context: Optional[Dict[str, Any]] = None,
+) -> None:
+    return sync(
+        self.merge_into_access_token_payload(
+            access_token_payload_update, user_context
+        )
+    )
+
+
+
+def sync_remove_claim(self, claim: SessionClaim[Any], user_context: Optional[Dict[str, Any]] = None) ‑> None +
+
+
+
+ +Expand source code + +
def sync_remove_claim(
+    self, claim: SessionClaim[Any], user_context: Optional[Dict[str, Any]] = None
+) -> None:
+    return sync(self.remove_claim(claim, user_context))
+
+
+
+def sync_revoke_session(self, user_context: Optional[Dict[str, Any]] = None) ‑> None +
+
+
+
+ +Expand source code + +
def sync_revoke_session(
+    self, user_context: Optional[Dict[str, Any]] = None
+) -> None:
+    return sync(self.revoke_session(user_context=user_context))
+
+
+
+def sync_set_claim_value(self, claim: SessionClaim[_T], value: _T, user_context: Optional[Dict[str, Any]] = None) ‑> None +
+
+
+
+ +Expand source code + +
def sync_set_claim_value(
+    self,
+    claim: SessionClaim[_T],
+    value: _T,
+    user_context: Optional[Dict[str, Any]] = None,
+) -> None:
+    return sync(self.set_claim_value(claim, value, user_context))
+
+
+
+def sync_update_session_data_in_database(self, new_session_data: Dict[str, Any], user_context: Optional[Dict[str, Any]] = None) ‑> None +
+
+
+
+ +Expand source code + +
def sync_update_session_data_in_database(
+    self,
+    new_session_data: Dict[str, Any],
+    user_context: Optional[Dict[str, Any]] = None,
+) -> None:
+    return sync(
+        self.update_session_data_in_database(new_session_data, user_context)
+    )
+
+
+
+async def update_session_data_in_database(self, new_session_data: Dict[str, Any], user_context: Optional[Dict[str, Any]] = None) ‑> None +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def update_session_data_in_database(
+    self,
+    new_session_data: Dict[str, Any],
+    user_context: Optional[Dict[str, Any]] = None,
+) -> None:
+    pass
+
+
+
+
+
+class SessionDoesNotExistError +
+
+
+
+ +Expand source code + +
class SessionDoesNotExistError:
+    pass
+
+
+
+class SessionInformationResult +(session_handle: str, user_id: str, session_data_in_database: Dict[str, Any], expiry: int, custom_claims_in_access_token_payload: Dict[str, Any], time_created: int, tenant_id: str) +
+
+
+
+ +Expand source code + +
class SessionInformationResult:
+    def __init__(
+        self,
+        session_handle: str,
+        user_id: str,
+        session_data_in_database: Dict[str, Any],
+        expiry: int,
+        custom_claims_in_access_token_payload: Dict[str, Any],
+        time_created: int,
+        tenant_id: str,
+    ):
+        self.session_handle = session_handle
+        self.user_id = user_id
+        self.session_data_in_database = session_data_in_database
+        self.expiry = expiry
+        self.custom_claims_in_access_token_payload = (
+            custom_claims_in_access_token_payload
+        )
+        self.time_created = time_created
+        self.tenant_id = tenant_id
+
+
+
+class SessionObj +(handle: str, user_id: str, user_data_in_jwt: Dict[str, Any], tenant_id: str) +
+
+
+
+ +Expand source code + +
class SessionObj:
+    def __init__(
+        self,
+        handle: str,
+        user_id: str,
+        user_data_in_jwt: Dict[str, Any],
+        tenant_id: str,
+    ):
+        self.handle = handle
+        self.user_id = user_id
+        self.user_data_in_jwt = user_data_in_jwt
+        self.tenant_id = tenant_id
+
+
+
+class SignOutOkayResponse +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class SignOutOkayResponse(APIResponse):
+    def __init__(self):
+        self.status = "OK"
+
+    def to_json(self) -> Dict[str, Any]:
+        return {"status": self.status}
+
+

Ancestors

+ +

Methods

+
+
+def to_json(self) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
def to_json(self) -> Dict[str, Any]:
+    return {"status": self.status}
+
+
+
+
+
+class TokenInfo +(token: str, expiry: int, created_time: int) +
+
+
+
+ +Expand source code + +
class TokenInfo:
+    def __init__(self, token: str, expiry: int, created_time: int):
+        self.token = token
+        self.expiry = expiry
+        self.created_time = created_time
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/session/jwks.html b/html/supertokens_python/recipe/session/jwks.html new file mode 100644 index 000000000..32c9d3f5d --- /dev/null +++ b/html/supertokens_python/recipe/session/jwks.html @@ -0,0 +1,442 @@ + + + + + + +supertokens_python.recipe.session.jwks API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.session.jwks

+
+
+
+ +Expand source code + +
# Copyright (c) 2023, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import requests
+from os import environ
+from typing import List, Optional
+from typing_extensions import TypedDict
+
+from jwt import PyJWK, PyJWKSet
+
+from supertokens_python.recipe.session.utils import SessionConfig
+from supertokens_python.utils import RWMutex, RWLockContext, get_timestamp_ms
+from supertokens_python.querier import Querier
+from supertokens_python.logger import log_debug_message
+
+
+class JWKSConfigType(TypedDict):
+    request_timeout: int
+
+
+JWKSConfig: JWKSConfigType = {
+    "request_timeout": 10000,  # 10s
+}
+
+
+class CachedKeys:
+    def __init__(self, keys: List[PyJWK], refresh_interval_sec: int):
+        self.keys = keys
+        self.last_refresh_time = get_timestamp_ms()
+        self.refresh_interval_sec = refresh_interval_sec
+
+    def is_fresh(self):
+        return (
+            get_timestamp_ms() - self.last_refresh_time
+            < self.refresh_interval_sec * 1000
+        )
+
+
+cached_keys: Optional[CachedKeys] = None
+mutex = RWMutex()
+
+# only for testing purposes
+def reset_jwks_cache():
+    with RWLockContext(mutex, read=False):
+        global cached_keys
+        cached_keys = None
+
+
+def get_cached_keys() -> Optional[List[PyJWK]]:
+    if cached_keys is not None:
+        # This means that we have valid JWKs for the given core path
+        # We check if we need to refresh before returning
+
+        # This means that the value in cache is not expired, in this case we return the cached value
+        # Note that this also means that the SDK will not try to query any other core (if there are multiple)
+        # if it has a valid cache entry from one of the core URLs. It will only attempt to fetch
+        # from the cores again after the entry in the cache is expired
+        if cached_keys.is_fresh():
+            return cached_keys.keys
+
+    return None
+
+
+def find_matching_keys(
+    keys: Optional[List[PyJWK]], kid: Optional[str]
+) -> Optional[List[PyJWK]]:
+    if kid is None or keys is None:
+        # return all keys since the token does not have a kid
+        return keys
+
+    # kid has been provided so filter the keys
+    matching_keys = [key for key in keys if key.key_id == kid]  # type: ignore
+    if len(matching_keys) > 0:
+        return matching_keys
+
+    return None
+
+
+def get_latest_keys(config: SessionConfig, kid: Optional[str] = None) -> List[PyJWK]:
+    global cached_keys
+
+    if environ.get("SUPERTOKENS_ENV") == "testing":
+        log_debug_message("Called find_jwk_client")
+
+    with RWLockContext(mutex, read=True):
+        matching_keys = find_matching_keys(get_cached_keys(), kid)
+        if matching_keys is not None:
+            if environ.get("SUPERTOKENS_ENV") == "testing":
+                log_debug_message("Returning JWKS from cache")
+            return matching_keys
+        # otherwise unknown kid, will continue to reload the keys
+
+    core_paths = Querier.get_instance().get_all_core_urls_for_path(
+        "./.well-known/jwks.json"
+    )
+
+    if len(core_paths) == 0:
+        raise Exception(
+            "No SuperTokens core available to query. Please pass supertokens > connection_uri to the init function, or override all the functions of the recipe you are using."
+        )
+
+    last_error: Exception = Exception("No valid JWKS found")
+
+    with RWLockContext(mutex, read=False):
+        # check again if the keys are in cache
+        # because another thread might have fetched the keys while this one was waiting for the lock
+        matching_keys = find_matching_keys(get_cached_keys(), kid)
+        if matching_keys is not None:
+            return matching_keys
+
+        for path in core_paths:
+            if environ.get("SUPERTOKENS_ENV") == "testing":
+                log_debug_message("Attempting to fetch JWKS from path: %s", path)
+
+            cached_jwks: Optional[List[PyJWK]] = None
+            try:
+                log_debug_message("Fetching jwk set from the configured uri")
+                with requests.get(
+                    path, timeout=JWKSConfig["request_timeout"] / 1000
+                ) as response:  # 5 second timeout
+                    response.raise_for_status()
+                    cached_jwks = PyJWKSet.from_dict(response.json()).keys  # type: ignore
+            except Exception as e:
+                last_error = e
+
+            if cached_jwks is not None:  # we found a valid JWKS
+                cached_keys = CachedKeys(cached_jwks, config.jwks_refresh_interval_sec)
+                log_debug_message("Returning JWKS from fetch")
+                matching_keys = find_matching_keys(get_cached_keys(), kid)
+                if matching_keys is not None:
+                    return matching_keys
+
+                raise Exception("No matching JWKS found")
+
+    raise last_error
+
+
+
+
+
+
+
+

Functions

+
+
+def find_matching_keys(keys: Optional[List[jwt.api_jwk.PyJWK]], kid: Optional[str]) ‑> Optional[List[jwt.api_jwk.PyJWK]] +
+
+
+
+ +Expand source code + +
def find_matching_keys(
+    keys: Optional[List[PyJWK]], kid: Optional[str]
+) -> Optional[List[PyJWK]]:
+    if kid is None or keys is None:
+        # return all keys since the token does not have a kid
+        return keys
+
+    # kid has been provided so filter the keys
+    matching_keys = [key for key in keys if key.key_id == kid]  # type: ignore
+    if len(matching_keys) > 0:
+        return matching_keys
+
+    return None
+
+
+
+def get_cached_keys() ‑> Optional[List[jwt.api_jwk.PyJWK]] +
+
+
+
+ +Expand source code + +
def get_cached_keys() -> Optional[List[PyJWK]]:
+    if cached_keys is not None:
+        # This means that we have valid JWKs for the given core path
+        # We check if we need to refresh before returning
+
+        # This means that the value in cache is not expired, in this case we return the cached value
+        # Note that this also means that the SDK will not try to query any other core (if there are multiple)
+        # if it has a valid cache entry from one of the core URLs. It will only attempt to fetch
+        # from the cores again after the entry in the cache is expired
+        if cached_keys.is_fresh():
+            return cached_keys.keys
+
+    return None
+
+
+
+def get_latest_keys(config: SessionConfig, kid: Optional[str] = None) ‑> List[jwt.api_jwk.PyJWK] +
+
+
+
+ +Expand source code + +
def get_latest_keys(config: SessionConfig, kid: Optional[str] = None) -> List[PyJWK]:
+    global cached_keys
+
+    if environ.get("SUPERTOKENS_ENV") == "testing":
+        log_debug_message("Called find_jwk_client")
+
+    with RWLockContext(mutex, read=True):
+        matching_keys = find_matching_keys(get_cached_keys(), kid)
+        if matching_keys is not None:
+            if environ.get("SUPERTOKENS_ENV") == "testing":
+                log_debug_message("Returning JWKS from cache")
+            return matching_keys
+        # otherwise unknown kid, will continue to reload the keys
+
+    core_paths = Querier.get_instance().get_all_core_urls_for_path(
+        "./.well-known/jwks.json"
+    )
+
+    if len(core_paths) == 0:
+        raise Exception(
+            "No SuperTokens core available to query. Please pass supertokens > connection_uri to the init function, or override all the functions of the recipe you are using."
+        )
+
+    last_error: Exception = Exception("No valid JWKS found")
+
+    with RWLockContext(mutex, read=False):
+        # check again if the keys are in cache
+        # because another thread might have fetched the keys while this one was waiting for the lock
+        matching_keys = find_matching_keys(get_cached_keys(), kid)
+        if matching_keys is not None:
+            return matching_keys
+
+        for path in core_paths:
+            if environ.get("SUPERTOKENS_ENV") == "testing":
+                log_debug_message("Attempting to fetch JWKS from path: %s", path)
+
+            cached_jwks: Optional[List[PyJWK]] = None
+            try:
+                log_debug_message("Fetching jwk set from the configured uri")
+                with requests.get(
+                    path, timeout=JWKSConfig["request_timeout"] / 1000
+                ) as response:  # 5 second timeout
+                    response.raise_for_status()
+                    cached_jwks = PyJWKSet.from_dict(response.json()).keys  # type: ignore
+            except Exception as e:
+                last_error = e
+
+            if cached_jwks is not None:  # we found a valid JWKS
+                cached_keys = CachedKeys(cached_jwks, config.jwks_refresh_interval_sec)
+                log_debug_message("Returning JWKS from fetch")
+                matching_keys = find_matching_keys(get_cached_keys(), kid)
+                if matching_keys is not None:
+                    return matching_keys
+
+                raise Exception("No matching JWKS found")
+
+    raise last_error
+
+
+
+def reset_jwks_cache() +
+
+
+
+ +Expand source code + +
def reset_jwks_cache():
+    with RWLockContext(mutex, read=False):
+        global cached_keys
+        cached_keys = None
+
+
+
+
+
+

Classes

+
+
+class CachedKeys +(keys: List[jwt.api_jwk.PyJWK], refresh_interval_sec: int) +
+
+
+
+ +Expand source code + +
class CachedKeys:
+    def __init__(self, keys: List[PyJWK], refresh_interval_sec: int):
+        self.keys = keys
+        self.last_refresh_time = get_timestamp_ms()
+        self.refresh_interval_sec = refresh_interval_sec
+
+    def is_fresh(self):
+        return (
+            get_timestamp_ms() - self.last_refresh_time
+            < self.refresh_interval_sec * 1000
+        )
+
+

Methods

+
+
+def is_fresh(self) +
+
+
+
+ +Expand source code + +
def is_fresh(self):
+    return (
+        get_timestamp_ms() - self.last_refresh_time
+        < self.refresh_interval_sec * 1000
+    )
+
+
+
+
+
+class JWKSConfigType +(*args, **kwargs) +
+
+

dict() -> new empty dictionary +dict(mapping) -> new dictionary initialized from a mapping object's +(key, value) pairs +dict(iterable) -> new dictionary initialized as if via: +d = {} +for k, v in iterable: +d[k] = v +dict(**kwargs) -> new dictionary initialized with the name=value pairs +in the keyword argument list. +For example: +dict(one=1, two=2)

+
+ +Expand source code + +
class JWKSConfigType(TypedDict):
+    request_timeout: int
+
+

Ancestors

+
    +
  • builtins.dict
  • +
+

Class variables

+
+
var request_timeout : int
+
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/session/jwt.html b/html/supertokens_python/recipe/session/jwt.html new file mode 100644 index 000000000..20620dfda --- /dev/null +++ b/html/supertokens_python/recipe/session/jwt.html @@ -0,0 +1,265 @@ + + + + + + +supertokens_python.recipe.session.jwt API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.session.jwt

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from json import dumps, loads
+from typing import Any, Dict, Optional
+
+from supertokens_python.utils import utf_base64decode, utf_base64encode
+
+# why separators is used in dumps:
+# - without it's use, output of dumps is: '{"alg": "RS256", "typ": "JWT", "version": "1"}'
+# - with it's use, output of dumps is: '{"alg":"RS256","typ":"JWT","version":"1"}'
+# we require the non-spaced version, else the base64 encoding string will end up different than required
+_allowed_headers = [
+    utf_base64encode(
+        dumps(
+            {"alg": "RS256", "typ": "JWT", "version": "2"},
+            separators=(",", ":"),
+            sort_keys=True,
+        ),
+        urlsafe=False,
+    )
+]
+
+
+class ParsedJWTInfo:
+    def __init__(
+        self,
+        version: int,
+        raw_token_string: str,
+        raw_payload: str,
+        header: str,
+        payload: Dict[str, Any],
+        signature: str,
+        kid: Optional[str],
+        parsed_header: Optional[Dict[str, Any]] = None,
+    ) -> None:
+        self.version = version
+        self.raw_token_string = raw_token_string
+        self.raw_payload = raw_payload
+        self.header = header
+        self.payload = payload
+        self.signature = signature
+        self.kid = kid
+        self.parsed_header = parsed_header
+
+
+def parse_jwt_without_signature_verification(jwt: str) -> ParsedJWTInfo:
+    splitted_input = jwt.split(".")
+    LATEST_TOKEN_VERSION = 3
+    if len(splitted_input) != 3:
+        raise Exception("invalid jwt")
+
+    # V1 and V2 are functionally identical, plus all legacy tokens should be V2 now.
+    # So we can assume these defaults:
+    version = 2
+    kid = None
+    parsed_header = None
+    # V2 or older tokens didn't save the key id
+    header, payload, signature = splitted_input
+    # checking the header
+    if header not in _allowed_headers:
+        parsed_header = loads(utf_base64decode(header, True))
+        header_version = parsed_header.get("version", str(LATEST_TOKEN_VERSION))
+
+        try:
+            version = int(header_version)
+        except ValueError:
+            version = None
+
+        kid = parsed_header.get("kid")
+        # isinstance(version, int) returns False for None (if it fails to parse the version)
+        if (
+            parsed_header["typ"] != "JWT"
+            or not isinstance(version, int)
+            or version < 3
+            or kid is None
+        ):
+            raise Exception("JWT header mismatch")
+
+    return ParsedJWTInfo(
+        version=version,
+        raw_token_string=jwt,
+        raw_payload=payload,
+        header=header,
+        # Ideally we would only parse this after the signature verification is done
+        # We do this at the start, since we want to check if a token can be a supertokens access token or not.
+        payload=loads(utf_base64decode(payload, True)),
+        signature=signature,
+        kid=kid,
+        parsed_header=parsed_header,
+    )
+
+
+
+
+
+
+
+

Functions

+
+
+def parse_jwt_without_signature_verification(jwt: str) ‑> ParsedJWTInfo +
+
+
+
+ +Expand source code + +
def parse_jwt_without_signature_verification(jwt: str) -> ParsedJWTInfo:
+    splitted_input = jwt.split(".")
+    LATEST_TOKEN_VERSION = 3
+    if len(splitted_input) != 3:
+        raise Exception("invalid jwt")
+
+    # V1 and V2 are functionally identical, plus all legacy tokens should be V2 now.
+    # So we can assume these defaults:
+    version = 2
+    kid = None
+    parsed_header = None
+    # V2 or older tokens didn't save the key id
+    header, payload, signature = splitted_input
+    # checking the header
+    if header not in _allowed_headers:
+        parsed_header = loads(utf_base64decode(header, True))
+        header_version = parsed_header.get("version", str(LATEST_TOKEN_VERSION))
+
+        try:
+            version = int(header_version)
+        except ValueError:
+            version = None
+
+        kid = parsed_header.get("kid")
+        # isinstance(version, int) returns False for None (if it fails to parse the version)
+        if (
+            parsed_header["typ"] != "JWT"
+            or not isinstance(version, int)
+            or version < 3
+            or kid is None
+        ):
+            raise Exception("JWT header mismatch")
+
+    return ParsedJWTInfo(
+        version=version,
+        raw_token_string=jwt,
+        raw_payload=payload,
+        header=header,
+        # Ideally we would only parse this after the signature verification is done
+        # We do this at the start, since we want to check if a token can be a supertokens access token or not.
+        payload=loads(utf_base64decode(payload, True)),
+        signature=signature,
+        kid=kid,
+        parsed_header=parsed_header,
+    )
+
+
+
+
+
+

Classes

+
+
+class ParsedJWTInfo +(version: int, raw_token_string: str, raw_payload: str, header: str, payload: Dict[str, Any], signature: str, kid: Optional[str], parsed_header: Optional[Dict[str, Any]] = None) +
+
+
+
+ +Expand source code + +
class ParsedJWTInfo:
+    def __init__(
+        self,
+        version: int,
+        raw_token_string: str,
+        raw_payload: str,
+        header: str,
+        payload: Dict[str, Any],
+        signature: str,
+        kid: Optional[str],
+        parsed_header: Optional[Dict[str, Any]] = None,
+    ) -> None:
+        self.version = version
+        self.raw_token_string = raw_token_string
+        self.raw_payload = raw_payload
+        self.header = header
+        self.payload = payload
+        self.signature = signature
+        self.kid = kid
+        self.parsed_header = parsed_header
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/session/recipe.html b/html/supertokens_python/recipe/session/recipe.html new file mode 100644 index 000000000..831a701e2 --- /dev/null +++ b/html/supertokens_python/recipe/session/recipe.html @@ -0,0 +1,1237 @@ + + + + + + +supertokens_python.recipe.session.recipe API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.session.recipe

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from os import environ
+from typing import TYPE_CHECKING, Any, Dict, List, Union, Callable, Optional
+
+from supertokens_python.framework.response import BaseResponse
+from typing_extensions import Literal
+
+from .cookie_and_header import (
+    get_cors_allowed_headers,
+)
+from .exceptions import (
+    ClearDuplicateSessionCookiesError,
+    SuperTokensSessionError,
+    TokenTheftError,
+    UnauthorisedError,
+    InvalidClaimsError,
+)
+from ...types import MaybeAwaitable
+
+if TYPE_CHECKING:
+    from supertokens_python.framework import BaseRequest
+    from supertokens_python.supertokens import AppInfo
+
+from supertokens_python.exceptions import SuperTokensError, raise_general_exception
+from supertokens_python.logger import log_debug_message
+from supertokens_python.normalised_url_path import NormalisedURLPath
+from supertokens_python.querier import Querier
+from supertokens_python.recipe.openid.recipe import OpenIdRecipe
+from supertokens_python.recipe_module import APIHandled, RecipeModule
+
+from .constants import SESSION_REFRESH, SIGNOUT
+from .interfaces import (
+    APIInterface,
+    APIOptions,
+    RecipeInterface,
+    SessionClaim,
+    SessionClaimValidator,
+    SessionContainer,
+)
+from .recipe_implementation import (
+    RecipeImplementation,
+)
+from .api import handle_refresh_api, handle_signout_api
+from .utils import (
+    InputErrorHandlers,
+    InputOverrideConfig,
+    TokenTransferMethod,
+    validate_and_normalise_user_input,
+)
+from .cookie_and_header import clear_session_from_all_token_transfer_methods
+
+
+class SessionRecipe(RecipeModule):
+    recipe_id = "session"
+    __instance = None
+
+    def __init__(
+        self,
+        recipe_id: str,
+        app_info: AppInfo,
+        cookie_domain: Union[str, None] = None,
+        older_cookie_domain: Union[str, None] = None,
+        cookie_secure: Union[bool, None] = None,
+        cookie_same_site: Union[Literal["lax", "none", "strict"], None] = None,
+        session_expired_status_code: Union[int, None] = None,
+        anti_csrf: Union[
+            Literal["VIA_TOKEN", "VIA_CUSTOM_HEADER", "NONE"], None
+        ] = None,
+        get_token_transfer_method: Union[
+            Callable[
+                [BaseRequest, bool, Dict[str, Any]],
+                Union[TokenTransferMethod, Literal["any"]],
+            ],
+            None,
+        ] = None,
+        error_handlers: Union[InputErrorHandlers, None] = None,
+        override: Union[InputOverrideConfig, None] = None,
+        invalid_claim_status_code: Union[int, None] = None,
+        use_dynamic_access_token_signing_key: Union[bool, None] = None,
+        expose_access_token_to_frontend_in_cookie_based_auth: Union[bool, None] = None,
+        jwks_refresh_interval_sec: Union[int, None] = None,
+    ):
+        super().__init__(recipe_id, app_info)
+        self.config = validate_and_normalise_user_input(
+            app_info,
+            cookie_domain,
+            older_cookie_domain,
+            cookie_secure,
+            cookie_same_site,
+            session_expired_status_code,
+            anti_csrf,
+            get_token_transfer_method,
+            error_handlers,
+            override,
+            invalid_claim_status_code,
+            use_dynamic_access_token_signing_key,
+            expose_access_token_to_frontend_in_cookie_based_auth,
+            jwks_refresh_interval_sec,
+        )
+        self.openid_recipe = OpenIdRecipe(
+            recipe_id,
+            app_info,
+            None,
+            None,
+            override.openid_feature if override is not None else None,
+        )
+        log_debug_message(
+            "session init: anti_csrf: %s", self.config.anti_csrf_function_or_string
+        )
+        if self.config.cookie_domain is not None:
+            log_debug_message(
+                "session init: cookie_domain: %s", self.config.cookie_domain
+            )
+        else:
+            log_debug_message("session init: cookie_domain: None")
+
+        # we check the input cookie_same_site because the normalised version is
+        # always a function.
+        if cookie_same_site is not None:
+            log_debug_message("session init: cookie_same_site: %s", cookie_same_site)
+        else:
+            log_debug_message("session init: cookie_same_site: function")
+
+        log_debug_message(
+            "session init: cookie_secure: %s", str(self.config.cookie_secure)
+        )
+        log_debug_message(
+            "session init: refresh_token_path: %s ",
+            self.config.refresh_token_path.get_as_string_dangerous(),
+        )
+        log_debug_message(
+            "session init: session_expired_status_code: %s",
+            str(self.config.session_expired_status_code),
+        )
+        recipe_implementation = RecipeImplementation(
+            Querier.get_instance(recipe_id), self.config, self.app_info
+        )
+        self.recipe_implementation: RecipeInterface = (
+            recipe_implementation
+            if self.config.override.functions is None
+            else self.config.override.functions(recipe_implementation)
+        )
+
+        from .api.implementation import APIImplementation
+
+        api_implementation = APIImplementation()
+        self.api_implementation: APIInterface = (
+            api_implementation
+            if self.config.override.apis is None
+            else self.config.override.apis(api_implementation)
+        )
+
+        self.claims_added_by_other_recipes: List[SessionClaim[Any]] = []
+        self.claim_validators_added_by_other_recipes: List[SessionClaimValidator] = []
+
+    def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
+        return isinstance(err, SuperTokensError) and (
+            isinstance(err, SuperTokensSessionError)
+            or self.openid_recipe.is_error_from_this_recipe_based_on_instance(err)
+        )
+
+    def get_apis_handled(self) -> List[APIHandled]:
+        apis_handled = [
+            APIHandled(
+                NormalisedURLPath(SESSION_REFRESH),
+                "post",
+                SESSION_REFRESH,
+                self.api_implementation.disable_refresh_post,
+            ),
+            APIHandled(
+                NormalisedURLPath(SIGNOUT),
+                "post",
+                SIGNOUT,
+                self.api_implementation.disable_signout_post,
+            ),
+        ]
+        apis_handled.extend(self.openid_recipe.get_apis_handled())
+
+        return apis_handled
+
+    async def handle_api_request(
+        self,
+        request_id: str,
+        tenant_id: str,
+        request: BaseRequest,
+        path: NormalisedURLPath,
+        method: str,
+        response: BaseResponse,
+        user_context: Dict[str, Any],
+    ) -> Union[BaseResponse, None]:
+        if request_id == SESSION_REFRESH:
+            return await handle_refresh_api(
+                self.api_implementation,
+                APIOptions(
+                    request,
+                    response,
+                    self.recipe_id,
+                    self.config,
+                    self.recipe_implementation,
+                ),
+                user_context,
+            )
+        if request_id == SIGNOUT:
+            return await handle_signout_api(
+                self.api_implementation,
+                APIOptions(
+                    request,
+                    response,
+                    self.recipe_id,
+                    self.config,
+                    self.recipe_implementation,
+                ),
+                user_context,
+            )
+        return await self.openid_recipe.handle_api_request(
+            request_id, tenant_id, request, path, method, response, user_context
+        )
+
+    async def handle_error(
+        self,
+        request: BaseRequest,
+        err: SuperTokensError,
+        response: BaseResponse,
+        user_context: Dict[str, Any],
+    ) -> BaseResponse:
+        if (
+            isinstance(err, SuperTokensSessionError)
+            and err.response_mutators is not None
+        ):
+            for mutator in err.response_mutators:
+                mutator(response, user_context)
+
+        if isinstance(err, UnauthorisedError):
+            log_debug_message("errorHandler: returning UNAUTHORISED")
+            if err.clear_tokens:
+                log_debug_message("Clearing tokens because of UNAUTHORISED response")
+                clear_session_from_all_token_transfer_methods(
+                    response, self, request, user_context
+                )
+            return await self.config.error_handlers.on_unauthorised(
+                request, str(err), response
+            )
+        if isinstance(err, TokenTheftError):
+            log_debug_message("errorHandler: returning TOKEN_THEFT_DETECTED")
+            log_debug_message(
+                "Clearing tokens because of TOKEN_THEFT_DETECTED response"
+            )
+            clear_session_from_all_token_transfer_methods(
+                response, self, request, user_context
+            )
+            return await self.config.error_handlers.on_token_theft_detected(
+                request, err.session_handle, err.user_id, response
+            )
+        if isinstance(err, InvalidClaimsError):
+            log_debug_message("errorHandler: returning INVALID_CLAIMS")
+            return await self.config.error_handlers.on_invalid_claim(
+                self, request, err.payload, response
+            )
+        if isinstance(err, ClearDuplicateSessionCookiesError):
+            log_debug_message("errorHandler: returning CLEAR_DUPLICATE_SESSION_COOKIES")
+            return await self.config.error_handlers.on_clear_duplicate_session_cookies(
+                request, str(err), response
+            )
+
+        log_debug_message("errorHandler: returning TRY_REFRESH_TOKEN")
+        return await self.config.error_handlers.on_try_refresh_token(
+            request, str(err), response
+        )
+
+    def get_all_cors_headers(self) -> List[str]:
+        cors_headers = get_cors_allowed_headers()
+        cors_headers.extend(self.openid_recipe.get_all_cors_headers())
+
+        return cors_headers
+
+    @staticmethod
+    def init(
+        cookie_domain: Union[str, None] = None,
+        older_cookie_domain: Union[str, None] = None,
+        cookie_secure: Union[bool, None] = None,
+        cookie_same_site: Union[Literal["lax", "none", "strict"], None] = None,
+        session_expired_status_code: Union[int, None] = None,
+        anti_csrf: Union[
+            Literal["VIA_TOKEN", "VIA_CUSTOM_HEADER", "NONE"], None
+        ] = None,
+        get_token_transfer_method: Union[
+            Callable[
+                [BaseRequest, bool, Dict[str, Any]],
+                Union[TokenTransferMethod, Literal["any"]],
+            ],
+            None,
+        ] = None,
+        error_handlers: Union[InputErrorHandlers, None] = None,
+        override: Union[InputOverrideConfig, None] = None,
+        invalid_claim_status_code: Union[int, None] = None,
+        use_dynamic_access_token_signing_key: Union[bool, None] = None,
+        expose_access_token_to_frontend_in_cookie_based_auth: Union[bool, None] = None,
+        jwks_refresh_interval_sec: Union[int, None] = None,
+    ):
+        def func(app_info: AppInfo):
+            if SessionRecipe.__instance is None:
+                SessionRecipe.__instance = SessionRecipe(
+                    SessionRecipe.recipe_id,
+                    app_info,
+                    cookie_domain,
+                    older_cookie_domain,
+                    cookie_secure,
+                    cookie_same_site,
+                    session_expired_status_code,
+                    anti_csrf,
+                    get_token_transfer_method,
+                    error_handlers,
+                    override,
+                    invalid_claim_status_code,
+                    use_dynamic_access_token_signing_key,
+                    expose_access_token_to_frontend_in_cookie_based_auth,
+                    jwks_refresh_interval_sec,
+                )
+                return SessionRecipe.__instance
+            raise_general_exception(
+                "Session recipe has already been initialised. Please check your code for bugs."
+            )
+
+        return func
+
+    @staticmethod
+    def get_instance() -> SessionRecipe:
+        if SessionRecipe.__instance is not None:
+            return SessionRecipe.__instance
+        raise_general_exception(
+            "Initialisation not done. Did you forget to call the SuperTokens.init function?"
+        )
+
+    @staticmethod
+    def reset():
+        if ("SUPERTOKENS_ENV" not in environ) or (
+            environ["SUPERTOKENS_ENV"] != "testing"
+        ):
+            raise_general_exception("calling testing function in non testing env")
+        SessionRecipe.__instance = None
+
+    def add_claim_from_other_recipe(self, claim: SessionClaim[Any]):
+        # We are throwing here (and not in addClaimValidatorFromOtherRecipe) because if multiple
+        # claims are added with the same key they will overwrite each other. Validators will all run
+        # and work as expected even if they are added multiple times.
+        if claim.key in [c.key for c in self.claims_added_by_other_recipes]:
+            raise Exception("Claim added by multiple recipes")
+
+        self.claims_added_by_other_recipes.append(claim)
+
+    def get_claims_added_by_other_recipes(self) -> List[SessionClaim[Any]]:
+        return self.claims_added_by_other_recipes
+
+    def add_claim_validator_from_other_recipe(
+        self, claim_validator: SessionClaimValidator
+    ):
+        self.claim_validators_added_by_other_recipes.append(claim_validator)
+
+    def get_claim_validators_added_by_other_recipes(
+        self,
+    ) -> List[SessionClaimValidator]:
+        return self.claim_validators_added_by_other_recipes
+
+    async def verify_session(
+        self,
+        request: BaseRequest,
+        anti_csrf_check: Union[bool, None],
+        session_required: bool,
+        check_database: bool,
+        override_global_claim_validators: Optional[
+            Callable[
+                [List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
+                MaybeAwaitable[List[SessionClaimValidator]],
+            ]
+        ],
+        user_context: Dict[str, Any],
+    ):
+        _ = user_context
+
+        return await self.api_implementation.verify_session(
+            APIOptions(
+                request,
+                None,
+                self.recipe_id,
+                self.config,
+                self.recipe_implementation,
+            ),
+            anti_csrf_check,
+            session_required,
+            check_database,
+            override_global_claim_validators,
+            user_context,
+        )
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class SessionRecipe +(recipe_id: str, app_info: AppInfo, cookie_domain: Union[str, None] = None, older_cookie_domain: Union[str, None] = None, cookie_secure: Union[bool, None] = None, cookie_same_site: "Union[Literal[('lax', 'none', 'strict')], None]" = None, session_expired_status_code: Union[int, None] = None, anti_csrf: "Union[Literal[('VIA_TOKEN', 'VIA_CUSTOM_HEADER', 'NONE')], None]" = None, get_token_transfer_method: "Union[Callable[[BaseRequest, bool, Dict[str, Any]], Union[TokenTransferMethod, Literal['any']]], None]" = None, error_handlers: Union[InputErrorHandlers, None] = None, override: Union[InputOverrideConfig, None] = None, invalid_claim_status_code: Union[int, None] = None, use_dynamic_access_token_signing_key: Union[bool, None] = None, expose_access_token_to_frontend_in_cookie_based_auth: Union[bool, None] = None, jwks_refresh_interval_sec: Union[int, None] = None) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class SessionRecipe(RecipeModule):
+    recipe_id = "session"
+    __instance = None
+
+    def __init__(
+        self,
+        recipe_id: str,
+        app_info: AppInfo,
+        cookie_domain: Union[str, None] = None,
+        older_cookie_domain: Union[str, None] = None,
+        cookie_secure: Union[bool, None] = None,
+        cookie_same_site: Union[Literal["lax", "none", "strict"], None] = None,
+        session_expired_status_code: Union[int, None] = None,
+        anti_csrf: Union[
+            Literal["VIA_TOKEN", "VIA_CUSTOM_HEADER", "NONE"], None
+        ] = None,
+        get_token_transfer_method: Union[
+            Callable[
+                [BaseRequest, bool, Dict[str, Any]],
+                Union[TokenTransferMethod, Literal["any"]],
+            ],
+            None,
+        ] = None,
+        error_handlers: Union[InputErrorHandlers, None] = None,
+        override: Union[InputOverrideConfig, None] = None,
+        invalid_claim_status_code: Union[int, None] = None,
+        use_dynamic_access_token_signing_key: Union[bool, None] = None,
+        expose_access_token_to_frontend_in_cookie_based_auth: Union[bool, None] = None,
+        jwks_refresh_interval_sec: Union[int, None] = None,
+    ):
+        super().__init__(recipe_id, app_info)
+        self.config = validate_and_normalise_user_input(
+            app_info,
+            cookie_domain,
+            older_cookie_domain,
+            cookie_secure,
+            cookie_same_site,
+            session_expired_status_code,
+            anti_csrf,
+            get_token_transfer_method,
+            error_handlers,
+            override,
+            invalid_claim_status_code,
+            use_dynamic_access_token_signing_key,
+            expose_access_token_to_frontend_in_cookie_based_auth,
+            jwks_refresh_interval_sec,
+        )
+        self.openid_recipe = OpenIdRecipe(
+            recipe_id,
+            app_info,
+            None,
+            None,
+            override.openid_feature if override is not None else None,
+        )
+        log_debug_message(
+            "session init: anti_csrf: %s", self.config.anti_csrf_function_or_string
+        )
+        if self.config.cookie_domain is not None:
+            log_debug_message(
+                "session init: cookie_domain: %s", self.config.cookie_domain
+            )
+        else:
+            log_debug_message("session init: cookie_domain: None")
+
+        # we check the input cookie_same_site because the normalised version is
+        # always a function.
+        if cookie_same_site is not None:
+            log_debug_message("session init: cookie_same_site: %s", cookie_same_site)
+        else:
+            log_debug_message("session init: cookie_same_site: function")
+
+        log_debug_message(
+            "session init: cookie_secure: %s", str(self.config.cookie_secure)
+        )
+        log_debug_message(
+            "session init: refresh_token_path: %s ",
+            self.config.refresh_token_path.get_as_string_dangerous(),
+        )
+        log_debug_message(
+            "session init: session_expired_status_code: %s",
+            str(self.config.session_expired_status_code),
+        )
+        recipe_implementation = RecipeImplementation(
+            Querier.get_instance(recipe_id), self.config, self.app_info
+        )
+        self.recipe_implementation: RecipeInterface = (
+            recipe_implementation
+            if self.config.override.functions is None
+            else self.config.override.functions(recipe_implementation)
+        )
+
+        from .api.implementation import APIImplementation
+
+        api_implementation = APIImplementation()
+        self.api_implementation: APIInterface = (
+            api_implementation
+            if self.config.override.apis is None
+            else self.config.override.apis(api_implementation)
+        )
+
+        self.claims_added_by_other_recipes: List[SessionClaim[Any]] = []
+        self.claim_validators_added_by_other_recipes: List[SessionClaimValidator] = []
+
+    def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
+        return isinstance(err, SuperTokensError) and (
+            isinstance(err, SuperTokensSessionError)
+            or self.openid_recipe.is_error_from_this_recipe_based_on_instance(err)
+        )
+
+    def get_apis_handled(self) -> List[APIHandled]:
+        apis_handled = [
+            APIHandled(
+                NormalisedURLPath(SESSION_REFRESH),
+                "post",
+                SESSION_REFRESH,
+                self.api_implementation.disable_refresh_post,
+            ),
+            APIHandled(
+                NormalisedURLPath(SIGNOUT),
+                "post",
+                SIGNOUT,
+                self.api_implementation.disable_signout_post,
+            ),
+        ]
+        apis_handled.extend(self.openid_recipe.get_apis_handled())
+
+        return apis_handled
+
+    async def handle_api_request(
+        self,
+        request_id: str,
+        tenant_id: str,
+        request: BaseRequest,
+        path: NormalisedURLPath,
+        method: str,
+        response: BaseResponse,
+        user_context: Dict[str, Any],
+    ) -> Union[BaseResponse, None]:
+        if request_id == SESSION_REFRESH:
+            return await handle_refresh_api(
+                self.api_implementation,
+                APIOptions(
+                    request,
+                    response,
+                    self.recipe_id,
+                    self.config,
+                    self.recipe_implementation,
+                ),
+                user_context,
+            )
+        if request_id == SIGNOUT:
+            return await handle_signout_api(
+                self.api_implementation,
+                APIOptions(
+                    request,
+                    response,
+                    self.recipe_id,
+                    self.config,
+                    self.recipe_implementation,
+                ),
+                user_context,
+            )
+        return await self.openid_recipe.handle_api_request(
+            request_id, tenant_id, request, path, method, response, user_context
+        )
+
+    async def handle_error(
+        self,
+        request: BaseRequest,
+        err: SuperTokensError,
+        response: BaseResponse,
+        user_context: Dict[str, Any],
+    ) -> BaseResponse:
+        if (
+            isinstance(err, SuperTokensSessionError)
+            and err.response_mutators is not None
+        ):
+            for mutator in err.response_mutators:
+                mutator(response, user_context)
+
+        if isinstance(err, UnauthorisedError):
+            log_debug_message("errorHandler: returning UNAUTHORISED")
+            if err.clear_tokens:
+                log_debug_message("Clearing tokens because of UNAUTHORISED response")
+                clear_session_from_all_token_transfer_methods(
+                    response, self, request, user_context
+                )
+            return await self.config.error_handlers.on_unauthorised(
+                request, str(err), response
+            )
+        if isinstance(err, TokenTheftError):
+            log_debug_message("errorHandler: returning TOKEN_THEFT_DETECTED")
+            log_debug_message(
+                "Clearing tokens because of TOKEN_THEFT_DETECTED response"
+            )
+            clear_session_from_all_token_transfer_methods(
+                response, self, request, user_context
+            )
+            return await self.config.error_handlers.on_token_theft_detected(
+                request, err.session_handle, err.user_id, response
+            )
+        if isinstance(err, InvalidClaimsError):
+            log_debug_message("errorHandler: returning INVALID_CLAIMS")
+            return await self.config.error_handlers.on_invalid_claim(
+                self, request, err.payload, response
+            )
+        if isinstance(err, ClearDuplicateSessionCookiesError):
+            log_debug_message("errorHandler: returning CLEAR_DUPLICATE_SESSION_COOKIES")
+            return await self.config.error_handlers.on_clear_duplicate_session_cookies(
+                request, str(err), response
+            )
+
+        log_debug_message("errorHandler: returning TRY_REFRESH_TOKEN")
+        return await self.config.error_handlers.on_try_refresh_token(
+            request, str(err), response
+        )
+
+    def get_all_cors_headers(self) -> List[str]:
+        cors_headers = get_cors_allowed_headers()
+        cors_headers.extend(self.openid_recipe.get_all_cors_headers())
+
+        return cors_headers
+
+    @staticmethod
+    def init(
+        cookie_domain: Union[str, None] = None,
+        older_cookie_domain: Union[str, None] = None,
+        cookie_secure: Union[bool, None] = None,
+        cookie_same_site: Union[Literal["lax", "none", "strict"], None] = None,
+        session_expired_status_code: Union[int, None] = None,
+        anti_csrf: Union[
+            Literal["VIA_TOKEN", "VIA_CUSTOM_HEADER", "NONE"], None
+        ] = None,
+        get_token_transfer_method: Union[
+            Callable[
+                [BaseRequest, bool, Dict[str, Any]],
+                Union[TokenTransferMethod, Literal["any"]],
+            ],
+            None,
+        ] = None,
+        error_handlers: Union[InputErrorHandlers, None] = None,
+        override: Union[InputOverrideConfig, None] = None,
+        invalid_claim_status_code: Union[int, None] = None,
+        use_dynamic_access_token_signing_key: Union[bool, None] = None,
+        expose_access_token_to_frontend_in_cookie_based_auth: Union[bool, None] = None,
+        jwks_refresh_interval_sec: Union[int, None] = None,
+    ):
+        def func(app_info: AppInfo):
+            if SessionRecipe.__instance is None:
+                SessionRecipe.__instance = SessionRecipe(
+                    SessionRecipe.recipe_id,
+                    app_info,
+                    cookie_domain,
+                    older_cookie_domain,
+                    cookie_secure,
+                    cookie_same_site,
+                    session_expired_status_code,
+                    anti_csrf,
+                    get_token_transfer_method,
+                    error_handlers,
+                    override,
+                    invalid_claim_status_code,
+                    use_dynamic_access_token_signing_key,
+                    expose_access_token_to_frontend_in_cookie_based_auth,
+                    jwks_refresh_interval_sec,
+                )
+                return SessionRecipe.__instance
+            raise_general_exception(
+                "Session recipe has already been initialised. Please check your code for bugs."
+            )
+
+        return func
+
+    @staticmethod
+    def get_instance() -> SessionRecipe:
+        if SessionRecipe.__instance is not None:
+            return SessionRecipe.__instance
+        raise_general_exception(
+            "Initialisation not done. Did you forget to call the SuperTokens.init function?"
+        )
+
+    @staticmethod
+    def reset():
+        if ("SUPERTOKENS_ENV" not in environ) or (
+            environ["SUPERTOKENS_ENV"] != "testing"
+        ):
+            raise_general_exception("calling testing function in non testing env")
+        SessionRecipe.__instance = None
+
+    def add_claim_from_other_recipe(self, claim: SessionClaim[Any]):
+        # We are throwing here (and not in addClaimValidatorFromOtherRecipe) because if multiple
+        # claims are added with the same key they will overwrite each other. Validators will all run
+        # and work as expected even if they are added multiple times.
+        if claim.key in [c.key for c in self.claims_added_by_other_recipes]:
+            raise Exception("Claim added by multiple recipes")
+
+        self.claims_added_by_other_recipes.append(claim)
+
+    def get_claims_added_by_other_recipes(self) -> List[SessionClaim[Any]]:
+        return self.claims_added_by_other_recipes
+
+    def add_claim_validator_from_other_recipe(
+        self, claim_validator: SessionClaimValidator
+    ):
+        self.claim_validators_added_by_other_recipes.append(claim_validator)
+
+    def get_claim_validators_added_by_other_recipes(
+        self,
+    ) -> List[SessionClaimValidator]:
+        return self.claim_validators_added_by_other_recipes
+
+    async def verify_session(
+        self,
+        request: BaseRequest,
+        anti_csrf_check: Union[bool, None],
+        session_required: bool,
+        check_database: bool,
+        override_global_claim_validators: Optional[
+            Callable[
+                [List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
+                MaybeAwaitable[List[SessionClaimValidator]],
+            ]
+        ],
+        user_context: Dict[str, Any],
+    ):
+        _ = user_context
+
+        return await self.api_implementation.verify_session(
+            APIOptions(
+                request,
+                None,
+                self.recipe_id,
+                self.config,
+                self.recipe_implementation,
+            ),
+            anti_csrf_check,
+            session_required,
+            check_database,
+            override_global_claim_validators,
+            user_context,
+        )
+
+

Ancestors

+ +

Class variables

+
+
var get_tenant_id : Optional[Callable[[str, Dict[str, Any]], Awaitable[str]]]
+
+
+
+
var recipe_id
+
+
+
+
+

Static methods

+
+
+def get_instance() ‑> SessionRecipe +
+
+
+
+ +Expand source code + +
@staticmethod
+def get_instance() -> SessionRecipe:
+    if SessionRecipe.__instance is not None:
+        return SessionRecipe.__instance
+    raise_general_exception(
+        "Initialisation not done. Did you forget to call the SuperTokens.init function?"
+    )
+
+
+
+def init(cookie_domain: Union[str, None] = None, older_cookie_domain: Union[str, None] = None, cookie_secure: Union[bool, None] = None, cookie_same_site: "Union[Literal[('lax', 'none', 'strict')], None]" = None, session_expired_status_code: Union[int, None] = None, anti_csrf: "Union[Literal[('VIA_TOKEN', 'VIA_CUSTOM_HEADER', 'NONE')], None]" = None, get_token_transfer_method: "Union[Callable[[BaseRequest, bool, Dict[str, Any]], Union[TokenTransferMethod, Literal['any']]], None]" = None, error_handlers: Union[InputErrorHandlers, None] = None, override: Union[InputOverrideConfig, None] = None, invalid_claim_status_code: Union[int, None] = None, use_dynamic_access_token_signing_key: Union[bool, None] = None, expose_access_token_to_frontend_in_cookie_based_auth: Union[bool, None] = None, jwks_refresh_interval_sec: Union[int, None] = None) +
+
+
+
+ +Expand source code + +
@staticmethod
+def init(
+    cookie_domain: Union[str, None] = None,
+    older_cookie_domain: Union[str, None] = None,
+    cookie_secure: Union[bool, None] = None,
+    cookie_same_site: Union[Literal["lax", "none", "strict"], None] = None,
+    session_expired_status_code: Union[int, None] = None,
+    anti_csrf: Union[
+        Literal["VIA_TOKEN", "VIA_CUSTOM_HEADER", "NONE"], None
+    ] = None,
+    get_token_transfer_method: Union[
+        Callable[
+            [BaseRequest, bool, Dict[str, Any]],
+            Union[TokenTransferMethod, Literal["any"]],
+        ],
+        None,
+    ] = None,
+    error_handlers: Union[InputErrorHandlers, None] = None,
+    override: Union[InputOverrideConfig, None] = None,
+    invalid_claim_status_code: Union[int, None] = None,
+    use_dynamic_access_token_signing_key: Union[bool, None] = None,
+    expose_access_token_to_frontend_in_cookie_based_auth: Union[bool, None] = None,
+    jwks_refresh_interval_sec: Union[int, None] = None,
+):
+    def func(app_info: AppInfo):
+        if SessionRecipe.__instance is None:
+            SessionRecipe.__instance = SessionRecipe(
+                SessionRecipe.recipe_id,
+                app_info,
+                cookie_domain,
+                older_cookie_domain,
+                cookie_secure,
+                cookie_same_site,
+                session_expired_status_code,
+                anti_csrf,
+                get_token_transfer_method,
+                error_handlers,
+                override,
+                invalid_claim_status_code,
+                use_dynamic_access_token_signing_key,
+                expose_access_token_to_frontend_in_cookie_based_auth,
+                jwks_refresh_interval_sec,
+            )
+            return SessionRecipe.__instance
+        raise_general_exception(
+            "Session recipe has already been initialised. Please check your code for bugs."
+        )
+
+    return func
+
+
+
+def reset() +
+
+
+
+ +Expand source code + +
@staticmethod
+def reset():
+    if ("SUPERTOKENS_ENV" not in environ) or (
+        environ["SUPERTOKENS_ENV"] != "testing"
+    ):
+        raise_general_exception("calling testing function in non testing env")
+    SessionRecipe.__instance = None
+
+
+
+

Methods

+
+
+def add_claim_from_other_recipe(self, claim: SessionClaim[Any]) +
+
+
+
+ +Expand source code + +
def add_claim_from_other_recipe(self, claim: SessionClaim[Any]):
+    # We are throwing here (and not in addClaimValidatorFromOtherRecipe) because if multiple
+    # claims are added with the same key they will overwrite each other. Validators will all run
+    # and work as expected even if they are added multiple times.
+    if claim.key in [c.key for c in self.claims_added_by_other_recipes]:
+        raise Exception("Claim added by multiple recipes")
+
+    self.claims_added_by_other_recipes.append(claim)
+
+
+
+def add_claim_validator_from_other_recipe(self, claim_validator: SessionClaimValidator) +
+
+
+
+ +Expand source code + +
def add_claim_validator_from_other_recipe(
+    self, claim_validator: SessionClaimValidator
+):
+    self.claim_validators_added_by_other_recipes.append(claim_validator)
+
+
+
+def get_all_cors_headers(self) ‑> List[str] +
+
+
+
+ +Expand source code + +
def get_all_cors_headers(self) -> List[str]:
+    cors_headers = get_cors_allowed_headers()
+    cors_headers.extend(self.openid_recipe.get_all_cors_headers())
+
+    return cors_headers
+
+
+
+def get_apis_handled(self) ‑> List[APIHandled] +
+
+
+
+ +Expand source code + +
def get_apis_handled(self) -> List[APIHandled]:
+    apis_handled = [
+        APIHandled(
+            NormalisedURLPath(SESSION_REFRESH),
+            "post",
+            SESSION_REFRESH,
+            self.api_implementation.disable_refresh_post,
+        ),
+        APIHandled(
+            NormalisedURLPath(SIGNOUT),
+            "post",
+            SIGNOUT,
+            self.api_implementation.disable_signout_post,
+        ),
+    ]
+    apis_handled.extend(self.openid_recipe.get_apis_handled())
+
+    return apis_handled
+
+
+
+def get_claim_validators_added_by_other_recipes(self) ‑> List[SessionClaimValidator] +
+
+
+
+ +Expand source code + +
def get_claim_validators_added_by_other_recipes(
+    self,
+) -> List[SessionClaimValidator]:
+    return self.claim_validators_added_by_other_recipes
+
+
+
+def get_claims_added_by_other_recipes(self) ‑> List[SessionClaim[Any]] +
+
+
+
+ +Expand source code + +
def get_claims_added_by_other_recipes(self) -> List[SessionClaim[Any]]:
+    return self.claims_added_by_other_recipes
+
+
+
+async def handle_api_request(self, request_id: str, tenant_id: str, request: BaseRequest, path: NormalisedURLPath, method: str, response: BaseResponse, user_context: Dict[str, Any]) ‑> Union[BaseResponse, None] +
+
+
+
+ +Expand source code + +
async def handle_api_request(
+    self,
+    request_id: str,
+    tenant_id: str,
+    request: BaseRequest,
+    path: NormalisedURLPath,
+    method: str,
+    response: BaseResponse,
+    user_context: Dict[str, Any],
+) -> Union[BaseResponse, None]:
+    if request_id == SESSION_REFRESH:
+        return await handle_refresh_api(
+            self.api_implementation,
+            APIOptions(
+                request,
+                response,
+                self.recipe_id,
+                self.config,
+                self.recipe_implementation,
+            ),
+            user_context,
+        )
+    if request_id == SIGNOUT:
+        return await handle_signout_api(
+            self.api_implementation,
+            APIOptions(
+                request,
+                response,
+                self.recipe_id,
+                self.config,
+                self.recipe_implementation,
+            ),
+            user_context,
+        )
+    return await self.openid_recipe.handle_api_request(
+        request_id, tenant_id, request, path, method, response, user_context
+    )
+
+
+
+async def handle_error(self, request: BaseRequest, err: SuperTokensError, response: BaseResponse, user_context: Dict[str, Any]) ‑> BaseResponse +
+
+
+
+ +Expand source code + +
async def handle_error(
+    self,
+    request: BaseRequest,
+    err: SuperTokensError,
+    response: BaseResponse,
+    user_context: Dict[str, Any],
+) -> BaseResponse:
+    if (
+        isinstance(err, SuperTokensSessionError)
+        and err.response_mutators is not None
+    ):
+        for mutator in err.response_mutators:
+            mutator(response, user_context)
+
+    if isinstance(err, UnauthorisedError):
+        log_debug_message("errorHandler: returning UNAUTHORISED")
+        if err.clear_tokens:
+            log_debug_message("Clearing tokens because of UNAUTHORISED response")
+            clear_session_from_all_token_transfer_methods(
+                response, self, request, user_context
+            )
+        return await self.config.error_handlers.on_unauthorised(
+            request, str(err), response
+        )
+    if isinstance(err, TokenTheftError):
+        log_debug_message("errorHandler: returning TOKEN_THEFT_DETECTED")
+        log_debug_message(
+            "Clearing tokens because of TOKEN_THEFT_DETECTED response"
+        )
+        clear_session_from_all_token_transfer_methods(
+            response, self, request, user_context
+        )
+        return await self.config.error_handlers.on_token_theft_detected(
+            request, err.session_handle, err.user_id, response
+        )
+    if isinstance(err, InvalidClaimsError):
+        log_debug_message("errorHandler: returning INVALID_CLAIMS")
+        return await self.config.error_handlers.on_invalid_claim(
+            self, request, err.payload, response
+        )
+    if isinstance(err, ClearDuplicateSessionCookiesError):
+        log_debug_message("errorHandler: returning CLEAR_DUPLICATE_SESSION_COOKIES")
+        return await self.config.error_handlers.on_clear_duplicate_session_cookies(
+            request, str(err), response
+        )
+
+    log_debug_message("errorHandler: returning TRY_REFRESH_TOKEN")
+    return await self.config.error_handlers.on_try_refresh_token(
+        request, str(err), response
+    )
+
+
+
+def is_error_from_this_recipe_based_on_instance(self, err: Exception) ‑> bool +
+
+
+
+ +Expand source code + +
def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
+    return isinstance(err, SuperTokensError) and (
+        isinstance(err, SuperTokensSessionError)
+        or self.openid_recipe.is_error_from_this_recipe_based_on_instance(err)
+    )
+
+
+
+async def verify_session(self, request: BaseRequest, anti_csrf_check: Union[bool, None], session_required: bool, check_database: bool, override_global_claim_validators: Optional[Callable[[List[SessionClaimValidator], SessionContainer, Dict[str, Any]], MaybeAwaitable[List[SessionClaimValidator]]]], user_context: Dict[str, Any]) +
+
+
+
+ +Expand source code + +
async def verify_session(
+    self,
+    request: BaseRequest,
+    anti_csrf_check: Union[bool, None],
+    session_required: bool,
+    check_database: bool,
+    override_global_claim_validators: Optional[
+        Callable[
+            [List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
+            MaybeAwaitable[List[SessionClaimValidator]],
+        ]
+    ],
+    user_context: Dict[str, Any],
+):
+    _ = user_context
+
+    return await self.api_implementation.verify_session(
+        APIOptions(
+            request,
+            None,
+            self.recipe_id,
+            self.config,
+            self.recipe_implementation,
+        ),
+        anti_csrf_check,
+        session_required,
+        check_database,
+        override_global_claim_validators,
+        user_context,
+    )
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/session/recipe_implementation.html b/html/supertokens_python/recipe/session/recipe_implementation.html new file mode 100644 index 000000000..fa4be8189 --- /dev/null +++ b/html/supertokens_python/recipe/session/recipe_implementation.html @@ -0,0 +1,1687 @@ + + + + + + +supertokens_python.recipe.session.recipe_implementation API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.session.recipe_implementation

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+import json
+from typing import TYPE_CHECKING, Any, Callable, Dict, Optional
+
+from supertokens_python.logger import log_debug_message
+from supertokens_python.normalised_url_path import NormalisedURLPath
+from supertokens_python.utils import resolve
+
+from ...types import MaybeAwaitable
+from . import session_functions
+from .access_token import validate_access_token_structure
+from .cookie_and_header import build_front_token
+from .exceptions import UnauthorisedError
+from .interfaces import (
+    AccessTokenObj,
+    ClaimsValidationResult,
+    GetClaimValueOkResult,
+    JSONObject,
+    RecipeInterface,
+    RegenerateAccessTokenOkResult,
+    SessionClaim,
+    SessionClaimValidator,
+    SessionDoesNotExistError,
+    SessionInformationResult,
+    SessionObj,
+)
+from .jwt import ParsedJWTInfo, parse_jwt_without_signature_verification
+from .session_class import Session
+from .utils import SessionConfig, validate_claims_in_payload
+
+if TYPE_CHECKING:
+    from typing import List, Union
+    from supertokens_python import AppInfo
+
+from .interfaces import SessionContainer
+from .constants import protected_props
+from supertokens_python.querier import Querier
+from supertokens_python.recipe.multitenancy.constants import DEFAULT_TENANT_ID
+
+
+class RecipeImplementation(RecipeInterface):  # pylint: disable=too-many-public-methods
+    def __init__(self, querier: Querier, config: SessionConfig, app_info: AppInfo):
+        super().__init__()
+        self.querier = querier
+        self.config = config
+        self.app_info = app_info
+
+    async def create_new_session(
+        self,
+        user_id: str,
+        access_token_payload: Optional[Dict[str, Any]],
+        session_data_in_database: Optional[Dict[str, Any]],
+        disable_anti_csrf: Optional[bool],
+        tenant_id: str,
+        user_context: Dict[str, Any],
+    ) -> SessionContainer:
+        log_debug_message("createNewSession: Started")
+
+        result = await session_functions.create_new_session(
+            self,
+            tenant_id,
+            user_id,
+            disable_anti_csrf is True,
+            access_token_payload,
+            session_data_in_database,
+            user_context=user_context,
+        )
+        log_debug_message("createNewSession: Finished")
+
+        payload = parse_jwt_without_signature_verification(
+            result.accessToken.token
+        ).payload
+
+        new_session = Session(
+            self,
+            self.config,
+            result.accessToken.token,
+            build_front_token(
+                result.session.userId, result.accessToken.expiry, payload
+            ),
+            result.refreshToken,
+            result.antiCsrfToken,
+            result.session.handle,
+            result.session.userId,
+            payload,
+            None,
+            True,
+            tenant_id,
+        )
+
+        return new_session
+
+    async def validate_claims(
+        self,
+        user_id: str,
+        access_token_payload: Dict[str, Any],
+        claim_validators: List[SessionClaimValidator],
+        user_context: Dict[str, Any],
+    ) -> ClaimsValidationResult:
+        access_token_payload_update = None
+        original_access_token_payload = json.dumps(access_token_payload)
+
+        for validator in claim_validators:
+            log_debug_message(
+                "update_claims_in_payload_if_needed checking should_refetch for %s",
+                validator.id,
+            )
+            if validator.claim is not None and validator.should_refetch(
+                access_token_payload, user_context
+            ):
+                log_debug_message(
+                    "update_claims_in_payload_if_needed refetching for %s", validator.id
+                )
+                value = await resolve(
+                    validator.claim.fetch_value(
+                        user_id,
+                        access_token_payload.get("tId", DEFAULT_TENANT_ID),
+                        user_context,
+                    )
+                )
+                log_debug_message(
+                    "update_claims_in_payload_if_needed %s refetch result %s",
+                    validator.id,
+                    json.dumps(value),
+                )
+                if value is not None:
+                    access_token_payload = validator.claim.add_to_payload_(
+                        access_token_payload, value, user_context
+                    )
+
+        if json.dumps(access_token_payload) != original_access_token_payload:
+            access_token_payload_update = access_token_payload
+
+        invalid_claims = await validate_claims_in_payload(
+            claim_validators, access_token_payload, user_context
+        )
+
+        return ClaimsValidationResult(invalid_claims, access_token_payload_update)
+
+    async def validate_claims_in_jwt_payload(
+        self,
+        user_id: str,
+        jwt_payload: JSONObject,
+        claim_validators: List[SessionClaimValidator],
+        user_context: Dict[str, Any],
+    ) -> ClaimsValidationResult:
+        invalid_claims = await validate_claims_in_payload(
+            claim_validators,
+            jwt_payload,
+            user_context,
+        )
+
+        return ClaimsValidationResult(invalid_claims)
+
+    async def get_session(
+        self,
+        access_token: Optional[str],
+        anti_csrf_token: Optional[str] = None,
+        anti_csrf_check: Optional[bool] = None,
+        session_required: Optional[bool] = None,
+        check_database: Optional[bool] = None,
+        override_global_claim_validators: Optional[
+            Callable[
+                [List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
+                MaybeAwaitable[List[SessionClaimValidator]],
+            ]
+        ] = None,
+        user_context: Optional[Dict[str, Any]] = None,
+    ) -> Optional[SessionContainer]:
+        if (
+            anti_csrf_check is not False
+            and isinstance(self.config.anti_csrf_function_or_string, str)
+            and self.config.anti_csrf_function_or_string == "VIA_CUSTOM_HEADER"
+        ):
+            raise Exception(
+                "Since the anti-csrf mode is VIA_CUSTOM_HEADER getSession can't check the CSRF token. Please either use VIA_TOKEN or set anti_csrf_check to false"
+            )
+
+        log_debug_message("getSession: Started")
+
+        if access_token is None:
+            if session_required is False:
+                log_debug_message(
+                    "getSession: returning None because access_token is undefined and session_required is False"
+                )
+                # there is no session that exists here, and the user wants session verification to be optional. So we return None
+                return None
+
+            log_debug_message(
+                "getSession: UNAUTHORISED because accessToken in request is undefined"
+            )
+            # we do not clear the session here because of a race condition mentioned in https://github.com/supertokens/supertokens-node/issues/17
+            raise UnauthorisedError(
+                "Session does not exist. Are you sending the session tokens in the request with the appropriate token transfer method?",
+                clear_tokens=False,
+            )
+
+        access_token_obj: Optional[ParsedJWTInfo] = None
+        try:
+            access_token_obj = parse_jwt_without_signature_verification(access_token)
+            validate_access_token_structure(
+                access_token_obj.payload, access_token_obj.version
+            )
+        except Exception as _:
+            if session_required is False:
+                log_debug_message(
+                    "getSession: Returning undefined because parsing failed and session_required is False"
+                )
+                return None
+
+            log_debug_message(
+                "getSession: UNAUTHORISED because the accessToken couldn't be parsed or had an invalid structure"
+            )
+            raise UnauthorisedError("Token parsing failed", clear_tokens=False)
+
+        response = await session_functions.get_session(
+            self,
+            access_token_obj,
+            anti_csrf_token,
+            (anti_csrf_check is not False),
+            (check_database is True),
+            user_context,
+        )
+
+        log_debug_message("getSession: Success!")
+
+        if access_token_obj.version >= 3:
+            if response.accessToken is not None:
+                payload = parse_jwt_without_signature_verification(
+                    response.accessToken.token
+                ).payload
+            else:
+                payload = access_token_obj.payload
+        else:
+            payload = response.session.userDataInJWT
+
+        if response.accessToken is not None:
+            access_token_str = response.accessToken.token
+            expiry_time = response.accessToken.expiry
+            access_token_updated = True
+        else:
+            access_token_str = access_token
+            expiry_time = response.session.expiryTime
+            access_token_updated = False
+
+        session = Session(
+            self,
+            self.config,
+            access_token_str,
+            build_front_token(response.session.userId, expiry_time, payload),
+            None,  # refresh_token
+            anti_csrf_token,
+            response.session.handle,
+            response.session.userId,
+            payload,
+            None,
+            access_token_updated,
+            response.session.tenant_id,
+        )
+
+        return session
+
+    async def refresh_session(
+        self,
+        refresh_token: str,
+        anti_csrf_token: Optional[str],
+        disable_anti_csrf: bool,
+        user_context: Dict[str, Any],
+    ) -> SessionContainer:
+        if (
+            disable_anti_csrf is not True
+            and isinstance(self.config.anti_csrf_function_or_string, str)
+            and self.config.anti_csrf_function_or_string == "VIA_CUSTOM_HEADER"
+        ):
+            raise Exception(
+                "Since the anti-csrf mode is VIA_CUSTOM_HEADER getSession can't check the CSRF token. Please either use VIA_TOKEN or set antiCsrfCheck to false"
+            )
+
+        log_debug_message("refreshSession: Started")
+
+        response = await session_functions.refresh_session(
+            self,
+            refresh_token,
+            anti_csrf_token,
+            disable_anti_csrf,
+            self.config.use_dynamic_access_token_signing_key,
+            user_context=user_context,
+        )
+
+        log_debug_message("refreshSession: Success!")
+
+        payload = parse_jwt_without_signature_verification(
+            response.accessToken.token,
+        ).payload
+
+        session = Session(
+            self,
+            self.config,
+            response.accessToken.token,
+            build_front_token(
+                response.session.userId,
+                response.accessToken.expiry,
+                payload,
+            ),
+            response.refreshToken,
+            response.antiCsrfToken,
+            response.session.handle,
+            response.session.userId,
+            user_data_in_access_token=payload,
+            req_res_info=None,
+            access_token_updated=True,
+            tenant_id=payload["tId"],
+        )
+
+        return session
+
+    async def revoke_session(
+        self, session_handle: str, user_context: Dict[str, Any]
+    ) -> bool:
+        return await session_functions.revoke_session(
+            self, session_handle, user_context
+        )
+
+    async def revoke_all_sessions_for_user(
+        self,
+        user_id: str,
+        tenant_id: Optional[str],
+        revoke_across_all_tenants: bool,
+        user_context: Dict[str, Any],
+    ) -> List[str]:
+        return await session_functions.revoke_all_sessions_for_user(
+            self, user_id, tenant_id, revoke_across_all_tenants, user_context
+        )
+
+    async def get_all_session_handles_for_user(
+        self,
+        user_id: str,
+        tenant_id: Optional[str],
+        fetch_across_all_tenants: bool,
+        user_context: Dict[str, Any],
+    ) -> List[str]:
+        return await session_functions.get_all_session_handles_for_user(
+            self, user_id, tenant_id, fetch_across_all_tenants, user_context
+        )
+
+    async def revoke_multiple_sessions(
+        self, session_handles: List[str], user_context: Dict[str, Any]
+    ) -> List[str]:
+        return await session_functions.revoke_multiple_sessions(
+            self, session_handles, user_context
+        )
+
+    async def get_session_information(
+        self, session_handle: str, user_context: Dict[str, Any]
+    ) -> Union[SessionInformationResult, None]:
+        return await session_functions.get_session_information(
+            self, session_handle, user_context
+        )
+
+    async def update_session_data_in_database(
+        self,
+        session_handle: str,
+        new_session_data: Dict[str, Any],
+        user_context: Dict[str, Any],
+    ) -> bool:
+        return await session_functions.update_session_data_in_database(
+            self, session_handle, new_session_data, user_context
+        )
+
+    async def merge_into_access_token_payload(
+        self,
+        session_handle: str,
+        access_token_payload_update: Dict[str, Any],
+        user_context: Dict[str, Any],
+    ) -> bool:
+        session_info = await self.get_session_information(session_handle, user_context)
+        if session_info is None:
+            return False
+
+        new_access_token_payload = session_info.custom_claims_in_access_token_payload
+        for k in protected_props:
+            if k in new_access_token_payload:
+                del new_access_token_payload[k]
+
+        new_access_token_payload = {
+            **new_access_token_payload,
+            **access_token_payload_update,
+        }
+        for k in access_token_payload_update.keys():
+            if new_access_token_payload[k] is None:
+                del new_access_token_payload[k]
+
+        return await session_functions.update_access_token_payload(
+            self, session_handle, new_access_token_payload, user_context
+        )
+
+    async def fetch_and_set_claim(
+        self,
+        session_handle: str,
+        claim: SessionClaim[Any],
+        user_context: Dict[str, Any],
+    ) -> bool:
+        session_info = await self.get_session_information(session_handle, user_context)
+        if session_info is None:
+            return False
+
+        access_token_payload_update = await claim.build(
+            session_info.user_id, session_info.tenant_id, user_context
+        )
+        return await self.merge_into_access_token_payload(
+            session_handle, access_token_payload_update, user_context
+        )
+
+    async def set_claim_value(
+        self,
+        session_handle: str,
+        claim: SessionClaim[Any],
+        value: Any,
+        user_context: Dict[str, Any],
+    ):
+        access_token_payload_update = claim.add_to_payload_({}, value, user_context)
+        return await self.merge_into_access_token_payload(
+            session_handle, access_token_payload_update, user_context
+        )
+
+    async def get_claim_value(
+        self,
+        session_handle: str,
+        claim: SessionClaim[Any],
+        user_context: Dict[str, Any],
+    ) -> Union[SessionDoesNotExistError, GetClaimValueOkResult[Any]]:
+        session_info = await self.get_session_information(session_handle, user_context)
+        if session_info is None:
+            return SessionDoesNotExistError()
+
+        return GetClaimValueOkResult(
+            value=claim.get_value_from_payload(
+                session_info.custom_claims_in_access_token_payload, user_context
+            )
+        )
+
+    def get_global_claim_validators(
+        self,
+        tenant_id: str,
+        user_id: str,
+        claim_validators_added_by_other_recipes: List[SessionClaimValidator],
+        user_context: Dict[str, Any],
+    ) -> MaybeAwaitable[List[SessionClaimValidator]]:
+        return claim_validators_added_by_other_recipes
+
+    async def remove_claim(
+        self,
+        session_handle: str,
+        claim: SessionClaim[Any],
+        user_context: Dict[str, Any],
+    ) -> bool:
+        access_token_payload = claim.remove_from_payload_by_merge_({}, user_context)
+        return await self.merge_into_access_token_payload(
+            session_handle, access_token_payload, user_context
+        )
+
+    async def regenerate_access_token(
+        self,
+        access_token: str,
+        new_access_token_payload: Union[Dict[str, Any], None],
+        user_context: Dict[str, Any],
+    ) -> Union[RegenerateAccessTokenOkResult, None]:
+        if new_access_token_payload is None:
+            new_access_token_payload = {}
+        response = await self.querier.send_post_request(
+            NormalisedURLPath("/recipe/session/regenerate"),
+            {"accessToken": access_token, "userDataInJWT": new_access_token_payload},
+            user_context=user_context,
+        )
+        if response["status"] == "UNAUTHORISED":
+            return None
+        access_token_obj: Union[None, AccessTokenObj] = None
+        if "accessToken" in response:
+            access_token_obj = AccessTokenObj(
+                response["accessToken"]["token"],
+                response["accessToken"]["expiry"],
+                response["accessToken"]["createdTime"],
+            )
+        session = SessionObj(
+            response["session"]["handle"],
+            response["session"]["userId"],
+            response["session"]["userDataInJWT"],
+            response["session"]["tenantId"],
+        )
+        return RegenerateAccessTokenOkResult(session, access_token_obj)
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class RecipeImplementation +(querier: Querier, config: SessionConfig, app_info: AppInfo) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class RecipeImplementation(RecipeInterface):  # pylint: disable=too-many-public-methods
+    def __init__(self, querier: Querier, config: SessionConfig, app_info: AppInfo):
+        super().__init__()
+        self.querier = querier
+        self.config = config
+        self.app_info = app_info
+
+    async def create_new_session(
+        self,
+        user_id: str,
+        access_token_payload: Optional[Dict[str, Any]],
+        session_data_in_database: Optional[Dict[str, Any]],
+        disable_anti_csrf: Optional[bool],
+        tenant_id: str,
+        user_context: Dict[str, Any],
+    ) -> SessionContainer:
+        log_debug_message("createNewSession: Started")
+
+        result = await session_functions.create_new_session(
+            self,
+            tenant_id,
+            user_id,
+            disable_anti_csrf is True,
+            access_token_payload,
+            session_data_in_database,
+            user_context=user_context,
+        )
+        log_debug_message("createNewSession: Finished")
+
+        payload = parse_jwt_without_signature_verification(
+            result.accessToken.token
+        ).payload
+
+        new_session = Session(
+            self,
+            self.config,
+            result.accessToken.token,
+            build_front_token(
+                result.session.userId, result.accessToken.expiry, payload
+            ),
+            result.refreshToken,
+            result.antiCsrfToken,
+            result.session.handle,
+            result.session.userId,
+            payload,
+            None,
+            True,
+            tenant_id,
+        )
+
+        return new_session
+
+    async def validate_claims(
+        self,
+        user_id: str,
+        access_token_payload: Dict[str, Any],
+        claim_validators: List[SessionClaimValidator],
+        user_context: Dict[str, Any],
+    ) -> ClaimsValidationResult:
+        access_token_payload_update = None
+        original_access_token_payload = json.dumps(access_token_payload)
+
+        for validator in claim_validators:
+            log_debug_message(
+                "update_claims_in_payload_if_needed checking should_refetch for %s",
+                validator.id,
+            )
+            if validator.claim is not None and validator.should_refetch(
+                access_token_payload, user_context
+            ):
+                log_debug_message(
+                    "update_claims_in_payload_if_needed refetching for %s", validator.id
+                )
+                value = await resolve(
+                    validator.claim.fetch_value(
+                        user_id,
+                        access_token_payload.get("tId", DEFAULT_TENANT_ID),
+                        user_context,
+                    )
+                )
+                log_debug_message(
+                    "update_claims_in_payload_if_needed %s refetch result %s",
+                    validator.id,
+                    json.dumps(value),
+                )
+                if value is not None:
+                    access_token_payload = validator.claim.add_to_payload_(
+                        access_token_payload, value, user_context
+                    )
+
+        if json.dumps(access_token_payload) != original_access_token_payload:
+            access_token_payload_update = access_token_payload
+
+        invalid_claims = await validate_claims_in_payload(
+            claim_validators, access_token_payload, user_context
+        )
+
+        return ClaimsValidationResult(invalid_claims, access_token_payload_update)
+
+    async def validate_claims_in_jwt_payload(
+        self,
+        user_id: str,
+        jwt_payload: JSONObject,
+        claim_validators: List[SessionClaimValidator],
+        user_context: Dict[str, Any],
+    ) -> ClaimsValidationResult:
+        invalid_claims = await validate_claims_in_payload(
+            claim_validators,
+            jwt_payload,
+            user_context,
+        )
+
+        return ClaimsValidationResult(invalid_claims)
+
+    async def get_session(
+        self,
+        access_token: Optional[str],
+        anti_csrf_token: Optional[str] = None,
+        anti_csrf_check: Optional[bool] = None,
+        session_required: Optional[bool] = None,
+        check_database: Optional[bool] = None,
+        override_global_claim_validators: Optional[
+            Callable[
+                [List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
+                MaybeAwaitable[List[SessionClaimValidator]],
+            ]
+        ] = None,
+        user_context: Optional[Dict[str, Any]] = None,
+    ) -> Optional[SessionContainer]:
+        if (
+            anti_csrf_check is not False
+            and isinstance(self.config.anti_csrf_function_or_string, str)
+            and self.config.anti_csrf_function_or_string == "VIA_CUSTOM_HEADER"
+        ):
+            raise Exception(
+                "Since the anti-csrf mode is VIA_CUSTOM_HEADER getSession can't check the CSRF token. Please either use VIA_TOKEN or set anti_csrf_check to false"
+            )
+
+        log_debug_message("getSession: Started")
+
+        if access_token is None:
+            if session_required is False:
+                log_debug_message(
+                    "getSession: returning None because access_token is undefined and session_required is False"
+                )
+                # there is no session that exists here, and the user wants session verification to be optional. So we return None
+                return None
+
+            log_debug_message(
+                "getSession: UNAUTHORISED because accessToken in request is undefined"
+            )
+            # we do not clear the session here because of a race condition mentioned in https://github.com/supertokens/supertokens-node/issues/17
+            raise UnauthorisedError(
+                "Session does not exist. Are you sending the session tokens in the request with the appropriate token transfer method?",
+                clear_tokens=False,
+            )
+
+        access_token_obj: Optional[ParsedJWTInfo] = None
+        try:
+            access_token_obj = parse_jwt_without_signature_verification(access_token)
+            validate_access_token_structure(
+                access_token_obj.payload, access_token_obj.version
+            )
+        except Exception as _:
+            if session_required is False:
+                log_debug_message(
+                    "getSession: Returning undefined because parsing failed and session_required is False"
+                )
+                return None
+
+            log_debug_message(
+                "getSession: UNAUTHORISED because the accessToken couldn't be parsed or had an invalid structure"
+            )
+            raise UnauthorisedError("Token parsing failed", clear_tokens=False)
+
+        response = await session_functions.get_session(
+            self,
+            access_token_obj,
+            anti_csrf_token,
+            (anti_csrf_check is not False),
+            (check_database is True),
+            user_context,
+        )
+
+        log_debug_message("getSession: Success!")
+
+        if access_token_obj.version >= 3:
+            if response.accessToken is not None:
+                payload = parse_jwt_without_signature_verification(
+                    response.accessToken.token
+                ).payload
+            else:
+                payload = access_token_obj.payload
+        else:
+            payload = response.session.userDataInJWT
+
+        if response.accessToken is not None:
+            access_token_str = response.accessToken.token
+            expiry_time = response.accessToken.expiry
+            access_token_updated = True
+        else:
+            access_token_str = access_token
+            expiry_time = response.session.expiryTime
+            access_token_updated = False
+
+        session = Session(
+            self,
+            self.config,
+            access_token_str,
+            build_front_token(response.session.userId, expiry_time, payload),
+            None,  # refresh_token
+            anti_csrf_token,
+            response.session.handle,
+            response.session.userId,
+            payload,
+            None,
+            access_token_updated,
+            response.session.tenant_id,
+        )
+
+        return session
+
+    async def refresh_session(
+        self,
+        refresh_token: str,
+        anti_csrf_token: Optional[str],
+        disable_anti_csrf: bool,
+        user_context: Dict[str, Any],
+    ) -> SessionContainer:
+        if (
+            disable_anti_csrf is not True
+            and isinstance(self.config.anti_csrf_function_or_string, str)
+            and self.config.anti_csrf_function_or_string == "VIA_CUSTOM_HEADER"
+        ):
+            raise Exception(
+                "Since the anti-csrf mode is VIA_CUSTOM_HEADER getSession can't check the CSRF token. Please either use VIA_TOKEN or set antiCsrfCheck to false"
+            )
+
+        log_debug_message("refreshSession: Started")
+
+        response = await session_functions.refresh_session(
+            self,
+            refresh_token,
+            anti_csrf_token,
+            disable_anti_csrf,
+            self.config.use_dynamic_access_token_signing_key,
+            user_context=user_context,
+        )
+
+        log_debug_message("refreshSession: Success!")
+
+        payload = parse_jwt_without_signature_verification(
+            response.accessToken.token,
+        ).payload
+
+        session = Session(
+            self,
+            self.config,
+            response.accessToken.token,
+            build_front_token(
+                response.session.userId,
+                response.accessToken.expiry,
+                payload,
+            ),
+            response.refreshToken,
+            response.antiCsrfToken,
+            response.session.handle,
+            response.session.userId,
+            user_data_in_access_token=payload,
+            req_res_info=None,
+            access_token_updated=True,
+            tenant_id=payload["tId"],
+        )
+
+        return session
+
+    async def revoke_session(
+        self, session_handle: str, user_context: Dict[str, Any]
+    ) -> bool:
+        return await session_functions.revoke_session(
+            self, session_handle, user_context
+        )
+
+    async def revoke_all_sessions_for_user(
+        self,
+        user_id: str,
+        tenant_id: Optional[str],
+        revoke_across_all_tenants: bool,
+        user_context: Dict[str, Any],
+    ) -> List[str]:
+        return await session_functions.revoke_all_sessions_for_user(
+            self, user_id, tenant_id, revoke_across_all_tenants, user_context
+        )
+
+    async def get_all_session_handles_for_user(
+        self,
+        user_id: str,
+        tenant_id: Optional[str],
+        fetch_across_all_tenants: bool,
+        user_context: Dict[str, Any],
+    ) -> List[str]:
+        return await session_functions.get_all_session_handles_for_user(
+            self, user_id, tenant_id, fetch_across_all_tenants, user_context
+        )
+
+    async def revoke_multiple_sessions(
+        self, session_handles: List[str], user_context: Dict[str, Any]
+    ) -> List[str]:
+        return await session_functions.revoke_multiple_sessions(
+            self, session_handles, user_context
+        )
+
+    async def get_session_information(
+        self, session_handle: str, user_context: Dict[str, Any]
+    ) -> Union[SessionInformationResult, None]:
+        return await session_functions.get_session_information(
+            self, session_handle, user_context
+        )
+
+    async def update_session_data_in_database(
+        self,
+        session_handle: str,
+        new_session_data: Dict[str, Any],
+        user_context: Dict[str, Any],
+    ) -> bool:
+        return await session_functions.update_session_data_in_database(
+            self, session_handle, new_session_data, user_context
+        )
+
+    async def merge_into_access_token_payload(
+        self,
+        session_handle: str,
+        access_token_payload_update: Dict[str, Any],
+        user_context: Dict[str, Any],
+    ) -> bool:
+        session_info = await self.get_session_information(session_handle, user_context)
+        if session_info is None:
+            return False
+
+        new_access_token_payload = session_info.custom_claims_in_access_token_payload
+        for k in protected_props:
+            if k in new_access_token_payload:
+                del new_access_token_payload[k]
+
+        new_access_token_payload = {
+            **new_access_token_payload,
+            **access_token_payload_update,
+        }
+        for k in access_token_payload_update.keys():
+            if new_access_token_payload[k] is None:
+                del new_access_token_payload[k]
+
+        return await session_functions.update_access_token_payload(
+            self, session_handle, new_access_token_payload, user_context
+        )
+
+    async def fetch_and_set_claim(
+        self,
+        session_handle: str,
+        claim: SessionClaim[Any],
+        user_context: Dict[str, Any],
+    ) -> bool:
+        session_info = await self.get_session_information(session_handle, user_context)
+        if session_info is None:
+            return False
+
+        access_token_payload_update = await claim.build(
+            session_info.user_id, session_info.tenant_id, user_context
+        )
+        return await self.merge_into_access_token_payload(
+            session_handle, access_token_payload_update, user_context
+        )
+
+    async def set_claim_value(
+        self,
+        session_handle: str,
+        claim: SessionClaim[Any],
+        value: Any,
+        user_context: Dict[str, Any],
+    ):
+        access_token_payload_update = claim.add_to_payload_({}, value, user_context)
+        return await self.merge_into_access_token_payload(
+            session_handle, access_token_payload_update, user_context
+        )
+
+    async def get_claim_value(
+        self,
+        session_handle: str,
+        claim: SessionClaim[Any],
+        user_context: Dict[str, Any],
+    ) -> Union[SessionDoesNotExistError, GetClaimValueOkResult[Any]]:
+        session_info = await self.get_session_information(session_handle, user_context)
+        if session_info is None:
+            return SessionDoesNotExistError()
+
+        return GetClaimValueOkResult(
+            value=claim.get_value_from_payload(
+                session_info.custom_claims_in_access_token_payload, user_context
+            )
+        )
+
+    def get_global_claim_validators(
+        self,
+        tenant_id: str,
+        user_id: str,
+        claim_validators_added_by_other_recipes: List[SessionClaimValidator],
+        user_context: Dict[str, Any],
+    ) -> MaybeAwaitable[List[SessionClaimValidator]]:
+        return claim_validators_added_by_other_recipes
+
+    async def remove_claim(
+        self,
+        session_handle: str,
+        claim: SessionClaim[Any],
+        user_context: Dict[str, Any],
+    ) -> bool:
+        access_token_payload = claim.remove_from_payload_by_merge_({}, user_context)
+        return await self.merge_into_access_token_payload(
+            session_handle, access_token_payload, user_context
+        )
+
+    async def regenerate_access_token(
+        self,
+        access_token: str,
+        new_access_token_payload: Union[Dict[str, Any], None],
+        user_context: Dict[str, Any],
+    ) -> Union[RegenerateAccessTokenOkResult, None]:
+        if new_access_token_payload is None:
+            new_access_token_payload = {}
+        response = await self.querier.send_post_request(
+            NormalisedURLPath("/recipe/session/regenerate"),
+            {"accessToken": access_token, "userDataInJWT": new_access_token_payload},
+            user_context=user_context,
+        )
+        if response["status"] == "UNAUTHORISED":
+            return None
+        access_token_obj: Union[None, AccessTokenObj] = None
+        if "accessToken" in response:
+            access_token_obj = AccessTokenObj(
+                response["accessToken"]["token"],
+                response["accessToken"]["expiry"],
+                response["accessToken"]["createdTime"],
+            )
+        session = SessionObj(
+            response["session"]["handle"],
+            response["session"]["userId"],
+            response["session"]["userDataInJWT"],
+            response["session"]["tenantId"],
+        )
+        return RegenerateAccessTokenOkResult(session, access_token_obj)
+
+

Ancestors

+ +

Methods

+
+
+async def create_new_session(self, user_id: str, access_token_payload: Optional[Dict[str, Any]], session_data_in_database: Optional[Dict[str, Any]], disable_anti_csrf: Optional[bool], tenant_id: str, user_context: Dict[str, Any]) ‑> SessionContainer +
+
+
+
+ +Expand source code + +
async def create_new_session(
+    self,
+    user_id: str,
+    access_token_payload: Optional[Dict[str, Any]],
+    session_data_in_database: Optional[Dict[str, Any]],
+    disable_anti_csrf: Optional[bool],
+    tenant_id: str,
+    user_context: Dict[str, Any],
+) -> SessionContainer:
+    log_debug_message("createNewSession: Started")
+
+    result = await session_functions.create_new_session(
+        self,
+        tenant_id,
+        user_id,
+        disable_anti_csrf is True,
+        access_token_payload,
+        session_data_in_database,
+        user_context=user_context,
+    )
+    log_debug_message("createNewSession: Finished")
+
+    payload = parse_jwt_without_signature_verification(
+        result.accessToken.token
+    ).payload
+
+    new_session = Session(
+        self,
+        self.config,
+        result.accessToken.token,
+        build_front_token(
+            result.session.userId, result.accessToken.expiry, payload
+        ),
+        result.refreshToken,
+        result.antiCsrfToken,
+        result.session.handle,
+        result.session.userId,
+        payload,
+        None,
+        True,
+        tenant_id,
+    )
+
+    return new_session
+
+
+
+async def fetch_and_set_claim(self, session_handle: str, claim: SessionClaim[Any], user_context: Dict[str, Any]) ‑> bool +
+
+
+
+ +Expand source code + +
async def fetch_and_set_claim(
+    self,
+    session_handle: str,
+    claim: SessionClaim[Any],
+    user_context: Dict[str, Any],
+) -> bool:
+    session_info = await self.get_session_information(session_handle, user_context)
+    if session_info is None:
+        return False
+
+    access_token_payload_update = await claim.build(
+        session_info.user_id, session_info.tenant_id, user_context
+    )
+    return await self.merge_into_access_token_payload(
+        session_handle, access_token_payload_update, user_context
+    )
+
+
+
+async def get_all_session_handles_for_user(self, user_id: str, tenant_id: Optional[str], fetch_across_all_tenants: bool, user_context: Dict[str, Any]) ‑> List[str] +
+
+
+
+ +Expand source code + +
async def get_all_session_handles_for_user(
+    self,
+    user_id: str,
+    tenant_id: Optional[str],
+    fetch_across_all_tenants: bool,
+    user_context: Dict[str, Any],
+) -> List[str]:
+    return await session_functions.get_all_session_handles_for_user(
+        self, user_id, tenant_id, fetch_across_all_tenants, user_context
+    )
+
+
+
+async def get_claim_value(self, session_handle: str, claim: SessionClaim[Any], user_context: Dict[str, Any]) ‑> Union[SessionDoesNotExistError, GetClaimValueOkResult[Any]] +
+
+
+
+ +Expand source code + +
async def get_claim_value(
+    self,
+    session_handle: str,
+    claim: SessionClaim[Any],
+    user_context: Dict[str, Any],
+) -> Union[SessionDoesNotExistError, GetClaimValueOkResult[Any]]:
+    session_info = await self.get_session_information(session_handle, user_context)
+    if session_info is None:
+        return SessionDoesNotExistError()
+
+    return GetClaimValueOkResult(
+        value=claim.get_value_from_payload(
+            session_info.custom_claims_in_access_token_payload, user_context
+        )
+    )
+
+
+
+def get_global_claim_validators(self, tenant_id: str, user_id: str, claim_validators_added_by_other_recipes: List[SessionClaimValidator], user_context: Dict[str, Any]) ‑> MaybeAwaitable[List[SessionClaimValidator]] +
+
+
+
+ +Expand source code + +
def get_global_claim_validators(
+    self,
+    tenant_id: str,
+    user_id: str,
+    claim_validators_added_by_other_recipes: List[SessionClaimValidator],
+    user_context: Dict[str, Any],
+) -> MaybeAwaitable[List[SessionClaimValidator]]:
+    return claim_validators_added_by_other_recipes
+
+
+
+async def get_session(self, access_token: Optional[str], anti_csrf_token: Optional[str] = None, anti_csrf_check: Optional[bool] = None, session_required: Optional[bool] = None, check_database: Optional[bool] = None, override_global_claim_validators: Optional[Callable[[List[SessionClaimValidator], SessionContainer, Dict[str, Any]], MaybeAwaitable[List[SessionClaimValidator]]]] = None, user_context: Optional[Dict[str, Any]] = None) ‑> Optional[SessionContainer] +
+
+
+
+ +Expand source code + +
async def get_session(
+    self,
+    access_token: Optional[str],
+    anti_csrf_token: Optional[str] = None,
+    anti_csrf_check: Optional[bool] = None,
+    session_required: Optional[bool] = None,
+    check_database: Optional[bool] = None,
+    override_global_claim_validators: Optional[
+        Callable[
+            [List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
+            MaybeAwaitable[List[SessionClaimValidator]],
+        ]
+    ] = None,
+    user_context: Optional[Dict[str, Any]] = None,
+) -> Optional[SessionContainer]:
+    if (
+        anti_csrf_check is not False
+        and isinstance(self.config.anti_csrf_function_or_string, str)
+        and self.config.anti_csrf_function_or_string == "VIA_CUSTOM_HEADER"
+    ):
+        raise Exception(
+            "Since the anti-csrf mode is VIA_CUSTOM_HEADER getSession can't check the CSRF token. Please either use VIA_TOKEN or set anti_csrf_check to false"
+        )
+
+    log_debug_message("getSession: Started")
+
+    if access_token is None:
+        if session_required is False:
+            log_debug_message(
+                "getSession: returning None because access_token is undefined and session_required is False"
+            )
+            # there is no session that exists here, and the user wants session verification to be optional. So we return None
+            return None
+
+        log_debug_message(
+            "getSession: UNAUTHORISED because accessToken in request is undefined"
+        )
+        # we do not clear the session here because of a race condition mentioned in https://github.com/supertokens/supertokens-node/issues/17
+        raise UnauthorisedError(
+            "Session does not exist. Are you sending the session tokens in the request with the appropriate token transfer method?",
+            clear_tokens=False,
+        )
+
+    access_token_obj: Optional[ParsedJWTInfo] = None
+    try:
+        access_token_obj = parse_jwt_without_signature_verification(access_token)
+        validate_access_token_structure(
+            access_token_obj.payload, access_token_obj.version
+        )
+    except Exception as _:
+        if session_required is False:
+            log_debug_message(
+                "getSession: Returning undefined because parsing failed and session_required is False"
+            )
+            return None
+
+        log_debug_message(
+            "getSession: UNAUTHORISED because the accessToken couldn't be parsed or had an invalid structure"
+        )
+        raise UnauthorisedError("Token parsing failed", clear_tokens=False)
+
+    response = await session_functions.get_session(
+        self,
+        access_token_obj,
+        anti_csrf_token,
+        (anti_csrf_check is not False),
+        (check_database is True),
+        user_context,
+    )
+
+    log_debug_message("getSession: Success!")
+
+    if access_token_obj.version >= 3:
+        if response.accessToken is not None:
+            payload = parse_jwt_without_signature_verification(
+                response.accessToken.token
+            ).payload
+        else:
+            payload = access_token_obj.payload
+    else:
+        payload = response.session.userDataInJWT
+
+    if response.accessToken is not None:
+        access_token_str = response.accessToken.token
+        expiry_time = response.accessToken.expiry
+        access_token_updated = True
+    else:
+        access_token_str = access_token
+        expiry_time = response.session.expiryTime
+        access_token_updated = False
+
+    session = Session(
+        self,
+        self.config,
+        access_token_str,
+        build_front_token(response.session.userId, expiry_time, payload),
+        None,  # refresh_token
+        anti_csrf_token,
+        response.session.handle,
+        response.session.userId,
+        payload,
+        None,
+        access_token_updated,
+        response.session.tenant_id,
+    )
+
+    return session
+
+
+
+async def get_session_information(self, session_handle: str, user_context: Dict[str, Any]) ‑> Union[SessionInformationResult, None] +
+
+
+
+ +Expand source code + +
async def get_session_information(
+    self, session_handle: str, user_context: Dict[str, Any]
+) -> Union[SessionInformationResult, None]:
+    return await session_functions.get_session_information(
+        self, session_handle, user_context
+    )
+
+
+
+async def merge_into_access_token_payload(self, session_handle: str, access_token_payload_update: Dict[str, Any], user_context: Dict[str, Any]) ‑> bool +
+
+
+
+ +Expand source code + +
async def merge_into_access_token_payload(
+    self,
+    session_handle: str,
+    access_token_payload_update: Dict[str, Any],
+    user_context: Dict[str, Any],
+) -> bool:
+    session_info = await self.get_session_information(session_handle, user_context)
+    if session_info is None:
+        return False
+
+    new_access_token_payload = session_info.custom_claims_in_access_token_payload
+    for k in protected_props:
+        if k in new_access_token_payload:
+            del new_access_token_payload[k]
+
+    new_access_token_payload = {
+        **new_access_token_payload,
+        **access_token_payload_update,
+    }
+    for k in access_token_payload_update.keys():
+        if new_access_token_payload[k] is None:
+            del new_access_token_payload[k]
+
+    return await session_functions.update_access_token_payload(
+        self, session_handle, new_access_token_payload, user_context
+    )
+
+
+
+async def refresh_session(self, refresh_token: str, anti_csrf_token: Optional[str], disable_anti_csrf: bool, user_context: Dict[str, Any]) ‑> SessionContainer +
+
+
+
+ +Expand source code + +
async def refresh_session(
+    self,
+    refresh_token: str,
+    anti_csrf_token: Optional[str],
+    disable_anti_csrf: bool,
+    user_context: Dict[str, Any],
+) -> SessionContainer:
+    if (
+        disable_anti_csrf is not True
+        and isinstance(self.config.anti_csrf_function_or_string, str)
+        and self.config.anti_csrf_function_or_string == "VIA_CUSTOM_HEADER"
+    ):
+        raise Exception(
+            "Since the anti-csrf mode is VIA_CUSTOM_HEADER getSession can't check the CSRF token. Please either use VIA_TOKEN or set antiCsrfCheck to false"
+        )
+
+    log_debug_message("refreshSession: Started")
+
+    response = await session_functions.refresh_session(
+        self,
+        refresh_token,
+        anti_csrf_token,
+        disable_anti_csrf,
+        self.config.use_dynamic_access_token_signing_key,
+        user_context=user_context,
+    )
+
+    log_debug_message("refreshSession: Success!")
+
+    payload = parse_jwt_without_signature_verification(
+        response.accessToken.token,
+    ).payload
+
+    session = Session(
+        self,
+        self.config,
+        response.accessToken.token,
+        build_front_token(
+            response.session.userId,
+            response.accessToken.expiry,
+            payload,
+        ),
+        response.refreshToken,
+        response.antiCsrfToken,
+        response.session.handle,
+        response.session.userId,
+        user_data_in_access_token=payload,
+        req_res_info=None,
+        access_token_updated=True,
+        tenant_id=payload["tId"],
+    )
+
+    return session
+
+
+
+async def regenerate_access_token(self, access_token: str, new_access_token_payload: Union[Dict[str, Any], None], user_context: Dict[str, Any]) ‑> Union[RegenerateAccessTokenOkResult, None] +
+
+
+
+ +Expand source code + +
async def regenerate_access_token(
+    self,
+    access_token: str,
+    new_access_token_payload: Union[Dict[str, Any], None],
+    user_context: Dict[str, Any],
+) -> Union[RegenerateAccessTokenOkResult, None]:
+    if new_access_token_payload is None:
+        new_access_token_payload = {}
+    response = await self.querier.send_post_request(
+        NormalisedURLPath("/recipe/session/regenerate"),
+        {"accessToken": access_token, "userDataInJWT": new_access_token_payload},
+        user_context=user_context,
+    )
+    if response["status"] == "UNAUTHORISED":
+        return None
+    access_token_obj: Union[None, AccessTokenObj] = None
+    if "accessToken" in response:
+        access_token_obj = AccessTokenObj(
+            response["accessToken"]["token"],
+            response["accessToken"]["expiry"],
+            response["accessToken"]["createdTime"],
+        )
+    session = SessionObj(
+        response["session"]["handle"],
+        response["session"]["userId"],
+        response["session"]["userDataInJWT"],
+        response["session"]["tenantId"],
+    )
+    return RegenerateAccessTokenOkResult(session, access_token_obj)
+
+
+
+async def remove_claim(self, session_handle: str, claim: SessionClaim[Any], user_context: Dict[str, Any]) ‑> bool +
+
+
+
+ +Expand source code + +
async def remove_claim(
+    self,
+    session_handle: str,
+    claim: SessionClaim[Any],
+    user_context: Dict[str, Any],
+) -> bool:
+    access_token_payload = claim.remove_from_payload_by_merge_({}, user_context)
+    return await self.merge_into_access_token_payload(
+        session_handle, access_token_payload, user_context
+    )
+
+
+
+async def revoke_all_sessions_for_user(self, user_id: str, tenant_id: Optional[str], revoke_across_all_tenants: bool, user_context: Dict[str, Any]) ‑> List[str] +
+
+
+
+ +Expand source code + +
async def revoke_all_sessions_for_user(
+    self,
+    user_id: str,
+    tenant_id: Optional[str],
+    revoke_across_all_tenants: bool,
+    user_context: Dict[str, Any],
+) -> List[str]:
+    return await session_functions.revoke_all_sessions_for_user(
+        self, user_id, tenant_id, revoke_across_all_tenants, user_context
+    )
+
+
+
+async def revoke_multiple_sessions(self, session_handles: List[str], user_context: Dict[str, Any]) ‑> List[str] +
+
+
+
+ +Expand source code + +
async def revoke_multiple_sessions(
+    self, session_handles: List[str], user_context: Dict[str, Any]
+) -> List[str]:
+    return await session_functions.revoke_multiple_sessions(
+        self, session_handles, user_context
+    )
+
+
+
+async def revoke_session(self, session_handle: str, user_context: Dict[str, Any]) ‑> bool +
+
+
+
+ +Expand source code + +
async def revoke_session(
+    self, session_handle: str, user_context: Dict[str, Any]
+) -> bool:
+    return await session_functions.revoke_session(
+        self, session_handle, user_context
+    )
+
+
+
+async def set_claim_value(self, session_handle: str, claim: SessionClaim[Any], value: Any, user_context: Dict[str, Any]) +
+
+
+
+ +Expand source code + +
async def set_claim_value(
+    self,
+    session_handle: str,
+    claim: SessionClaim[Any],
+    value: Any,
+    user_context: Dict[str, Any],
+):
+    access_token_payload_update = claim.add_to_payload_({}, value, user_context)
+    return await self.merge_into_access_token_payload(
+        session_handle, access_token_payload_update, user_context
+    )
+
+
+
+async def update_session_data_in_database(self, session_handle: str, new_session_data: Dict[str, Any], user_context: Dict[str, Any]) ‑> bool +
+
+
+
+ +Expand source code + +
async def update_session_data_in_database(
+    self,
+    session_handle: str,
+    new_session_data: Dict[str, Any],
+    user_context: Dict[str, Any],
+) -> bool:
+    return await session_functions.update_session_data_in_database(
+        self, session_handle, new_session_data, user_context
+    )
+
+
+
+async def validate_claims(self, user_id: str, access_token_payload: Dict[str, Any], claim_validators: List[SessionClaimValidator], user_context: Dict[str, Any]) ‑> ClaimsValidationResult +
+
+
+
+ +Expand source code + +
async def validate_claims(
+    self,
+    user_id: str,
+    access_token_payload: Dict[str, Any],
+    claim_validators: List[SessionClaimValidator],
+    user_context: Dict[str, Any],
+) -> ClaimsValidationResult:
+    access_token_payload_update = None
+    original_access_token_payload = json.dumps(access_token_payload)
+
+    for validator in claim_validators:
+        log_debug_message(
+            "update_claims_in_payload_if_needed checking should_refetch for %s",
+            validator.id,
+        )
+        if validator.claim is not None and validator.should_refetch(
+            access_token_payload, user_context
+        ):
+            log_debug_message(
+                "update_claims_in_payload_if_needed refetching for %s", validator.id
+            )
+            value = await resolve(
+                validator.claim.fetch_value(
+                    user_id,
+                    access_token_payload.get("tId", DEFAULT_TENANT_ID),
+                    user_context,
+                )
+            )
+            log_debug_message(
+                "update_claims_in_payload_if_needed %s refetch result %s",
+                validator.id,
+                json.dumps(value),
+            )
+            if value is not None:
+                access_token_payload = validator.claim.add_to_payload_(
+                    access_token_payload, value, user_context
+                )
+
+    if json.dumps(access_token_payload) != original_access_token_payload:
+        access_token_payload_update = access_token_payload
+
+    invalid_claims = await validate_claims_in_payload(
+        claim_validators, access_token_payload, user_context
+    )
+
+    return ClaimsValidationResult(invalid_claims, access_token_payload_update)
+
+
+
+async def validate_claims_in_jwt_payload(self, user_id: str, jwt_payload: JSONObject, claim_validators: List[SessionClaimValidator], user_context: Dict[str, Any]) ‑> ClaimsValidationResult +
+
+
+
+ +Expand source code + +
async def validate_claims_in_jwt_payload(
+    self,
+    user_id: str,
+    jwt_payload: JSONObject,
+    claim_validators: List[SessionClaimValidator],
+    user_context: Dict[str, Any],
+) -> ClaimsValidationResult:
+    invalid_claims = await validate_claims_in_payload(
+        claim_validators,
+        jwt_payload,
+        user_context,
+    )
+
+    return ClaimsValidationResult(invalid_claims)
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/session/session_class.html b/html/supertokens_python/recipe/session/session_class.html new file mode 100644 index 000000000..5f900c5c0 --- /dev/null +++ b/html/supertokens_python/recipe/session/session_class.html @@ -0,0 +1,1208 @@ + + + + + + +supertokens_python.recipe.session.session_class API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.session.session_class

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from typing import Any, Dict, List, Optional, TypeVar, Union
+
+from supertokens_python.recipe.session.exceptions import (
+    raise_invalid_claims_exception,
+    raise_unauthorised_exception,
+)
+from .jwt import parse_jwt_without_signature_verification
+from .utils import TokenTransferMethod
+
+from .cookie_and_header import (
+    clear_session_response_mutator,
+    token_response_mutator,
+    build_front_token,
+    anti_csrf_response_mutator,
+    access_token_mutator,
+)
+from .interfaces import (
+    ReqResInfo,
+    SessionClaim,
+    SessionClaimValidator,
+    SessionContainer,
+    GetSessionTokensDangerouslyDict,
+)
+from .constants import protected_props
+from ...framework import BaseRequest
+from supertokens_python.utils import log_debug_message
+
+_T = TypeVar("_T")
+
+
+class Session(SessionContainer):
+    async def attach_to_request_response(
+        self,
+        request: BaseRequest,
+        transfer_method: TokenTransferMethod,
+        user_context: Optional[Dict[str, Any]],
+    ) -> None:
+        self.req_res_info = ReqResInfo(request, transfer_method)
+
+        if self.access_token_updated:
+            self.response_mutators.append(
+                access_token_mutator(
+                    self.access_token,
+                    self.front_token,
+                    self.config,
+                    transfer_method,
+                    request,
+                )
+            )
+            if self.refresh_token is not None:
+                self.response_mutators.append(
+                    token_response_mutator(
+                        self.config,
+                        "refresh",
+                        self.refresh_token.token,
+                        self.refresh_token.expiry,
+                        transfer_method,
+                        request,
+                    )
+                )
+            if self.anti_csrf_token is not None:
+                self.response_mutators.append(
+                    anti_csrf_response_mutator(self.anti_csrf_token)
+                )
+
+        request.set_session(
+            self
+        )  # Although this is called in recipe/session/framework/**/__init__.py. It's required in case of python because functions like create_new_session(req, "user-id") can be called in the framework view handler as well
+
+    async def revoke_session(self, user_context: Union[Any, None] = None) -> None:
+        if user_context is None:
+            user_context = {}
+
+        await self.recipe_implementation.revoke_session(
+            self.session_handle, user_context
+        )
+
+        if self.req_res_info is not None:
+            # we do not check the output of calling revokeSession
+            # before clearing the cookies because we are revoking the
+            # current API request's session.
+            # If we instead clear the cookies only when revokeSession
+            # returns true, it can cause this kind of bug:
+            # https://github.com/supertokens/supertokens-node/issues/343
+            transfer_method: TokenTransferMethod = self.req_res_info.transfer_method  # type: ignore
+            self.response_mutators.append(
+                clear_session_response_mutator(
+                    self.config,
+                    transfer_method,
+                    self.req_res_info.request,
+                )
+            )
+
+    async def get_session_data_from_database(
+        self, user_context: Union[Dict[str, Any], None] = None
+    ) -> Dict[str, Any]:
+        if user_context is None:
+            user_context = {}
+        session_info = await self.recipe_implementation.get_session_information(
+            self.session_handle, user_context
+        )
+        if session_info is None:
+            log_debug_message(
+                "getSessionDataFromDatabase: Throwing UNAUTHORISED because session does not exist anymore"
+            )
+            raise_unauthorised_exception("Session does not exist anymore.")
+
+        return session_info.session_data_in_database
+
+    async def update_session_data_in_database(
+        self,
+        new_session_data: Dict[str, Any],
+        user_context: Union[Dict[str, Any], None] = None,
+    ) -> None:
+        if user_context is None:
+            user_context = {}
+        updated = await self.recipe_implementation.update_session_data_in_database(
+            self.session_handle, new_session_data, user_context
+        )
+        if not updated:
+            log_debug_message(
+                "updateSessionDataInDatabase: Throwing UNAUTHORISED because session does not exist anymore"
+            )
+            raise_unauthorised_exception("Session does not exist anymore.")
+
+    def get_user_id(self, user_context: Union[Dict[str, Any], None] = None) -> str:
+        return self.user_id
+
+    def get_tenant_id(self, user_context: Union[Dict[str, Any], None] = None) -> str:
+        return self.tenant_id
+
+    def get_access_token_payload(
+        self, user_context: Union[Dict[str, Any], None] = None
+    ) -> Dict[str, Any]:
+        return self.user_data_in_access_token
+
+    def get_handle(self, user_context: Union[Dict[str, Any], None] = None) -> str:
+        return self.session_handle
+
+    def get_access_token(self, user_context: Union[Dict[str, Any], None] = None) -> str:
+        return self.access_token
+
+    def get_all_session_tokens_dangerously(self) -> GetSessionTokensDangerouslyDict:
+        return {
+            "accessToken": self.access_token,
+            "accessAndFrontTokenUpdated": self.access_token_updated,
+            "refreshToken": None
+            if self.refresh_token is None
+            else self.refresh_token.token,
+            "frontToken": self.front_token,
+            "antiCsrfToken": self.anti_csrf_token,
+        }
+
+    async def get_time_created(
+        self, user_context: Union[Dict[str, Any], None] = None
+    ) -> int:
+        if user_context is None:
+            user_context = {}
+        session_info = await self.recipe_implementation.get_session_information(
+            self.session_handle, user_context
+        )
+        if session_info is None:
+            log_debug_message(
+                "getTimeCreated: Throwing UNAUTHORISED because session does not exist anymore"
+            )
+            raise_unauthorised_exception("Session does not exist anymore.")
+
+        return session_info.time_created
+
+    async def get_expiry(self, user_context: Union[Dict[str, Any], None] = None) -> int:
+        if user_context is None:
+            user_context = {}
+        session_info = await self.recipe_implementation.get_session_information(
+            self.session_handle, user_context
+        )
+        if session_info is None:
+            log_debug_message(
+                "getExpiry: Throwing UNAUTHORISED because session does not exist anymore"
+            )
+            raise_unauthorised_exception("Session does not exist anymore.")
+
+        return session_info.expiry
+
+    async def assert_claims(
+        self,
+        claim_validators: List[SessionClaimValidator],
+        user_context: Union[Dict[str, Any], None] = None,
+    ) -> None:
+        if user_context is None:
+            user_context = {}
+
+        validate_claim_res = await self.recipe_implementation.validate_claims(
+            self.get_user_id(user_context),
+            self.get_access_token_payload(user_context),
+            claim_validators,
+            user_context,
+        )
+
+        if validate_claim_res.access_token_payload_update is not None:
+            for k in protected_props:
+                try:
+                    del validate_claim_res.access_token_payload_update[k]
+                except KeyError:
+                    pass
+            await self.merge_into_access_token_payload(
+                validate_claim_res.access_token_payload_update, user_context
+            )
+
+        validation_errors = validate_claim_res.invalid_claims
+        if len(validation_errors) > 0:
+            raise_invalid_claims_exception("INVALID_CLAIMS", validation_errors)
+
+    async def fetch_and_set_claim(
+        self, claim: SessionClaim[Any], user_context: Union[Dict[str, Any], None] = None
+    ) -> None:
+        if user_context is None:
+            user_context = {}
+
+        update = await claim.build(
+            self.get_user_id(), self.get_tenant_id(), user_context
+        )
+        return await self.merge_into_access_token_payload(update, user_context)
+
+    async def set_claim_value(
+        self,
+        claim: SessionClaim[_T],
+        value: _T,
+        user_context: Union[Dict[str, Any], None] = None,
+    ) -> None:
+        if user_context is None:
+            user_context = {}
+
+        update = claim.add_to_payload_({}, value, user_context)
+        return await self.merge_into_access_token_payload(update, user_context)
+
+    async def get_claim_value(
+        self, claim: SessionClaim[_T], user_context: Union[Dict[str, Any], None] = None
+    ) -> Union[_T, None]:
+        if user_context is None:
+            user_context = {}
+
+        return claim.get_value_from_payload(
+            self.get_access_token_payload(user_context), user_context
+        )
+
+    async def remove_claim(
+        self, claim: SessionClaim[Any], user_context: Union[Dict[str, Any], None] = None
+    ) -> None:
+        if user_context is None:
+            user_context = {}
+
+        update = claim.remove_from_payload_by_merge_({}, user_context)
+        return await self.merge_into_access_token_payload(update, user_context)
+
+    async def merge_into_access_token_payload(
+        self,
+        access_token_payload_update: Dict[str, Any],
+        user_context: Union[Dict[str, Any], None] = None,
+    ) -> None:
+        if user_context is None:
+            user_context = {}
+
+        new_access_token_payload = {**self.get_access_token_payload(user_context)}
+        for k in protected_props:
+            try:
+                del new_access_token_payload[k]
+            except KeyError:
+                pass
+
+        new_access_token_payload = {
+            **new_access_token_payload,
+            **access_token_payload_update,
+        }
+
+        for k in access_token_payload_update.keys():
+            if access_token_payload_update[k] is None:
+                del new_access_token_payload[k]
+
+        response = await self.recipe_implementation.regenerate_access_token(
+            self.get_access_token(), new_access_token_payload, user_context
+        )
+
+        if response is None:
+            log_debug_message(
+                "mergeIntoAccessTokenPayload: Throwing UNAUTHORISED because session does not exist anymore"
+            )
+            raise_unauthorised_exception("Session does not exist anymore.")
+
+        if response.access_token is not None:
+            resp_token = parse_jwt_without_signature_verification(
+                response.access_token.token
+            )
+            payload = (
+                resp_token.payload
+                if resp_token.version >= 3
+                else response.session.user_data_in_jwt
+            )
+            self.user_data_in_access_token = payload
+            self.access_token = response.access_token.token
+            self.front_token = build_front_token(
+                self.get_user_id(), response.access_token.expiry, payload
+            )
+            self.access_token_updated = True
+            if self.req_res_info is not None:
+                transfer_method: TokenTransferMethod = self.req_res_info.transfer_method  # type: ignore
+                self.response_mutators.append(
+                    access_token_mutator(
+                        self.access_token,
+                        self.front_token,
+                        self.config,
+                        transfer_method,
+                        self.req_res_info.request,
+                    )
+                )
+        else:
+            # This case means that the access token has expired between the validation and this update
+            # We can't update the access token on the FE, as it will need to call refresh anyway but we handle this as a successful update during this request
+            # the changes will be reflected on the FE after refresh is called
+            self.user_data_in_access_token = {
+                **self.get_access_token_payload(),
+                **response.session.user_data_in_jwt,
+            }
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class Session +(recipe_implementation: RecipeInterface, config: SessionConfig, access_token: str, front_token: str, refresh_token: Optional[TokenInfo], anti_csrf_token: Optional[str], session_handle: str, user_id: str, user_data_in_access_token: Optional[Dict[str, Any]], req_res_info: Optional[ReqResInfo], access_token_updated: bool, tenant_id: str) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class Session(SessionContainer):
+    async def attach_to_request_response(
+        self,
+        request: BaseRequest,
+        transfer_method: TokenTransferMethod,
+        user_context: Optional[Dict[str, Any]],
+    ) -> None:
+        self.req_res_info = ReqResInfo(request, transfer_method)
+
+        if self.access_token_updated:
+            self.response_mutators.append(
+                access_token_mutator(
+                    self.access_token,
+                    self.front_token,
+                    self.config,
+                    transfer_method,
+                    request,
+                )
+            )
+            if self.refresh_token is not None:
+                self.response_mutators.append(
+                    token_response_mutator(
+                        self.config,
+                        "refresh",
+                        self.refresh_token.token,
+                        self.refresh_token.expiry,
+                        transfer_method,
+                        request,
+                    )
+                )
+            if self.anti_csrf_token is not None:
+                self.response_mutators.append(
+                    anti_csrf_response_mutator(self.anti_csrf_token)
+                )
+
+        request.set_session(
+            self
+        )  # Although this is called in recipe/session/framework/**/__init__.py. It's required in case of python because functions like create_new_session(req, "user-id") can be called in the framework view handler as well
+
+    async def revoke_session(self, user_context: Union[Any, None] = None) -> None:
+        if user_context is None:
+            user_context = {}
+
+        await self.recipe_implementation.revoke_session(
+            self.session_handle, user_context
+        )
+
+        if self.req_res_info is not None:
+            # we do not check the output of calling revokeSession
+            # before clearing the cookies because we are revoking the
+            # current API request's session.
+            # If we instead clear the cookies only when revokeSession
+            # returns true, it can cause this kind of bug:
+            # https://github.com/supertokens/supertokens-node/issues/343
+            transfer_method: TokenTransferMethod = self.req_res_info.transfer_method  # type: ignore
+            self.response_mutators.append(
+                clear_session_response_mutator(
+                    self.config,
+                    transfer_method,
+                    self.req_res_info.request,
+                )
+            )
+
+    async def get_session_data_from_database(
+        self, user_context: Union[Dict[str, Any], None] = None
+    ) -> Dict[str, Any]:
+        if user_context is None:
+            user_context = {}
+        session_info = await self.recipe_implementation.get_session_information(
+            self.session_handle, user_context
+        )
+        if session_info is None:
+            log_debug_message(
+                "getSessionDataFromDatabase: Throwing UNAUTHORISED because session does not exist anymore"
+            )
+            raise_unauthorised_exception("Session does not exist anymore.")
+
+        return session_info.session_data_in_database
+
+    async def update_session_data_in_database(
+        self,
+        new_session_data: Dict[str, Any],
+        user_context: Union[Dict[str, Any], None] = None,
+    ) -> None:
+        if user_context is None:
+            user_context = {}
+        updated = await self.recipe_implementation.update_session_data_in_database(
+            self.session_handle, new_session_data, user_context
+        )
+        if not updated:
+            log_debug_message(
+                "updateSessionDataInDatabase: Throwing UNAUTHORISED because session does not exist anymore"
+            )
+            raise_unauthorised_exception("Session does not exist anymore.")
+
+    def get_user_id(self, user_context: Union[Dict[str, Any], None] = None) -> str:
+        return self.user_id
+
+    def get_tenant_id(self, user_context: Union[Dict[str, Any], None] = None) -> str:
+        return self.tenant_id
+
+    def get_access_token_payload(
+        self, user_context: Union[Dict[str, Any], None] = None
+    ) -> Dict[str, Any]:
+        return self.user_data_in_access_token
+
+    def get_handle(self, user_context: Union[Dict[str, Any], None] = None) -> str:
+        return self.session_handle
+
+    def get_access_token(self, user_context: Union[Dict[str, Any], None] = None) -> str:
+        return self.access_token
+
+    def get_all_session_tokens_dangerously(self) -> GetSessionTokensDangerouslyDict:
+        return {
+            "accessToken": self.access_token,
+            "accessAndFrontTokenUpdated": self.access_token_updated,
+            "refreshToken": None
+            if self.refresh_token is None
+            else self.refresh_token.token,
+            "frontToken": self.front_token,
+            "antiCsrfToken": self.anti_csrf_token,
+        }
+
+    async def get_time_created(
+        self, user_context: Union[Dict[str, Any], None] = None
+    ) -> int:
+        if user_context is None:
+            user_context = {}
+        session_info = await self.recipe_implementation.get_session_information(
+            self.session_handle, user_context
+        )
+        if session_info is None:
+            log_debug_message(
+                "getTimeCreated: Throwing UNAUTHORISED because session does not exist anymore"
+            )
+            raise_unauthorised_exception("Session does not exist anymore.")
+
+        return session_info.time_created
+
+    async def get_expiry(self, user_context: Union[Dict[str, Any], None] = None) -> int:
+        if user_context is None:
+            user_context = {}
+        session_info = await self.recipe_implementation.get_session_information(
+            self.session_handle, user_context
+        )
+        if session_info is None:
+            log_debug_message(
+                "getExpiry: Throwing UNAUTHORISED because session does not exist anymore"
+            )
+            raise_unauthorised_exception("Session does not exist anymore.")
+
+        return session_info.expiry
+
+    async def assert_claims(
+        self,
+        claim_validators: List[SessionClaimValidator],
+        user_context: Union[Dict[str, Any], None] = None,
+    ) -> None:
+        if user_context is None:
+            user_context = {}
+
+        validate_claim_res = await self.recipe_implementation.validate_claims(
+            self.get_user_id(user_context),
+            self.get_access_token_payload(user_context),
+            claim_validators,
+            user_context,
+        )
+
+        if validate_claim_res.access_token_payload_update is not None:
+            for k in protected_props:
+                try:
+                    del validate_claim_res.access_token_payload_update[k]
+                except KeyError:
+                    pass
+            await self.merge_into_access_token_payload(
+                validate_claim_res.access_token_payload_update, user_context
+            )
+
+        validation_errors = validate_claim_res.invalid_claims
+        if len(validation_errors) > 0:
+            raise_invalid_claims_exception("INVALID_CLAIMS", validation_errors)
+
+    async def fetch_and_set_claim(
+        self, claim: SessionClaim[Any], user_context: Union[Dict[str, Any], None] = None
+    ) -> None:
+        if user_context is None:
+            user_context = {}
+
+        update = await claim.build(
+            self.get_user_id(), self.get_tenant_id(), user_context
+        )
+        return await self.merge_into_access_token_payload(update, user_context)
+
+    async def set_claim_value(
+        self,
+        claim: SessionClaim[_T],
+        value: _T,
+        user_context: Union[Dict[str, Any], None] = None,
+    ) -> None:
+        if user_context is None:
+            user_context = {}
+
+        update = claim.add_to_payload_({}, value, user_context)
+        return await self.merge_into_access_token_payload(update, user_context)
+
+    async def get_claim_value(
+        self, claim: SessionClaim[_T], user_context: Union[Dict[str, Any], None] = None
+    ) -> Union[_T, None]:
+        if user_context is None:
+            user_context = {}
+
+        return claim.get_value_from_payload(
+            self.get_access_token_payload(user_context), user_context
+        )
+
+    async def remove_claim(
+        self, claim: SessionClaim[Any], user_context: Union[Dict[str, Any], None] = None
+    ) -> None:
+        if user_context is None:
+            user_context = {}
+
+        update = claim.remove_from_payload_by_merge_({}, user_context)
+        return await self.merge_into_access_token_payload(update, user_context)
+
+    async def merge_into_access_token_payload(
+        self,
+        access_token_payload_update: Dict[str, Any],
+        user_context: Union[Dict[str, Any], None] = None,
+    ) -> None:
+        if user_context is None:
+            user_context = {}
+
+        new_access_token_payload = {**self.get_access_token_payload(user_context)}
+        for k in protected_props:
+            try:
+                del new_access_token_payload[k]
+            except KeyError:
+                pass
+
+        new_access_token_payload = {
+            **new_access_token_payload,
+            **access_token_payload_update,
+        }
+
+        for k in access_token_payload_update.keys():
+            if access_token_payload_update[k] is None:
+                del new_access_token_payload[k]
+
+        response = await self.recipe_implementation.regenerate_access_token(
+            self.get_access_token(), new_access_token_payload, user_context
+        )
+
+        if response is None:
+            log_debug_message(
+                "mergeIntoAccessTokenPayload: Throwing UNAUTHORISED because session does not exist anymore"
+            )
+            raise_unauthorised_exception("Session does not exist anymore.")
+
+        if response.access_token is not None:
+            resp_token = parse_jwt_without_signature_verification(
+                response.access_token.token
+            )
+            payload = (
+                resp_token.payload
+                if resp_token.version >= 3
+                else response.session.user_data_in_jwt
+            )
+            self.user_data_in_access_token = payload
+            self.access_token = response.access_token.token
+            self.front_token = build_front_token(
+                self.get_user_id(), response.access_token.expiry, payload
+            )
+            self.access_token_updated = True
+            if self.req_res_info is not None:
+                transfer_method: TokenTransferMethod = self.req_res_info.transfer_method  # type: ignore
+                self.response_mutators.append(
+                    access_token_mutator(
+                        self.access_token,
+                        self.front_token,
+                        self.config,
+                        transfer_method,
+                        self.req_res_info.request,
+                    )
+                )
+        else:
+            # This case means that the access token has expired between the validation and this update
+            # We can't update the access token on the FE, as it will need to call refresh anyway but we handle this as a successful update during this request
+            # the changes will be reflected on the FE after refresh is called
+            self.user_data_in_access_token = {
+                **self.get_access_token_payload(),
+                **response.session.user_data_in_jwt,
+            }
+
+

Ancestors

+ +

Methods

+
+
+async def assert_claims(self, claim_validators: List[SessionClaimValidator], user_context: Optional[Dict[str, Any]] = None) ‑> None +
+
+
+
+ +Expand source code + +
async def assert_claims(
+    self,
+    claim_validators: List[SessionClaimValidator],
+    user_context: Union[Dict[str, Any], None] = None,
+) -> None:
+    if user_context is None:
+        user_context = {}
+
+    validate_claim_res = await self.recipe_implementation.validate_claims(
+        self.get_user_id(user_context),
+        self.get_access_token_payload(user_context),
+        claim_validators,
+        user_context,
+    )
+
+    if validate_claim_res.access_token_payload_update is not None:
+        for k in protected_props:
+            try:
+                del validate_claim_res.access_token_payload_update[k]
+            except KeyError:
+                pass
+        await self.merge_into_access_token_payload(
+            validate_claim_res.access_token_payload_update, user_context
+        )
+
+    validation_errors = validate_claim_res.invalid_claims
+    if len(validation_errors) > 0:
+        raise_invalid_claims_exception("INVALID_CLAIMS", validation_errors)
+
+
+
+async def attach_to_request_response(self, request: BaseRequest, transfer_method: typing_extensions.Literal['cookie', 'header'], user_context: Optional[Dict[str, Any]]) ‑> None +
+
+
+
+ +Expand source code + +
async def attach_to_request_response(
+    self,
+    request: BaseRequest,
+    transfer_method: TokenTransferMethod,
+    user_context: Optional[Dict[str, Any]],
+) -> None:
+    self.req_res_info = ReqResInfo(request, transfer_method)
+
+    if self.access_token_updated:
+        self.response_mutators.append(
+            access_token_mutator(
+                self.access_token,
+                self.front_token,
+                self.config,
+                transfer_method,
+                request,
+            )
+        )
+        if self.refresh_token is not None:
+            self.response_mutators.append(
+                token_response_mutator(
+                    self.config,
+                    "refresh",
+                    self.refresh_token.token,
+                    self.refresh_token.expiry,
+                    transfer_method,
+                    request,
+                )
+            )
+        if self.anti_csrf_token is not None:
+            self.response_mutators.append(
+                anti_csrf_response_mutator(self.anti_csrf_token)
+            )
+
+    request.set_session(
+        self
+    )  # Although this is called in recipe/session/framework/**/__init__.py. It's required in case of python because functions like create_new_session(req, "user-id") can be called in the framework view handler as well
+
+
+
+async def fetch_and_set_claim(self, claim: SessionClaim[typing.Any], user_context: Optional[Dict[str, Any]] = None) ‑> None +
+
+
+
+ +Expand source code + +
async def fetch_and_set_claim(
+    self, claim: SessionClaim[Any], user_context: Union[Dict[str, Any], None] = None
+) -> None:
+    if user_context is None:
+        user_context = {}
+
+    update = await claim.build(
+        self.get_user_id(), self.get_tenant_id(), user_context
+    )
+    return await self.merge_into_access_token_payload(update, user_context)
+
+
+
+def get_access_token(self, user_context: Optional[Dict[str, Any]] = None) ‑> str +
+
+
+
+ +Expand source code + +
def get_access_token(self, user_context: Union[Dict[str, Any], None] = None) -> str:
+    return self.access_token
+
+
+
+def get_access_token_payload(self, user_context: Optional[Dict[str, Any]] = None) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
def get_access_token_payload(
+    self, user_context: Union[Dict[str, Any], None] = None
+) -> Dict[str, Any]:
+    return self.user_data_in_access_token
+
+
+
+def get_all_session_tokens_dangerously(self) ‑> GetSessionTokensDangerouslyDict +
+
+
+
+ +Expand source code + +
def get_all_session_tokens_dangerously(self) -> GetSessionTokensDangerouslyDict:
+    return {
+        "accessToken": self.access_token,
+        "accessAndFrontTokenUpdated": self.access_token_updated,
+        "refreshToken": None
+        if self.refresh_token is None
+        else self.refresh_token.token,
+        "frontToken": self.front_token,
+        "antiCsrfToken": self.anti_csrf_token,
+    }
+
+
+
+async def get_claim_value(self, claim: SessionClaim[~_T], user_context: Optional[Dict[str, Any]] = None) ‑> Optional[~_T] +
+
+
+
+ +Expand source code + +
async def get_claim_value(
+    self, claim: SessionClaim[_T], user_context: Union[Dict[str, Any], None] = None
+) -> Union[_T, None]:
+    if user_context is None:
+        user_context = {}
+
+    return claim.get_value_from_payload(
+        self.get_access_token_payload(user_context), user_context
+    )
+
+
+
+async def get_expiry(self, user_context: Optional[Dict[str, Any]] = None) ‑> int +
+
+
+
+ +Expand source code + +
async def get_expiry(self, user_context: Union[Dict[str, Any], None] = None) -> int:
+    if user_context is None:
+        user_context = {}
+    session_info = await self.recipe_implementation.get_session_information(
+        self.session_handle, user_context
+    )
+    if session_info is None:
+        log_debug_message(
+            "getExpiry: Throwing UNAUTHORISED because session does not exist anymore"
+        )
+        raise_unauthorised_exception("Session does not exist anymore.")
+
+    return session_info.expiry
+
+
+
+def get_handle(self, user_context: Optional[Dict[str, Any]] = None) ‑> str +
+
+
+
+ +Expand source code + +
def get_handle(self, user_context: Union[Dict[str, Any], None] = None) -> str:
+    return self.session_handle
+
+
+
+async def get_session_data_from_database(self, user_context: Optional[Dict[str, Any]] = None) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
async def get_session_data_from_database(
+    self, user_context: Union[Dict[str, Any], None] = None
+) -> Dict[str, Any]:
+    if user_context is None:
+        user_context = {}
+    session_info = await self.recipe_implementation.get_session_information(
+        self.session_handle, user_context
+    )
+    if session_info is None:
+        log_debug_message(
+            "getSessionDataFromDatabase: Throwing UNAUTHORISED because session does not exist anymore"
+        )
+        raise_unauthorised_exception("Session does not exist anymore.")
+
+    return session_info.session_data_in_database
+
+
+
+def get_tenant_id(self, user_context: Optional[Dict[str, Any]] = None) ‑> str +
+
+
+
+ +Expand source code + +
def get_tenant_id(self, user_context: Union[Dict[str, Any], None] = None) -> str:
+    return self.tenant_id
+
+
+
+async def get_time_created(self, user_context: Optional[Dict[str, Any]] = None) ‑> int +
+
+
+
+ +Expand source code + +
async def get_time_created(
+    self, user_context: Union[Dict[str, Any], None] = None
+) -> int:
+    if user_context is None:
+        user_context = {}
+    session_info = await self.recipe_implementation.get_session_information(
+        self.session_handle, user_context
+    )
+    if session_info is None:
+        log_debug_message(
+            "getTimeCreated: Throwing UNAUTHORISED because session does not exist anymore"
+        )
+        raise_unauthorised_exception("Session does not exist anymore.")
+
+    return session_info.time_created
+
+
+
+def get_user_id(self, user_context: Optional[Dict[str, Any]] = None) ‑> str +
+
+
+
+ +Expand source code + +
def get_user_id(self, user_context: Union[Dict[str, Any], None] = None) -> str:
+    return self.user_id
+
+
+
+async def merge_into_access_token_payload(self, access_token_payload_update: Dict[str, Any], user_context: Optional[Dict[str, Any]] = None) ‑> None +
+
+
+
+ +Expand source code + +
async def merge_into_access_token_payload(
+    self,
+    access_token_payload_update: Dict[str, Any],
+    user_context: Union[Dict[str, Any], None] = None,
+) -> None:
+    if user_context is None:
+        user_context = {}
+
+    new_access_token_payload = {**self.get_access_token_payload(user_context)}
+    for k in protected_props:
+        try:
+            del new_access_token_payload[k]
+        except KeyError:
+            pass
+
+    new_access_token_payload = {
+        **new_access_token_payload,
+        **access_token_payload_update,
+    }
+
+    for k in access_token_payload_update.keys():
+        if access_token_payload_update[k] is None:
+            del new_access_token_payload[k]
+
+    response = await self.recipe_implementation.regenerate_access_token(
+        self.get_access_token(), new_access_token_payload, user_context
+    )
+
+    if response is None:
+        log_debug_message(
+            "mergeIntoAccessTokenPayload: Throwing UNAUTHORISED because session does not exist anymore"
+        )
+        raise_unauthorised_exception("Session does not exist anymore.")
+
+    if response.access_token is not None:
+        resp_token = parse_jwt_without_signature_verification(
+            response.access_token.token
+        )
+        payload = (
+            resp_token.payload
+            if resp_token.version >= 3
+            else response.session.user_data_in_jwt
+        )
+        self.user_data_in_access_token = payload
+        self.access_token = response.access_token.token
+        self.front_token = build_front_token(
+            self.get_user_id(), response.access_token.expiry, payload
+        )
+        self.access_token_updated = True
+        if self.req_res_info is not None:
+            transfer_method: TokenTransferMethod = self.req_res_info.transfer_method  # type: ignore
+            self.response_mutators.append(
+                access_token_mutator(
+                    self.access_token,
+                    self.front_token,
+                    self.config,
+                    transfer_method,
+                    self.req_res_info.request,
+                )
+            )
+    else:
+        # This case means that the access token has expired between the validation and this update
+        # We can't update the access token on the FE, as it will need to call refresh anyway but we handle this as a successful update during this request
+        # the changes will be reflected on the FE after refresh is called
+        self.user_data_in_access_token = {
+            **self.get_access_token_payload(),
+            **response.session.user_data_in_jwt,
+        }
+
+
+
+async def remove_claim(self, claim: SessionClaim[typing.Any], user_context: Optional[Dict[str, Any]] = None) ‑> None +
+
+
+
+ +Expand source code + +
async def remove_claim(
+    self, claim: SessionClaim[Any], user_context: Union[Dict[str, Any], None] = None
+) -> None:
+    if user_context is None:
+        user_context = {}
+
+    update = claim.remove_from_payload_by_merge_({}, user_context)
+    return await self.merge_into_access_token_payload(update, user_context)
+
+
+
+async def revoke_session(self, user_context: Optional[Any] = None) ‑> None +
+
+
+
+ +Expand source code + +
async def revoke_session(self, user_context: Union[Any, None] = None) -> None:
+    if user_context is None:
+        user_context = {}
+
+    await self.recipe_implementation.revoke_session(
+        self.session_handle, user_context
+    )
+
+    if self.req_res_info is not None:
+        # we do not check the output of calling revokeSession
+        # before clearing the cookies because we are revoking the
+        # current API request's session.
+        # If we instead clear the cookies only when revokeSession
+        # returns true, it can cause this kind of bug:
+        # https://github.com/supertokens/supertokens-node/issues/343
+        transfer_method: TokenTransferMethod = self.req_res_info.transfer_method  # type: ignore
+        self.response_mutators.append(
+            clear_session_response_mutator(
+                self.config,
+                transfer_method,
+                self.req_res_info.request,
+            )
+        )
+
+
+
+async def set_claim_value(self, claim: SessionClaim[~_T], value: ~_T, user_context: Optional[Dict[str, Any]] = None) ‑> None +
+
+
+
+ +Expand source code + +
async def set_claim_value(
+    self,
+    claim: SessionClaim[_T],
+    value: _T,
+    user_context: Union[Dict[str, Any], None] = None,
+) -> None:
+    if user_context is None:
+        user_context = {}
+
+    update = claim.add_to_payload_({}, value, user_context)
+    return await self.merge_into_access_token_payload(update, user_context)
+
+
+
+async def update_session_data_in_database(self, new_session_data: Dict[str, Any], user_context: Optional[Dict[str, Any]] = None) ‑> None +
+
+
+
+ +Expand source code + +
async def update_session_data_in_database(
+    self,
+    new_session_data: Dict[str, Any],
+    user_context: Union[Dict[str, Any], None] = None,
+) -> None:
+    if user_context is None:
+        user_context = {}
+    updated = await self.recipe_implementation.update_session_data_in_database(
+        self.session_handle, new_session_data, user_context
+    )
+    if not updated:
+        log_debug_message(
+            "updateSessionDataInDatabase: Throwing UNAUTHORISED because session does not exist anymore"
+        )
+        raise_unauthorised_exception("Session does not exist anymore.")
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/session/session_functions.html b/html/supertokens_python/recipe/session/session_functions.html new file mode 100644 index 000000000..5621a67d5 --- /dev/null +++ b/html/supertokens_python/recipe/session/session_functions.html @@ -0,0 +1,1262 @@ + + + + + + +supertokens_python.recipe.session.session_functions API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.session.session_functions

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+import time
+from typing import TYPE_CHECKING, Any, Dict, List, Union, Optional
+
+from supertokens_python.recipe.session.interfaces import SessionInformationResult
+
+from .access_token import get_info_from_access_token
+from .jwt import ParsedJWTInfo
+
+if TYPE_CHECKING:
+    from .recipe_implementation import RecipeImplementation
+
+from supertokens_python.logger import log_debug_message
+from supertokens_python.normalised_url_path import NormalisedURLPath
+from supertokens_python.process_state import AllowedProcessStates, ProcessState
+from supertokens_python.recipe.session.interfaces import TokenInfo
+
+from .exceptions import (
+    TryRefreshTokenError,
+    raise_token_theft_exception,
+    raise_try_refresh_token_exception,
+    raise_unauthorised_exception,
+)
+
+from supertokens_python.recipe.multitenancy.constants import DEFAULT_TENANT_ID
+
+
+class CreateOrRefreshAPIResponseSession:
+    def __init__(self, handle: str, userId: str, userDataInJWT: Any, tenant_id: str):
+        self.handle = handle
+        self.userId = userId
+        self.userDataInJWT = userDataInJWT
+        self.tenant_id = tenant_id
+
+
+class CreateOrRefreshAPIResponse:
+    def __init__(
+        self,
+        session: CreateOrRefreshAPIResponseSession,
+        accessToken: TokenInfo,
+        refreshToken: TokenInfo,
+        antiCsrfToken: Optional[str],
+    ):
+        self.session = session
+        self.accessToken = accessToken
+        self.refreshToken = refreshToken
+        self.antiCsrfToken = antiCsrfToken
+
+
+class GetSessionAPIResponseSession:
+    def __init__(
+        self,
+        handle: str,
+        userId: str,
+        userDataInJWT: Dict[str, Any],
+        expiryTime: int,
+        tenant_id: str,
+    ) -> None:
+        self.handle = handle
+        self.userId = userId
+        self.userDataInJWT = userDataInJWT
+        self.expiryTime = expiryTime
+        self.tenant_id = tenant_id
+
+
+class GetSessionAPIResponseAccessToken:
+    def __init__(self, token: str, expiry: int, createdTime: int) -> None:
+        self.token = token
+        self.expiry = expiry
+        self.createdTime = createdTime
+
+
+class GetSessionAPIResponse:
+    def __init__(
+        self,
+        session: GetSessionAPIResponseSession,
+        accessToken: Optional[GetSessionAPIResponseAccessToken] = None,
+    ) -> None:
+        self.session = session
+        self.accessToken = accessToken
+
+
+async def create_new_session(
+    recipe_implementation: RecipeImplementation,
+    tenant_id: str,
+    user_id: str,
+    disable_anti_csrf: bool,
+    access_token_payload: Union[None, Dict[str, Any]],
+    session_data_in_database: Union[None, Dict[str, Any]],
+    user_context: Optional[Dict[str, Any]],
+) -> CreateOrRefreshAPIResponse:
+    if session_data_in_database is None:
+        session_data_in_database = {}
+    if access_token_payload is None:
+        access_token_payload = {}
+    enable_anti_csrf = (
+        disable_anti_csrf is False
+        # We dont need to check if anti csrf is a function here because checking for "VIA_TOKEN" is enough
+        and recipe_implementation.config.anti_csrf_function_or_string == "VIA_TOKEN"
+    )
+    response = await recipe_implementation.querier.send_post_request(
+        NormalisedURLPath(f"{tenant_id}/recipe/session"),
+        {
+            "userId": user_id,
+            "userDataInJWT": access_token_payload,
+            "userDataInDatabase": session_data_in_database,
+            "useDynamicSigningKey": recipe_implementation.config.use_dynamic_access_token_signing_key,
+            "enableAntiCsrf": enable_anti_csrf,
+        },
+        user_context=user_context,
+    )
+
+    return CreateOrRefreshAPIResponse(
+        CreateOrRefreshAPIResponseSession(
+            response["session"]["handle"],
+            response["session"]["userId"],
+            response["session"]["userDataInJWT"],
+            response["session"]["tenantId"],
+        ),
+        TokenInfo(
+            response["accessToken"]["token"],
+            response["accessToken"]["expiry"],
+            response["accessToken"]["createdTime"],
+        ),
+        TokenInfo(
+            response["refreshToken"]["token"],
+            response["refreshToken"]["expiry"],
+            response["refreshToken"]["createdTime"],
+        ),
+        response["antiCsrfToken"] if "antiCsrfToken" in response else None,
+    )
+
+
+async def get_session(
+    recipe_implementation: RecipeImplementation,
+    parsed_access_token: ParsedJWTInfo,
+    anti_csrf_token: Union[str, None],
+    do_anti_csrf_check: bool,
+    always_check_core: bool,
+    user_context: Optional[Dict[str, Any]],
+) -> GetSessionAPIResponse:
+    config = recipe_implementation.config
+    access_token_info: Optional[Dict[str, Any]] = None
+
+    try:
+        access_token_info = get_info_from_access_token(
+            config,
+            parsed_access_token,
+            config.anti_csrf_function_or_string == "VIA_TOKEN" and do_anti_csrf_check,
+        )
+
+    except Exception as e:
+        if not isinstance(e, TryRefreshTokenError):
+            raise e
+
+        # if it comes here, it means token verification has failed.
+        # It may be due to:
+        # - signing key was updated and this token was signed with new key
+        # - access token is actually expired
+        # - access token was signed with the older signing key
+
+        # if access token is actually expired, we don't need to call core and
+        # just return TRY_REFRESH_TOKEN to the client
+
+        # if access token creation time is after this signing key was created
+        # we need to call core as there are chances that the token
+        # was signed with the updated signing key
+
+        # if access token creation time is before oldest signing key was created,
+        # so if foundASigningKeyThatIsOlderThanTheAccessToken is still false after
+        # the loop we just return TRY_REFRESH_TOKEN
+
+        payload = parsed_access_token.payload
+
+        time_created = payload.get("timeCreated")
+        expiry_time = payload.get("expiryTime")
+
+        if not isinstance(time_created, int) or not isinstance(expiry_time, int):
+            raise e
+
+        if parsed_access_token.version < 3:
+            if expiry_time < time.time():
+                raise e
+
+            # We check if the token was created since the last time we refreshed the keys from the core
+            # Since we do not know the exact timing of the last refresh, we check against the max age
+
+            if time_created <= time.time() - config.jwks_refresh_interval_sec:
+                raise e
+        else:
+            # Since v3 (and above) tokens contain a kid we can trust the cache refresh mechanism built on top of the pyjwt lib
+            # This means we do not need to call the core since the signature wouldn't pass verification anyway.
+            raise e
+
+    if parsed_access_token.version >= 3:
+        token_use_dynamic_key = (
+            parsed_access_token.kid.startswith("d-")
+            if parsed_access_token.kid is not None
+            else False
+        )
+
+        if token_use_dynamic_key != config.use_dynamic_access_token_signing_key:
+            log_debug_message(
+                "getSession: Returning TRY_REFRESH_TOKEN because the access token doesn't match the useDynamicAccessTokenSigningKey in the config"
+            )
+
+            raise_try_refresh_token_exception(
+                "The access token doesn't match the use_dynamic_access_token_signing_key setting"
+            )
+
+    # If we get here we either have a V2 token that doesn't pass verification or a valid V3> token
+    # anti-csrf check if accesstokenInfo is not undefined which means token verification was successful
+
+    if do_anti_csrf_check:
+        if config.anti_csrf_function_or_string == "VIA_TOKEN":
+            if access_token_info is not None:
+                if (
+                    anti_csrf_token is None
+                    or anti_csrf_token != access_token_info["antiCsrfToken"]
+                ):
+                    if anti_csrf_token is None:
+                        log_debug_message(
+                            "getSession: Returning TRY_REFRESH_TOKEN because antiCsrfToken is missing from request"
+                        )
+                        raise_try_refresh_token_exception(
+                            "Provided antiCsrfToken is undefined. If you do not want anti-csrf check for this API, please set doAntiCsrfCheck to false for this API"
+                        )
+                    else:
+                        log_debug_message(
+                            "getSession: Returning TRY_REFRESH_TOKEN because the passed antiCsrfToken is not the same as in the access token"
+                        )
+                        raise_try_refresh_token_exception("anti-csrf check failed")
+
+        elif (
+            isinstance(config.anti_csrf_function_or_string, str)
+            and config.anti_csrf_function_or_string == "VIA_CUSTOM_HEADER"
+        ):
+            # The function should never be called by this (we check this outside the function as well)
+            # There we can add a bit more information to the error, so that's the primary check, this is just making sure.
+            raise Exception(
+                "Please either use VIA_TOKEN, NONE or call with doAntiCsrfCheck false"
+            )
+
+    if (
+        access_token_info is not None
+        and not always_check_core
+        and access_token_info["parentRefreshTokenHash1"] is None
+    ):
+        return GetSessionAPIResponse(
+            GetSessionAPIResponseSession(
+                access_token_info["sessionHandle"],
+                access_token_info["userId"],
+                access_token_info["userData"],
+                access_token_info["expiryTime"],
+                access_token_info["tenantId"],
+            )
+        )
+
+    ProcessState.get_instance().add_state(
+        AllowedProcessStates.CALLING_SERVICE_IN_VERIFY
+    )
+
+    data = {
+        "accessToken": parsed_access_token.raw_token_string,
+        "doAntiCsrfCheck": do_anti_csrf_check,
+        "enableAntiCsrf": config.anti_csrf_function_or_string == "VIA_TOKEN",
+        "checkDatabase": always_check_core,
+    }
+    if anti_csrf_token is not None:
+        data["antiCsrfToken"] = anti_csrf_token
+
+    response = await recipe_implementation.querier.send_post_request(
+        NormalisedURLPath("/recipe/session/verify"),
+        data,
+        user_context=user_context,
+    )
+    if response["status"] == "OK":
+        return GetSessionAPIResponse(
+            GetSessionAPIResponseSession(
+                response["session"]["handle"],
+                response["session"]["userId"],
+                response["session"]["userDataInJWT"],
+                (
+                    response.get("accessToken", {}).get(
+                        "expiry"
+                    )  # if we got a new accesstoken we take the expiry time from there
+                    or (
+                        access_token_info is not None
+                        and access_token_info.get("expiryTime")
+                    )  # if we didn't get a new access token but could validate the token take that info (alwaysCheckCore === true, or parentRefreshTokenHash1 !== null)
+                    or parsed_access_token.payload[
+                        "expiryTime"
+                    ]  # if the token didn't pass validation, but we got here, it means it was a v2 token that we didn't have the key cached for.
+                ),  # This will throw error if others are none and 'expiryTime' key doesn't exist in the payload
+                response["session"].get("tenantId")
+                or (access_token_info or {}).get("tenantId"),
+            ),
+            (
+                GetSessionAPIResponseAccessToken(
+                    response["accessToken"]["token"],
+                    response["accessToken"]["expiry"],
+                    response["accessToken"]["createdTime"],
+                )
+                if "accessToken" in response
+                else None
+            ),
+        )
+    if response["status"] == "UNAUTHORISED":
+        log_debug_message("getSession: Returning UNAUTHORISED because of core response")
+        raise_unauthorised_exception(response["message"])
+
+    log_debug_message(
+        "getSession: Returning TRY_REFRESH_TOKEN because of core response"
+    )
+    raise_try_refresh_token_exception(response["message"])
+
+
+async def refresh_session(
+    recipe_implementation: RecipeImplementation,
+    refresh_token: str,
+    anti_csrf_token: Union[str, None],
+    disable_anti_csrf: bool,
+    use_dynamic_access_token_signing_key: bool,
+    user_context: Optional[Dict[str, Any]],
+) -> CreateOrRefreshAPIResponse:
+    data = {
+        "refreshToken": refresh_token,
+        "enableAntiCsrf": (
+            not disable_anti_csrf
+            and recipe_implementation.config.anti_csrf_function_or_string == "VIA_TOKEN"
+        ),
+        "useDynamicSigningKey": use_dynamic_access_token_signing_key,
+    }
+
+    if anti_csrf_token is not None:
+        data["antiCsrfToken"] = anti_csrf_token
+
+    if (
+        isinstance(recipe_implementation.config.anti_csrf_function_or_string, str)
+        and recipe_implementation.config.anti_csrf_function_or_string
+        == "VIA_CUSTOM_HEADER"
+        and not disable_anti_csrf
+    ):
+        # The function should never be called by this (we check this outside the function as well)
+        # There we can add a bit more information to the error, so that's the primary check, this is just making sure.
+        raise Exception(
+            "Please either use VIA_TOKEN, NONE or call with doAntiCsrfCheck false"
+        )
+
+    response = await recipe_implementation.querier.send_post_request(
+        NormalisedURLPath("/recipe/session/refresh"),
+        data,
+        user_context=user_context,
+    )
+    if response["status"] == "OK":
+        return CreateOrRefreshAPIResponse(
+            CreateOrRefreshAPIResponseSession(
+                response["session"]["handle"],
+                response["session"]["userId"],
+                response["session"]["userDataInJWT"],
+                response["session"]["tenantId"],
+            ),
+            TokenInfo(
+                response["accessToken"]["token"],
+                response["accessToken"]["expiry"],
+                response["accessToken"]["createdTime"],
+            ),
+            TokenInfo(
+                response["refreshToken"]["token"],
+                response["refreshToken"]["expiry"],
+                response["refreshToken"]["createdTime"],
+            ),
+            response["antiCsrfToken"] if "antiCsrfToken" in response else None,
+        )
+    if response["status"] == "UNAUTHORISED":
+        log_debug_message(
+            "refreshSession: Returning UNAUTHORISED because of core response"
+        )
+        raise_unauthorised_exception(response["message"])
+    log_debug_message(
+        "refreshSession: Returning TOKEN_THEFT_DETECTED because of core response"
+    )
+    raise_token_theft_exception(
+        response["session"]["userId"], response["session"]["handle"]
+    )
+
+
+async def revoke_all_sessions_for_user(
+    recipe_implementation: RecipeImplementation,
+    user_id: str,
+    tenant_id: Optional[str],
+    revoke_across_all_tenants: bool,
+    user_context: Optional[Dict[str, Any]],
+) -> List[str]:
+    if tenant_id is None:
+        tenant_id = DEFAULT_TENANT_ID
+
+    if revoke_across_all_tenants:
+        response = await recipe_implementation.querier.send_post_request(
+            NormalisedURLPath("/recipe/session/remove"),
+            {"userId": user_id, "revokeAcrossAllTenants": revoke_across_all_tenants},
+            user_context=user_context,
+        )
+    else:
+        response = await recipe_implementation.querier.send_post_request(
+            NormalisedURLPath(f"{tenant_id}/recipe/session/remove"),
+            {"userId": user_id, "revokeAcrossAllTenants": revoke_across_all_tenants},
+            user_context=user_context,
+        )
+    return response["sessionHandlesRevoked"]
+
+
+async def get_all_session_handles_for_user(
+    recipe_implementation: RecipeImplementation,
+    user_id: str,
+    tenant_id: Optional[str],
+    fetch_across_all_tenants: bool,
+    user_context: Optional[Dict[str, Any]],
+) -> List[str]:
+    if tenant_id is None:
+        tenant_id = DEFAULT_TENANT_ID
+
+    if fetch_across_all_tenants:
+        response = await recipe_implementation.querier.send_get_request(
+            NormalisedURLPath("/recipe/session/user"),
+            {"userId": user_id, "fetchAcrossAllTenants": fetch_across_all_tenants},
+            user_context=user_context,
+        )
+    else:
+        response = await recipe_implementation.querier.send_get_request(
+            NormalisedURLPath(f"{tenant_id}/recipe/session/user"),
+            {"userId": user_id, "fetchAcrossAllTenants": fetch_across_all_tenants},
+            user_context=user_context,
+        )
+    return response["sessionHandles"]
+
+
+async def revoke_session(
+    recipe_implementation: RecipeImplementation,
+    session_handle: str,
+    user_context: Optional[Dict[str, Any]],
+) -> bool:
+    response = await recipe_implementation.querier.send_post_request(
+        NormalisedURLPath("/recipe/session/remove"),
+        {"sessionHandles": [session_handle]},
+        user_context=user_context,
+    )
+    return len(response["sessionHandlesRevoked"]) == 1
+
+
+async def revoke_multiple_sessions(
+    recipe_implementation: RecipeImplementation,
+    session_handles: List[str],
+    user_context: Optional[Dict[str, Any]],
+) -> List[str]:
+    response = await recipe_implementation.querier.send_post_request(
+        NormalisedURLPath("/recipe/session/remove"),
+        {"sessionHandles": session_handles},
+        user_context=user_context,
+    )
+    return response["sessionHandlesRevoked"]
+
+
+async def update_session_data_in_database(
+    recipe_implementation: RecipeImplementation,
+    session_handle: str,
+    new_session_data: Dict[str, Any],
+    user_context: Optional[Dict[str, Any]],
+) -> bool:
+    response = await recipe_implementation.querier.send_put_request(
+        NormalisedURLPath("/recipe/session/data"),
+        {"sessionHandle": session_handle, "userDataInDatabase": new_session_data},
+        user_context=user_context,
+    )
+    if response["status"] == "UNAUTHORISED":
+        return False
+
+    return True
+
+
+async def update_access_token_payload(
+    recipe_implementation: RecipeImplementation,
+    session_handle: str,
+    new_access_token_payload: Dict[str, Any],
+    user_context: Optional[Dict[str, Any]],
+) -> bool:
+    response = await recipe_implementation.querier.send_put_request(
+        NormalisedURLPath("/recipe/jwt/data"),
+        {"sessionHandle": session_handle, "userDataInJWT": new_access_token_payload},
+        user_context=user_context,
+    )
+    if response["status"] == "UNAUTHORISED":
+        return False
+
+    return True
+
+
+async def get_session_information(
+    recipe_implementation: RecipeImplementation,
+    session_handle: str,
+    user_context: Optional[Dict[str, Any]],
+) -> Union[SessionInformationResult, None]:
+    response = await recipe_implementation.querier.send_get_request(
+        NormalisedURLPath("/recipe/session"),
+        {"sessionHandle": session_handle},
+        user_context=user_context,
+    )
+    if response["status"] == "OK":
+        return SessionInformationResult(
+            response["sessionHandle"],
+            response["userId"],
+            response["userDataInDatabase"],
+            response["expiry"],
+            response["userDataInJWT"],
+            response["timeCreated"],
+            response["tenantId"],
+        )
+    return None
+
+
+
+
+
+
+
+

Functions

+
+
+async def create_new_session(recipe_implementation: RecipeImplementation, tenant_id: str, user_id: str, disable_anti_csrf: bool, access_token_payload: Union[None, Dict[str, Any]], session_data_in_database: Union[None, Dict[str, Any]], user_context: Optional[Dict[str, Any]]) ‑> CreateOrRefreshAPIResponse +
+
+
+
+ +Expand source code + +
async def create_new_session(
+    recipe_implementation: RecipeImplementation,
+    tenant_id: str,
+    user_id: str,
+    disable_anti_csrf: bool,
+    access_token_payload: Union[None, Dict[str, Any]],
+    session_data_in_database: Union[None, Dict[str, Any]],
+    user_context: Optional[Dict[str, Any]],
+) -> CreateOrRefreshAPIResponse:
+    if session_data_in_database is None:
+        session_data_in_database = {}
+    if access_token_payload is None:
+        access_token_payload = {}
+    enable_anti_csrf = (
+        disable_anti_csrf is False
+        # We dont need to check if anti csrf is a function here because checking for "VIA_TOKEN" is enough
+        and recipe_implementation.config.anti_csrf_function_or_string == "VIA_TOKEN"
+    )
+    response = await recipe_implementation.querier.send_post_request(
+        NormalisedURLPath(f"{tenant_id}/recipe/session"),
+        {
+            "userId": user_id,
+            "userDataInJWT": access_token_payload,
+            "userDataInDatabase": session_data_in_database,
+            "useDynamicSigningKey": recipe_implementation.config.use_dynamic_access_token_signing_key,
+            "enableAntiCsrf": enable_anti_csrf,
+        },
+        user_context=user_context,
+    )
+
+    return CreateOrRefreshAPIResponse(
+        CreateOrRefreshAPIResponseSession(
+            response["session"]["handle"],
+            response["session"]["userId"],
+            response["session"]["userDataInJWT"],
+            response["session"]["tenantId"],
+        ),
+        TokenInfo(
+            response["accessToken"]["token"],
+            response["accessToken"]["expiry"],
+            response["accessToken"]["createdTime"],
+        ),
+        TokenInfo(
+            response["refreshToken"]["token"],
+            response["refreshToken"]["expiry"],
+            response["refreshToken"]["createdTime"],
+        ),
+        response["antiCsrfToken"] if "antiCsrfToken" in response else None,
+    )
+
+
+
+async def get_all_session_handles_for_user(recipe_implementation: RecipeImplementation, user_id: str, tenant_id: Optional[str], fetch_across_all_tenants: bool, user_context: Optional[Dict[str, Any]]) ‑> List[str] +
+
+
+
+ +Expand source code + +
async def get_all_session_handles_for_user(
+    recipe_implementation: RecipeImplementation,
+    user_id: str,
+    tenant_id: Optional[str],
+    fetch_across_all_tenants: bool,
+    user_context: Optional[Dict[str, Any]],
+) -> List[str]:
+    if tenant_id is None:
+        tenant_id = DEFAULT_TENANT_ID
+
+    if fetch_across_all_tenants:
+        response = await recipe_implementation.querier.send_get_request(
+            NormalisedURLPath("/recipe/session/user"),
+            {"userId": user_id, "fetchAcrossAllTenants": fetch_across_all_tenants},
+            user_context=user_context,
+        )
+    else:
+        response = await recipe_implementation.querier.send_get_request(
+            NormalisedURLPath(f"{tenant_id}/recipe/session/user"),
+            {"userId": user_id, "fetchAcrossAllTenants": fetch_across_all_tenants},
+            user_context=user_context,
+        )
+    return response["sessionHandles"]
+
+
+
+async def get_session(recipe_implementation: RecipeImplementation, parsed_access_token: ParsedJWTInfo, anti_csrf_token: Union[str, None], do_anti_csrf_check: bool, always_check_core: bool, user_context: Optional[Dict[str, Any]]) ‑> GetSessionAPIResponse +
+
+
+
+ +Expand source code + +
async def get_session(
+    recipe_implementation: RecipeImplementation,
+    parsed_access_token: ParsedJWTInfo,
+    anti_csrf_token: Union[str, None],
+    do_anti_csrf_check: bool,
+    always_check_core: bool,
+    user_context: Optional[Dict[str, Any]],
+) -> GetSessionAPIResponse:
+    config = recipe_implementation.config
+    access_token_info: Optional[Dict[str, Any]] = None
+
+    try:
+        access_token_info = get_info_from_access_token(
+            config,
+            parsed_access_token,
+            config.anti_csrf_function_or_string == "VIA_TOKEN" and do_anti_csrf_check,
+        )
+
+    except Exception as e:
+        if not isinstance(e, TryRefreshTokenError):
+            raise e
+
+        # if it comes here, it means token verification has failed.
+        # It may be due to:
+        # - signing key was updated and this token was signed with new key
+        # - access token is actually expired
+        # - access token was signed with the older signing key
+
+        # if access token is actually expired, we don't need to call core and
+        # just return TRY_REFRESH_TOKEN to the client
+
+        # if access token creation time is after this signing key was created
+        # we need to call core as there are chances that the token
+        # was signed with the updated signing key
+
+        # if access token creation time is before oldest signing key was created,
+        # so if foundASigningKeyThatIsOlderThanTheAccessToken is still false after
+        # the loop we just return TRY_REFRESH_TOKEN
+
+        payload = parsed_access_token.payload
+
+        time_created = payload.get("timeCreated")
+        expiry_time = payload.get("expiryTime")
+
+        if not isinstance(time_created, int) or not isinstance(expiry_time, int):
+            raise e
+
+        if parsed_access_token.version < 3:
+            if expiry_time < time.time():
+                raise e
+
+            # We check if the token was created since the last time we refreshed the keys from the core
+            # Since we do not know the exact timing of the last refresh, we check against the max age
+
+            if time_created <= time.time() - config.jwks_refresh_interval_sec:
+                raise e
+        else:
+            # Since v3 (and above) tokens contain a kid we can trust the cache refresh mechanism built on top of the pyjwt lib
+            # This means we do not need to call the core since the signature wouldn't pass verification anyway.
+            raise e
+
+    if parsed_access_token.version >= 3:
+        token_use_dynamic_key = (
+            parsed_access_token.kid.startswith("d-")
+            if parsed_access_token.kid is not None
+            else False
+        )
+
+        if token_use_dynamic_key != config.use_dynamic_access_token_signing_key:
+            log_debug_message(
+                "getSession: Returning TRY_REFRESH_TOKEN because the access token doesn't match the useDynamicAccessTokenSigningKey in the config"
+            )
+
+            raise_try_refresh_token_exception(
+                "The access token doesn't match the use_dynamic_access_token_signing_key setting"
+            )
+
+    # If we get here we either have a V2 token that doesn't pass verification or a valid V3> token
+    # anti-csrf check if accesstokenInfo is not undefined which means token verification was successful
+
+    if do_anti_csrf_check:
+        if config.anti_csrf_function_or_string == "VIA_TOKEN":
+            if access_token_info is not None:
+                if (
+                    anti_csrf_token is None
+                    or anti_csrf_token != access_token_info["antiCsrfToken"]
+                ):
+                    if anti_csrf_token is None:
+                        log_debug_message(
+                            "getSession: Returning TRY_REFRESH_TOKEN because antiCsrfToken is missing from request"
+                        )
+                        raise_try_refresh_token_exception(
+                            "Provided antiCsrfToken is undefined. If you do not want anti-csrf check for this API, please set doAntiCsrfCheck to false for this API"
+                        )
+                    else:
+                        log_debug_message(
+                            "getSession: Returning TRY_REFRESH_TOKEN because the passed antiCsrfToken is not the same as in the access token"
+                        )
+                        raise_try_refresh_token_exception("anti-csrf check failed")
+
+        elif (
+            isinstance(config.anti_csrf_function_or_string, str)
+            and config.anti_csrf_function_or_string == "VIA_CUSTOM_HEADER"
+        ):
+            # The function should never be called by this (we check this outside the function as well)
+            # There we can add a bit more information to the error, so that's the primary check, this is just making sure.
+            raise Exception(
+                "Please either use VIA_TOKEN, NONE or call with doAntiCsrfCheck false"
+            )
+
+    if (
+        access_token_info is not None
+        and not always_check_core
+        and access_token_info["parentRefreshTokenHash1"] is None
+    ):
+        return GetSessionAPIResponse(
+            GetSessionAPIResponseSession(
+                access_token_info["sessionHandle"],
+                access_token_info["userId"],
+                access_token_info["userData"],
+                access_token_info["expiryTime"],
+                access_token_info["tenantId"],
+            )
+        )
+
+    ProcessState.get_instance().add_state(
+        AllowedProcessStates.CALLING_SERVICE_IN_VERIFY
+    )
+
+    data = {
+        "accessToken": parsed_access_token.raw_token_string,
+        "doAntiCsrfCheck": do_anti_csrf_check,
+        "enableAntiCsrf": config.anti_csrf_function_or_string == "VIA_TOKEN",
+        "checkDatabase": always_check_core,
+    }
+    if anti_csrf_token is not None:
+        data["antiCsrfToken"] = anti_csrf_token
+
+    response = await recipe_implementation.querier.send_post_request(
+        NormalisedURLPath("/recipe/session/verify"),
+        data,
+        user_context=user_context,
+    )
+    if response["status"] == "OK":
+        return GetSessionAPIResponse(
+            GetSessionAPIResponseSession(
+                response["session"]["handle"],
+                response["session"]["userId"],
+                response["session"]["userDataInJWT"],
+                (
+                    response.get("accessToken", {}).get(
+                        "expiry"
+                    )  # if we got a new accesstoken we take the expiry time from there
+                    or (
+                        access_token_info is not None
+                        and access_token_info.get("expiryTime")
+                    )  # if we didn't get a new access token but could validate the token take that info (alwaysCheckCore === true, or parentRefreshTokenHash1 !== null)
+                    or parsed_access_token.payload[
+                        "expiryTime"
+                    ]  # if the token didn't pass validation, but we got here, it means it was a v2 token that we didn't have the key cached for.
+                ),  # This will throw error if others are none and 'expiryTime' key doesn't exist in the payload
+                response["session"].get("tenantId")
+                or (access_token_info or {}).get("tenantId"),
+            ),
+            (
+                GetSessionAPIResponseAccessToken(
+                    response["accessToken"]["token"],
+                    response["accessToken"]["expiry"],
+                    response["accessToken"]["createdTime"],
+                )
+                if "accessToken" in response
+                else None
+            ),
+        )
+    if response["status"] == "UNAUTHORISED":
+        log_debug_message("getSession: Returning UNAUTHORISED because of core response")
+        raise_unauthorised_exception(response["message"])
+
+    log_debug_message(
+        "getSession: Returning TRY_REFRESH_TOKEN because of core response"
+    )
+    raise_try_refresh_token_exception(response["message"])
+
+
+
+async def get_session_information(recipe_implementation: RecipeImplementation, session_handle: str, user_context: Optional[Dict[str, Any]]) ‑> Union[SessionInformationResult, None] +
+
+
+
+ +Expand source code + +
async def get_session_information(
+    recipe_implementation: RecipeImplementation,
+    session_handle: str,
+    user_context: Optional[Dict[str, Any]],
+) -> Union[SessionInformationResult, None]:
+    response = await recipe_implementation.querier.send_get_request(
+        NormalisedURLPath("/recipe/session"),
+        {"sessionHandle": session_handle},
+        user_context=user_context,
+    )
+    if response["status"] == "OK":
+        return SessionInformationResult(
+            response["sessionHandle"],
+            response["userId"],
+            response["userDataInDatabase"],
+            response["expiry"],
+            response["userDataInJWT"],
+            response["timeCreated"],
+            response["tenantId"],
+        )
+    return None
+
+
+
+async def refresh_session(recipe_implementation: RecipeImplementation, refresh_token: str, anti_csrf_token: Union[str, None], disable_anti_csrf: bool, use_dynamic_access_token_signing_key: bool, user_context: Optional[Dict[str, Any]]) ‑> CreateOrRefreshAPIResponse +
+
+
+
+ +Expand source code + +
async def refresh_session(
+    recipe_implementation: RecipeImplementation,
+    refresh_token: str,
+    anti_csrf_token: Union[str, None],
+    disable_anti_csrf: bool,
+    use_dynamic_access_token_signing_key: bool,
+    user_context: Optional[Dict[str, Any]],
+) -> CreateOrRefreshAPIResponse:
+    data = {
+        "refreshToken": refresh_token,
+        "enableAntiCsrf": (
+            not disable_anti_csrf
+            and recipe_implementation.config.anti_csrf_function_or_string == "VIA_TOKEN"
+        ),
+        "useDynamicSigningKey": use_dynamic_access_token_signing_key,
+    }
+
+    if anti_csrf_token is not None:
+        data["antiCsrfToken"] = anti_csrf_token
+
+    if (
+        isinstance(recipe_implementation.config.anti_csrf_function_or_string, str)
+        and recipe_implementation.config.anti_csrf_function_or_string
+        == "VIA_CUSTOM_HEADER"
+        and not disable_anti_csrf
+    ):
+        # The function should never be called by this (we check this outside the function as well)
+        # There we can add a bit more information to the error, so that's the primary check, this is just making sure.
+        raise Exception(
+            "Please either use VIA_TOKEN, NONE or call with doAntiCsrfCheck false"
+        )
+
+    response = await recipe_implementation.querier.send_post_request(
+        NormalisedURLPath("/recipe/session/refresh"),
+        data,
+        user_context=user_context,
+    )
+    if response["status"] == "OK":
+        return CreateOrRefreshAPIResponse(
+            CreateOrRefreshAPIResponseSession(
+                response["session"]["handle"],
+                response["session"]["userId"],
+                response["session"]["userDataInJWT"],
+                response["session"]["tenantId"],
+            ),
+            TokenInfo(
+                response["accessToken"]["token"],
+                response["accessToken"]["expiry"],
+                response["accessToken"]["createdTime"],
+            ),
+            TokenInfo(
+                response["refreshToken"]["token"],
+                response["refreshToken"]["expiry"],
+                response["refreshToken"]["createdTime"],
+            ),
+            response["antiCsrfToken"] if "antiCsrfToken" in response else None,
+        )
+    if response["status"] == "UNAUTHORISED":
+        log_debug_message(
+            "refreshSession: Returning UNAUTHORISED because of core response"
+        )
+        raise_unauthorised_exception(response["message"])
+    log_debug_message(
+        "refreshSession: Returning TOKEN_THEFT_DETECTED because of core response"
+    )
+    raise_token_theft_exception(
+        response["session"]["userId"], response["session"]["handle"]
+    )
+
+
+
+async def revoke_all_sessions_for_user(recipe_implementation: RecipeImplementation, user_id: str, tenant_id: Optional[str], revoke_across_all_tenants: bool, user_context: Optional[Dict[str, Any]]) ‑> List[str] +
+
+
+
+ +Expand source code + +
async def revoke_all_sessions_for_user(
+    recipe_implementation: RecipeImplementation,
+    user_id: str,
+    tenant_id: Optional[str],
+    revoke_across_all_tenants: bool,
+    user_context: Optional[Dict[str, Any]],
+) -> List[str]:
+    if tenant_id is None:
+        tenant_id = DEFAULT_TENANT_ID
+
+    if revoke_across_all_tenants:
+        response = await recipe_implementation.querier.send_post_request(
+            NormalisedURLPath("/recipe/session/remove"),
+            {"userId": user_id, "revokeAcrossAllTenants": revoke_across_all_tenants},
+            user_context=user_context,
+        )
+    else:
+        response = await recipe_implementation.querier.send_post_request(
+            NormalisedURLPath(f"{tenant_id}/recipe/session/remove"),
+            {"userId": user_id, "revokeAcrossAllTenants": revoke_across_all_tenants},
+            user_context=user_context,
+        )
+    return response["sessionHandlesRevoked"]
+
+
+
+async def revoke_multiple_sessions(recipe_implementation: RecipeImplementation, session_handles: List[str], user_context: Optional[Dict[str, Any]]) ‑> List[str] +
+
+
+
+ +Expand source code + +
async def revoke_multiple_sessions(
+    recipe_implementation: RecipeImplementation,
+    session_handles: List[str],
+    user_context: Optional[Dict[str, Any]],
+) -> List[str]:
+    response = await recipe_implementation.querier.send_post_request(
+        NormalisedURLPath("/recipe/session/remove"),
+        {"sessionHandles": session_handles},
+        user_context=user_context,
+    )
+    return response["sessionHandlesRevoked"]
+
+
+
+async def revoke_session(recipe_implementation: RecipeImplementation, session_handle: str, user_context: Optional[Dict[str, Any]]) ‑> bool +
+
+
+
+ +Expand source code + +
async def revoke_session(
+    recipe_implementation: RecipeImplementation,
+    session_handle: str,
+    user_context: Optional[Dict[str, Any]],
+) -> bool:
+    response = await recipe_implementation.querier.send_post_request(
+        NormalisedURLPath("/recipe/session/remove"),
+        {"sessionHandles": [session_handle]},
+        user_context=user_context,
+    )
+    return len(response["sessionHandlesRevoked"]) == 1
+
+
+
+async def update_access_token_payload(recipe_implementation: RecipeImplementation, session_handle: str, new_access_token_payload: Dict[str, Any], user_context: Optional[Dict[str, Any]]) ‑> bool +
+
+
+
+ +Expand source code + +
async def update_access_token_payload(
+    recipe_implementation: RecipeImplementation,
+    session_handle: str,
+    new_access_token_payload: Dict[str, Any],
+    user_context: Optional[Dict[str, Any]],
+) -> bool:
+    response = await recipe_implementation.querier.send_put_request(
+        NormalisedURLPath("/recipe/jwt/data"),
+        {"sessionHandle": session_handle, "userDataInJWT": new_access_token_payload},
+        user_context=user_context,
+    )
+    if response["status"] == "UNAUTHORISED":
+        return False
+
+    return True
+
+
+
+async def update_session_data_in_database(recipe_implementation: RecipeImplementation, session_handle: str, new_session_data: Dict[str, Any], user_context: Optional[Dict[str, Any]]) ‑> bool +
+
+
+
+ +Expand source code + +
async def update_session_data_in_database(
+    recipe_implementation: RecipeImplementation,
+    session_handle: str,
+    new_session_data: Dict[str, Any],
+    user_context: Optional[Dict[str, Any]],
+) -> bool:
+    response = await recipe_implementation.querier.send_put_request(
+        NormalisedURLPath("/recipe/session/data"),
+        {"sessionHandle": session_handle, "userDataInDatabase": new_session_data},
+        user_context=user_context,
+    )
+    if response["status"] == "UNAUTHORISED":
+        return False
+
+    return True
+
+
+
+
+
+

Classes

+
+
+class CreateOrRefreshAPIResponse +(session: CreateOrRefreshAPIResponseSession, accessToken: TokenInfo, refreshToken: TokenInfo, antiCsrfToken: Optional[str]) +
+
+
+
+ +Expand source code + +
class CreateOrRefreshAPIResponse:
+    def __init__(
+        self,
+        session: CreateOrRefreshAPIResponseSession,
+        accessToken: TokenInfo,
+        refreshToken: TokenInfo,
+        antiCsrfToken: Optional[str],
+    ):
+        self.session = session
+        self.accessToken = accessToken
+        self.refreshToken = refreshToken
+        self.antiCsrfToken = antiCsrfToken
+
+
+
+class CreateOrRefreshAPIResponseSession +(handle: str, userId: str, userDataInJWT: Any, tenant_id: str) +
+
+
+
+ +Expand source code + +
class CreateOrRefreshAPIResponseSession:
+    def __init__(self, handle: str, userId: str, userDataInJWT: Any, tenant_id: str):
+        self.handle = handle
+        self.userId = userId
+        self.userDataInJWT = userDataInJWT
+        self.tenant_id = tenant_id
+
+
+
+class GetSessionAPIResponse +(session: GetSessionAPIResponseSession, accessToken: Optional[GetSessionAPIResponseAccessToken] = None) +
+
+
+
+ +Expand source code + +
class GetSessionAPIResponse:
+    def __init__(
+        self,
+        session: GetSessionAPIResponseSession,
+        accessToken: Optional[GetSessionAPIResponseAccessToken] = None,
+    ) -> None:
+        self.session = session
+        self.accessToken = accessToken
+
+
+
+class GetSessionAPIResponseAccessToken +(token: str, expiry: int, createdTime: int) +
+
+
+
+ +Expand source code + +
class GetSessionAPIResponseAccessToken:
+    def __init__(self, token: str, expiry: int, createdTime: int) -> None:
+        self.token = token
+        self.expiry = expiry
+        self.createdTime = createdTime
+
+
+
+class GetSessionAPIResponseSession +(handle: str, userId: str, userDataInJWT: Dict[str, Any], expiryTime: int, tenant_id: str) +
+
+
+
+ +Expand source code + +
class GetSessionAPIResponseSession:
+    def __init__(
+        self,
+        handle: str,
+        userId: str,
+        userDataInJWT: Dict[str, Any],
+        expiryTime: int,
+        tenant_id: str,
+    ) -> None:
+        self.handle = handle
+        self.userId = userId
+        self.userDataInJWT = userDataInJWT
+        self.expiryTime = expiryTime
+        self.tenant_id = tenant_id
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/session/session_request_functions.html b/html/supertokens_python/recipe/session/session_request_functions.html new file mode 100644 index 000000000..65f0ced09 --- /dev/null +++ b/html/supertokens_python/recipe/session/session_request_functions.html @@ -0,0 +1,1084 @@ + + + + + + +supertokens_python.recipe.session.session_request_functions API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.session.session_request_functions

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from typing import Any, Callable, Dict, List, Optional, Union, TYPE_CHECKING
+
+from supertokens_python.logger import log_debug_message
+from supertokens_python.recipe.session.access_token import (
+    validate_access_token_structure,
+)
+from supertokens_python.recipe.session.constants import available_token_transfer_methods
+from supertokens_python.recipe.session.cookie_and_header import (
+    clear_session_cookies_from_older_cookie_domain,
+    clear_session_mutator,
+    get_anti_csrf_header,
+    get_token,
+    has_multiple_cookies_for_token_type,
+    set_cookie_response_mutator,
+)
+from supertokens_python.recipe.session.exceptions import (
+    raise_try_refresh_token_exception,
+    raise_unauthorised_exception,
+)
+from supertokens_python.recipe.session.interfaces import (
+    RecipeInterface as SessionRecipeInterface,
+)
+from supertokens_python.recipe.session.interfaces import (
+    SessionClaimValidator,
+    SessionContainer,
+)
+from supertokens_python.recipe.session.exceptions import (
+    SuperTokensSessionError,
+    TokenTheftError,
+    UnauthorisedError,
+)
+from supertokens_python.recipe.session.jwt import (
+    ParsedJWTInfo,
+    parse_jwt_without_signature_verification,
+)
+from supertokens_python.recipe.session.utils import (
+    SessionConfig,
+    TokenTransferMethod,
+    get_required_claim_validators,
+    get_auth_mode_from_header,
+)
+from supertokens_python.types import MaybeAwaitable
+from supertokens_python.utils import (
+    FRAMEWORKS,
+    get_rid_from_header,
+    is_an_ip_address,
+    normalise_http_method,
+    set_request_in_user_context_if_not_defined,
+)
+from supertokens_python.supertokens import Supertokens
+from .constants import protected_props
+
+if TYPE_CHECKING:
+    from supertokens_python.recipe.session.recipe import SessionRecipe
+    from supertokens_python.supertokens import AppInfo
+    from .interfaces import ResponseMutator
+
+LEGACY_ID_REFRESH_TOKEN_COOKIE_NAME = "sIdRefreshToken"
+
+
+async def get_session_from_request(
+    request: Any,
+    config: SessionConfig,
+    recipe_interface_impl: SessionRecipeInterface,
+    session_required: Optional[bool] = None,
+    anti_csrf_check: Optional[bool] = None,
+    check_database: Optional[bool] = None,
+    override_global_claim_validators: Optional[
+        Callable[
+            [List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
+            MaybeAwaitable[List[SessionClaimValidator]],
+        ]
+    ] = None,
+    user_context: Optional[Dict[str, Any]] = None,
+) -> Optional[SessionContainer]:
+    log_debug_message("getSession: Started")
+
+    if not hasattr(request, "wrapper_used") or not request.wrapper_used:
+        request = FRAMEWORKS[
+            Supertokens.get_instance().app_info.framework
+        ].wrap_request(request)
+
+    log_debug_message("getSession: Wrapping done")
+
+    user_context = set_request_in_user_context_if_not_defined(user_context, request)
+
+    # This token isn't handled by getToken to limit the scope of this legacy/migration code
+    if request.get_cookie(LEGACY_ID_REFRESH_TOKEN_COOKIE_NAME) is not None:
+        log_debug_message(
+            "getSession: Throwing TRY_REFRESH_TOKEN because the request is using a legacy session"
+        )
+        # This could create a spike on refresh calls during the update of the backend SDK
+        return raise_try_refresh_token_exception(
+            "using legacy session, please call the refresh API"
+        )
+
+    session_optional = not session_required
+    log_debug_message("getSession: optional validation: %s", session_optional)
+
+    access_tokens: Dict[TokenTransferMethod, ParsedJWTInfo] = {}
+
+    # We check all token transfer methods for available access tokens
+    for transfer_method in available_token_transfer_methods:
+        token_string = get_token(request, "access", transfer_method)
+
+        if token_string is not None:
+            try:
+                info = parse_jwt_without_signature_verification(token_string)
+                validate_access_token_structure(info.payload, info.version)
+                log_debug_message(
+                    "getSession: got access token from %s", transfer_method
+                )
+                access_tokens[transfer_method] = info
+            except Exception:
+                log_debug_message(
+                    "getSession: ignoring token in %s, because it doesn't match our access token structure",
+                    transfer_method,
+                )
+
+    allowed_transfer_method = config.get_token_transfer_method(
+        request, False, user_context
+    )
+    request_transfer_method: Optional[TokenTransferMethod] = None
+    request_access_token: Union[ParsedJWTInfo, None] = None
+
+    if (allowed_transfer_method in ("any", "header")) and access_tokens.get(
+        "header"
+    ) is not None:
+        log_debug_message("getSession: using header transfer method")
+        request_transfer_method = "header"
+        request_access_token = access_tokens["header"]
+    elif (allowed_transfer_method in ("any", "cookie")) and access_tokens.get(
+        "cookie"
+    ) is not None:
+        log_debug_message("getSession: using cookie transfer method")
+
+        # If multiple access tokens exist in the request cookie, throw TRY_REFRESH_TOKEN.
+        # This prompts the client to call the refresh endpoint, clearing older_cookie_domain cookies (if set).
+        # ensuring outdated token payload isn't used.
+        if has_multiple_cookies_for_token_type(request, "access"):
+            log_debug_message(
+                "getSession: Throwing TRY_REFRESH_TOKEN because multiple access tokens are present in request cookies"
+            )
+            raise_try_refresh_token_exception(
+                "Multiple access tokens present in the request cookies."
+            )
+
+        request_transfer_method = "cookie"
+        request_access_token = access_tokens["cookie"]
+
+    anti_csrf_token = get_anti_csrf_header(request)
+    do_anti_csrf_check = anti_csrf_check
+
+    if do_anti_csrf_check is None:
+        do_anti_csrf_check = normalise_http_method(request.method()) != "get"
+    if request_transfer_method == "header":
+        do_anti_csrf_check = False
+    if request_access_token is None:
+        do_anti_csrf_check = False
+
+    if callable(config.anti_csrf_function_or_string):
+        anti_csrf = config.anti_csrf_function_or_string(request, user_context)
+    else:
+        anti_csrf = config.anti_csrf_function_or_string
+
+    if do_anti_csrf_check and anti_csrf == "VIA_CUSTOM_HEADER":
+        if anti_csrf == "VIA_CUSTOM_HEADER":
+            if get_rid_from_header(request) is None:
+                log_debug_message(
+                    "getSession: Returning TRY_REFRESH_TOKEN because custom header (rid) was not passed"
+                )
+                raise_try_refresh_token_exception(
+                    "anti-csrf check failed. Please pass 'rid: \"session\"' header in the request, or set doAntiCsrfCheck to false for this API"
+                )
+
+            log_debug_message("getSession: VIA_CUSTOM_HEADER anti-csrf check passed")
+            do_anti_csrf_check = False
+
+    log_debug_message("getSession: Value of antiCsrfToken is: %s", do_anti_csrf_check)
+
+    session = await recipe_interface_impl.get_session(
+        access_token=(
+            request_access_token.raw_token_string
+            if request_access_token is not None
+            else None
+        ),
+        anti_csrf_token=anti_csrf_token,
+        anti_csrf_check=do_anti_csrf_check,
+        session_required=session_required,
+        check_database=check_database,
+        override_global_claim_validators=override_global_claim_validators,
+        user_context=user_context,
+    )
+
+    if session is not None:
+        claim_validators = await get_required_claim_validators(
+            session, override_global_claim_validators, user_context
+        )
+        await session.assert_claims(claim_validators, user_context)
+
+        # request_transfer_method can only be None here if the user overriddes get_session
+        # to load the session by a custom method in that (very niche) case they also need to
+        # override how the session is attached to the response.
+        # In that scenario the transferMethod passed to attachToRequestResponse likely doesn't
+        # matter, still, we follow the general fallback logic
+
+        if request_transfer_method is not None:
+            final_transfer_method = request_transfer_method
+        elif allowed_transfer_method != "any":
+            final_transfer_method = allowed_transfer_method
+        else:
+            final_transfer_method = "header"
+
+        await session.attach_to_request_response(
+            request, final_transfer_method, user_context
+        )
+
+    return session
+
+
+async def create_new_session_in_request(
+    request: Any,
+    user_context: Dict[str, Any],
+    recipe_instance: SessionRecipe,
+    access_token_payload: Dict[str, Any],
+    user_id: str,
+    config: SessionConfig,
+    app_info: AppInfo,
+    session_data_in_database: Dict[str, Any],
+    tenant_id: str,
+) -> SessionContainer:
+    log_debug_message("createNewSession: Started")
+
+    # Handling framework specific request/response wrapping
+    if not hasattr(request, "wrapper_used") or not request.wrapper_used:
+        request = FRAMEWORKS[
+            Supertokens.get_instance().app_info.framework
+        ].wrap_request(request)
+
+    log_debug_message("createNewSession: Wrapping done")
+    user_context = set_request_in_user_context_if_not_defined(user_context, request)
+
+    claims_added_by_other_recipes = recipe_instance.get_claims_added_by_other_recipes()
+    issuer = (
+        app_info.api_domain.get_as_string_dangerous()
+        + app_info.api_base_path.get_as_string_dangerous()
+    )
+
+    final_access_token_payload = {**access_token_payload, "iss": issuer}
+
+    for prop in protected_props:
+        if prop in final_access_token_payload:
+            del final_access_token_payload[prop]
+
+    for claim in claims_added_by_other_recipes:
+        update = await claim.build(user_id, tenant_id, user_context)
+        final_access_token_payload.update(update)
+
+    log_debug_message("createNewSession: Access token payload built")
+
+    output_transfer_method = config.get_token_transfer_method(
+        request, True, user_context
+    )
+    if output_transfer_method == "any":
+        auth_mode_header = get_auth_mode_from_header(request)
+        if auth_mode_header == "cookie":
+            output_transfer_method = auth_mode_header
+        else:
+            output_transfer_method = "header"
+
+    log_debug_message(
+        "createNewSession: using transfer method %s", output_transfer_method
+    )
+
+    if (
+        output_transfer_method == "cookie"
+        and config.get_cookie_same_site(request, user_context) == "none"
+        and not config.cookie_secure
+        and not (
+            (
+                app_info.top_level_api_domain == "localhost"
+                or is_an_ip_address(app_info.top_level_api_domain)
+            )
+            and (
+                app_info.get_top_level_website_domain(request, user_context)
+                == "localhost"
+                or is_an_ip_address(
+                    app_info.get_top_level_website_domain(request, user_context)
+                )
+            )
+        )
+    ):
+        # We can allow insecure cookie when both website & API domain are localhost or an IP
+        # When either of them is a different domain, API domain needs to have https and a secure cookie to work
+        raise Exception(
+            "Since your API and website domain are different, for sessions to work, please use https on your apiDomain and don't set cookieSecure to false."
+        )
+
+    disable_anti_csrf = output_transfer_method == "header"
+    session = await recipe_instance.recipe_implementation.create_new_session(
+        user_id,
+        final_access_token_payload,
+        session_data_in_database,
+        disable_anti_csrf,
+        tenant_id,
+        user_context=user_context,
+    )
+
+    log_debug_message("createNewSession: Session created in core built")
+
+    for transfer_method in available_token_transfer_methods:
+        if (
+            transfer_method != output_transfer_method
+            and get_token(request, "access", transfer_method) is not None
+        ):
+            session.response_mutators.append(
+                clear_session_mutator(config, transfer_method, request)
+            )
+
+    log_debug_message("createNewSession: Cleared old tokens")
+
+    await session.attach_to_request_response(
+        request, output_transfer_method, user_context
+    )
+    log_debug_message("createNewSession: Attached new tokens to res")
+
+    return session
+
+
+# In all cases: if sIdRefreshToken token exists (so it's a legacy session) we clear it.
+# Check http://localhost:3002/docs/contribute/decisions/session/0008 for further details and a table of expected behaviours
+
+
+async def refresh_session_in_request(
+    request: Any,
+    user_context: Dict[str, Any],
+    config: SessionConfig,
+    recipe_interface_impl: SessionRecipeInterface,
+) -> SessionContainer:
+    log_debug_message("refreshSession: Started")
+
+    response_mutators: List[ResponseMutator] = []
+
+    if not hasattr(request, "wrapper_used") or not request.wrapper_used:
+        request = FRAMEWORKS[
+            Supertokens.get_instance().app_info.framework
+        ].wrap_request(request)
+
+    log_debug_message("refreshSession: Wrapping done")
+    user_context = set_request_in_user_context_if_not_defined(user_context, request)
+
+    clear_session_cookies_from_older_cookie_domain(request, config, user_context)
+
+    refresh_tokens: Dict[TokenTransferMethod, Optional[str]] = {}
+
+    for transfer_method in available_token_transfer_methods:
+        refresh_tokens[transfer_method] = get_token(request, "refresh", transfer_method)
+        if refresh_tokens[transfer_method] is not None:
+            log_debug_message(
+                "refreshSession: got refresh token from %s", transfer_method
+            )
+
+    allowed_transfer_method = config.get_token_transfer_method(
+        request, False, user_context
+    )
+    log_debug_message(
+        "refreshSession: getTokenTransferMethod returned: %s",
+        allowed_transfer_method,
+    )
+
+    request_transfer_method: TokenTransferMethod
+    refresh_token: Optional[str]
+
+    if (allowed_transfer_method in ("any", "header")) and (
+        refresh_tokens.get("header")
+    ):
+        log_debug_message("refreshSession: using header transfer method")
+        request_transfer_method = "header"
+        refresh_token = refresh_tokens["header"]
+    elif (allowed_transfer_method in ("any", "cookie")) and (
+        refresh_tokens.get("cookie")
+    ):
+        log_debug_message("refreshSession: using cookie transfer method")
+        request_transfer_method = "cookie"
+        refresh_token = refresh_tokens["cookie"]
+    else:
+        # This token isn't handled by getToken/setToken to limit the scope of this legacy/migration code
+        if request.get_cookie(LEGACY_ID_REFRESH_TOKEN_COOKIE_NAME) is not None:
+            log_debug_message(
+                "refreshSession: cleared legacy id refresh token because refresh token was not found"
+            )
+            response_mutators.append(
+                set_cookie_response_mutator(
+                    config,
+                    LEGACY_ID_REFRESH_TOKEN_COOKIE_NAME,
+                    "",
+                    0,
+                    "access_token_path",
+                    request,
+                )
+            )
+
+        # We need to clear the access token cookie if
+        # - the refresh token is not found, and
+        # - the allowed_transfer_method is 'cookie' or 'any', and
+        # - an access token cookie exists (otherwise it'd be a no-op)
+        # See: https://github.com/supertokens/supertokens-node/issues/790
+        if (
+            allowed_transfer_method in ("cookie", "any")
+            and get_token(request, "access", "cookie") is not None
+        ):
+            log_debug_message(
+                "refreshSession: cleared all session tokens and returning UNAUTHORISED because refresh_token in request is None"
+            )
+
+            # We're clearing all session tokens instead of just the access token and then throwing an UNAUTHORISED
+            # error with `clear_tokens: False`. This approach avoids confusion and we don't want to retain session
+            # tokens on the client in any case if the refresh API is called without a refresh token but with an access token.
+            return raise_unauthorised_exception(
+                "Refresh token not found but access token is present. Clearing all tokens.",
+                clear_tokens=True,
+                response_mutators=response_mutators,
+            )
+
+        return raise_unauthorised_exception(
+            "Refresh token not found. Are you sending the refresh token in the request?",
+            clear_tokens=True,
+            response_mutators=response_mutators,
+        )
+
+    assert refresh_token is not None
+
+    disable_anti_csrf = request_transfer_method == "header"
+    anti_csrf_token = get_anti_csrf_header(request)
+
+    anti_csrf = config.anti_csrf_function_or_string
+    if callable(anti_csrf):
+        anti_csrf = anti_csrf(request, user_context)
+
+    if anti_csrf == "VIA_CUSTOM_HEADER" and not disable_anti_csrf:
+        if get_rid_from_header(request) is None:
+            log_debug_message(
+                "refreshSession: Returning UNAUTHORISED because anti-csrf token is undefined"
+            )
+            # see https://github.com/supertokens/supertokens-node/issues/141
+            raise_unauthorised_exception(
+                "anti-csrf check failed. Please pass 'rid: \"session\"' header in the request.",
+                clear_tokens=True,
+            )
+        disable_anti_csrf = True
+
+    session: Optional[SessionContainer] = None
+    try:
+        session = await recipe_interface_impl.refresh_session(
+            refresh_token, anti_csrf_token, disable_anti_csrf, user_context
+        )
+    except SuperTokensSessionError as e:
+        if isinstance(e, TokenTheftError) or (
+            isinstance(e, UnauthorisedError) and getattr(e, "clear_tokens") is True
+        ):
+            # We clear the LEGACY_ID_REFRESH_TOKEN_COOKIE_NAME here because we want to limit the scope of
+            # this legacy/migration code so the token clearing functions in the error handlers do not.
+            if request.get_cookie(LEGACY_ID_REFRESH_TOKEN_COOKIE_NAME) is not None:
+                log_debug_message(
+                    "refreshSession: cleared legacy id refresh token because refresh token was not found"
+                )
+                response_mutators.append(
+                    set_cookie_response_mutator(
+                        config,
+                        LEGACY_ID_REFRESH_TOKEN_COOKIE_NAME,
+                        "",
+                        0,
+                        "access_token_path",
+                        request,
+                    )
+                )
+
+        e.response_mutators.extend(response_mutators)
+        raise e
+
+    log_debug_message(
+        "refreshSession: Attaching refreshed session info as %s",
+        request_transfer_method,
+    )
+
+    # We clear the tokens in all token transfer methods we are not going to overwrite:
+    for transfer_method in available_token_transfer_methods:
+        if (
+            transfer_method != request_transfer_method
+            and refresh_tokens[transfer_method] is not None
+        ):
+            response_mutators.append(
+                clear_session_mutator(config, transfer_method, request)
+            )
+
+    await session.attach_to_request_response(
+        request, request_transfer_method, user_context
+    )
+    log_debug_message("refreshSession: Success!")
+
+    # This token isn't handled by getToken/setToken to limit the scope of this legacy/migration code
+    if request.get_cookie(LEGACY_ID_REFRESH_TOKEN_COOKIE_NAME) is not None:
+        log_debug_message(
+            "refreshSession: cleared legacy id refresh token after successful refresh"
+        )
+        response_mutators.append(
+            set_cookie_response_mutator(
+                config,
+                LEGACY_ID_REFRESH_TOKEN_COOKIE_NAME,
+                "",
+                0,
+                "access_token_path",
+                request,
+            )
+        )
+
+    session.response_mutators.extend(response_mutators)
+    return session
+
+
+
+
+
+
+
+

Functions

+
+
+async def create_new_session_in_request(request: Any, user_context: Dict[str, Any], recipe_instance: SessionRecipe, access_token_payload: Dict[str, Any], user_id: str, config: SessionConfig, app_info: AppInfo, session_data_in_database: Dict[str, Any], tenant_id: str) ‑> SessionContainer +
+
+
+
+ +Expand source code + +
async def create_new_session_in_request(
+    request: Any,
+    user_context: Dict[str, Any],
+    recipe_instance: SessionRecipe,
+    access_token_payload: Dict[str, Any],
+    user_id: str,
+    config: SessionConfig,
+    app_info: AppInfo,
+    session_data_in_database: Dict[str, Any],
+    tenant_id: str,
+) -> SessionContainer:
+    log_debug_message("createNewSession: Started")
+
+    # Handling framework specific request/response wrapping
+    if not hasattr(request, "wrapper_used") or not request.wrapper_used:
+        request = FRAMEWORKS[
+            Supertokens.get_instance().app_info.framework
+        ].wrap_request(request)
+
+    log_debug_message("createNewSession: Wrapping done")
+    user_context = set_request_in_user_context_if_not_defined(user_context, request)
+
+    claims_added_by_other_recipes = recipe_instance.get_claims_added_by_other_recipes()
+    issuer = (
+        app_info.api_domain.get_as_string_dangerous()
+        + app_info.api_base_path.get_as_string_dangerous()
+    )
+
+    final_access_token_payload = {**access_token_payload, "iss": issuer}
+
+    for prop in protected_props:
+        if prop in final_access_token_payload:
+            del final_access_token_payload[prop]
+
+    for claim in claims_added_by_other_recipes:
+        update = await claim.build(user_id, tenant_id, user_context)
+        final_access_token_payload.update(update)
+
+    log_debug_message("createNewSession: Access token payload built")
+
+    output_transfer_method = config.get_token_transfer_method(
+        request, True, user_context
+    )
+    if output_transfer_method == "any":
+        auth_mode_header = get_auth_mode_from_header(request)
+        if auth_mode_header == "cookie":
+            output_transfer_method = auth_mode_header
+        else:
+            output_transfer_method = "header"
+
+    log_debug_message(
+        "createNewSession: using transfer method %s", output_transfer_method
+    )
+
+    if (
+        output_transfer_method == "cookie"
+        and config.get_cookie_same_site(request, user_context) == "none"
+        and not config.cookie_secure
+        and not (
+            (
+                app_info.top_level_api_domain == "localhost"
+                or is_an_ip_address(app_info.top_level_api_domain)
+            )
+            and (
+                app_info.get_top_level_website_domain(request, user_context)
+                == "localhost"
+                or is_an_ip_address(
+                    app_info.get_top_level_website_domain(request, user_context)
+                )
+            )
+        )
+    ):
+        # We can allow insecure cookie when both website & API domain are localhost or an IP
+        # When either of them is a different domain, API domain needs to have https and a secure cookie to work
+        raise Exception(
+            "Since your API and website domain are different, for sessions to work, please use https on your apiDomain and don't set cookieSecure to false."
+        )
+
+    disable_anti_csrf = output_transfer_method == "header"
+    session = await recipe_instance.recipe_implementation.create_new_session(
+        user_id,
+        final_access_token_payload,
+        session_data_in_database,
+        disable_anti_csrf,
+        tenant_id,
+        user_context=user_context,
+    )
+
+    log_debug_message("createNewSession: Session created in core built")
+
+    for transfer_method in available_token_transfer_methods:
+        if (
+            transfer_method != output_transfer_method
+            and get_token(request, "access", transfer_method) is not None
+        ):
+            session.response_mutators.append(
+                clear_session_mutator(config, transfer_method, request)
+            )
+
+    log_debug_message("createNewSession: Cleared old tokens")
+
+    await session.attach_to_request_response(
+        request, output_transfer_method, user_context
+    )
+    log_debug_message("createNewSession: Attached new tokens to res")
+
+    return session
+
+
+
+async def get_session_from_request(request: Any, config: SessionConfig, recipe_interface_impl: SessionRecipeInterface, session_required: Optional[bool] = None, anti_csrf_check: Optional[bool] = None, check_database: Optional[bool] = None, override_global_claim_validators: Optional[Callable[[List[SessionClaimValidator], SessionContainer, Dict[str, Any]], MaybeAwaitable[List[SessionClaimValidator]]]] = None, user_context: Optional[Dict[str, Any]] = None) ‑> Optional[SessionContainer] +
+
+
+
+ +Expand source code + +
async def get_session_from_request(
+    request: Any,
+    config: SessionConfig,
+    recipe_interface_impl: SessionRecipeInterface,
+    session_required: Optional[bool] = None,
+    anti_csrf_check: Optional[bool] = None,
+    check_database: Optional[bool] = None,
+    override_global_claim_validators: Optional[
+        Callable[
+            [List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
+            MaybeAwaitable[List[SessionClaimValidator]],
+        ]
+    ] = None,
+    user_context: Optional[Dict[str, Any]] = None,
+) -> Optional[SessionContainer]:
+    log_debug_message("getSession: Started")
+
+    if not hasattr(request, "wrapper_used") or not request.wrapper_used:
+        request = FRAMEWORKS[
+            Supertokens.get_instance().app_info.framework
+        ].wrap_request(request)
+
+    log_debug_message("getSession: Wrapping done")
+
+    user_context = set_request_in_user_context_if_not_defined(user_context, request)
+
+    # This token isn't handled by getToken to limit the scope of this legacy/migration code
+    if request.get_cookie(LEGACY_ID_REFRESH_TOKEN_COOKIE_NAME) is not None:
+        log_debug_message(
+            "getSession: Throwing TRY_REFRESH_TOKEN because the request is using a legacy session"
+        )
+        # This could create a spike on refresh calls during the update of the backend SDK
+        return raise_try_refresh_token_exception(
+            "using legacy session, please call the refresh API"
+        )
+
+    session_optional = not session_required
+    log_debug_message("getSession: optional validation: %s", session_optional)
+
+    access_tokens: Dict[TokenTransferMethod, ParsedJWTInfo] = {}
+
+    # We check all token transfer methods for available access tokens
+    for transfer_method in available_token_transfer_methods:
+        token_string = get_token(request, "access", transfer_method)
+
+        if token_string is not None:
+            try:
+                info = parse_jwt_without_signature_verification(token_string)
+                validate_access_token_structure(info.payload, info.version)
+                log_debug_message(
+                    "getSession: got access token from %s", transfer_method
+                )
+                access_tokens[transfer_method] = info
+            except Exception:
+                log_debug_message(
+                    "getSession: ignoring token in %s, because it doesn't match our access token structure",
+                    transfer_method,
+                )
+
+    allowed_transfer_method = config.get_token_transfer_method(
+        request, False, user_context
+    )
+    request_transfer_method: Optional[TokenTransferMethod] = None
+    request_access_token: Union[ParsedJWTInfo, None] = None
+
+    if (allowed_transfer_method in ("any", "header")) and access_tokens.get(
+        "header"
+    ) is not None:
+        log_debug_message("getSession: using header transfer method")
+        request_transfer_method = "header"
+        request_access_token = access_tokens["header"]
+    elif (allowed_transfer_method in ("any", "cookie")) and access_tokens.get(
+        "cookie"
+    ) is not None:
+        log_debug_message("getSession: using cookie transfer method")
+
+        # If multiple access tokens exist in the request cookie, throw TRY_REFRESH_TOKEN.
+        # This prompts the client to call the refresh endpoint, clearing older_cookie_domain cookies (if set).
+        # ensuring outdated token payload isn't used.
+        if has_multiple_cookies_for_token_type(request, "access"):
+            log_debug_message(
+                "getSession: Throwing TRY_REFRESH_TOKEN because multiple access tokens are present in request cookies"
+            )
+            raise_try_refresh_token_exception(
+                "Multiple access tokens present in the request cookies."
+            )
+
+        request_transfer_method = "cookie"
+        request_access_token = access_tokens["cookie"]
+
+    anti_csrf_token = get_anti_csrf_header(request)
+    do_anti_csrf_check = anti_csrf_check
+
+    if do_anti_csrf_check is None:
+        do_anti_csrf_check = normalise_http_method(request.method()) != "get"
+    if request_transfer_method == "header":
+        do_anti_csrf_check = False
+    if request_access_token is None:
+        do_anti_csrf_check = False
+
+    if callable(config.anti_csrf_function_or_string):
+        anti_csrf = config.anti_csrf_function_or_string(request, user_context)
+    else:
+        anti_csrf = config.anti_csrf_function_or_string
+
+    if do_anti_csrf_check and anti_csrf == "VIA_CUSTOM_HEADER":
+        if anti_csrf == "VIA_CUSTOM_HEADER":
+            if get_rid_from_header(request) is None:
+                log_debug_message(
+                    "getSession: Returning TRY_REFRESH_TOKEN because custom header (rid) was not passed"
+                )
+                raise_try_refresh_token_exception(
+                    "anti-csrf check failed. Please pass 'rid: \"session\"' header in the request, or set doAntiCsrfCheck to false for this API"
+                )
+
+            log_debug_message("getSession: VIA_CUSTOM_HEADER anti-csrf check passed")
+            do_anti_csrf_check = False
+
+    log_debug_message("getSession: Value of antiCsrfToken is: %s", do_anti_csrf_check)
+
+    session = await recipe_interface_impl.get_session(
+        access_token=(
+            request_access_token.raw_token_string
+            if request_access_token is not None
+            else None
+        ),
+        anti_csrf_token=anti_csrf_token,
+        anti_csrf_check=do_anti_csrf_check,
+        session_required=session_required,
+        check_database=check_database,
+        override_global_claim_validators=override_global_claim_validators,
+        user_context=user_context,
+    )
+
+    if session is not None:
+        claim_validators = await get_required_claim_validators(
+            session, override_global_claim_validators, user_context
+        )
+        await session.assert_claims(claim_validators, user_context)
+
+        # request_transfer_method can only be None here if the user overriddes get_session
+        # to load the session by a custom method in that (very niche) case they also need to
+        # override how the session is attached to the response.
+        # In that scenario the transferMethod passed to attachToRequestResponse likely doesn't
+        # matter, still, we follow the general fallback logic
+
+        if request_transfer_method is not None:
+            final_transfer_method = request_transfer_method
+        elif allowed_transfer_method != "any":
+            final_transfer_method = allowed_transfer_method
+        else:
+            final_transfer_method = "header"
+
+        await session.attach_to_request_response(
+            request, final_transfer_method, user_context
+        )
+
+    return session
+
+
+
+async def refresh_session_in_request(request: Any, user_context: Dict[str, Any], config: SessionConfig, recipe_interface_impl: SessionRecipeInterface) ‑> SessionContainer +
+
+
+
+ +Expand source code + +
async def refresh_session_in_request(
+    request: Any,
+    user_context: Dict[str, Any],
+    config: SessionConfig,
+    recipe_interface_impl: SessionRecipeInterface,
+) -> SessionContainer:
+    log_debug_message("refreshSession: Started")
+
+    response_mutators: List[ResponseMutator] = []
+
+    if not hasattr(request, "wrapper_used") or not request.wrapper_used:
+        request = FRAMEWORKS[
+            Supertokens.get_instance().app_info.framework
+        ].wrap_request(request)
+
+    log_debug_message("refreshSession: Wrapping done")
+    user_context = set_request_in_user_context_if_not_defined(user_context, request)
+
+    clear_session_cookies_from_older_cookie_domain(request, config, user_context)
+
+    refresh_tokens: Dict[TokenTransferMethod, Optional[str]] = {}
+
+    for transfer_method in available_token_transfer_methods:
+        refresh_tokens[transfer_method] = get_token(request, "refresh", transfer_method)
+        if refresh_tokens[transfer_method] is not None:
+            log_debug_message(
+                "refreshSession: got refresh token from %s", transfer_method
+            )
+
+    allowed_transfer_method = config.get_token_transfer_method(
+        request, False, user_context
+    )
+    log_debug_message(
+        "refreshSession: getTokenTransferMethod returned: %s",
+        allowed_transfer_method,
+    )
+
+    request_transfer_method: TokenTransferMethod
+    refresh_token: Optional[str]
+
+    if (allowed_transfer_method in ("any", "header")) and (
+        refresh_tokens.get("header")
+    ):
+        log_debug_message("refreshSession: using header transfer method")
+        request_transfer_method = "header"
+        refresh_token = refresh_tokens["header"]
+    elif (allowed_transfer_method in ("any", "cookie")) and (
+        refresh_tokens.get("cookie")
+    ):
+        log_debug_message("refreshSession: using cookie transfer method")
+        request_transfer_method = "cookie"
+        refresh_token = refresh_tokens["cookie"]
+    else:
+        # This token isn't handled by getToken/setToken to limit the scope of this legacy/migration code
+        if request.get_cookie(LEGACY_ID_REFRESH_TOKEN_COOKIE_NAME) is not None:
+            log_debug_message(
+                "refreshSession: cleared legacy id refresh token because refresh token was not found"
+            )
+            response_mutators.append(
+                set_cookie_response_mutator(
+                    config,
+                    LEGACY_ID_REFRESH_TOKEN_COOKIE_NAME,
+                    "",
+                    0,
+                    "access_token_path",
+                    request,
+                )
+            )
+
+        # We need to clear the access token cookie if
+        # - the refresh token is not found, and
+        # - the allowed_transfer_method is 'cookie' or 'any', and
+        # - an access token cookie exists (otherwise it'd be a no-op)
+        # See: https://github.com/supertokens/supertokens-node/issues/790
+        if (
+            allowed_transfer_method in ("cookie", "any")
+            and get_token(request, "access", "cookie") is not None
+        ):
+            log_debug_message(
+                "refreshSession: cleared all session tokens and returning UNAUTHORISED because refresh_token in request is None"
+            )
+
+            # We're clearing all session tokens instead of just the access token and then throwing an UNAUTHORISED
+            # error with `clear_tokens: False`. This approach avoids confusion and we don't want to retain session
+            # tokens on the client in any case if the refresh API is called without a refresh token but with an access token.
+            return raise_unauthorised_exception(
+                "Refresh token not found but access token is present. Clearing all tokens.",
+                clear_tokens=True,
+                response_mutators=response_mutators,
+            )
+
+        return raise_unauthorised_exception(
+            "Refresh token not found. Are you sending the refresh token in the request?",
+            clear_tokens=True,
+            response_mutators=response_mutators,
+        )
+
+    assert refresh_token is not None
+
+    disable_anti_csrf = request_transfer_method == "header"
+    anti_csrf_token = get_anti_csrf_header(request)
+
+    anti_csrf = config.anti_csrf_function_or_string
+    if callable(anti_csrf):
+        anti_csrf = anti_csrf(request, user_context)
+
+    if anti_csrf == "VIA_CUSTOM_HEADER" and not disable_anti_csrf:
+        if get_rid_from_header(request) is None:
+            log_debug_message(
+                "refreshSession: Returning UNAUTHORISED because anti-csrf token is undefined"
+            )
+            # see https://github.com/supertokens/supertokens-node/issues/141
+            raise_unauthorised_exception(
+                "anti-csrf check failed. Please pass 'rid: \"session\"' header in the request.",
+                clear_tokens=True,
+            )
+        disable_anti_csrf = True
+
+    session: Optional[SessionContainer] = None
+    try:
+        session = await recipe_interface_impl.refresh_session(
+            refresh_token, anti_csrf_token, disable_anti_csrf, user_context
+        )
+    except SuperTokensSessionError as e:
+        if isinstance(e, TokenTheftError) or (
+            isinstance(e, UnauthorisedError) and getattr(e, "clear_tokens") is True
+        ):
+            # We clear the LEGACY_ID_REFRESH_TOKEN_COOKIE_NAME here because we want to limit the scope of
+            # this legacy/migration code so the token clearing functions in the error handlers do not.
+            if request.get_cookie(LEGACY_ID_REFRESH_TOKEN_COOKIE_NAME) is not None:
+                log_debug_message(
+                    "refreshSession: cleared legacy id refresh token because refresh token was not found"
+                )
+                response_mutators.append(
+                    set_cookie_response_mutator(
+                        config,
+                        LEGACY_ID_REFRESH_TOKEN_COOKIE_NAME,
+                        "",
+                        0,
+                        "access_token_path",
+                        request,
+                    )
+                )
+
+        e.response_mutators.extend(response_mutators)
+        raise e
+
+    log_debug_message(
+        "refreshSession: Attaching refreshed session info as %s",
+        request_transfer_method,
+    )
+
+    # We clear the tokens in all token transfer methods we are not going to overwrite:
+    for transfer_method in available_token_transfer_methods:
+        if (
+            transfer_method != request_transfer_method
+            and refresh_tokens[transfer_method] is not None
+        ):
+            response_mutators.append(
+                clear_session_mutator(config, transfer_method, request)
+            )
+
+    await session.attach_to_request_response(
+        request, request_transfer_method, user_context
+    )
+    log_debug_message("refreshSession: Success!")
+
+    # This token isn't handled by getToken/setToken to limit the scope of this legacy/migration code
+    if request.get_cookie(LEGACY_ID_REFRESH_TOKEN_COOKIE_NAME) is not None:
+        log_debug_message(
+            "refreshSession: cleared legacy id refresh token after successful refresh"
+        )
+        response_mutators.append(
+            set_cookie_response_mutator(
+                config,
+                LEGACY_ID_REFRESH_TOKEN_COOKIE_NAME,
+                "",
+                0,
+                "access_token_path",
+                request,
+            )
+        )
+
+    session.response_mutators.extend(response_mutators)
+    return session
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/session/syncio/index.html b/html/supertokens_python/recipe/session/syncio/index.html new file mode 100644 index 000000000..43d714068 --- /dev/null +++ b/html/supertokens_python/recipe/session/syncio/index.html @@ -0,0 +1,1033 @@ + + + + + + +supertokens_python.recipe.session.syncio API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.session.syncio

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+from typing import Any, Dict, List, Union, Callable, Optional, TypeVar
+
+from supertokens_python.async_to_sync_wrapper import sync
+from supertokens_python.recipe.openid.interfaces import (
+    GetOpenIdDiscoveryConfigurationResult,
+)
+from supertokens_python.types import MaybeAwaitable
+
+from ...jwt.interfaces import (
+    CreateJwtOkResult,
+    CreateJwtResultUnsupportedAlgorithm,
+    GetJWKSResult,
+)
+from ..interfaces import (
+    SessionContainer,
+    SessionInformationResult,
+    SessionClaimValidator,
+    SessionClaim,
+    JSONObject,
+    ClaimsValidationResult,
+    SessionDoesNotExistError,
+    GetClaimValueOkResult,
+)
+
+
+def create_new_session(
+    request: Any,
+    tenant_id: str,
+    user_id: str,
+    access_token_payload: Union[Dict[str, Any], None] = None,
+    session_data_in_database: Union[Dict[str, Any], None] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> SessionContainer:
+    from supertokens_python.recipe.session.asyncio import (
+        create_new_session as async_create_new_session,
+    )
+
+    return sync(
+        async_create_new_session(
+            tenant_id=tenant_id,
+            request=request,
+            user_id=user_id,
+            access_token_payload=access_token_payload,
+            session_data_in_database=session_data_in_database,
+            user_context=user_context,
+        )
+    )
+
+
+def create_new_session_without_request_response(
+    tenant_id: str,
+    user_id: str,
+    access_token_payload: Union[Dict[str, Any], None] = None,
+    session_data_in_database: Union[Dict[str, Any], None] = None,
+    disable_anti_csrf: bool = False,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> SessionContainer:
+    from supertokens_python.recipe.session.asyncio import (
+        create_new_session_without_request_response as async_create_new_session_without_request_response,
+    )
+
+    return sync(
+        async_create_new_session_without_request_response(
+            tenant_id,
+            user_id,
+            access_token_payload,
+            session_data_in_database,
+            disable_anti_csrf,
+            user_context,
+        )
+    )
+
+
+def get_session(
+    request: Any,
+    session_required: bool = True,
+    anti_csrf_check: Union[bool, None] = None,
+    check_database: Optional[bool] = None,
+    override_global_claim_validators: Optional[
+        Callable[
+            [List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
+            MaybeAwaitable[List[SessionClaimValidator]],
+        ]
+    ] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> Union[SessionContainer, None]:
+    from supertokens_python.recipe.session.asyncio import (
+        get_session as async_get_session,
+    )
+
+    return sync(
+        async_get_session(
+            request,
+            session_required,
+            anti_csrf_check,
+            check_database,
+            override_global_claim_validators,
+            user_context,
+        )
+    )
+
+
+def get_session_without_request_response(
+    access_token: str,
+    anti_csrf_token: Optional[str] = None,
+    anti_csrf_check: Optional[bool] = None,
+    session_required: Optional[bool] = None,
+    check_database: Optional[bool] = None,
+    override_global_claim_validators: Optional[
+        Callable[
+            [List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
+            MaybeAwaitable[List[SessionClaimValidator]],
+        ]
+    ] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> Optional[SessionContainer]:
+    from supertokens_python.recipe.session.asyncio import (
+        get_session_without_request_response as async_get_session_without_request_response,
+    )
+
+    return sync(
+        async_get_session_without_request_response(
+            access_token,
+            anti_csrf_token,
+            anti_csrf_check,
+            session_required,
+            check_database,
+            override_global_claim_validators,
+            user_context,
+        )
+    )
+
+
+def refresh_session(
+    request: Any, user_context: Union[None, Dict[str, Any]] = None
+) -> SessionContainer:
+    from supertokens_python.recipe.session.asyncio import (
+        refresh_session as async_refresh_session,
+    )
+
+    return sync(async_refresh_session(request, user_context))
+
+
+def refresh_session_without_request_response(
+    refresh_token: str,
+    disable_anti_csrf: bool = False,
+    anti_csrf_token: Optional[str] = None,
+    user_context: Optional[Dict[str, Any]] = None,
+) -> SessionContainer:
+    from supertokens_python.recipe.session.asyncio import (
+        refresh_session_without_request_response as async_refresh_session_without_request_response,
+    )
+
+    return sync(
+        async_refresh_session_without_request_response(
+            refresh_token,
+            disable_anti_csrf,
+            anti_csrf_token,
+            user_context,
+        )
+    )
+
+
+def revoke_session(
+    session_handle: str, user_context: Union[None, Dict[str, Any]] = None
+) -> bool:
+    from supertokens_python.recipe.session.asyncio import (
+        revoke_session as async_revoke_session,
+    )
+
+    return sync(async_revoke_session(session_handle, user_context))
+
+
+def revoke_all_sessions_for_user(
+    user_id: str,
+    tenant_id: Optional[str] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> List[str]:
+    from supertokens_python.recipe.session.asyncio import (
+        revoke_all_sessions_for_user as async_revoke_all_sessions_for_user,
+    )
+
+    return sync(async_revoke_all_sessions_for_user(user_id, tenant_id, user_context))
+
+
+def get_all_session_handles_for_user(
+    user_id: str,
+    tenant_id: Optional[str] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> List[str]:
+    from supertokens_python.recipe.session.asyncio import (
+        get_all_session_handles_for_user as async_get_all_session_handles_for_user,
+    )
+
+    return sync(
+        async_get_all_session_handles_for_user(user_id, tenant_id, user_context)
+    )
+
+
+def revoke_multiple_sessions(
+    session_handles: List[str], user_context: Union[None, Dict[str, Any]] = None
+) -> List[str]:
+    from supertokens_python.recipe.session.asyncio import (
+        revoke_multiple_sessions as async_revoke_multiple_sessions,
+    )
+
+    return sync(async_revoke_multiple_sessions(session_handles, user_context))
+
+
+def get_session_information(
+    session_handle: str, user_context: Union[None, Dict[str, Any]] = None
+) -> Union[SessionInformationResult, None]:
+    from supertokens_python.recipe.session.asyncio import (
+        get_session_information as async_get_session_information,
+    )
+
+    return sync(async_get_session_information(session_handle, user_context))
+
+
+def update_session_data_in_database(
+    session_handle: str,
+    new_session_data: Dict[str, Any],
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> bool:
+    from supertokens_python.recipe.session.asyncio import (
+        update_session_data_in_database as async_update_session_data_in_database,
+    )
+
+    return sync(
+        async_update_session_data_in_database(
+            session_handle, new_session_data, user_context
+        )
+    )
+
+
+def merge_into_access_token_payload(
+    session_handle: str,
+    new_access_token_payload: Dict[str, Any],
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> bool:
+    from supertokens_python.recipe.session.asyncio import (
+        merge_into_access_token_payload as async_merge_into_access_token_payload,
+    )
+
+    return sync(
+        async_merge_into_access_token_payload(
+            session_handle, new_access_token_payload, user_context
+        )
+    )
+
+
+def create_jwt(
+    payload: Dict[str, Any],
+    validity_seconds: Optional[int] = None,
+    use_static_signing_key: Optional[bool] = None,
+    user_context: Optional[Dict[str, Any]] = None,
+) -> Union[CreateJwtOkResult, CreateJwtResultUnsupportedAlgorithm]:
+    from supertokens_python.recipe.session.asyncio import create_jwt as async_create_jwt
+
+    return sync(
+        async_create_jwt(
+            payload, validity_seconds, use_static_signing_key, user_context
+        )
+    )
+
+
+def get_jwks(user_context: Optional[Dict[str, Any]] = None) -> GetJWKSResult:
+    from supertokens_python.recipe.session.asyncio import get_jwks as async_get_jwks
+
+    return sync(async_get_jwks(user_context))
+
+
+def get_open_id_discovery_configuration(
+    user_context: Optional[Dict[str, Any]] = None
+) -> GetOpenIdDiscoveryConfigurationResult:
+    from supertokens_python.recipe.session.asyncio import (
+        get_open_id_discovery_configuration as async_get_open_id_discovery_configuration,
+    )
+
+    return sync(async_get_open_id_discovery_configuration(user_context))
+
+
+_T = TypeVar("_T")
+
+
+def fetch_and_set_claim(
+    session_handle: str,
+    claim: SessionClaim[Any],
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> bool:
+    from supertokens_python.recipe.session.asyncio import (
+        fetch_and_set_claim as async_fetch_and_set_claim,
+    )
+
+    return sync(async_fetch_and_set_claim(session_handle, claim, user_context))
+
+
+def set_claim_value(
+    session_handle: str,
+    claim: SessionClaim[_T],
+    value: _T,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> bool:
+    from supertokens_python.recipe.session.asyncio import (
+        set_claim_value as async_set_claim_value,
+    )
+
+    return sync(async_set_claim_value(session_handle, claim, value, user_context))
+
+
+def get_claim_value(
+    session_handle: str,
+    claim: SessionClaim[_T],
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> Union[SessionDoesNotExistError, GetClaimValueOkResult[_T]]:
+    from supertokens_python.recipe.session.asyncio import (
+        get_claim_value as async_get_claim_value,
+    )
+
+    return sync(async_get_claim_value(session_handle, claim, user_context))
+
+
+def remove_claim(
+    session_handle: str,
+    claim: SessionClaim[Any],
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> bool:
+    from supertokens_python.recipe.session.asyncio import (
+        remove_claim as async_remove_claim,
+    )
+
+    return sync(async_remove_claim(session_handle, claim, user_context))
+
+
+def validate_claims_for_session_handle(
+    session_handle: str,
+    override_global_claim_validators: Optional[
+        Callable[
+            [List[SessionClaimValidator], SessionInformationResult, Dict[str, Any]],
+            MaybeAwaitable[List[SessionClaimValidator]],
+        ]
+    ] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> Union[SessionDoesNotExistError, ClaimsValidationResult]:
+    from supertokens_python.recipe.session.asyncio import (
+        validate_claims_for_session_handle as async_validate_claims_for_session_handle,
+    )
+
+    return sync(
+        async_validate_claims_for_session_handle(
+            session_handle, override_global_claim_validators, user_context
+        )
+    )
+
+
+def validate_claims_in_jwt_payload(
+    tenant_id: str,
+    user_id: str,
+    jwt_payload: JSONObject,
+    override_global_claim_validators: Optional[
+        Callable[
+            [List[SessionClaimValidator], str, Dict[str, Any]],
+            MaybeAwaitable[List[SessionClaimValidator]],
+        ]
+    ] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+):
+    from supertokens_python.recipe.session.asyncio import (
+        validate_claims_in_jwt_payload as async_validate_claims_in_jwt_payload,
+    )
+
+    return sync(
+        async_validate_claims_in_jwt_payload(
+            tenant_id,
+            user_id,
+            jwt_payload,
+            override_global_claim_validators,
+            user_context,
+        )
+    )
+
+
+
+
+
+
+
+

Functions

+
+
+def create_jwt(payload: Dict[str, Any], validity_seconds: Optional[int] = None, use_static_signing_key: Optional[bool] = None, user_context: Optional[Dict[str, Any]] = None) ‑> Union[CreateJwtOkResultCreateJwtResultUnsupportedAlgorithm] +
+
+
+
+ +Expand source code + +
def create_jwt(
+    payload: Dict[str, Any],
+    validity_seconds: Optional[int] = None,
+    use_static_signing_key: Optional[bool] = None,
+    user_context: Optional[Dict[str, Any]] = None,
+) -> Union[CreateJwtOkResult, CreateJwtResultUnsupportedAlgorithm]:
+    from supertokens_python.recipe.session.asyncio import create_jwt as async_create_jwt
+
+    return sync(
+        async_create_jwt(
+            payload, validity_seconds, use_static_signing_key, user_context
+        )
+    )
+
+
+
+def create_new_session(request: Any, tenant_id: str, user_id: str, access_token_payload: Union[Dict[str, Any], None] = None, session_data_in_database: Union[Dict[str, Any], None] = None, user_context: Union[None, Dict[str, Any]] = None) ‑> SessionContainer +
+
+
+
+ +Expand source code + +
def create_new_session(
+    request: Any,
+    tenant_id: str,
+    user_id: str,
+    access_token_payload: Union[Dict[str, Any], None] = None,
+    session_data_in_database: Union[Dict[str, Any], None] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> SessionContainer:
+    from supertokens_python.recipe.session.asyncio import (
+        create_new_session as async_create_new_session,
+    )
+
+    return sync(
+        async_create_new_session(
+            tenant_id=tenant_id,
+            request=request,
+            user_id=user_id,
+            access_token_payload=access_token_payload,
+            session_data_in_database=session_data_in_database,
+            user_context=user_context,
+        )
+    )
+
+
+
+def create_new_session_without_request_response(tenant_id: str, user_id: str, access_token_payload: Union[Dict[str, Any], None] = None, session_data_in_database: Union[Dict[str, Any], None] = None, disable_anti_csrf: bool = False, user_context: Union[None, Dict[str, Any]] = None) ‑> SessionContainer +
+
+
+
+ +Expand source code + +
def create_new_session_without_request_response(
+    tenant_id: str,
+    user_id: str,
+    access_token_payload: Union[Dict[str, Any], None] = None,
+    session_data_in_database: Union[Dict[str, Any], None] = None,
+    disable_anti_csrf: bool = False,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> SessionContainer:
+    from supertokens_python.recipe.session.asyncio import (
+        create_new_session_without_request_response as async_create_new_session_without_request_response,
+    )
+
+    return sync(
+        async_create_new_session_without_request_response(
+            tenant_id,
+            user_id,
+            access_token_payload,
+            session_data_in_database,
+            disable_anti_csrf,
+            user_context,
+        )
+    )
+
+
+
+def fetch_and_set_claim(session_handle: str, claim: SessionClaim[Any], user_context: Union[None, Dict[str, Any]] = None) ‑> bool +
+
+
+
+ +Expand source code + +
def fetch_and_set_claim(
+    session_handle: str,
+    claim: SessionClaim[Any],
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> bool:
+    from supertokens_python.recipe.session.asyncio import (
+        fetch_and_set_claim as async_fetch_and_set_claim,
+    )
+
+    return sync(async_fetch_and_set_claim(session_handle, claim, user_context))
+
+
+
+def get_all_session_handles_for_user(user_id: str, tenant_id: Optional[str] = None, user_context: Union[None, Dict[str, Any]] = None) ‑> List[str] +
+
+
+
+ +Expand source code + +
def get_all_session_handles_for_user(
+    user_id: str,
+    tenant_id: Optional[str] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> List[str]:
+    from supertokens_python.recipe.session.asyncio import (
+        get_all_session_handles_for_user as async_get_all_session_handles_for_user,
+    )
+
+    return sync(
+        async_get_all_session_handles_for_user(user_id, tenant_id, user_context)
+    )
+
+
+
+def get_claim_value(session_handle: str, claim: SessionClaim[_T], user_context: Union[None, Dict[str, Any]] = None) ‑> Union[SessionDoesNotExistErrorGetClaimValueOkResult[~_T]] +
+
+
+
+ +Expand source code + +
def get_claim_value(
+    session_handle: str,
+    claim: SessionClaim[_T],
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> Union[SessionDoesNotExistError, GetClaimValueOkResult[_T]]:
+    from supertokens_python.recipe.session.asyncio import (
+        get_claim_value as async_get_claim_value,
+    )
+
+    return sync(async_get_claim_value(session_handle, claim, user_context))
+
+
+
+def get_jwks(user_context: Optional[Dict[str, Any]] = None) ‑> GetJWKSResult +
+
+
+
+ +Expand source code + +
def get_jwks(user_context: Optional[Dict[str, Any]] = None) -> GetJWKSResult:
+    from supertokens_python.recipe.session.asyncio import get_jwks as async_get_jwks
+
+    return sync(async_get_jwks(user_context))
+
+
+
+def get_open_id_discovery_configuration(user_context: Optional[Dict[str, Any]] = None) ‑> GetOpenIdDiscoveryConfigurationResult +
+
+
+
+ +Expand source code + +
def get_open_id_discovery_configuration(
+    user_context: Optional[Dict[str, Any]] = None
+) -> GetOpenIdDiscoveryConfigurationResult:
+    from supertokens_python.recipe.session.asyncio import (
+        get_open_id_discovery_configuration as async_get_open_id_discovery_configuration,
+    )
+
+    return sync(async_get_open_id_discovery_configuration(user_context))
+
+
+
+def get_session(request: Any, session_required: bool = True, anti_csrf_check: Union[bool, None] = None, check_database: Optional[bool] = None, override_global_claim_validators: Optional[Callable[[List[SessionClaimValidator], SessionContainer, Dict[str, Any]], MaybeAwaitable[List[SessionClaimValidator]]]] = None, user_context: Union[None, Dict[str, Any]] = None) ‑> Optional[SessionContainer] +
+
+
+
+ +Expand source code + +
def get_session(
+    request: Any,
+    session_required: bool = True,
+    anti_csrf_check: Union[bool, None] = None,
+    check_database: Optional[bool] = None,
+    override_global_claim_validators: Optional[
+        Callable[
+            [List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
+            MaybeAwaitable[List[SessionClaimValidator]],
+        ]
+    ] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> Union[SessionContainer, None]:
+    from supertokens_python.recipe.session.asyncio import (
+        get_session as async_get_session,
+    )
+
+    return sync(
+        async_get_session(
+            request,
+            session_required,
+            anti_csrf_check,
+            check_database,
+            override_global_claim_validators,
+            user_context,
+        )
+    )
+
+
+
+def get_session_information(session_handle: str, user_context: Union[None, Dict[str, Any]] = None) ‑> Optional[SessionInformationResult] +
+
+
+
+ +Expand source code + +
def get_session_information(
+    session_handle: str, user_context: Union[None, Dict[str, Any]] = None
+) -> Union[SessionInformationResult, None]:
+    from supertokens_python.recipe.session.asyncio import (
+        get_session_information as async_get_session_information,
+    )
+
+    return sync(async_get_session_information(session_handle, user_context))
+
+
+
+def get_session_without_request_response(access_token: str, anti_csrf_token: Optional[str] = None, anti_csrf_check: Optional[bool] = None, session_required: Optional[bool] = None, check_database: Optional[bool] = None, override_global_claim_validators: Optional[Callable[[List[SessionClaimValidator], SessionContainer, Dict[str, Any]], MaybeAwaitable[List[SessionClaimValidator]]]] = None, user_context: Union[None, Dict[str, Any]] = None) ‑> Optional[SessionContainer] +
+
+
+
+ +Expand source code + +
def get_session_without_request_response(
+    access_token: str,
+    anti_csrf_token: Optional[str] = None,
+    anti_csrf_check: Optional[bool] = None,
+    session_required: Optional[bool] = None,
+    check_database: Optional[bool] = None,
+    override_global_claim_validators: Optional[
+        Callable[
+            [List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
+            MaybeAwaitable[List[SessionClaimValidator]],
+        ]
+    ] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> Optional[SessionContainer]:
+    from supertokens_python.recipe.session.asyncio import (
+        get_session_without_request_response as async_get_session_without_request_response,
+    )
+
+    return sync(
+        async_get_session_without_request_response(
+            access_token,
+            anti_csrf_token,
+            anti_csrf_check,
+            session_required,
+            check_database,
+            override_global_claim_validators,
+            user_context,
+        )
+    )
+
+
+
+def merge_into_access_token_payload(session_handle: str, new_access_token_payload: Dict[str, Any], user_context: Union[None, Dict[str, Any]] = None) ‑> bool +
+
+
+
+ +Expand source code + +
def merge_into_access_token_payload(
+    session_handle: str,
+    new_access_token_payload: Dict[str, Any],
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> bool:
+    from supertokens_python.recipe.session.asyncio import (
+        merge_into_access_token_payload as async_merge_into_access_token_payload,
+    )
+
+    return sync(
+        async_merge_into_access_token_payload(
+            session_handle, new_access_token_payload, user_context
+        )
+    )
+
+
+
+def refresh_session(request: Any, user_context: Union[None, Dict[str, Any]] = None) ‑> SessionContainer +
+
+
+
+ +Expand source code + +
def refresh_session(
+    request: Any, user_context: Union[None, Dict[str, Any]] = None
+) -> SessionContainer:
+    from supertokens_python.recipe.session.asyncio import (
+        refresh_session as async_refresh_session,
+    )
+
+    return sync(async_refresh_session(request, user_context))
+
+
+
+def refresh_session_without_request_response(refresh_token: str, disable_anti_csrf: bool = False, anti_csrf_token: Optional[str] = None, user_context: Optional[Dict[str, Any]] = None) ‑> SessionContainer +
+
+
+
+ +Expand source code + +
def refresh_session_without_request_response(
+    refresh_token: str,
+    disable_anti_csrf: bool = False,
+    anti_csrf_token: Optional[str] = None,
+    user_context: Optional[Dict[str, Any]] = None,
+) -> SessionContainer:
+    from supertokens_python.recipe.session.asyncio import (
+        refresh_session_without_request_response as async_refresh_session_without_request_response,
+    )
+
+    return sync(
+        async_refresh_session_without_request_response(
+            refresh_token,
+            disable_anti_csrf,
+            anti_csrf_token,
+            user_context,
+        )
+    )
+
+
+
+def remove_claim(session_handle: str, claim: SessionClaim[Any], user_context: Union[None, Dict[str, Any]] = None) ‑> bool +
+
+
+
+ +Expand source code + +
def remove_claim(
+    session_handle: str,
+    claim: SessionClaim[Any],
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> bool:
+    from supertokens_python.recipe.session.asyncio import (
+        remove_claim as async_remove_claim,
+    )
+
+    return sync(async_remove_claim(session_handle, claim, user_context))
+
+
+
+def revoke_all_sessions_for_user(user_id: str, tenant_id: Optional[str] = None, user_context: Union[None, Dict[str, Any]] = None) ‑> List[str] +
+
+
+
+ +Expand source code + +
def revoke_all_sessions_for_user(
+    user_id: str,
+    tenant_id: Optional[str] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> List[str]:
+    from supertokens_python.recipe.session.asyncio import (
+        revoke_all_sessions_for_user as async_revoke_all_sessions_for_user,
+    )
+
+    return sync(async_revoke_all_sessions_for_user(user_id, tenant_id, user_context))
+
+
+
+def revoke_multiple_sessions(session_handles: List[str], user_context: Union[None, Dict[str, Any]] = None) ‑> List[str] +
+
+
+
+ +Expand source code + +
def revoke_multiple_sessions(
+    session_handles: List[str], user_context: Union[None, Dict[str, Any]] = None
+) -> List[str]:
+    from supertokens_python.recipe.session.asyncio import (
+        revoke_multiple_sessions as async_revoke_multiple_sessions,
+    )
+
+    return sync(async_revoke_multiple_sessions(session_handles, user_context))
+
+
+
+def revoke_session(session_handle: str, user_context: Union[None, Dict[str, Any]] = None) ‑> bool +
+
+
+
+ +Expand source code + +
def revoke_session(
+    session_handle: str, user_context: Union[None, Dict[str, Any]] = None
+) -> bool:
+    from supertokens_python.recipe.session.asyncio import (
+        revoke_session as async_revoke_session,
+    )
+
+    return sync(async_revoke_session(session_handle, user_context))
+
+
+
+def set_claim_value(session_handle: str, claim: SessionClaim[_T], value: _T, user_context: Union[None, Dict[str, Any]] = None) ‑> bool +
+
+
+
+ +Expand source code + +
def set_claim_value(
+    session_handle: str,
+    claim: SessionClaim[_T],
+    value: _T,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> bool:
+    from supertokens_python.recipe.session.asyncio import (
+        set_claim_value as async_set_claim_value,
+    )
+
+    return sync(async_set_claim_value(session_handle, claim, value, user_context))
+
+
+
+def update_session_data_in_database(session_handle: str, new_session_data: Dict[str, Any], user_context: Union[None, Dict[str, Any]] = None) ‑> bool +
+
+
+
+ +Expand source code + +
def update_session_data_in_database(
+    session_handle: str,
+    new_session_data: Dict[str, Any],
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> bool:
+    from supertokens_python.recipe.session.asyncio import (
+        update_session_data_in_database as async_update_session_data_in_database,
+    )
+
+    return sync(
+        async_update_session_data_in_database(
+            session_handle, new_session_data, user_context
+        )
+    )
+
+
+
+def validate_claims_for_session_handle(session_handle: str, override_global_claim_validators: Optional[Callable[[List[SessionClaimValidator], SessionInformationResult, Dict[str, Any]], MaybeAwaitable[List[SessionClaimValidator]]]] = None, user_context: Union[None, Dict[str, Any]] = None) ‑> Union[SessionDoesNotExistErrorClaimsValidationResult] +
+
+
+
+ +Expand source code + +
def validate_claims_for_session_handle(
+    session_handle: str,
+    override_global_claim_validators: Optional[
+        Callable[
+            [List[SessionClaimValidator], SessionInformationResult, Dict[str, Any]],
+            MaybeAwaitable[List[SessionClaimValidator]],
+        ]
+    ] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> Union[SessionDoesNotExistError, ClaimsValidationResult]:
+    from supertokens_python.recipe.session.asyncio import (
+        validate_claims_for_session_handle as async_validate_claims_for_session_handle,
+    )
+
+    return sync(
+        async_validate_claims_for_session_handle(
+            session_handle, override_global_claim_validators, user_context
+        )
+    )
+
+
+
+def validate_claims_in_jwt_payload(tenant_id: str, user_id: str, jwt_payload: JSONObject, override_global_claim_validators: Optional[Callable[[List[SessionClaimValidator], str, Dict[str, Any]], MaybeAwaitable[List[SessionClaimValidator]]]] = None, user_context: Union[None, Dict[str, Any]] = None) +
+
+
+
+ +Expand source code + +
def validate_claims_in_jwt_payload(
+    tenant_id: str,
+    user_id: str,
+    jwt_payload: JSONObject,
+    override_global_claim_validators: Optional[
+        Callable[
+            [List[SessionClaimValidator], str, Dict[str, Any]],
+            MaybeAwaitable[List[SessionClaimValidator]],
+        ]
+    ] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+):
+    from supertokens_python.recipe.session.asyncio import (
+        validate_claims_in_jwt_payload as async_validate_claims_in_jwt_payload,
+    )
+
+    return sync(
+        async_validate_claims_in_jwt_payload(
+            tenant_id,
+            user_id,
+            jwt_payload,
+            override_global_claim_validators,
+            user_context,
+        )
+    )
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/session/utils.html b/html/supertokens_python/recipe/session/utils.html new file mode 100644 index 000000000..cb890cf47 --- /dev/null +++ b/html/supertokens_python/recipe/session/utils.html @@ -0,0 +1,1556 @@ + + + + + + +supertokens_python.recipe.session.utils API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.session.utils

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+import json
+from typing import TYPE_CHECKING, Any, Awaitable, Callable, Dict, List, Optional, Union
+from urllib.parse import urlparse
+
+from typing_extensions import Literal
+
+from supertokens_python.exceptions import raise_general_exception
+from supertokens_python.framework import BaseResponse
+from supertokens_python.normalised_url_path import NormalisedURLPath
+from supertokens_python.utils import (
+    is_an_ip_address,
+    resolve,
+    send_200_response,
+    send_non_200_response,
+    send_non_200_response_with_message,
+)
+
+from ...types import MaybeAwaitable
+from .constants import AUTH_MODE_HEADER_KEY, SESSION_REFRESH
+from .exceptions import ClaimValidationError
+
+if TYPE_CHECKING:
+    from supertokens_python.framework import BaseRequest
+    from supertokens_python.supertokens import AppInfo
+    from supertokens_python.recipe.openid import (
+        InputOverrideConfig as OpenIdInputOverrideConfig,
+    )
+
+    from .interfaces import (
+        APIInterface,
+        RecipeInterface,
+        SessionContainer,
+        SessionClaimValidator,
+    )
+    from .recipe import SessionRecipe
+
+from supertokens_python.logger import log_debug_message
+
+
+def normalise_session_scope(session_scope: str) -> str:
+    def helper(scope: str) -> str:
+        scope = scope.strip()
+
+        if scope.startswith("."):
+            scope = scope[1:]
+
+        if (not scope.startswith("https://")) and (not scope.startswith("http://")):
+            scope = "http://" + scope
+
+        try:
+            url_obj = urlparse(scope)
+            if url_obj.hostname is None:
+                raise Exception("Should not come here")
+            scope = url_obj.hostname
+
+            return scope
+        except Exception:
+            raise_general_exception("Please provide a valid session_scope")
+
+    no_dot_normalised = helper(session_scope)
+    if no_dot_normalised == "localhost" or is_an_ip_address(no_dot_normalised):
+        return no_dot_normalised
+
+    if session_scope.startswith("."):
+        return "." + no_dot_normalised
+
+    return no_dot_normalised
+
+
+def normalise_same_site(same_site: str) -> Literal["strict", "lax", "none"]:
+    same_site = same_site.strip()
+    same_site = same_site.lower()
+    allowed_values = {"strict", "lax", "none"}
+    if same_site not in allowed_values:
+        raise Exception('cookie same site must be one of "strict", "lax", or "none"')
+    return same_site  # type: ignore
+
+
+def get_url_scheme(url: str) -> str:
+    url_obj = urlparse(url)
+    return url_obj.scheme
+
+
+class ErrorHandlers:
+    def __init__(
+        self,
+        on_token_theft_detected: Callable[
+            [BaseRequest, str, str, BaseResponse],
+            Union[BaseResponse, Awaitable[BaseResponse]],
+        ],
+        on_try_refresh_token: Callable[
+            [BaseRequest, str, BaseResponse],
+            Union[BaseResponse, Awaitable[BaseResponse]],
+        ],
+        on_unauthorised: Callable[
+            [BaseRequest, str, BaseResponse],
+            Union[BaseResponse, Awaitable[BaseResponse]],
+        ],
+        on_invalid_claim: Callable[
+            [BaseRequest, List[ClaimValidationError], BaseResponse],
+            Union[BaseResponse, Awaitable[BaseResponse]],
+        ],
+        on_clear_duplicate_session_cookies: Callable[
+            [BaseRequest, str, BaseResponse],
+            Union[BaseResponse, Awaitable[BaseResponse]],
+        ],
+    ):
+        self.__on_token_theft_detected = on_token_theft_detected
+        self.__on_try_refresh_token = on_try_refresh_token
+        self.__on_unauthorised = on_unauthorised
+        self.__on_invalid_claim = on_invalid_claim
+        self.__on_clear_duplicate_session_cookies = on_clear_duplicate_session_cookies
+
+    async def on_token_theft_detected(
+        self,
+        request: BaseRequest,
+        session_handle: str,
+        user_id: str,
+        response: BaseResponse,
+    ) -> BaseResponse:
+        return await resolve(
+            self.__on_token_theft_detected(request, session_handle, user_id, response)
+        )
+
+    async def on_try_refresh_token(
+        self, request: BaseRequest, message: str, response: BaseResponse
+    ):
+        result = await resolve(self.__on_try_refresh_token(request, message, response))
+        return result
+
+    async def on_unauthorised(
+        self,
+        request: BaseRequest,
+        message: str,
+        response: BaseResponse,
+    ):
+        return await resolve(self.__on_unauthorised(request, message, response))
+
+    async def on_invalid_claim(
+        self,
+        recipe: SessionRecipe,
+        request: BaseRequest,
+        claim_validation_errors: List[ClaimValidationError],
+        response: BaseResponse,
+    ):
+        _ = recipe
+        result = await resolve(
+            self.__on_invalid_claim(request, claim_validation_errors, response)
+        )
+        return result
+
+    async def on_clear_duplicate_session_cookies(
+        self,
+        request: BaseRequest,
+        message: str,
+        response: BaseResponse,
+    ):
+        result = await resolve(
+            self.__on_clear_duplicate_session_cookies(request, message, response)
+        )
+        return result
+
+
+class InputErrorHandlers(ErrorHandlers):
+    def __init__(
+        self,
+        on_token_theft_detected: Union[
+            None,
+            Callable[
+                [BaseRequest, str, str, BaseResponse],
+                Union[BaseResponse, Awaitable[BaseResponse]],
+            ],
+        ] = None,
+        on_try_refresh_token: Union[
+            None,
+            Callable[
+                [BaseRequest, str, BaseResponse],
+                Union[BaseResponse, Awaitable[BaseResponse]],
+            ],
+        ] = None,
+        on_unauthorised: Union[
+            Callable[
+                [BaseRequest, str, BaseResponse],
+                Union[BaseResponse, Awaitable[BaseResponse]],
+            ],
+            None,
+        ] = None,
+        on_invalid_claim: Union[
+            Callable[
+                [BaseRequest, List[ClaimValidationError], BaseResponse],
+                Union[BaseResponse, Awaitable[BaseResponse]],
+            ],
+            None,
+        ] = None,
+        on_clear_duplicate_session_cookies: Union[
+            None,
+            Callable[
+                [BaseRequest, str, BaseResponse],
+                Union[BaseResponse, Awaitable[BaseResponse]],
+            ],
+        ] = None,
+    ):
+        if on_token_theft_detected is None:
+            on_token_theft_detected = default_token_theft_detected_callback
+        if on_try_refresh_token is None:
+            on_try_refresh_token = default_try_refresh_token_callback
+        if on_unauthorised is None:
+            on_unauthorised = default_unauthorised_callback
+        if on_invalid_claim is None:
+            on_invalid_claim = default_invalid_claim_callback
+        if on_clear_duplicate_session_cookies is None:
+            on_clear_duplicate_session_cookies = (
+                default_clear_duplicate_session_cookies_callback
+            )
+        super().__init__(
+            on_token_theft_detected,
+            on_try_refresh_token,
+            on_unauthorised,
+            on_invalid_claim,
+            on_clear_duplicate_session_cookies,
+        )
+
+
+async def default_unauthorised_callback(
+    _: BaseRequest, __: str, response: BaseResponse
+) -> BaseResponse:
+    from .recipe import SessionRecipe
+
+    return send_non_200_response_with_message(
+        "unauthorised",
+        SessionRecipe.get_instance().config.session_expired_status_code,
+        response,
+    )
+
+
+async def default_try_refresh_token_callback(
+    _: BaseRequest, __: str, response: BaseResponse
+) -> BaseResponse:
+    from .recipe import SessionRecipe
+
+    return send_non_200_response_with_message(
+        "try refresh token",
+        SessionRecipe.get_instance().config.session_expired_status_code,
+        response,
+    )
+
+
+async def default_token_theft_detected_callback(
+    _: BaseRequest, session_handle: str, __: str, response: BaseResponse
+) -> BaseResponse:
+    from .recipe import SessionRecipe
+
+    await SessionRecipe.get_instance().recipe_implementation.revoke_session(
+        session_handle, {}
+    )
+    return send_non_200_response_with_message(
+        "token theft detected",
+        SessionRecipe.get_instance().config.session_expired_status_code,
+        response,
+    )
+
+
+async def default_invalid_claim_callback(
+    _: BaseRequest,
+    claim_validation_errors: List[ClaimValidationError],
+    response: BaseResponse,
+) -> BaseResponse:
+    from .recipe import SessionRecipe
+
+    return send_non_200_response(
+        {
+            "message": "invalid claim",
+            "claimValidationErrors": [err.to_json() for err in claim_validation_errors],
+        },
+        SessionRecipe.get_instance().config.invalid_claim_status_code,
+        response,
+    )
+
+
+async def default_clear_duplicate_session_cookies_callback(
+    _: BaseRequest, message: str, response: BaseResponse
+) -> BaseResponse:
+    # This error occurs in the `refresh_post` API when multiple session
+    # cookies are found in the request and the user has set `older_cookie_domain`.
+    # We remove session cookies from the older_cookie_domain. The response must return `200 OK`
+    # to avoid logging out the user, allowing the session to continue with the valid cookie.
+    return send_200_response({"message": message}, response)
+
+
+def get_auth_mode_from_header(request: BaseRequest) -> Optional[str]:
+    auth_mode = request.get_header(AUTH_MODE_HEADER_KEY)
+    if auth_mode is None:
+        return None
+    return auth_mode.lower()
+
+
+def get_token_transfer_method_default(
+    req: BaseRequest,
+    for_create_new_session: bool,
+    user_context: Dict[str, Any],
+):
+    _ = user_context
+
+    # We allow fallback (checking headers then cookies) by default when validating
+    if not for_create_new_session:
+        return "any"
+
+    auth_mode = get_auth_mode_from_header(req)
+    if auth_mode in ("header", "cookie"):
+        return auth_mode
+
+    return "any"
+
+
+class InputOverrideConfig:
+    def __init__(
+        self,
+        functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None,
+        apis: Union[Callable[[APIInterface], APIInterface], None] = None,
+        openid_feature: Union[OpenIdInputOverrideConfig, None] = None,
+    ):
+        self.functions = functions
+        self.apis = apis
+        self.openid_feature = openid_feature
+
+
+class OverrideConfig:
+    def __init__(
+        self,
+        functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None,
+        apis: Union[Callable[[APIInterface], APIInterface], None] = None,
+    ):
+        self.functions = functions
+        self.apis = apis
+
+
+TokenType = Literal["access", "refresh"]
+TokenTransferMethod = Literal["cookie", "header"]
+
+
+class SessionConfig:
+    def __init__(
+        self,
+        refresh_token_path: NormalisedURLPath,
+        cookie_domain: Union[None, str],
+        older_cookie_domain: Union[None, str],
+        get_cookie_same_site: Callable[
+            [Optional[BaseRequest], Dict[str, Any]],
+            Literal["lax", "strict", "none"],
+        ],
+        cookie_secure: bool,
+        session_expired_status_code: int,
+        error_handlers: ErrorHandlers,
+        anti_csrf_function_or_string: Union[
+            Callable[
+                [Optional[BaseRequest], Dict[str, Any]],
+                Literal["VIA_CUSTOM_HEADER", "NONE"],
+            ],
+            Literal["VIA_CUSTOM_HEADER", "NONE", "VIA_TOKEN"],
+        ],
+        get_token_transfer_method: Callable[
+            [BaseRequest, bool, Dict[str, Any]],
+            Union[TokenTransferMethod, Literal["any"]],
+        ],
+        override: OverrideConfig,
+        framework: str,
+        mode: str,
+        invalid_claim_status_code: int,
+        use_dynamic_access_token_signing_key: bool,
+        expose_access_token_to_frontend_in_cookie_based_auth: bool,
+        jwks_refresh_interval_sec: int,
+    ):
+        self.session_expired_status_code = session_expired_status_code
+        self.invalid_claim_status_code = invalid_claim_status_code
+        self.use_dynamic_access_token_signing_key = use_dynamic_access_token_signing_key
+        self.expose_access_token_to_frontend_in_cookie_based_auth = (
+            expose_access_token_to_frontend_in_cookie_based_auth
+        )
+
+        self.refresh_token_path = refresh_token_path
+        self.cookie_domain = cookie_domain
+        self.older_cookie_domain = older_cookie_domain
+        self.get_cookie_same_site = get_cookie_same_site
+        self.cookie_secure = cookie_secure
+        self.error_handlers = error_handlers
+        self.anti_csrf_function_or_string = anti_csrf_function_or_string
+        self.get_token_transfer_method = get_token_transfer_method
+        self.override = override
+        self.framework = framework
+        self.mode = mode
+        self.jwks_refresh_interval_sec = jwks_refresh_interval_sec
+
+
+def validate_and_normalise_user_input(
+    app_info: AppInfo,
+    cookie_domain: Union[str, None] = None,
+    older_cookie_domain: Union[str, None] = None,
+    cookie_secure: Union[bool, None] = None,
+    cookie_same_site: Union[Literal["lax", "strict", "none"], None] = None,
+    session_expired_status_code: Union[int, None] = None,
+    anti_csrf: Union[Literal["VIA_TOKEN", "VIA_CUSTOM_HEADER", "NONE"], None] = None,
+    get_token_transfer_method: Union[
+        Callable[
+            [BaseRequest, bool, Dict[str, Any]],
+            Union[TokenTransferMethod, Literal["any"]],
+        ],
+        None,
+    ] = None,
+    error_handlers: Union[ErrorHandlers, None] = None,
+    override: Union[InputOverrideConfig, None] = None,
+    invalid_claim_status_code: Union[int, None] = None,
+    use_dynamic_access_token_signing_key: Union[bool, None] = None,
+    expose_access_token_to_frontend_in_cookie_based_auth: Union[bool, None] = None,
+    jwks_refresh_interval_sec: Union[int, None] = None,
+):
+    _ = cookie_same_site  # we have this otherwise pylint complains that cookie_same_site is unused, but it is being used in the get_cookie_same_site function.
+    if anti_csrf not in {"VIA_TOKEN", "VIA_CUSTOM_HEADER", "NONE", None}:
+        raise ValueError(
+            "anti_csrf must be one of VIA_TOKEN, VIA_CUSTOM_HEADER, NONE or None"
+        )
+
+    if error_handlers is not None and not isinstance(error_handlers, ErrorHandlers):  # type: ignore
+        raise ValueError("error_handlers must be an instance of ErrorHandlers or None")
+
+    if override is not None and not isinstance(override, InputOverrideConfig):  # type: ignore
+        raise ValueError("override must be an instance of InputOverrideConfig or None")
+
+    cookie_domain = (
+        normalise_session_scope(cookie_domain) if cookie_domain is not None else None
+    )
+
+    older_cookie_domain = (
+        older_cookie_domain
+        if older_cookie_domain is None or older_cookie_domain == ""
+        else normalise_session_scope(older_cookie_domain)
+    )
+
+    cookie_secure = (
+        cookie_secure
+        if cookie_secure is not None
+        else app_info.api_domain.get_as_string_dangerous().startswith("https")
+    )
+
+    session_expired_status_code = (
+        session_expired_status_code if session_expired_status_code is not None else 401
+    )
+
+    invalid_claim_status_code = (
+        invalid_claim_status_code if invalid_claim_status_code is not None else 403
+    )
+
+    if session_expired_status_code == invalid_claim_status_code:
+        raise Exception(
+            "session_expired_status_code and invalid_claim_status_code cannot be the same "
+            f"({invalid_claim_status_code})"
+        )
+
+    if get_token_transfer_method is None:
+        get_token_transfer_method = get_token_transfer_method_default
+
+    if error_handlers is None:
+        error_handlers = InputErrorHandlers()
+
+    if override is None:
+        override = InputOverrideConfig()
+
+    if use_dynamic_access_token_signing_key is None:
+        use_dynamic_access_token_signing_key = True
+
+    if expose_access_token_to_frontend_in_cookie_based_auth is None:
+        expose_access_token_to_frontend_in_cookie_based_auth = False
+
+    if cookie_same_site is not None:
+        # this is just so that we check that the user has provided the right
+        # values, since normalise_same_site throws an error if the user
+        # as provided an empty string.
+        _ = normalise_same_site(cookie_same_site)
+
+    def get_cookie_same_site(
+        request: Optional[BaseRequest], user_context: Dict[str, Any]
+    ) -> Literal["lax", "strict", "none"]:
+        nonlocal cookie_same_site
+        if cookie_same_site is not None:
+            return normalise_same_site(cookie_same_site)
+        top_level_api_domain = app_info.top_level_api_domain
+        top_level_website_domain = app_info.get_top_level_website_domain(
+            request, user_context
+        )
+
+        api_domain_scheme = get_url_scheme(
+            app_info.api_domain.get_as_string_dangerous()
+        )
+        website_domain_scheme = get_url_scheme(
+            app_info.get_origin(request, user_context).get_as_string_dangerous()
+        )
+        if (top_level_api_domain != top_level_website_domain) or (
+            api_domain_scheme != website_domain_scheme
+        ):
+            cookie_same_site = "none"
+        else:
+            cookie_same_site = "lax"
+        return cookie_same_site
+
+    def anti_csrf_function(
+        request: Optional[BaseRequest], user_context: Dict[str, Any]
+    ) -> Literal["NONE", "VIA_CUSTOM_HEADER"]:
+        same_site = get_cookie_same_site(request, user_context)
+        if same_site == "none":
+            return "VIA_CUSTOM_HEADER"
+        return "NONE"
+
+    anti_csrf_function_or_string: Union[
+        Callable[
+            [Optional[BaseRequest], Dict[str, Any]],
+            Literal["VIA_CUSTOM_HEADER", "NONE"],
+        ],
+        Literal["VIA_CUSTOM_HEADER", "NONE", "VIA_TOKEN"],
+    ] = anti_csrf_function
+    if anti_csrf is not None:
+        anti_csrf_function_or_string = anti_csrf
+
+    if jwks_refresh_interval_sec is None:
+        jwks_refresh_interval_sec = 4 * 3600  # 4 hours
+
+    return SessionConfig(
+        app_info.api_base_path.append(NormalisedURLPath(SESSION_REFRESH)),
+        cookie_domain,
+        older_cookie_domain,
+        get_cookie_same_site,
+        cookie_secure,
+        session_expired_status_code,
+        error_handlers,
+        anti_csrf_function_or_string,
+        get_token_transfer_method,
+        OverrideConfig(override.functions, override.apis),
+        app_info.framework,
+        app_info.mode,
+        invalid_claim_status_code,
+        use_dynamic_access_token_signing_key,
+        expose_access_token_to_frontend_in_cookie_based_auth,
+        jwks_refresh_interval_sec,
+    )
+
+
+async def get_required_claim_validators(
+    session: SessionContainer,
+    override_global_claim_validators: Optional[
+        Callable[
+            [List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
+            MaybeAwaitable[List[SessionClaimValidator]],
+        ]
+    ],
+    user_context: Dict[str, Any],
+) -> List[SessionClaimValidator]:
+    from .recipe import SessionRecipe
+
+    claim_validators_added_by_other_recipes = (
+        SessionRecipe.get_instance().get_claim_validators_added_by_other_recipes()
+    )
+    global_claim_validators = await resolve(
+        SessionRecipe.get_instance().recipe_implementation.get_global_claim_validators(
+            session.get_tenant_id(),
+            session.get_user_id(),
+            claim_validators_added_by_other_recipes,
+            user_context,
+        )
+    )
+
+    if override_global_claim_validators is not None:
+        return await resolve(
+            override_global_claim_validators(
+                global_claim_validators, session, user_context
+            )
+        )
+
+    return global_claim_validators
+
+
+async def validate_claims_in_payload(
+    claim_validators: List[SessionClaimValidator],
+    new_access_token_payload: Dict[str, Any],
+    user_context: Dict[str, Any],
+):
+    validation_errors: List[ClaimValidationError] = []
+    for validator in claim_validators:
+        claim_validation_res = await validator.validate(
+            new_access_token_payload, user_context
+        )
+        log_debug_message(
+            "validate_claims_in_payload %s validate res %s",
+            validator.id,
+            json.dumps(claim_validation_res.__dict__),
+        )
+        if not claim_validation_res.is_valid:
+            validation_errors.append(
+                ClaimValidationError(validator.id, claim_validation_res.reason)
+            )
+
+    return validation_errors
+
+
+
+
+
+
+
+

Functions

+
+
+async def default_clear_duplicate_session_cookies_callback(_: BaseRequest, message: str, response: BaseResponse) ‑> BaseResponse +
+
+
+
+ +Expand source code + +
async def default_clear_duplicate_session_cookies_callback(
+    _: BaseRequest, message: str, response: BaseResponse
+) -> BaseResponse:
+    # This error occurs in the `refresh_post` API when multiple session
+    # cookies are found in the request and the user has set `older_cookie_domain`.
+    # We remove session cookies from the older_cookie_domain. The response must return `200 OK`
+    # to avoid logging out the user, allowing the session to continue with the valid cookie.
+    return send_200_response({"message": message}, response)
+
+
+
+async def default_invalid_claim_callback(_: BaseRequest, claim_validation_errors: List[ClaimValidationError], response: BaseResponse) ‑> BaseResponse +
+
+
+
+ +Expand source code + +
async def default_invalid_claim_callback(
+    _: BaseRequest,
+    claim_validation_errors: List[ClaimValidationError],
+    response: BaseResponse,
+) -> BaseResponse:
+    from .recipe import SessionRecipe
+
+    return send_non_200_response(
+        {
+            "message": "invalid claim",
+            "claimValidationErrors": [err.to_json() for err in claim_validation_errors],
+        },
+        SessionRecipe.get_instance().config.invalid_claim_status_code,
+        response,
+    )
+
+
+
+async def default_token_theft_detected_callback(_: BaseRequest, session_handle: str, __: str, response: BaseResponse) ‑> BaseResponse +
+
+
+
+ +Expand source code + +
async def default_token_theft_detected_callback(
+    _: BaseRequest, session_handle: str, __: str, response: BaseResponse
+) -> BaseResponse:
+    from .recipe import SessionRecipe
+
+    await SessionRecipe.get_instance().recipe_implementation.revoke_session(
+        session_handle, {}
+    )
+    return send_non_200_response_with_message(
+        "token theft detected",
+        SessionRecipe.get_instance().config.session_expired_status_code,
+        response,
+    )
+
+
+
+async def default_try_refresh_token_callback(_: BaseRequest, __: str, response: BaseResponse) ‑> BaseResponse +
+
+
+
+ +Expand source code + +
async def default_try_refresh_token_callback(
+    _: BaseRequest, __: str, response: BaseResponse
+) -> BaseResponse:
+    from .recipe import SessionRecipe
+
+    return send_non_200_response_with_message(
+        "try refresh token",
+        SessionRecipe.get_instance().config.session_expired_status_code,
+        response,
+    )
+
+
+
+async def default_unauthorised_callback(_: BaseRequest, __: str, response: BaseResponse) ‑> BaseResponse +
+
+
+
+ +Expand source code + +
async def default_unauthorised_callback(
+    _: BaseRequest, __: str, response: BaseResponse
+) -> BaseResponse:
+    from .recipe import SessionRecipe
+
+    return send_non_200_response_with_message(
+        "unauthorised",
+        SessionRecipe.get_instance().config.session_expired_status_code,
+        response,
+    )
+
+
+
+def get_auth_mode_from_header(request: BaseRequest) ‑> Optional[str] +
+
+
+
+ +Expand source code + +
def get_auth_mode_from_header(request: BaseRequest) -> Optional[str]:
+    auth_mode = request.get_header(AUTH_MODE_HEADER_KEY)
+    if auth_mode is None:
+        return None
+    return auth_mode.lower()
+
+
+
+async def get_required_claim_validators(session: SessionContainer, override_global_claim_validators: Optional[Callable[[List[SessionClaimValidator], SessionContainer, Dict[str, Any]], MaybeAwaitable[List[SessionClaimValidator]]]], user_context: Dict[str, Any]) ‑> List[SessionClaimValidator] +
+
+
+
+ +Expand source code + +
async def get_required_claim_validators(
+    session: SessionContainer,
+    override_global_claim_validators: Optional[
+        Callable[
+            [List[SessionClaimValidator], SessionContainer, Dict[str, Any]],
+            MaybeAwaitable[List[SessionClaimValidator]],
+        ]
+    ],
+    user_context: Dict[str, Any],
+) -> List[SessionClaimValidator]:
+    from .recipe import SessionRecipe
+
+    claim_validators_added_by_other_recipes = (
+        SessionRecipe.get_instance().get_claim_validators_added_by_other_recipes()
+    )
+    global_claim_validators = await resolve(
+        SessionRecipe.get_instance().recipe_implementation.get_global_claim_validators(
+            session.get_tenant_id(),
+            session.get_user_id(),
+            claim_validators_added_by_other_recipes,
+            user_context,
+        )
+    )
+
+    if override_global_claim_validators is not None:
+        return await resolve(
+            override_global_claim_validators(
+                global_claim_validators, session, user_context
+            )
+        )
+
+    return global_claim_validators
+
+
+
+def get_token_transfer_method_default(req: BaseRequest, for_create_new_session: bool, user_context: Dict[str, Any]) +
+
+
+
+ +Expand source code + +
def get_token_transfer_method_default(
+    req: BaseRequest,
+    for_create_new_session: bool,
+    user_context: Dict[str, Any],
+):
+    _ = user_context
+
+    # We allow fallback (checking headers then cookies) by default when validating
+    if not for_create_new_session:
+        return "any"
+
+    auth_mode = get_auth_mode_from_header(req)
+    if auth_mode in ("header", "cookie"):
+        return auth_mode
+
+    return "any"
+
+
+
+def get_url_scheme(url: str) ‑> str +
+
+
+
+ +Expand source code + +
def get_url_scheme(url: str) -> str:
+    url_obj = urlparse(url)
+    return url_obj.scheme
+
+
+
+def normalise_same_site(same_site: str) ‑> typing_extensions.Literal['strict', 'lax', 'none'] +
+
+
+
+ +Expand source code + +
def normalise_same_site(same_site: str) -> Literal["strict", "lax", "none"]:
+    same_site = same_site.strip()
+    same_site = same_site.lower()
+    allowed_values = {"strict", "lax", "none"}
+    if same_site not in allowed_values:
+        raise Exception('cookie same site must be one of "strict", "lax", or "none"')
+    return same_site  # type: ignore
+
+
+
+def normalise_session_scope(session_scope: str) ‑> str +
+
+
+
+ +Expand source code + +
def normalise_session_scope(session_scope: str) -> str:
+    def helper(scope: str) -> str:
+        scope = scope.strip()
+
+        if scope.startswith("."):
+            scope = scope[1:]
+
+        if (not scope.startswith("https://")) and (not scope.startswith("http://")):
+            scope = "http://" + scope
+
+        try:
+            url_obj = urlparse(scope)
+            if url_obj.hostname is None:
+                raise Exception("Should not come here")
+            scope = url_obj.hostname
+
+            return scope
+        except Exception:
+            raise_general_exception("Please provide a valid session_scope")
+
+    no_dot_normalised = helper(session_scope)
+    if no_dot_normalised == "localhost" or is_an_ip_address(no_dot_normalised):
+        return no_dot_normalised
+
+    if session_scope.startswith("."):
+        return "." + no_dot_normalised
+
+    return no_dot_normalised
+
+
+
+def validate_and_normalise_user_input(app_info: AppInfo, cookie_domain: Union[str, None] = None, older_cookie_domain: Union[str, None] = None, cookie_secure: Union[bool, None] = None, cookie_same_site: "Union[Literal[('lax', 'strict', 'none')], None]" = None, session_expired_status_code: Union[int, None] = None, anti_csrf: "Union[Literal[('VIA_TOKEN', 'VIA_CUSTOM_HEADER', 'NONE')], None]" = None, get_token_transfer_method: "Union[Callable[[BaseRequest, bool, Dict[str, Any]], Union[TokenTransferMethod, Literal['any']]], None]" = None, error_handlers: Union[ErrorHandlers, None] = None, override: Union[InputOverrideConfig, None] = None, invalid_claim_status_code: Union[int, None] = None, use_dynamic_access_token_signing_key: Union[bool, None] = None, expose_access_token_to_frontend_in_cookie_based_auth: Union[bool, None] = None, jwks_refresh_interval_sec: Union[int, None] = None) +
+
+
+
+ +Expand source code + +
def validate_and_normalise_user_input(
+    app_info: AppInfo,
+    cookie_domain: Union[str, None] = None,
+    older_cookie_domain: Union[str, None] = None,
+    cookie_secure: Union[bool, None] = None,
+    cookie_same_site: Union[Literal["lax", "strict", "none"], None] = None,
+    session_expired_status_code: Union[int, None] = None,
+    anti_csrf: Union[Literal["VIA_TOKEN", "VIA_CUSTOM_HEADER", "NONE"], None] = None,
+    get_token_transfer_method: Union[
+        Callable[
+            [BaseRequest, bool, Dict[str, Any]],
+            Union[TokenTransferMethod, Literal["any"]],
+        ],
+        None,
+    ] = None,
+    error_handlers: Union[ErrorHandlers, None] = None,
+    override: Union[InputOverrideConfig, None] = None,
+    invalid_claim_status_code: Union[int, None] = None,
+    use_dynamic_access_token_signing_key: Union[bool, None] = None,
+    expose_access_token_to_frontend_in_cookie_based_auth: Union[bool, None] = None,
+    jwks_refresh_interval_sec: Union[int, None] = None,
+):
+    _ = cookie_same_site  # we have this otherwise pylint complains that cookie_same_site is unused, but it is being used in the get_cookie_same_site function.
+    if anti_csrf not in {"VIA_TOKEN", "VIA_CUSTOM_HEADER", "NONE", None}:
+        raise ValueError(
+            "anti_csrf must be one of VIA_TOKEN, VIA_CUSTOM_HEADER, NONE or None"
+        )
+
+    if error_handlers is not None and not isinstance(error_handlers, ErrorHandlers):  # type: ignore
+        raise ValueError("error_handlers must be an instance of ErrorHandlers or None")
+
+    if override is not None and not isinstance(override, InputOverrideConfig):  # type: ignore
+        raise ValueError("override must be an instance of InputOverrideConfig or None")
+
+    cookie_domain = (
+        normalise_session_scope(cookie_domain) if cookie_domain is not None else None
+    )
+
+    older_cookie_domain = (
+        older_cookie_domain
+        if older_cookie_domain is None or older_cookie_domain == ""
+        else normalise_session_scope(older_cookie_domain)
+    )
+
+    cookie_secure = (
+        cookie_secure
+        if cookie_secure is not None
+        else app_info.api_domain.get_as_string_dangerous().startswith("https")
+    )
+
+    session_expired_status_code = (
+        session_expired_status_code if session_expired_status_code is not None else 401
+    )
+
+    invalid_claim_status_code = (
+        invalid_claim_status_code if invalid_claim_status_code is not None else 403
+    )
+
+    if session_expired_status_code == invalid_claim_status_code:
+        raise Exception(
+            "session_expired_status_code and invalid_claim_status_code cannot be the same "
+            f"({invalid_claim_status_code})"
+        )
+
+    if get_token_transfer_method is None:
+        get_token_transfer_method = get_token_transfer_method_default
+
+    if error_handlers is None:
+        error_handlers = InputErrorHandlers()
+
+    if override is None:
+        override = InputOverrideConfig()
+
+    if use_dynamic_access_token_signing_key is None:
+        use_dynamic_access_token_signing_key = True
+
+    if expose_access_token_to_frontend_in_cookie_based_auth is None:
+        expose_access_token_to_frontend_in_cookie_based_auth = False
+
+    if cookie_same_site is not None:
+        # this is just so that we check that the user has provided the right
+        # values, since normalise_same_site throws an error if the user
+        # as provided an empty string.
+        _ = normalise_same_site(cookie_same_site)
+
+    def get_cookie_same_site(
+        request: Optional[BaseRequest], user_context: Dict[str, Any]
+    ) -> Literal["lax", "strict", "none"]:
+        nonlocal cookie_same_site
+        if cookie_same_site is not None:
+            return normalise_same_site(cookie_same_site)
+        top_level_api_domain = app_info.top_level_api_domain
+        top_level_website_domain = app_info.get_top_level_website_domain(
+            request, user_context
+        )
+
+        api_domain_scheme = get_url_scheme(
+            app_info.api_domain.get_as_string_dangerous()
+        )
+        website_domain_scheme = get_url_scheme(
+            app_info.get_origin(request, user_context).get_as_string_dangerous()
+        )
+        if (top_level_api_domain != top_level_website_domain) or (
+            api_domain_scheme != website_domain_scheme
+        ):
+            cookie_same_site = "none"
+        else:
+            cookie_same_site = "lax"
+        return cookie_same_site
+
+    def anti_csrf_function(
+        request: Optional[BaseRequest], user_context: Dict[str, Any]
+    ) -> Literal["NONE", "VIA_CUSTOM_HEADER"]:
+        same_site = get_cookie_same_site(request, user_context)
+        if same_site == "none":
+            return "VIA_CUSTOM_HEADER"
+        return "NONE"
+
+    anti_csrf_function_or_string: Union[
+        Callable[
+            [Optional[BaseRequest], Dict[str, Any]],
+            Literal["VIA_CUSTOM_HEADER", "NONE"],
+        ],
+        Literal["VIA_CUSTOM_HEADER", "NONE", "VIA_TOKEN"],
+    ] = anti_csrf_function
+    if anti_csrf is not None:
+        anti_csrf_function_or_string = anti_csrf
+
+    if jwks_refresh_interval_sec is None:
+        jwks_refresh_interval_sec = 4 * 3600  # 4 hours
+
+    return SessionConfig(
+        app_info.api_base_path.append(NormalisedURLPath(SESSION_REFRESH)),
+        cookie_domain,
+        older_cookie_domain,
+        get_cookie_same_site,
+        cookie_secure,
+        session_expired_status_code,
+        error_handlers,
+        anti_csrf_function_or_string,
+        get_token_transfer_method,
+        OverrideConfig(override.functions, override.apis),
+        app_info.framework,
+        app_info.mode,
+        invalid_claim_status_code,
+        use_dynamic_access_token_signing_key,
+        expose_access_token_to_frontend_in_cookie_based_auth,
+        jwks_refresh_interval_sec,
+    )
+
+
+
+async def validate_claims_in_payload(claim_validators: List[SessionClaimValidator], new_access_token_payload: Dict[str, Any], user_context: Dict[str, Any]) +
+
+
+
+ +Expand source code + +
async def validate_claims_in_payload(
+    claim_validators: List[SessionClaimValidator],
+    new_access_token_payload: Dict[str, Any],
+    user_context: Dict[str, Any],
+):
+    validation_errors: List[ClaimValidationError] = []
+    for validator in claim_validators:
+        claim_validation_res = await validator.validate(
+            new_access_token_payload, user_context
+        )
+        log_debug_message(
+            "validate_claims_in_payload %s validate res %s",
+            validator.id,
+            json.dumps(claim_validation_res.__dict__),
+        )
+        if not claim_validation_res.is_valid:
+            validation_errors.append(
+                ClaimValidationError(validator.id, claim_validation_res.reason)
+            )
+
+    return validation_errors
+
+
+
+
+
+

Classes

+
+
+class ErrorHandlers +(on_token_theft_detected: Callable[[BaseRequest, str, str, BaseResponse], Union[BaseResponse, Awaitable[BaseResponse]]], on_try_refresh_token: Callable[[BaseRequest, str, BaseResponse], Union[BaseResponse, Awaitable[BaseResponse]]], on_unauthorised: Callable[[BaseRequest, str, BaseResponse], Union[BaseResponse, Awaitable[BaseResponse]]], on_invalid_claim: Callable[[BaseRequest, List[ClaimValidationError], BaseResponse], Union[BaseResponse, Awaitable[BaseResponse]]], on_clear_duplicate_session_cookies: Callable[[BaseRequest, str, BaseResponse], Union[BaseResponse, Awaitable[BaseResponse]]]) +
+
+
+
+ +Expand source code + +
class ErrorHandlers:
+    def __init__(
+        self,
+        on_token_theft_detected: Callable[
+            [BaseRequest, str, str, BaseResponse],
+            Union[BaseResponse, Awaitable[BaseResponse]],
+        ],
+        on_try_refresh_token: Callable[
+            [BaseRequest, str, BaseResponse],
+            Union[BaseResponse, Awaitable[BaseResponse]],
+        ],
+        on_unauthorised: Callable[
+            [BaseRequest, str, BaseResponse],
+            Union[BaseResponse, Awaitable[BaseResponse]],
+        ],
+        on_invalid_claim: Callable[
+            [BaseRequest, List[ClaimValidationError], BaseResponse],
+            Union[BaseResponse, Awaitable[BaseResponse]],
+        ],
+        on_clear_duplicate_session_cookies: Callable[
+            [BaseRequest, str, BaseResponse],
+            Union[BaseResponse, Awaitable[BaseResponse]],
+        ],
+    ):
+        self.__on_token_theft_detected = on_token_theft_detected
+        self.__on_try_refresh_token = on_try_refresh_token
+        self.__on_unauthorised = on_unauthorised
+        self.__on_invalid_claim = on_invalid_claim
+        self.__on_clear_duplicate_session_cookies = on_clear_duplicate_session_cookies
+
+    async def on_token_theft_detected(
+        self,
+        request: BaseRequest,
+        session_handle: str,
+        user_id: str,
+        response: BaseResponse,
+    ) -> BaseResponse:
+        return await resolve(
+            self.__on_token_theft_detected(request, session_handle, user_id, response)
+        )
+
+    async def on_try_refresh_token(
+        self, request: BaseRequest, message: str, response: BaseResponse
+    ):
+        result = await resolve(self.__on_try_refresh_token(request, message, response))
+        return result
+
+    async def on_unauthorised(
+        self,
+        request: BaseRequest,
+        message: str,
+        response: BaseResponse,
+    ):
+        return await resolve(self.__on_unauthorised(request, message, response))
+
+    async def on_invalid_claim(
+        self,
+        recipe: SessionRecipe,
+        request: BaseRequest,
+        claim_validation_errors: List[ClaimValidationError],
+        response: BaseResponse,
+    ):
+        _ = recipe
+        result = await resolve(
+            self.__on_invalid_claim(request, claim_validation_errors, response)
+        )
+        return result
+
+    async def on_clear_duplicate_session_cookies(
+        self,
+        request: BaseRequest,
+        message: str,
+        response: BaseResponse,
+    ):
+        result = await resolve(
+            self.__on_clear_duplicate_session_cookies(request, message, response)
+        )
+        return result
+
+

Subclasses

+ +

Methods

+
+
+async def on_clear_duplicate_session_cookies(self, request: BaseRequest, message: str, response: BaseResponse) +
+
+
+
+ +Expand source code + +
async def on_clear_duplicate_session_cookies(
+    self,
+    request: BaseRequest,
+    message: str,
+    response: BaseResponse,
+):
+    result = await resolve(
+        self.__on_clear_duplicate_session_cookies(request, message, response)
+    )
+    return result
+
+
+
+async def on_invalid_claim(self, recipe: SessionRecipe, request: BaseRequest, claim_validation_errors: List[ClaimValidationError], response: BaseResponse) +
+
+
+
+ +Expand source code + +
async def on_invalid_claim(
+    self,
+    recipe: SessionRecipe,
+    request: BaseRequest,
+    claim_validation_errors: List[ClaimValidationError],
+    response: BaseResponse,
+):
+    _ = recipe
+    result = await resolve(
+        self.__on_invalid_claim(request, claim_validation_errors, response)
+    )
+    return result
+
+
+
+async def on_token_theft_detected(self, request: BaseRequest, session_handle: str, user_id: str, response: BaseResponse) ‑> BaseResponse +
+
+
+
+ +Expand source code + +
async def on_token_theft_detected(
+    self,
+    request: BaseRequest,
+    session_handle: str,
+    user_id: str,
+    response: BaseResponse,
+) -> BaseResponse:
+    return await resolve(
+        self.__on_token_theft_detected(request, session_handle, user_id, response)
+    )
+
+
+
+async def on_try_refresh_token(self, request: BaseRequest, message: str, response: BaseResponse) +
+
+
+
+ +Expand source code + +
async def on_try_refresh_token(
+    self, request: BaseRequest, message: str, response: BaseResponse
+):
+    result = await resolve(self.__on_try_refresh_token(request, message, response))
+    return result
+
+
+
+async def on_unauthorised(self, request: BaseRequest, message: str, response: BaseResponse) +
+
+
+
+ +Expand source code + +
async def on_unauthorised(
+    self,
+    request: BaseRequest,
+    message: str,
+    response: BaseResponse,
+):
+    return await resolve(self.__on_unauthorised(request, message, response))
+
+
+
+
+
+class InputErrorHandlers +(on_token_theft_detected: Union[None, Callable[[BaseRequest, str, str, BaseResponse], Union[BaseResponse, Awaitable[BaseResponse]]]] = None, on_try_refresh_token: Union[None, Callable[[BaseRequest, str, BaseResponse], Union[BaseResponse, Awaitable[BaseResponse]]]] = None, on_unauthorised: Union[Callable[[BaseRequest, str, BaseResponse], Union[BaseResponse, Awaitable[BaseResponse]]], None] = None, on_invalid_claim: Union[Callable[[BaseRequest, List[ClaimValidationError], BaseResponse], Union[BaseResponse, Awaitable[BaseResponse]]], None] = None, on_clear_duplicate_session_cookies: Union[None, Callable[[BaseRequest, str, BaseResponse], Union[BaseResponse, Awaitable[BaseResponse]]]] = None) +
+
+
+
+ +Expand source code + +
class InputErrorHandlers(ErrorHandlers):
+    def __init__(
+        self,
+        on_token_theft_detected: Union[
+            None,
+            Callable[
+                [BaseRequest, str, str, BaseResponse],
+                Union[BaseResponse, Awaitable[BaseResponse]],
+            ],
+        ] = None,
+        on_try_refresh_token: Union[
+            None,
+            Callable[
+                [BaseRequest, str, BaseResponse],
+                Union[BaseResponse, Awaitable[BaseResponse]],
+            ],
+        ] = None,
+        on_unauthorised: Union[
+            Callable[
+                [BaseRequest, str, BaseResponse],
+                Union[BaseResponse, Awaitable[BaseResponse]],
+            ],
+            None,
+        ] = None,
+        on_invalid_claim: Union[
+            Callable[
+                [BaseRequest, List[ClaimValidationError], BaseResponse],
+                Union[BaseResponse, Awaitable[BaseResponse]],
+            ],
+            None,
+        ] = None,
+        on_clear_duplicate_session_cookies: Union[
+            None,
+            Callable[
+                [BaseRequest, str, BaseResponse],
+                Union[BaseResponse, Awaitable[BaseResponse]],
+            ],
+        ] = None,
+    ):
+        if on_token_theft_detected is None:
+            on_token_theft_detected = default_token_theft_detected_callback
+        if on_try_refresh_token is None:
+            on_try_refresh_token = default_try_refresh_token_callback
+        if on_unauthorised is None:
+            on_unauthorised = default_unauthorised_callback
+        if on_invalid_claim is None:
+            on_invalid_claim = default_invalid_claim_callback
+        if on_clear_duplicate_session_cookies is None:
+            on_clear_duplicate_session_cookies = (
+                default_clear_duplicate_session_cookies_callback
+            )
+        super().__init__(
+            on_token_theft_detected,
+            on_try_refresh_token,
+            on_unauthorised,
+            on_invalid_claim,
+            on_clear_duplicate_session_cookies,
+        )
+
+

Ancestors

+ +
+
+class InputOverrideConfig +(functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None, apis: Union[Callable[[APIInterface], APIInterface], None] = None, openid_feature: Union[OpenIdInputOverrideConfig, None] = None) +
+
+
+
+ +Expand source code + +
class InputOverrideConfig:
+    def __init__(
+        self,
+        functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None,
+        apis: Union[Callable[[APIInterface], APIInterface], None] = None,
+        openid_feature: Union[OpenIdInputOverrideConfig, None] = None,
+    ):
+        self.functions = functions
+        self.apis = apis
+        self.openid_feature = openid_feature
+
+
+
+class OverrideConfig +(functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None, apis: Union[Callable[[APIInterface], APIInterface], None] = None) +
+
+
+
+ +Expand source code + +
class OverrideConfig:
+    def __init__(
+        self,
+        functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None,
+        apis: Union[Callable[[APIInterface], APIInterface], None] = None,
+    ):
+        self.functions = functions
+        self.apis = apis
+
+
+
+class SessionConfig +(refresh_token_path: NormalisedURLPath, cookie_domain: Union[None, str], older_cookie_domain: Union[None, str], get_cookie_same_site: "Callable[[Optional[BaseRequest], Dict[str, Any]], Literal[('lax', 'strict', 'none')]]", cookie_secure: bool, session_expired_status_code: int, error_handlers: ErrorHandlers, anti_csrf_function_or_string: "Union[Callable[[Optional[BaseRequest], Dict[str, Any]], Literal[('VIA_CUSTOM_HEADER', 'NONE')]], Literal[('VIA_CUSTOM_HEADER', 'NONE', 'VIA_TOKEN')]]", get_token_transfer_method: "Callable[[BaseRequest, bool, Dict[str, Any]], Union[TokenTransferMethod, Literal['any']]]", override: OverrideConfig, framework: str, mode: str, invalid_claim_status_code: int, use_dynamic_access_token_signing_key: bool, expose_access_token_to_frontend_in_cookie_based_auth: bool, jwks_refresh_interval_sec: int) +
+
+
+
+ +Expand source code + +
class SessionConfig:
+    def __init__(
+        self,
+        refresh_token_path: NormalisedURLPath,
+        cookie_domain: Union[None, str],
+        older_cookie_domain: Union[None, str],
+        get_cookie_same_site: Callable[
+            [Optional[BaseRequest], Dict[str, Any]],
+            Literal["lax", "strict", "none"],
+        ],
+        cookie_secure: bool,
+        session_expired_status_code: int,
+        error_handlers: ErrorHandlers,
+        anti_csrf_function_or_string: Union[
+            Callable[
+                [Optional[BaseRequest], Dict[str, Any]],
+                Literal["VIA_CUSTOM_HEADER", "NONE"],
+            ],
+            Literal["VIA_CUSTOM_HEADER", "NONE", "VIA_TOKEN"],
+        ],
+        get_token_transfer_method: Callable[
+            [BaseRequest, bool, Dict[str, Any]],
+            Union[TokenTransferMethod, Literal["any"]],
+        ],
+        override: OverrideConfig,
+        framework: str,
+        mode: str,
+        invalid_claim_status_code: int,
+        use_dynamic_access_token_signing_key: bool,
+        expose_access_token_to_frontend_in_cookie_based_auth: bool,
+        jwks_refresh_interval_sec: int,
+    ):
+        self.session_expired_status_code = session_expired_status_code
+        self.invalid_claim_status_code = invalid_claim_status_code
+        self.use_dynamic_access_token_signing_key = use_dynamic_access_token_signing_key
+        self.expose_access_token_to_frontend_in_cookie_based_auth = (
+            expose_access_token_to_frontend_in_cookie_based_auth
+        )
+
+        self.refresh_token_path = refresh_token_path
+        self.cookie_domain = cookie_domain
+        self.older_cookie_domain = older_cookie_domain
+        self.get_cookie_same_site = get_cookie_same_site
+        self.cookie_secure = cookie_secure
+        self.error_handlers = error_handlers
+        self.anti_csrf_function_or_string = anti_csrf_function_or_string
+        self.get_token_transfer_method = get_token_transfer_method
+        self.override = override
+        self.framework = framework
+        self.mode = mode
+        self.jwks_refresh_interval_sec = jwks_refresh_interval_sec
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/thirdparty/api/apple_redirect.html b/html/supertokens_python/recipe/thirdparty/api/apple_redirect.html new file mode 100644 index 000000000..d2c5c4962 --- /dev/null +++ b/html/supertokens_python/recipe/thirdparty/api/apple_redirect.html @@ -0,0 +1,130 @@ + + + + + + +supertokens_python.recipe.thirdparty.api.apple_redirect API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.thirdparty.api.apple_redirect

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Any, Dict
+
+if TYPE_CHECKING:
+    from supertokens_python.recipe.thirdparty.interfaces import APIInterface, APIOptions
+
+
+async def handle_apple_redirect_api(
+    api_implementation: APIInterface,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+):
+    if api_implementation.disable_apple_redirect_handler_post:
+        return None
+
+    body = await api_options.request.form_data()
+
+    # this will redirect the user...
+    await api_implementation.apple_redirect_handler_post(
+        body, api_options, user_context
+    )
+
+    return api_options.response
+
+
+
+
+
+
+
+

Functions

+
+
+async def handle_apple_redirect_api(api_implementation: APIInterface, api_options: APIOptions, user_context: Dict[str, Any]) +
+
+
+
+ +Expand source code + +
async def handle_apple_redirect_api(
+    api_implementation: APIInterface,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+):
+    if api_implementation.disable_apple_redirect_handler_post:
+        return None
+
+    body = await api_options.request.form_data()
+
+    # this will redirect the user...
+    await api_implementation.apple_redirect_handler_post(
+        body, api_options, user_context
+    )
+
+    return api_options.response
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/thirdparty/api/authorisation_url.html b/html/supertokens_python/recipe/thirdparty/api/authorisation_url.html new file mode 100644 index 000000000..6e9d4680d --- /dev/null +++ b/html/supertokens_python/recipe/thirdparty/api/authorisation_url.html @@ -0,0 +1,188 @@ + + + + + + +supertokens_python.recipe.thirdparty.api.authorisation_url API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.thirdparty.api.authorisation_url

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Any, Dict
+
+
+if TYPE_CHECKING:
+    from supertokens_python.recipe.thirdparty.interfaces import APIOptions, APIInterface
+
+from supertokens_python.exceptions import raise_bad_input_exception, BadInputError
+from supertokens_python.utils import send_200_response
+
+
+async def handle_authorisation_url_api(
+    api_implementation: APIInterface,
+    tenant_id: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+):
+    if api_implementation.disable_authorisation_url_get:
+        return None
+
+    third_party_id = api_options.request.get_query_param("thirdPartyId")
+    redirect_uri_on_provider_dashboard = api_options.request.get_query_param(
+        "redirectURIOnProviderDashboard"
+    )
+    client_type = api_options.request.get_query_param("clientType")
+
+    if third_party_id is None:
+        raise_bad_input_exception("Please provide the thirdPartyId as a GET param")
+
+    if redirect_uri_on_provider_dashboard is None:
+        raise_bad_input_exception(
+            "Please provide the redirectURIOnProviderDashboard as a GET param"
+        )
+
+    provider_response = await api_options.recipe_implementation.get_provider(
+        third_party_id=third_party_id,
+        client_type=client_type,
+        tenant_id=tenant_id,
+        user_context=user_context,
+    )
+
+    if provider_response is None:
+        raise BadInputError(
+            f"the provider {third_party_id} could not be found in the configuration"
+        )
+
+    provider = provider_response
+    result = await api_implementation.authorisation_url_get(
+        provider=provider,
+        redirect_uri_on_provider_dashboard=redirect_uri_on_provider_dashboard,
+        api_options=api_options,
+        user_context=user_context,
+    )
+    return send_200_response(result.to_json(), api_options.response)
+
+
+
+
+
+
+
+

Functions

+
+
+async def handle_authorisation_url_api(api_implementation: APIInterface, tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) +
+
+
+
+ +Expand source code + +
async def handle_authorisation_url_api(
+    api_implementation: APIInterface,
+    tenant_id: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+):
+    if api_implementation.disable_authorisation_url_get:
+        return None
+
+    third_party_id = api_options.request.get_query_param("thirdPartyId")
+    redirect_uri_on_provider_dashboard = api_options.request.get_query_param(
+        "redirectURIOnProviderDashboard"
+    )
+    client_type = api_options.request.get_query_param("clientType")
+
+    if third_party_id is None:
+        raise_bad_input_exception("Please provide the thirdPartyId as a GET param")
+
+    if redirect_uri_on_provider_dashboard is None:
+        raise_bad_input_exception(
+            "Please provide the redirectURIOnProviderDashboard as a GET param"
+        )
+
+    provider_response = await api_options.recipe_implementation.get_provider(
+        third_party_id=third_party_id,
+        client_type=client_type,
+        tenant_id=tenant_id,
+        user_context=user_context,
+    )
+
+    if provider_response is None:
+        raise BadInputError(
+            f"the provider {third_party_id} could not be found in the configuration"
+        )
+
+    provider = provider_response
+    result = await api_implementation.authorisation_url_get(
+        provider=provider,
+        redirect_uri_on_provider_dashboard=redirect_uri_on_provider_dashboard,
+        api_options=api_options,
+        user_context=user_context,
+    )
+    return send_200_response(result.to_json(), api_options.response)
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/thirdparty/api/implementation.html b/html/supertokens_python/recipe/thirdparty/api/implementation.html new file mode 100644 index 000000000..ea736a1dd --- /dev/null +++ b/html/supertokens_python/recipe/thirdparty/api/implementation.html @@ -0,0 +1,546 @@ + + + + + + +supertokens_python.recipe.thirdparty.api.implementation API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.thirdparty.api.implementation

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+from base64 import b64decode
+import json
+
+from typing import TYPE_CHECKING, Any, Dict, Optional, Union
+from urllib.parse import parse_qs, urlencode, urlparse
+
+from supertokens_python.recipe.emailverification import EmailVerificationRecipe
+from supertokens_python.recipe.emailverification.interfaces import (
+    CreateEmailVerificationTokenOkResult,
+)
+from supertokens_python.recipe.session.asyncio import create_new_session
+from supertokens_python.recipe.thirdparty.interfaces import (
+    APIInterface,
+    AuthorisationUrlGetOkResult,
+    SignInUpPostNoEmailGivenByProviderResponse,
+    SignInUpPostOkResult,
+)
+from supertokens_python.recipe.thirdparty.provider import RedirectUriInfo
+from supertokens_python.recipe.thirdparty.types import UserInfoEmail
+
+if TYPE_CHECKING:
+    from supertokens_python.recipe.thirdparty.interfaces import APIOptions
+    from supertokens_python.recipe.thirdparty.provider import Provider
+
+from supertokens_python.types import GeneralErrorResponse
+
+
+class APIImplementation(APIInterface):
+    async def authorisation_url_get(
+        self,
+        provider: Provider,
+        redirect_uri_on_provider_dashboard: str,
+        api_options: APIOptions,
+        user_context: Dict[str, Any],
+    ) -> Union[AuthorisationUrlGetOkResult, GeneralErrorResponse]:
+        authorisation_url_info = await provider.get_authorisation_redirect_url(
+            redirect_uri_on_provider_dashboard=redirect_uri_on_provider_dashboard,
+            user_context=user_context,
+        )
+
+        return AuthorisationUrlGetOkResult(
+            url_with_query_params=authorisation_url_info.url_with_query_params,
+            pkce_code_verifier=authorisation_url_info.pkce_code_verifier,
+        )
+
+    async def sign_in_up_post(
+        self,
+        provider: Provider,
+        redirect_uri_info: Optional[RedirectUriInfo],
+        oauth_tokens: Optional[Dict[str, Any]],
+        tenant_id: str,
+        api_options: APIOptions,
+        user_context: Dict[str, Any],
+    ) -> Union[
+        SignInUpPostOkResult,
+        SignInUpPostNoEmailGivenByProviderResponse,
+        GeneralErrorResponse,
+    ]:
+        oauth_tokens_to_use: Dict[str, Any] = {}
+
+        if redirect_uri_info is not None:
+            oauth_tokens_to_use = await provider.exchange_auth_code_for_oauth_tokens(
+                redirect_uri_info=redirect_uri_info,
+                user_context=user_context,
+            )
+        else:
+            oauth_tokens_to_use = oauth_tokens  # type: ignore
+
+        user_info = await provider.get_user_info(
+            oauth_tokens=oauth_tokens_to_use,
+            user_context=user_context,
+        )
+
+        if user_info.email is None and provider.config.require_email is False:
+            # We don't expect to get an email from this provider.
+            # So we generate a fake one
+            if provider.config.generate_fake_email is not None:
+                user_info.email = UserInfoEmail(
+                    email=await provider.config.generate_fake_email(
+                        tenant_id, user_info.third_party_user_id, user_context
+                    ),
+                    is_verified=True,
+                )
+
+        email = user_info.email.id if user_info.email is not None else None
+        email_verified = (
+            user_info.email.is_verified if user_info.email is not None else None
+        )
+        if email is None:
+            return SignInUpPostNoEmailGivenByProviderResponse()
+
+        signinup_response = await api_options.recipe_implementation.sign_in_up(
+            third_party_id=provider.id,
+            third_party_user_id=user_info.third_party_user_id,
+            email=email,
+            oauth_tokens=oauth_tokens_to_use,
+            raw_user_info_from_provider=user_info.raw_user_info_from_provider,
+            tenant_id=tenant_id,
+            user_context=user_context,
+        )
+
+        if email_verified:
+            ev_instance = EmailVerificationRecipe.get_instance_optional()
+            if ev_instance is not None:
+                token_response = await ev_instance.recipe_implementation.create_email_verification_token(
+                    tenant_id=tenant_id,
+                    user_id=signinup_response.user.user_id,
+                    email=signinup_response.user.email,
+                    user_context=user_context,
+                )
+
+                if isinstance(token_response, CreateEmailVerificationTokenOkResult):
+                    await ev_instance.recipe_implementation.verify_email_using_token(
+                        token=token_response.token,
+                        tenant_id=tenant_id,
+                        user_context=user_context,
+                    )
+
+        user = signinup_response.user
+        session = await create_new_session(
+            request=api_options.request,
+            tenant_id=tenant_id,
+            user_id=user.user_id,
+            user_context=user_context,
+        )
+
+        return SignInUpPostOkResult(
+            created_new_user=signinup_response.created_new_user,
+            user=user,
+            session=session,
+            oauth_tokens=oauth_tokens_to_use,
+            raw_user_info_from_provider=user_info.raw_user_info_from_provider,
+        )
+
+    async def apple_redirect_handler_post(
+        self,
+        form_post_info: Dict[str, Any],
+        api_options: APIOptions,
+        user_context: Dict[str, Any],
+    ):
+        state_in_b64: str = form_post_info["state"]
+        state = b64decode(state_in_b64).decode("utf-8")
+        state_obj = json.loads(state)
+        redirect_uri: str = state_obj["frontendRedirectURI"]
+
+        url_obj = urlparse(redirect_uri)
+        qparams = parse_qs(url_obj.query)
+        for k, v in form_post_info.items():
+            qparams[k] = [v]
+
+        redirect_uri = url_obj._replace(query=urlencode(qparams, doseq=True)).geturl()
+
+        api_options.response.set_header("Location", redirect_uri)
+        api_options.response.set_status_code(303)
+        api_options.response.set_html_content("")
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class APIImplementation +
+
+
+
+ +Expand source code + +
class APIImplementation(APIInterface):
+    async def authorisation_url_get(
+        self,
+        provider: Provider,
+        redirect_uri_on_provider_dashboard: str,
+        api_options: APIOptions,
+        user_context: Dict[str, Any],
+    ) -> Union[AuthorisationUrlGetOkResult, GeneralErrorResponse]:
+        authorisation_url_info = await provider.get_authorisation_redirect_url(
+            redirect_uri_on_provider_dashboard=redirect_uri_on_provider_dashboard,
+            user_context=user_context,
+        )
+
+        return AuthorisationUrlGetOkResult(
+            url_with_query_params=authorisation_url_info.url_with_query_params,
+            pkce_code_verifier=authorisation_url_info.pkce_code_verifier,
+        )
+
+    async def sign_in_up_post(
+        self,
+        provider: Provider,
+        redirect_uri_info: Optional[RedirectUriInfo],
+        oauth_tokens: Optional[Dict[str, Any]],
+        tenant_id: str,
+        api_options: APIOptions,
+        user_context: Dict[str, Any],
+    ) -> Union[
+        SignInUpPostOkResult,
+        SignInUpPostNoEmailGivenByProviderResponse,
+        GeneralErrorResponse,
+    ]:
+        oauth_tokens_to_use: Dict[str, Any] = {}
+
+        if redirect_uri_info is not None:
+            oauth_tokens_to_use = await provider.exchange_auth_code_for_oauth_tokens(
+                redirect_uri_info=redirect_uri_info,
+                user_context=user_context,
+            )
+        else:
+            oauth_tokens_to_use = oauth_tokens  # type: ignore
+
+        user_info = await provider.get_user_info(
+            oauth_tokens=oauth_tokens_to_use,
+            user_context=user_context,
+        )
+
+        if user_info.email is None and provider.config.require_email is False:
+            # We don't expect to get an email from this provider.
+            # So we generate a fake one
+            if provider.config.generate_fake_email is not None:
+                user_info.email = UserInfoEmail(
+                    email=await provider.config.generate_fake_email(
+                        tenant_id, user_info.third_party_user_id, user_context
+                    ),
+                    is_verified=True,
+                )
+
+        email = user_info.email.id if user_info.email is not None else None
+        email_verified = (
+            user_info.email.is_verified if user_info.email is not None else None
+        )
+        if email is None:
+            return SignInUpPostNoEmailGivenByProviderResponse()
+
+        signinup_response = await api_options.recipe_implementation.sign_in_up(
+            third_party_id=provider.id,
+            third_party_user_id=user_info.third_party_user_id,
+            email=email,
+            oauth_tokens=oauth_tokens_to_use,
+            raw_user_info_from_provider=user_info.raw_user_info_from_provider,
+            tenant_id=tenant_id,
+            user_context=user_context,
+        )
+
+        if email_verified:
+            ev_instance = EmailVerificationRecipe.get_instance_optional()
+            if ev_instance is not None:
+                token_response = await ev_instance.recipe_implementation.create_email_verification_token(
+                    tenant_id=tenant_id,
+                    user_id=signinup_response.user.user_id,
+                    email=signinup_response.user.email,
+                    user_context=user_context,
+                )
+
+                if isinstance(token_response, CreateEmailVerificationTokenOkResult):
+                    await ev_instance.recipe_implementation.verify_email_using_token(
+                        token=token_response.token,
+                        tenant_id=tenant_id,
+                        user_context=user_context,
+                    )
+
+        user = signinup_response.user
+        session = await create_new_session(
+            request=api_options.request,
+            tenant_id=tenant_id,
+            user_id=user.user_id,
+            user_context=user_context,
+        )
+
+        return SignInUpPostOkResult(
+            created_new_user=signinup_response.created_new_user,
+            user=user,
+            session=session,
+            oauth_tokens=oauth_tokens_to_use,
+            raw_user_info_from_provider=user_info.raw_user_info_from_provider,
+        )
+
+    async def apple_redirect_handler_post(
+        self,
+        form_post_info: Dict[str, Any],
+        api_options: APIOptions,
+        user_context: Dict[str, Any],
+    ):
+        state_in_b64: str = form_post_info["state"]
+        state = b64decode(state_in_b64).decode("utf-8")
+        state_obj = json.loads(state)
+        redirect_uri: str = state_obj["frontendRedirectURI"]
+
+        url_obj = urlparse(redirect_uri)
+        qparams = parse_qs(url_obj.query)
+        for k, v in form_post_info.items():
+            qparams[k] = [v]
+
+        redirect_uri = url_obj._replace(query=urlencode(qparams, doseq=True)).geturl()
+
+        api_options.response.set_header("Location", redirect_uri)
+        api_options.response.set_status_code(303)
+        api_options.response.set_html_content("")
+
+

Ancestors

+ +

Methods

+
+
+async def apple_redirect_handler_post(self, form_post_info: Dict[str, Any], api_options: APIOptions, user_context: Dict[str, Any]) +
+
+
+
+ +Expand source code + +
async def apple_redirect_handler_post(
+    self,
+    form_post_info: Dict[str, Any],
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+):
+    state_in_b64: str = form_post_info["state"]
+    state = b64decode(state_in_b64).decode("utf-8")
+    state_obj = json.loads(state)
+    redirect_uri: str = state_obj["frontendRedirectURI"]
+
+    url_obj = urlparse(redirect_uri)
+    qparams = parse_qs(url_obj.query)
+    for k, v in form_post_info.items():
+        qparams[k] = [v]
+
+    redirect_uri = url_obj._replace(query=urlencode(qparams, doseq=True)).geturl()
+
+    api_options.response.set_header("Location", redirect_uri)
+    api_options.response.set_status_code(303)
+    api_options.response.set_html_content("")
+
+
+
+async def authorisation_url_get(self, provider: Provider, redirect_uri_on_provider_dashboard: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[AuthorisationUrlGetOkResult, GeneralErrorResponse] +
+
+
+
+ +Expand source code + +
async def authorisation_url_get(
+    self,
+    provider: Provider,
+    redirect_uri_on_provider_dashboard: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+) -> Union[AuthorisationUrlGetOkResult, GeneralErrorResponse]:
+    authorisation_url_info = await provider.get_authorisation_redirect_url(
+        redirect_uri_on_provider_dashboard=redirect_uri_on_provider_dashboard,
+        user_context=user_context,
+    )
+
+    return AuthorisationUrlGetOkResult(
+        url_with_query_params=authorisation_url_info.url_with_query_params,
+        pkce_code_verifier=authorisation_url_info.pkce_code_verifier,
+    )
+
+
+
+async def sign_in_up_post(self, provider: Provider, redirect_uri_info: Optional[RedirectUriInfo], oauth_tokens: Optional[Dict[str, Any]], tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[SignInUpPostOkResult, SignInUpPostNoEmailGivenByProviderResponse, GeneralErrorResponse] +
+
+
+
+ +Expand source code + +
async def sign_in_up_post(
+    self,
+    provider: Provider,
+    redirect_uri_info: Optional[RedirectUriInfo],
+    oauth_tokens: Optional[Dict[str, Any]],
+    tenant_id: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+) -> Union[
+    SignInUpPostOkResult,
+    SignInUpPostNoEmailGivenByProviderResponse,
+    GeneralErrorResponse,
+]:
+    oauth_tokens_to_use: Dict[str, Any] = {}
+
+    if redirect_uri_info is not None:
+        oauth_tokens_to_use = await provider.exchange_auth_code_for_oauth_tokens(
+            redirect_uri_info=redirect_uri_info,
+            user_context=user_context,
+        )
+    else:
+        oauth_tokens_to_use = oauth_tokens  # type: ignore
+
+    user_info = await provider.get_user_info(
+        oauth_tokens=oauth_tokens_to_use,
+        user_context=user_context,
+    )
+
+    if user_info.email is None and provider.config.require_email is False:
+        # We don't expect to get an email from this provider.
+        # So we generate a fake one
+        if provider.config.generate_fake_email is not None:
+            user_info.email = UserInfoEmail(
+                email=await provider.config.generate_fake_email(
+                    tenant_id, user_info.third_party_user_id, user_context
+                ),
+                is_verified=True,
+            )
+
+    email = user_info.email.id if user_info.email is not None else None
+    email_verified = (
+        user_info.email.is_verified if user_info.email is not None else None
+    )
+    if email is None:
+        return SignInUpPostNoEmailGivenByProviderResponse()
+
+    signinup_response = await api_options.recipe_implementation.sign_in_up(
+        third_party_id=provider.id,
+        third_party_user_id=user_info.third_party_user_id,
+        email=email,
+        oauth_tokens=oauth_tokens_to_use,
+        raw_user_info_from_provider=user_info.raw_user_info_from_provider,
+        tenant_id=tenant_id,
+        user_context=user_context,
+    )
+
+    if email_verified:
+        ev_instance = EmailVerificationRecipe.get_instance_optional()
+        if ev_instance is not None:
+            token_response = await ev_instance.recipe_implementation.create_email_verification_token(
+                tenant_id=tenant_id,
+                user_id=signinup_response.user.user_id,
+                email=signinup_response.user.email,
+                user_context=user_context,
+            )
+
+            if isinstance(token_response, CreateEmailVerificationTokenOkResult):
+                await ev_instance.recipe_implementation.verify_email_using_token(
+                    token=token_response.token,
+                    tenant_id=tenant_id,
+                    user_context=user_context,
+                )
+
+    user = signinup_response.user
+    session = await create_new_session(
+        request=api_options.request,
+        tenant_id=tenant_id,
+        user_id=user.user_id,
+        user_context=user_context,
+    )
+
+    return SignInUpPostOkResult(
+        created_new_user=signinup_response.created_new_user,
+        user=user,
+        session=session,
+        oauth_tokens=oauth_tokens_to_use,
+        raw_user_info_from_provider=user_info.raw_user_info_from_provider,
+    )
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/thirdparty/api/index.html b/html/supertokens_python/recipe/thirdparty/api/index.html new file mode 100644 index 000000000..c0ef90f06 --- /dev/null +++ b/html/supertokens_python/recipe/thirdparty/api/index.html @@ -0,0 +1,275 @@ + + + + + + +supertokens_python.recipe.thirdparty.api API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.thirdparty.api

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from .apple_redirect import handle_apple_redirect_api
+from .authorisation_url import handle_authorisation_url_api
+from .signinup import handle_sign_in_up_api
+
+__all__ = [
+    "handle_apple_redirect_api",
+    "handle_authorisation_url_api",
+    "handle_sign_in_up_api",
+]
+
+
+
+

Sub-modules

+
+
supertokens_python.recipe.thirdparty.api.apple_redirect
+
+
+
+
supertokens_python.recipe.thirdparty.api.authorisation_url
+
+
+
+
supertokens_python.recipe.thirdparty.api.implementation
+
+
+
+
supertokens_python.recipe.thirdparty.api.signinup
+
+
+
+
+
+
+
+
+

Functions

+
+
+async def handle_apple_redirect_api(api_implementation: APIInterface, api_options: APIOptions, user_context: Dict[str, Any]) +
+
+
+
+ +Expand source code + +
async def handle_apple_redirect_api(
+    api_implementation: APIInterface,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+):
+    if api_implementation.disable_apple_redirect_handler_post:
+        return None
+
+    body = await api_options.request.form_data()
+
+    # this will redirect the user...
+    await api_implementation.apple_redirect_handler_post(
+        body, api_options, user_context
+    )
+
+    return api_options.response
+
+
+
+async def handle_authorisation_url_api(api_implementation: APIInterface, tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) +
+
+
+
+ +Expand source code + +
async def handle_authorisation_url_api(
+    api_implementation: APIInterface,
+    tenant_id: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+):
+    if api_implementation.disable_authorisation_url_get:
+        return None
+
+    third_party_id = api_options.request.get_query_param("thirdPartyId")
+    redirect_uri_on_provider_dashboard = api_options.request.get_query_param(
+        "redirectURIOnProviderDashboard"
+    )
+    client_type = api_options.request.get_query_param("clientType")
+
+    if third_party_id is None:
+        raise_bad_input_exception("Please provide the thirdPartyId as a GET param")
+
+    if redirect_uri_on_provider_dashboard is None:
+        raise_bad_input_exception(
+            "Please provide the redirectURIOnProviderDashboard as a GET param"
+        )
+
+    provider_response = await api_options.recipe_implementation.get_provider(
+        third_party_id=third_party_id,
+        client_type=client_type,
+        tenant_id=tenant_id,
+        user_context=user_context,
+    )
+
+    if provider_response is None:
+        raise BadInputError(
+            f"the provider {third_party_id} could not be found in the configuration"
+        )
+
+    provider = provider_response
+    result = await api_implementation.authorisation_url_get(
+        provider=provider,
+        redirect_uri_on_provider_dashboard=redirect_uri_on_provider_dashboard,
+        api_options=api_options,
+        user_context=user_context,
+    )
+    return send_200_response(result.to_json(), api_options.response)
+
+
+
+async def handle_sign_in_up_api(api_implementation: APIInterface, tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) +
+
+
+
+ +Expand source code + +
async def handle_sign_in_up_api(
+    api_implementation: APIInterface,
+    tenant_id: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+):
+    if api_implementation.disable_sign_in_up_post:
+        return None
+
+    body = await api_options.request.json()
+    if body is None:
+        raise_bad_input_exception("Please provide a JSON input")
+
+    third_party_id = body.get("thirdPartyId")
+    client_type = body.get("clientType")
+
+    if third_party_id is None or not isinstance(third_party_id, str):
+        raise_bad_input_exception("Please provide the thirdPartyId in request body")
+
+    oauth_tokens = None
+    redirect_uri_info = None
+    if body.get("redirectURIInfo") is not None:
+        if body.get("redirectURIInfo").get("redirectURIOnProviderDashboard") is None:
+            raise_bad_input_exception(
+                "Please provide the redirectURIOnProviderDashboard in request body"
+            )
+        redirect_uri_info = body.get("redirectURIInfo")
+    elif body.get("oAuthTokens") is not None:
+        oauth_tokens = body.get("oAuthTokens")
+    else:
+        raise_bad_input_exception(
+            "Please provide one of redirectURIInfo or oAuthTokens in the request body"
+        )
+
+    provider_response = await api_options.recipe_implementation.get_provider(
+        third_party_id=third_party_id,
+        client_type=client_type,
+        tenant_id=tenant_id,
+        user_context=user_context,
+    )
+
+    if provider_response is None:
+        raise BadInputError(
+            f"the provider {third_party_id} could not be found in the configuration"
+        )
+
+    provider = provider_response
+
+    if redirect_uri_info is not None:
+        redirect_uri_info = RedirectUriInfo(
+            redirect_uri_on_provider_dashboard=redirect_uri_info.get(
+                "redirectURIOnProviderDashboard"
+            ),
+            redirect_uri_query_params=redirect_uri_info.get("redirectURIQueryParams"),
+            pkce_code_verifier=redirect_uri_info.get("pkceCodeVerifier"),
+        )
+
+    result = await api_implementation.sign_in_up_post(
+        provider=provider,
+        redirect_uri_info=redirect_uri_info,
+        oauth_tokens=oauth_tokens,
+        tenant_id=tenant_id,
+        api_options=api_options,
+        user_context=user_context,
+    )
+    return send_200_response(result.to_json(), api_options.response)
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/thirdparty/api/signinup.html b/html/supertokens_python/recipe/thirdparty/api/signinup.html new file mode 100644 index 000000000..df45e0cad --- /dev/null +++ b/html/supertokens_python/recipe/thirdparty/api/signinup.html @@ -0,0 +1,234 @@ + + + + + + +supertokens_python.recipe.thirdparty.api.signinup API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.thirdparty.api.signinup

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Any, Dict
+from supertokens_python.recipe.thirdparty.provider import RedirectUriInfo
+
+if TYPE_CHECKING:
+    from supertokens_python.recipe.thirdparty.interfaces import APIOptions, APIInterface
+
+from supertokens_python.exceptions import raise_bad_input_exception, BadInputError
+from supertokens_python.utils import send_200_response
+
+
+async def handle_sign_in_up_api(
+    api_implementation: APIInterface,
+    tenant_id: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+):
+    if api_implementation.disable_sign_in_up_post:
+        return None
+
+    body = await api_options.request.json()
+    if body is None:
+        raise_bad_input_exception("Please provide a JSON input")
+
+    third_party_id = body.get("thirdPartyId")
+    client_type = body.get("clientType")
+
+    if third_party_id is None or not isinstance(third_party_id, str):
+        raise_bad_input_exception("Please provide the thirdPartyId in request body")
+
+    oauth_tokens = None
+    redirect_uri_info = None
+    if body.get("redirectURIInfo") is not None:
+        if body.get("redirectURIInfo").get("redirectURIOnProviderDashboard") is None:
+            raise_bad_input_exception(
+                "Please provide the redirectURIOnProviderDashboard in request body"
+            )
+        redirect_uri_info = body.get("redirectURIInfo")
+    elif body.get("oAuthTokens") is not None:
+        oauth_tokens = body.get("oAuthTokens")
+    else:
+        raise_bad_input_exception(
+            "Please provide one of redirectURIInfo or oAuthTokens in the request body"
+        )
+
+    provider_response = await api_options.recipe_implementation.get_provider(
+        third_party_id=third_party_id,
+        client_type=client_type,
+        tenant_id=tenant_id,
+        user_context=user_context,
+    )
+
+    if provider_response is None:
+        raise BadInputError(
+            f"the provider {third_party_id} could not be found in the configuration"
+        )
+
+    provider = provider_response
+
+    if redirect_uri_info is not None:
+        redirect_uri_info = RedirectUriInfo(
+            redirect_uri_on_provider_dashboard=redirect_uri_info.get(
+                "redirectURIOnProviderDashboard"
+            ),
+            redirect_uri_query_params=redirect_uri_info.get("redirectURIQueryParams"),
+            pkce_code_verifier=redirect_uri_info.get("pkceCodeVerifier"),
+        )
+
+    result = await api_implementation.sign_in_up_post(
+        provider=provider,
+        redirect_uri_info=redirect_uri_info,
+        oauth_tokens=oauth_tokens,
+        tenant_id=tenant_id,
+        api_options=api_options,
+        user_context=user_context,
+    )
+    return send_200_response(result.to_json(), api_options.response)
+
+
+
+
+
+
+
+

Functions

+
+
+async def handle_sign_in_up_api(api_implementation: APIInterface, tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) +
+
+
+
+ +Expand source code + +
async def handle_sign_in_up_api(
+    api_implementation: APIInterface,
+    tenant_id: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+):
+    if api_implementation.disable_sign_in_up_post:
+        return None
+
+    body = await api_options.request.json()
+    if body is None:
+        raise_bad_input_exception("Please provide a JSON input")
+
+    third_party_id = body.get("thirdPartyId")
+    client_type = body.get("clientType")
+
+    if third_party_id is None or not isinstance(third_party_id, str):
+        raise_bad_input_exception("Please provide the thirdPartyId in request body")
+
+    oauth_tokens = None
+    redirect_uri_info = None
+    if body.get("redirectURIInfo") is not None:
+        if body.get("redirectURIInfo").get("redirectURIOnProviderDashboard") is None:
+            raise_bad_input_exception(
+                "Please provide the redirectURIOnProviderDashboard in request body"
+            )
+        redirect_uri_info = body.get("redirectURIInfo")
+    elif body.get("oAuthTokens") is not None:
+        oauth_tokens = body.get("oAuthTokens")
+    else:
+        raise_bad_input_exception(
+            "Please provide one of redirectURIInfo or oAuthTokens in the request body"
+        )
+
+    provider_response = await api_options.recipe_implementation.get_provider(
+        third_party_id=third_party_id,
+        client_type=client_type,
+        tenant_id=tenant_id,
+        user_context=user_context,
+    )
+
+    if provider_response is None:
+        raise BadInputError(
+            f"the provider {third_party_id} could not be found in the configuration"
+        )
+
+    provider = provider_response
+
+    if redirect_uri_info is not None:
+        redirect_uri_info = RedirectUriInfo(
+            redirect_uri_on_provider_dashboard=redirect_uri_info.get(
+                "redirectURIOnProviderDashboard"
+            ),
+            redirect_uri_query_params=redirect_uri_info.get("redirectURIQueryParams"),
+            pkce_code_verifier=redirect_uri_info.get("pkceCodeVerifier"),
+        )
+
+    result = await api_implementation.sign_in_up_post(
+        provider=provider,
+        redirect_uri_info=redirect_uri_info,
+        oauth_tokens=oauth_tokens,
+        tenant_id=tenant_id,
+        api_options=api_options,
+        user_context=user_context,
+    )
+    return send_200_response(result.to_json(), api_options.response)
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/thirdparty/asyncio/index.html b/html/supertokens_python/recipe/thirdparty/asyncio/index.html new file mode 100644 index 000000000..621358407 --- /dev/null +++ b/html/supertokens_python/recipe/thirdparty/asyncio/index.html @@ -0,0 +1,272 @@ + + + + + + +supertokens_python.recipe.thirdparty.asyncio API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.thirdparty.asyncio

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from typing import Any, Dict, List, Optional, Union
+
+from supertokens_python.recipe.thirdparty.recipe import ThirdPartyRecipe
+
+from ..types import User
+
+
+async def get_user_by_id(
+    user_id: str, user_context: Union[None, Dict[str, Any]] = None
+) -> Union[User, None]:
+    if user_context is None:
+        user_context = {}
+    return await ThirdPartyRecipe.get_instance().recipe_implementation.get_user_by_id(
+        user_id, user_context
+    )
+
+
+async def get_users_by_email(
+    tenant_id: str, email: str, user_context: Union[None, Dict[str, Any]] = None
+) -> List[User]:
+    if user_context is None:
+        user_context = {}
+    return (
+        await ThirdPartyRecipe.get_instance().recipe_implementation.get_users_by_email(
+            email, tenant_id, user_context
+        )
+    )
+
+
+async def get_user_by_third_party_info(
+    tenant_id: str,
+    third_party_id: str,
+    third_party_user_id: str,
+    user_context: Union[None, Dict[str, Any]] = None,
+):
+    if user_context is None:
+        user_context = {}
+    return await ThirdPartyRecipe.get_instance().recipe_implementation.get_user_by_thirdparty_info(
+        third_party_id,
+        third_party_user_id,
+        tenant_id,
+        user_context,
+    )
+
+
+async def manually_create_or_update_user(
+    tenant_id: str,
+    third_party_id: str,
+    third_party_user_id: str,
+    email: str,
+    user_context: Union[None, Dict[str, Any]] = None,
+):
+    if user_context is None:
+        user_context = {}
+    return await ThirdPartyRecipe.get_instance().recipe_implementation.manually_create_or_update_user(
+        third_party_id,
+        third_party_user_id,
+        email,
+        tenant_id,
+        user_context,
+    )
+
+
+async def get_provider(
+    tenant_id: str,
+    third_party_id: str,
+    client_type: Optional[str] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+):
+    if user_context is None:
+        user_context = {}
+    return await ThirdPartyRecipe.get_instance().recipe_implementation.get_provider(
+        third_party_id, client_type, tenant_id, user_context
+    )
+
+
+
+
+
+
+
+

Functions

+
+
+async def get_provider(tenant_id: str, third_party_id: str, client_type: Optional[str] = None, user_context: Optional[Dict[str, Any]] = None) +
+
+
+
+ +Expand source code + +
async def get_provider(
+    tenant_id: str,
+    third_party_id: str,
+    client_type: Optional[str] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+):
+    if user_context is None:
+        user_context = {}
+    return await ThirdPartyRecipe.get_instance().recipe_implementation.get_provider(
+        third_party_id, client_type, tenant_id, user_context
+    )
+
+
+
+async def get_user_by_id(user_id: str, user_context: Optional[Dict[str, Any]] = None) ‑> Optional[User] +
+
+
+
+ +Expand source code + +
async def get_user_by_id(
+    user_id: str, user_context: Union[None, Dict[str, Any]] = None
+) -> Union[User, None]:
+    if user_context is None:
+        user_context = {}
+    return await ThirdPartyRecipe.get_instance().recipe_implementation.get_user_by_id(
+        user_id, user_context
+    )
+
+
+
+async def get_user_by_third_party_info(tenant_id: str, third_party_id: str, third_party_user_id: str, user_context: Optional[Dict[str, Any]] = None) +
+
+
+
+ +Expand source code + +
async def get_user_by_third_party_info(
+    tenant_id: str,
+    third_party_id: str,
+    third_party_user_id: str,
+    user_context: Union[None, Dict[str, Any]] = None,
+):
+    if user_context is None:
+        user_context = {}
+    return await ThirdPartyRecipe.get_instance().recipe_implementation.get_user_by_thirdparty_info(
+        third_party_id,
+        third_party_user_id,
+        tenant_id,
+        user_context,
+    )
+
+
+
+async def get_users_by_email(tenant_id: str, email: str, user_context: Optional[Dict[str, Any]] = None) ‑> List[User] +
+
+
+
+ +Expand source code + +
async def get_users_by_email(
+    tenant_id: str, email: str, user_context: Union[None, Dict[str, Any]] = None
+) -> List[User]:
+    if user_context is None:
+        user_context = {}
+    return (
+        await ThirdPartyRecipe.get_instance().recipe_implementation.get_users_by_email(
+            email, tenant_id, user_context
+        )
+    )
+
+
+
+async def manually_create_or_update_user(tenant_id: str, third_party_id: str, third_party_user_id: str, email: str, user_context: Optional[Dict[str, Any]] = None) +
+
+
+
+ +Expand source code + +
async def manually_create_or_update_user(
+    tenant_id: str,
+    third_party_id: str,
+    third_party_user_id: str,
+    email: str,
+    user_context: Union[None, Dict[str, Any]] = None,
+):
+    if user_context is None:
+        user_context = {}
+    return await ThirdPartyRecipe.get_instance().recipe_implementation.manually_create_or_update_user(
+        third_party_id,
+        third_party_user_id,
+        email,
+        tenant_id,
+        user_context,
+    )
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/thirdparty/constants.html b/html/supertokens_python/recipe/thirdparty/constants.html new file mode 100644 index 000000000..8c400fa86 --- /dev/null +++ b/html/supertokens_python/recipe/thirdparty/constants.html @@ -0,0 +1,77 @@ + + + + + + +supertokens_python.recipe.thirdparty.constants API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.thirdparty.constants

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+AUTHORISATIONURL = "/authorisationurl"
+SIGNINUP = "/signinup"
+SIGNOUT = "/signout"
+SIGNUP_EMAIL_EXISTS = "/signup/email/exists"
+RESET_PASSWORD = "/reset-password"
+APPLE_REDIRECT_HANDLER = "/callback/apple"
+
+
+
+
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/thirdparty/exceptions.html b/html/supertokens_python/recipe/thirdparty/exceptions.html new file mode 100644 index 000000000..0b8ae4059 --- /dev/null +++ b/html/supertokens_python/recipe/thirdparty/exceptions.html @@ -0,0 +1,139 @@ + + + + + + +supertokens_python.recipe.thirdparty.exceptions API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.thirdparty.exceptions

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from supertokens_python.exceptions import SuperTokensError
+
+
+class SuperTokensThirdPartyError(SuperTokensError):
+    pass
+
+
+class ClientTypeNotFoundError(SuperTokensError):
+    def __init__(self, message: str):
+        super().__init__()
+        self.message = message
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class ClientTypeNotFoundError +(message: str) +
+
+

Common base class for all non-exit exceptions.

+
+ +Expand source code + +
class ClientTypeNotFoundError(SuperTokensError):
+    def __init__(self, message: str):
+        super().__init__()
+        self.message = message
+
+

Ancestors

+ +
+
+class SuperTokensThirdPartyError +(*args, **kwargs) +
+
+

Common base class for all non-exit exceptions.

+
+ +Expand source code + +
class SuperTokensThirdPartyError(SuperTokensError):
+    pass
+
+

Ancestors

+ +
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/thirdparty/index.html b/html/supertokens_python/recipe/thirdparty/index.html new file mode 100644 index 000000000..24fc6586f --- /dev/null +++ b/html/supertokens_python/recipe/thirdparty/index.html @@ -0,0 +1,194 @@ + + + + + + +supertokens_python.recipe.thirdparty API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.thirdparty

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Callable, Optional, Union
+
+
+from . import exceptions as ex
+from . import utils, provider
+from .recipe import ThirdPartyRecipe
+
+InputOverrideConfig = utils.InputOverrideConfig
+SignInAndUpFeature = utils.SignInAndUpFeature
+ProviderInput = provider.ProviderInput
+ProviderConfig = provider.ProviderConfig
+ProviderClientConfig = provider.ProviderClientConfig
+exceptions = ex
+
+if TYPE_CHECKING:
+    from supertokens_python.supertokens import AppInfo
+
+    from ...recipe_module import RecipeModule
+
+
+def init(
+    sign_in_and_up_feature: Optional[SignInAndUpFeature] = None,
+    override: Union[InputOverrideConfig, None] = None,
+) -> Callable[[AppInfo], RecipeModule]:
+    if sign_in_and_up_feature is None:
+        sign_in_and_up_feature = SignInAndUpFeature()
+    return ThirdPartyRecipe.init(sign_in_and_up_feature, override)
+
+
+
+

Sub-modules

+
+
supertokens_python.recipe.thirdparty.api
+
+
+
+
supertokens_python.recipe.thirdparty.asyncio
+
+
+
+
supertokens_python.recipe.thirdparty.constants
+
+
+
+
supertokens_python.recipe.thirdparty.exceptions
+
+
+
+
supertokens_python.recipe.thirdparty.interfaces
+
+
+
+
supertokens_python.recipe.thirdparty.provider
+
+
+
+
supertokens_python.recipe.thirdparty.providers
+
+
+
+
supertokens_python.recipe.thirdparty.recipe
+
+
+
+
supertokens_python.recipe.thirdparty.recipe_implementation
+
+
+
+
supertokens_python.recipe.thirdparty.syncio
+
+
+
+
supertokens_python.recipe.thirdparty.types
+
+
+
+
supertokens_python.recipe.thirdparty.utils
+
+
+
+
+
+
+
+
+

Functions

+
+
+def init(sign_in_and_up_feature: Optional[SignInAndUpFeature] = None, override: Union[InputOverrideConfig, None] = None) ‑> Callable[[AppInfo], RecipeModule] +
+
+
+
+ +Expand source code + +
def init(
+    sign_in_and_up_feature: Optional[SignInAndUpFeature] = None,
+    override: Union[InputOverrideConfig, None] = None,
+) -> Callable[[AppInfo], RecipeModule]:
+    if sign_in_and_up_feature is None:
+        sign_in_and_up_feature = SignInAndUpFeature()
+    return ThirdPartyRecipe.init(sign_in_and_up_feature, override)
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/thirdparty/interfaces.html b/html/supertokens_python/recipe/thirdparty/interfaces.html new file mode 100644 index 000000000..02434433c --- /dev/null +++ b/html/supertokens_python/recipe/thirdparty/interfaces.html @@ -0,0 +1,953 @@ + + + + + + +supertokens_python.recipe.thirdparty.interfaces API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.thirdparty.interfaces

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from abc import ABC, abstractmethod
+from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union
+
+from ...types import APIResponse, GeneralErrorResponse
+from .provider import Provider, ProviderInput, RedirectUriInfo
+
+if TYPE_CHECKING:
+    from supertokens_python.framework import BaseRequest, BaseResponse
+    from supertokens_python.recipe.session import SessionContainer
+    from supertokens_python.supertokens import AppInfo
+
+    from .types import User, RawUserInfoFromProvider
+    from .utils import ThirdPartyConfig
+
+
+class SignInUpOkResult:
+    def __init__(
+        self,
+        user: User,
+        created_new_user: bool,
+        oauth_tokens: Dict[str, Any],
+        raw_user_info_from_provider: RawUserInfoFromProvider,
+    ):
+        self.user = user
+        self.created_new_user = created_new_user
+        self.oauth_tokens = oauth_tokens
+        self.raw_user_info_from_provider = raw_user_info_from_provider
+
+
+class ManuallyCreateOrUpdateUserOkResult:
+    def __init__(
+        self,
+        user: User,
+        created_new_user: bool,
+    ):
+        self.user = user
+        self.created_new_user = created_new_user
+
+
+class GetProviderOkResult:
+    def __init__(self, provider: Provider):
+        self.provider = provider
+
+
+class RecipeInterface(ABC):
+    def __init__(self):
+        pass
+
+    @abstractmethod
+    async def get_user_by_id(
+        self, user_id: str, user_context: Dict[str, Any]
+    ) -> Union[User, None]:
+        pass
+
+    @abstractmethod
+    async def get_users_by_email(
+        self, email: str, tenant_id: str, user_context: Dict[str, Any]
+    ) -> List[User]:
+        pass
+
+    @abstractmethod
+    async def get_user_by_thirdparty_info(
+        self,
+        third_party_id: str,
+        third_party_user_id: str,
+        tenant_id: str,
+        user_context: Dict[str, Any],
+    ) -> Union[User, None]:
+        pass
+
+    @abstractmethod
+    async def manually_create_or_update_user(
+        self,
+        third_party_id: str,
+        third_party_user_id: str,
+        email: str,
+        tenant_id: str,
+        user_context: Dict[str, Any],
+    ) -> ManuallyCreateOrUpdateUserOkResult:
+        pass
+
+    @abstractmethod
+    async def sign_in_up(
+        self,
+        third_party_id: str,
+        third_party_user_id: str,
+        email: str,
+        oauth_tokens: Dict[str, Any],
+        raw_user_info_from_provider: RawUserInfoFromProvider,
+        tenant_id: str,
+        user_context: Dict[str, Any],
+    ) -> SignInUpOkResult:
+        pass
+
+    @abstractmethod
+    async def get_provider(
+        self,
+        third_party_id: str,
+        client_type: Optional[str],
+        tenant_id: str,
+        user_context: Dict[str, Any],
+    ) -> Optional[Provider]:
+        pass
+
+
+class APIOptions:
+    def __init__(
+        self,
+        request: BaseRequest,
+        response: BaseResponse,
+        recipe_id: str,
+        config: ThirdPartyConfig,
+        recipe_implementation: RecipeInterface,
+        providers: List[ProviderInput],
+        app_info: AppInfo,
+    ):
+        self.request: BaseRequest = request
+        self.response: BaseResponse = response
+        self.recipe_id: str = recipe_id
+        self.config: ThirdPartyConfig = config
+        self.providers: List[ProviderInput] = providers
+        self.recipe_implementation: RecipeInterface = recipe_implementation
+        self.app_info: AppInfo = app_info
+
+
+class SignInUpPostOkResult(APIResponse):
+    status: str = "OK"
+
+    def __init__(
+        self,
+        user: User,
+        created_new_user: bool,
+        session: SessionContainer,
+        oauth_tokens: Dict[str, Any],
+        raw_user_info_from_provider: RawUserInfoFromProvider,
+    ):
+        self.user = user
+        self.created_new_user = created_new_user
+        self.session = session
+        self.oauth_tokens = oauth_tokens
+        self.raw_user_info_from_provider = raw_user_info_from_provider
+
+    def to_json(self) -> Dict[str, Any]:
+        return {
+            "status": self.status,
+            "user": {
+                "id": self.user.user_id,
+                "email": self.user.email,
+                "timeJoined": self.user.time_joined,
+                "thirdParty": {
+                    "id": self.user.third_party_info.id,
+                    "userId": self.user.third_party_info.user_id,
+                },
+            },
+            "createdNewUser": self.created_new_user,
+        }
+
+
+class SignInUpPostNoEmailGivenByProviderResponse(APIResponse):
+    status: str = "NO_EMAIL_GIVEN_BY_PROVIDER"
+
+    def to_json(self) -> Dict[str, Any]:
+        return {"status": self.status}
+
+
+class AuthorisationUrlGetOkResult(APIResponse):
+    status: str = "OK"
+
+    def __init__(
+        self, url_with_query_params: str, pkce_code_verifier: Optional[str] = None
+    ):
+        self.url_with_query_params = url_with_query_params
+        self.pkce_code_verifier = pkce_code_verifier
+
+    def to_json(self):
+        return {
+            "status": self.status,
+            "urlWithQueryParams": self.url_with_query_params,
+            "pkceCodeVerifier": self.pkce_code_verifier,
+        }
+
+
+class APIInterface:
+    def __init__(self):
+        self.disable_sign_in_up_post = False
+        self.disable_authorisation_url_get = False
+        self.disable_apple_redirect_handler_post = False
+
+    @abstractmethod
+    async def authorisation_url_get(
+        self,
+        provider: Provider,
+        redirect_uri_on_provider_dashboard: str,
+        api_options: APIOptions,
+        user_context: Dict[str, Any],
+    ) -> Union[AuthorisationUrlGetOkResult, GeneralErrorResponse]:
+        pass
+
+    @abstractmethod
+    async def sign_in_up_post(
+        self,
+        provider: Provider,
+        redirect_uri_info: Optional[RedirectUriInfo],
+        oauth_tokens: Optional[Dict[str, Any]],
+        tenant_id: str,
+        api_options: APIOptions,
+        user_context: Dict[str, Any],
+    ) -> Union[
+        SignInUpPostOkResult,
+        SignInUpPostNoEmailGivenByProviderResponse,
+        GeneralErrorResponse,
+    ]:
+        pass
+
+    @abstractmethod
+    async def apple_redirect_handler_post(
+        self,
+        form_post_info: Dict[str, Any],
+        api_options: APIOptions,
+        user_context: Dict[str, Any],
+    ):
+        pass
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class APIInterface +
+
+
+
+ +Expand source code + +
class APIInterface:
+    def __init__(self):
+        self.disable_sign_in_up_post = False
+        self.disable_authorisation_url_get = False
+        self.disable_apple_redirect_handler_post = False
+
+    @abstractmethod
+    async def authorisation_url_get(
+        self,
+        provider: Provider,
+        redirect_uri_on_provider_dashboard: str,
+        api_options: APIOptions,
+        user_context: Dict[str, Any],
+    ) -> Union[AuthorisationUrlGetOkResult, GeneralErrorResponse]:
+        pass
+
+    @abstractmethod
+    async def sign_in_up_post(
+        self,
+        provider: Provider,
+        redirect_uri_info: Optional[RedirectUriInfo],
+        oauth_tokens: Optional[Dict[str, Any]],
+        tenant_id: str,
+        api_options: APIOptions,
+        user_context: Dict[str, Any],
+    ) -> Union[
+        SignInUpPostOkResult,
+        SignInUpPostNoEmailGivenByProviderResponse,
+        GeneralErrorResponse,
+    ]:
+        pass
+
+    @abstractmethod
+    async def apple_redirect_handler_post(
+        self,
+        form_post_info: Dict[str, Any],
+        api_options: APIOptions,
+        user_context: Dict[str, Any],
+    ):
+        pass
+
+

Subclasses

+ +

Methods

+
+
+async def apple_redirect_handler_post(self, form_post_info: Dict[str, Any], api_options: APIOptions, user_context: Dict[str, Any]) +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def apple_redirect_handler_post(
+    self,
+    form_post_info: Dict[str, Any],
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+):
+    pass
+
+
+
+async def authorisation_url_get(self, provider: Provider, redirect_uri_on_provider_dashboard: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[AuthorisationUrlGetOkResultGeneralErrorResponse] +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def authorisation_url_get(
+    self,
+    provider: Provider,
+    redirect_uri_on_provider_dashboard: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+) -> Union[AuthorisationUrlGetOkResult, GeneralErrorResponse]:
+    pass
+
+
+
+async def sign_in_up_post(self, provider: Provider, redirect_uri_info: Optional[RedirectUriInfo], oauth_tokens: Optional[Dict[str, Any]], tenant_id: str, api_options: APIOptions, user_context: Dict[str, Any]) ‑> Union[SignInUpPostOkResultSignInUpPostNoEmailGivenByProviderResponseGeneralErrorResponse] +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def sign_in_up_post(
+    self,
+    provider: Provider,
+    redirect_uri_info: Optional[RedirectUriInfo],
+    oauth_tokens: Optional[Dict[str, Any]],
+    tenant_id: str,
+    api_options: APIOptions,
+    user_context: Dict[str, Any],
+) -> Union[
+    SignInUpPostOkResult,
+    SignInUpPostNoEmailGivenByProviderResponse,
+    GeneralErrorResponse,
+]:
+    pass
+
+
+
+
+
+class APIOptions +(request: BaseRequest, response: BaseResponse, recipe_id: str, config: ThirdPartyConfig, recipe_implementation: RecipeInterface, providers: List[ProviderInput], app_info: AppInfo) +
+
+
+
+ +Expand source code + +
class APIOptions:
+    def __init__(
+        self,
+        request: BaseRequest,
+        response: BaseResponse,
+        recipe_id: str,
+        config: ThirdPartyConfig,
+        recipe_implementation: RecipeInterface,
+        providers: List[ProviderInput],
+        app_info: AppInfo,
+    ):
+        self.request: BaseRequest = request
+        self.response: BaseResponse = response
+        self.recipe_id: str = recipe_id
+        self.config: ThirdPartyConfig = config
+        self.providers: List[ProviderInput] = providers
+        self.recipe_implementation: RecipeInterface = recipe_implementation
+        self.app_info: AppInfo = app_info
+
+
+
+class AuthorisationUrlGetOkResult +(url_with_query_params: str, pkce_code_verifier: Optional[str] = None) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class AuthorisationUrlGetOkResult(APIResponse):
+    status: str = "OK"
+
+    def __init__(
+        self, url_with_query_params: str, pkce_code_verifier: Optional[str] = None
+    ):
+        self.url_with_query_params = url_with_query_params
+        self.pkce_code_verifier = pkce_code_verifier
+
+    def to_json(self):
+        return {
+            "status": self.status,
+            "urlWithQueryParams": self.url_with_query_params,
+            "pkceCodeVerifier": self.pkce_code_verifier,
+        }
+
+

Ancestors

+ +

Class variables

+
+
var status : str
+
+
+
+
+

Methods

+
+
+def to_json(self) +
+
+
+
+ +Expand source code + +
def to_json(self):
+    return {
+        "status": self.status,
+        "urlWithQueryParams": self.url_with_query_params,
+        "pkceCodeVerifier": self.pkce_code_verifier,
+    }
+
+
+
+
+
+class GetProviderOkResult +(provider: Provider) +
+
+
+
+ +Expand source code + +
class GetProviderOkResult:
+    def __init__(self, provider: Provider):
+        self.provider = provider
+
+
+
+class ManuallyCreateOrUpdateUserOkResult +(user: User, created_new_user: bool) +
+
+
+
+ +Expand source code + +
class ManuallyCreateOrUpdateUserOkResult:
+    def __init__(
+        self,
+        user: User,
+        created_new_user: bool,
+    ):
+        self.user = user
+        self.created_new_user = created_new_user
+
+
+
+class RecipeInterface +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class RecipeInterface(ABC):
+    def __init__(self):
+        pass
+
+    @abstractmethod
+    async def get_user_by_id(
+        self, user_id: str, user_context: Dict[str, Any]
+    ) -> Union[User, None]:
+        pass
+
+    @abstractmethod
+    async def get_users_by_email(
+        self, email: str, tenant_id: str, user_context: Dict[str, Any]
+    ) -> List[User]:
+        pass
+
+    @abstractmethod
+    async def get_user_by_thirdparty_info(
+        self,
+        third_party_id: str,
+        third_party_user_id: str,
+        tenant_id: str,
+        user_context: Dict[str, Any],
+    ) -> Union[User, None]:
+        pass
+
+    @abstractmethod
+    async def manually_create_or_update_user(
+        self,
+        third_party_id: str,
+        third_party_user_id: str,
+        email: str,
+        tenant_id: str,
+        user_context: Dict[str, Any],
+    ) -> ManuallyCreateOrUpdateUserOkResult:
+        pass
+
+    @abstractmethod
+    async def sign_in_up(
+        self,
+        third_party_id: str,
+        third_party_user_id: str,
+        email: str,
+        oauth_tokens: Dict[str, Any],
+        raw_user_info_from_provider: RawUserInfoFromProvider,
+        tenant_id: str,
+        user_context: Dict[str, Any],
+    ) -> SignInUpOkResult:
+        pass
+
+    @abstractmethod
+    async def get_provider(
+        self,
+        third_party_id: str,
+        client_type: Optional[str],
+        tenant_id: str,
+        user_context: Dict[str, Any],
+    ) -> Optional[Provider]:
+        pass
+
+

Ancestors

+
    +
  • abc.ABC
  • +
+

Subclasses

+ +

Methods

+
+
+async def get_provider(self, third_party_id: str, client_type: Optional[str], tenant_id: str, user_context: Dict[str, Any]) ‑> Optional[Provider] +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def get_provider(
+    self,
+    third_party_id: str,
+    client_type: Optional[str],
+    tenant_id: str,
+    user_context: Dict[str, Any],
+) -> Optional[Provider]:
+    pass
+
+
+
+async def get_user_by_id(self, user_id: str, user_context: Dict[str, Any]) ‑> Union[User, None] +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def get_user_by_id(
+    self, user_id: str, user_context: Dict[str, Any]
+) -> Union[User, None]:
+    pass
+
+
+
+async def get_user_by_thirdparty_info(self, third_party_id: str, third_party_user_id: str, tenant_id: str, user_context: Dict[str, Any]) ‑> Union[User, None] +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def get_user_by_thirdparty_info(
+    self,
+    third_party_id: str,
+    third_party_user_id: str,
+    tenant_id: str,
+    user_context: Dict[str, Any],
+) -> Union[User, None]:
+    pass
+
+
+
+async def get_users_by_email(self, email: str, tenant_id: str, user_context: Dict[str, Any]) ‑> List[User] +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def get_users_by_email(
+    self, email: str, tenant_id: str, user_context: Dict[str, Any]
+) -> List[User]:
+    pass
+
+
+
+async def manually_create_or_update_user(self, third_party_id: str, third_party_user_id: str, email: str, tenant_id: str, user_context: Dict[str, Any]) ‑> ManuallyCreateOrUpdateUserOkResult +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def manually_create_or_update_user(
+    self,
+    third_party_id: str,
+    third_party_user_id: str,
+    email: str,
+    tenant_id: str,
+    user_context: Dict[str, Any],
+) -> ManuallyCreateOrUpdateUserOkResult:
+    pass
+
+
+
+async def sign_in_up(self, third_party_id: str, third_party_user_id: str, email: str, oauth_tokens: Dict[str, Any], raw_user_info_from_provider: RawUserInfoFromProvider, tenant_id: str, user_context: Dict[str, Any]) ‑> SignInUpOkResult +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def sign_in_up(
+    self,
+    third_party_id: str,
+    third_party_user_id: str,
+    email: str,
+    oauth_tokens: Dict[str, Any],
+    raw_user_info_from_provider: RawUserInfoFromProvider,
+    tenant_id: str,
+    user_context: Dict[str, Any],
+) -> SignInUpOkResult:
+    pass
+
+
+
+
+
+class SignInUpOkResult +(user: User, created_new_user: bool, oauth_tokens: Dict[str, Any], raw_user_info_from_provider: RawUserInfoFromProvider) +
+
+
+
+ +Expand source code + +
class SignInUpOkResult:
+    def __init__(
+        self,
+        user: User,
+        created_new_user: bool,
+        oauth_tokens: Dict[str, Any],
+        raw_user_info_from_provider: RawUserInfoFromProvider,
+    ):
+        self.user = user
+        self.created_new_user = created_new_user
+        self.oauth_tokens = oauth_tokens
+        self.raw_user_info_from_provider = raw_user_info_from_provider
+
+
+
+class SignInUpPostNoEmailGivenByProviderResponse +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class SignInUpPostNoEmailGivenByProviderResponse(APIResponse):
+    status: str = "NO_EMAIL_GIVEN_BY_PROVIDER"
+
+    def to_json(self) -> Dict[str, Any]:
+        return {"status": self.status}
+
+

Ancestors

+ +

Class variables

+
+
var status : str
+
+
+
+
+

Methods

+
+
+def to_json(self) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
def to_json(self) -> Dict[str, Any]:
+    return {"status": self.status}
+
+
+
+
+
+class SignInUpPostOkResult +(user: User, created_new_user: bool, session: SessionContainer, oauth_tokens: Dict[str, Any], raw_user_info_from_provider: RawUserInfoFromProvider) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class SignInUpPostOkResult(APIResponse):
+    status: str = "OK"
+
+    def __init__(
+        self,
+        user: User,
+        created_new_user: bool,
+        session: SessionContainer,
+        oauth_tokens: Dict[str, Any],
+        raw_user_info_from_provider: RawUserInfoFromProvider,
+    ):
+        self.user = user
+        self.created_new_user = created_new_user
+        self.session = session
+        self.oauth_tokens = oauth_tokens
+        self.raw_user_info_from_provider = raw_user_info_from_provider
+
+    def to_json(self) -> Dict[str, Any]:
+        return {
+            "status": self.status,
+            "user": {
+                "id": self.user.user_id,
+                "email": self.user.email,
+                "timeJoined": self.user.time_joined,
+                "thirdParty": {
+                    "id": self.user.third_party_info.id,
+                    "userId": self.user.third_party_info.user_id,
+                },
+            },
+            "createdNewUser": self.created_new_user,
+        }
+
+

Ancestors

+ +

Class variables

+
+
var status : str
+
+
+
+
+

Methods

+
+
+def to_json(self) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
def to_json(self) -> Dict[str, Any]:
+    return {
+        "status": self.status,
+        "user": {
+            "id": self.user.user_id,
+            "email": self.user.email,
+            "timeJoined": self.user.time_joined,
+            "thirdParty": {
+                "id": self.user.third_party_info.id,
+                "userId": self.user.third_party_info.user_id,
+            },
+        },
+        "createdNewUser": self.created_new_user,
+    }
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/thirdparty/provider.html b/html/supertokens_python/recipe/thirdparty/provider.html new file mode 100644 index 000000000..47c10c2e9 --- /dev/null +++ b/html/supertokens_python/recipe/thirdparty/provider.html @@ -0,0 +1,1193 @@ + + + + + + +supertokens_python.recipe.thirdparty.provider API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.thirdparty.provider

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Any, Awaitable, Dict, Union, Optional, List, Callable
+
+if TYPE_CHECKING:
+    from .types import UserInfo
+
+
+class AuthorisationRedirect:
+    def __init__(
+        self, url_with_query_params: str, pkce_code_verifier: Optional[str] = None
+    ):
+        self.url_with_query_params = url_with_query_params
+        self.pkce_code_verifier = pkce_code_verifier
+
+
+class RedirectUriInfo:
+    def __init__(
+        self,
+        redirect_uri_on_provider_dashboard: str,
+        redirect_uri_query_params: Dict[str, Any],
+        pkce_code_verifier: Optional[str] = None,
+    ):
+        self.redirect_uri_on_provider_dashboard = redirect_uri_on_provider_dashboard
+        self.redirect_uri_query_params = redirect_uri_query_params
+        self.pkce_code_verifier = pkce_code_verifier
+
+
+class Provider:
+    def __init__(
+        self, id: str, config: ProviderConfigForClient
+    ):  # pylint: disable=redefined-builtin
+        self.id = id
+        self.config = config
+
+    async def get_config_for_client_type(  # pylint: disable=no-self-use
+        self, client_type: Optional[str], user_context: Dict[str, Any]
+    ) -> ProviderConfigForClient:
+        _ = client_type
+        __ = user_context
+        raise NotImplementedError()
+
+    async def get_authorisation_redirect_url(  # pylint: disable=no-self-use
+        self,
+        redirect_uri_on_provider_dashboard: str,
+        user_context: Dict[str, Any],
+    ) -> AuthorisationRedirect:
+        _ = redirect_uri_on_provider_dashboard
+        __ = user_context
+        raise NotImplementedError()
+
+    async def exchange_auth_code_for_oauth_tokens(  # pylint: disable=no-self-use
+        self,
+        redirect_uri_info: RedirectUriInfo,
+        user_context: Dict[str, Any],
+    ) -> Dict[str, Any]:
+        _ = redirect_uri_info
+        __ = user_context
+        raise NotImplementedError()
+
+    async def get_user_info(  # pylint: disable=no-self-use
+        self,
+        oauth_tokens: Dict[str, Any],
+        user_context: Dict[str, Any],
+    ) -> UserInfo:
+        _ = oauth_tokens
+        __ = user_context
+        raise NotImplementedError()
+
+
+class ProviderClientConfig:
+    def __init__(
+        self,
+        client_id: str,
+        client_secret: Optional[str] = None,
+        client_type: Optional[str] = None,
+        scope: Optional[List[str]] = None,
+        force_pkce: Optional[bool] = None,
+        additional_config: Optional[Dict[str, Any]] = None,
+    ):
+        self.client_id = client_id
+        self.client_secret = client_secret
+        self.client_type = client_type
+        self.scope = scope
+        self.force_pkce = force_pkce
+        self.additional_config = additional_config
+
+    def to_json(self) -> Dict[str, Any]:
+        res = {
+            "clientId": self.client_id,
+            "clientSecret": self.client_secret,
+            "clientType": self.client_type,
+            "scope": self.scope,
+            "forcePKCE": self.force_pkce,
+            "additionalConfig": self.additional_config,
+        }
+
+        return {k: v for k, v in res.items() if v is not None}
+
+
+class UserFields:
+    def __init__(
+        self,
+        user_id: Optional[str] = None,
+        email: Optional[str] = None,
+        email_verified: Optional[str] = None,
+    ):
+        self.user_id = user_id
+        self.email = email
+        self.email_verified = email_verified
+
+    def to_json(self) -> Dict[str, Any]:
+        res = {
+            "userId": self.user_id,
+            "email": self.email,
+            "emailVerified": self.email_verified,
+        }
+
+        return {k: v for k, v in res.items() if v is not None}
+
+
+class UserInfoMap:
+    def __init__(
+        self,
+        from_id_token_payload: Optional[UserFields] = None,
+        from_user_info_api: Optional[UserFields] = None,
+    ):
+        self.from_id_token_payload = from_id_token_payload
+        self.from_user_info_api = from_user_info_api
+
+    def to_json(self) -> Dict[str, Any]:
+        res: Dict[str, Any] = {}
+        if self.from_id_token_payload:
+            res["fromIdTokenPayload"] = self.from_id_token_payload.to_json()
+        if self.from_user_info_api:
+            res["fromUserInfoAPI"] = self.from_user_info_api.to_json()
+        return res
+
+
+class CommonProviderConfig:
+    def __init__(
+        self,
+        third_party_id: str,
+        name: Optional[str] = None,
+        authorization_endpoint: Optional[str] = None,
+        authorization_endpoint_query_params: Optional[Dict[str, Any]] = None,
+        token_endpoint: Optional[str] = None,
+        token_endpoint_body_params: Optional[Dict[str, Union[str, None]]] = None,
+        user_info_endpoint: Optional[str] = None,
+        user_info_endpoint_query_params: Optional[Dict[str, Union[str, None]]] = None,
+        user_info_endpoint_headers: Optional[Dict[str, Union[str, None]]] = None,
+        jwks_uri: Optional[str] = None,
+        oidc_discovery_endpoint: Optional[str] = None,
+        user_info_map: Optional[UserInfoMap] = None,
+        require_email: Optional[bool] = None,
+        validate_id_token_payload: Optional[
+            Callable[
+                [Dict[str, Any], ProviderConfigForClient, Dict[str, Any]],
+                Awaitable[None],
+            ]
+        ] = None,
+        generate_fake_email: Optional[
+            Callable[[str, str, Dict[str, Any]], Awaitable[str]]
+        ] = None,
+        validate_access_token: Optional[
+            Callable[
+                [str, ProviderConfigForClient, Dict[str, Any]],
+                Awaitable[None],
+            ]
+        ] = None,
+    ):
+        self.third_party_id = third_party_id
+        self.name = name
+        self.authorization_endpoint = authorization_endpoint
+        self.authorization_endpoint_query_params = authorization_endpoint_query_params
+        self.token_endpoint = token_endpoint
+        self.token_endpoint_body_params = token_endpoint_body_params
+        self.user_info_endpoint = user_info_endpoint
+        self.user_info_endpoint_query_params = user_info_endpoint_query_params
+        self.user_info_endpoint_headers = user_info_endpoint_headers
+        self.jwks_uri = jwks_uri
+        self.oidc_discovery_endpoint = oidc_discovery_endpoint
+        self.user_info_map = user_info_map
+        self.require_email = require_email
+        self.validate_id_token_payload = validate_id_token_payload
+        self.generate_fake_email = generate_fake_email
+        self.validate_access_token = validate_access_token
+
+    def to_json(self) -> Dict[str, Any]:
+        res = {
+            "thirdPartyId": self.third_party_id,
+            "name": self.name,
+            "authorizationEndpoint": self.authorization_endpoint,
+            "authorizationEndpointQueryParams": self.authorization_endpoint_query_params,
+            "tokenEndpoint": self.token_endpoint,
+            "tokenEndpointBodyParams": self.token_endpoint_body_params,
+            "userInfoEndpoint": self.user_info_endpoint,
+            "userInfoEndpointQueryParams": self.user_info_endpoint_query_params,
+            "userInfoEndpointHeaders": self.user_info_endpoint_headers,
+            "jwksURI": self.jwks_uri,
+            "oidcDiscoveryEndpoint": self.oidc_discovery_endpoint,
+            "userInfoMap": self.user_info_map.to_json()
+            if self.user_info_map is not None
+            else None,
+            "requireEmail": self.require_email,
+        }
+
+        return {k: v for k, v in res.items() if v is not None}
+
+
+class ProviderConfigForClient(ProviderClientConfig, CommonProviderConfig):
+    def __init__(
+        self,
+        # ProviderClientConfig:
+        client_id: str,
+        client_secret: Optional[str] = None,
+        client_type: Optional[str] = None,
+        scope: Optional[List[str]] = None,
+        force_pkce: Optional[bool] = None,
+        additional_config: Optional[Dict[str, Any]] = None,
+        # CommonProviderConfig:
+        third_party_id: str = "temp",
+        name: Optional[str] = None,
+        authorization_endpoint: Optional[str] = None,
+        authorization_endpoint_query_params: Optional[
+            Dict[str, Union[str, None]]
+        ] = None,
+        token_endpoint: Optional[str] = None,
+        token_endpoint_body_params: Optional[Dict[str, Union[str, None]]] = None,
+        user_info_endpoint: Optional[str] = None,
+        user_info_endpoint_query_params: Optional[Dict[str, Union[str, None]]] = None,
+        user_info_endpoint_headers: Optional[Dict[str, Union[str, None]]] = None,
+        jwks_uri: Optional[str] = None,
+        oidc_discovery_endpoint: Optional[str] = None,
+        user_info_map: Optional[UserInfoMap] = None,
+        require_email: Optional[bool] = None,
+        validate_id_token_payload: Optional[
+            Callable[
+                [Dict[str, Any], ProviderConfigForClient, Dict[str, Any]],
+                Awaitable[None],
+            ]
+        ] = None,
+        generate_fake_email: Optional[
+            Callable[[str, str, Dict[str, Any]], Awaitable[str]]
+        ] = None,
+        validate_access_token: Optional[
+            Callable[
+                [str, ProviderConfigForClient, Dict[str, Any]],
+                Awaitable[None],
+            ]
+        ] = None,
+    ):
+        ProviderClientConfig.__init__(
+            self,
+            client_id,
+            client_secret,
+            client_type,
+            scope,
+            force_pkce,
+            additional_config,
+        )
+        CommonProviderConfig.__init__(
+            self,
+            third_party_id,
+            name,
+            authorization_endpoint,
+            authorization_endpoint_query_params,
+            token_endpoint,
+            token_endpoint_body_params,
+            user_info_endpoint,
+            user_info_endpoint_query_params,
+            user_info_endpoint_headers,
+            jwks_uri,
+            oidc_discovery_endpoint,
+            user_info_map,
+            require_email,
+            validate_id_token_payload,
+            generate_fake_email,
+            validate_access_token,
+        )
+
+    def to_json(self) -> Dict[str, Any]:
+        d1 = ProviderClientConfig.to_json(self)
+        d2 = CommonProviderConfig.to_json(self)
+        return {**d1, **d2}
+
+
+class ProviderConfig(CommonProviderConfig):
+    def __init__(
+        self,
+        third_party_id: str,
+        name: Optional[str] = None,
+        clients: Optional[List[ProviderClientConfig]] = None,
+        authorization_endpoint: Optional[str] = None,
+        authorization_endpoint_query_params: Optional[
+            Dict[str, Union[str, None]]
+        ] = None,
+        token_endpoint: Optional[str] = None,
+        token_endpoint_body_params: Optional[Dict[str, Union[str, None]]] = None,
+        user_info_endpoint: Optional[str] = None,
+        user_info_endpoint_query_params: Optional[Dict[str, Union[str, None]]] = None,
+        user_info_endpoint_headers: Optional[Dict[str, Union[str, None]]] = None,
+        jwks_uri: Optional[str] = None,
+        oidc_discovery_endpoint: Optional[str] = None,
+        user_info_map: Optional[UserInfoMap] = None,
+        require_email: Optional[bool] = None,
+        validate_id_token_payload: Optional[
+            Callable[
+                [Dict[str, Any], ProviderConfigForClient, Dict[str, Any]],
+                Awaitable[None],
+            ]
+        ] = None,
+        generate_fake_email: Optional[
+            Callable[[str, str, Dict[str, Any]], Awaitable[str]]
+        ] = None,
+        validate_access_token: Optional[
+            Callable[
+                [str, ProviderConfigForClient, Dict[str, Any]],
+                Awaitable[None],
+            ]
+        ] = None,
+    ):
+        super().__init__(
+            third_party_id,
+            name,
+            authorization_endpoint,
+            authorization_endpoint_query_params,
+            token_endpoint,
+            token_endpoint_body_params,
+            user_info_endpoint,
+            user_info_endpoint_query_params,
+            user_info_endpoint_headers,
+            jwks_uri,
+            oidc_discovery_endpoint,
+            user_info_map,
+            require_email,
+            validate_id_token_payload,
+            generate_fake_email,
+            validate_access_token,
+        )
+        self.clients = clients
+
+    def to_json(self) -> Dict[str, Any]:
+        d = CommonProviderConfig.to_json(self)
+
+        if self.clients is not None:
+            d["clients"] = [c.to_json() for c in self.clients]
+
+        return d
+
+
+class ProviderInput:
+    def __init__(
+        self,
+        config: ProviderConfig,
+        include_in_non_public_tenants_by_default: bool = False,
+        override: Optional[Callable[[Provider], Provider]] = None,
+    ):
+        self.config = config
+        self.include_in_non_public_tenants_by_default = (
+            include_in_non_public_tenants_by_default
+        )
+        self.override = override
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class AuthorisationRedirect +(url_with_query_params: str, pkce_code_verifier: Optional[str] = None) +
+
+
+
+ +Expand source code + +
class AuthorisationRedirect:
+    def __init__(
+        self, url_with_query_params: str, pkce_code_verifier: Optional[str] = None
+    ):
+        self.url_with_query_params = url_with_query_params
+        self.pkce_code_verifier = pkce_code_verifier
+
+
+
+class CommonProviderConfig +(third_party_id: str, name: Optional[str] = None, authorization_endpoint: Optional[str] = None, authorization_endpoint_query_params: Optional[Dict[str, Any]] = None, token_endpoint: Optional[str] = None, token_endpoint_body_params: Optional[Dict[str, Union[str, None]]] = None, user_info_endpoint: Optional[str] = None, user_info_endpoint_query_params: Optional[Dict[str, Union[str, None]]] = None, user_info_endpoint_headers: Optional[Dict[str, Union[str, None]]] = None, jwks_uri: Optional[str] = None, oidc_discovery_endpoint: Optional[str] = None, user_info_map: Optional[UserInfoMap] = None, require_email: Optional[bool] = None, validate_id_token_payload: Optional[Callable[[Dict[str, Any], ProviderConfigForClient, Dict[str, Any]], Awaitable[None]]] = None, generate_fake_email: Optional[Callable[[str, str, Dict[str, Any]], Awaitable[str]]] = None, validate_access_token: Optional[Callable[[str, ProviderConfigForClient, Dict[str, Any]], Awaitable[None]]] = None) +
+
+
+
+ +Expand source code + +
class CommonProviderConfig:
+    def __init__(
+        self,
+        third_party_id: str,
+        name: Optional[str] = None,
+        authorization_endpoint: Optional[str] = None,
+        authorization_endpoint_query_params: Optional[Dict[str, Any]] = None,
+        token_endpoint: Optional[str] = None,
+        token_endpoint_body_params: Optional[Dict[str, Union[str, None]]] = None,
+        user_info_endpoint: Optional[str] = None,
+        user_info_endpoint_query_params: Optional[Dict[str, Union[str, None]]] = None,
+        user_info_endpoint_headers: Optional[Dict[str, Union[str, None]]] = None,
+        jwks_uri: Optional[str] = None,
+        oidc_discovery_endpoint: Optional[str] = None,
+        user_info_map: Optional[UserInfoMap] = None,
+        require_email: Optional[bool] = None,
+        validate_id_token_payload: Optional[
+            Callable[
+                [Dict[str, Any], ProviderConfigForClient, Dict[str, Any]],
+                Awaitable[None],
+            ]
+        ] = None,
+        generate_fake_email: Optional[
+            Callable[[str, str, Dict[str, Any]], Awaitable[str]]
+        ] = None,
+        validate_access_token: Optional[
+            Callable[
+                [str, ProviderConfigForClient, Dict[str, Any]],
+                Awaitable[None],
+            ]
+        ] = None,
+    ):
+        self.third_party_id = third_party_id
+        self.name = name
+        self.authorization_endpoint = authorization_endpoint
+        self.authorization_endpoint_query_params = authorization_endpoint_query_params
+        self.token_endpoint = token_endpoint
+        self.token_endpoint_body_params = token_endpoint_body_params
+        self.user_info_endpoint = user_info_endpoint
+        self.user_info_endpoint_query_params = user_info_endpoint_query_params
+        self.user_info_endpoint_headers = user_info_endpoint_headers
+        self.jwks_uri = jwks_uri
+        self.oidc_discovery_endpoint = oidc_discovery_endpoint
+        self.user_info_map = user_info_map
+        self.require_email = require_email
+        self.validate_id_token_payload = validate_id_token_payload
+        self.generate_fake_email = generate_fake_email
+        self.validate_access_token = validate_access_token
+
+    def to_json(self) -> Dict[str, Any]:
+        res = {
+            "thirdPartyId": self.third_party_id,
+            "name": self.name,
+            "authorizationEndpoint": self.authorization_endpoint,
+            "authorizationEndpointQueryParams": self.authorization_endpoint_query_params,
+            "tokenEndpoint": self.token_endpoint,
+            "tokenEndpointBodyParams": self.token_endpoint_body_params,
+            "userInfoEndpoint": self.user_info_endpoint,
+            "userInfoEndpointQueryParams": self.user_info_endpoint_query_params,
+            "userInfoEndpointHeaders": self.user_info_endpoint_headers,
+            "jwksURI": self.jwks_uri,
+            "oidcDiscoveryEndpoint": self.oidc_discovery_endpoint,
+            "userInfoMap": self.user_info_map.to_json()
+            if self.user_info_map is not None
+            else None,
+            "requireEmail": self.require_email,
+        }
+
+        return {k: v for k, v in res.items() if v is not None}
+
+

Subclasses

+ +

Methods

+
+
+def to_json(self) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
def to_json(self) -> Dict[str, Any]:
+    res = {
+        "thirdPartyId": self.third_party_id,
+        "name": self.name,
+        "authorizationEndpoint": self.authorization_endpoint,
+        "authorizationEndpointQueryParams": self.authorization_endpoint_query_params,
+        "tokenEndpoint": self.token_endpoint,
+        "tokenEndpointBodyParams": self.token_endpoint_body_params,
+        "userInfoEndpoint": self.user_info_endpoint,
+        "userInfoEndpointQueryParams": self.user_info_endpoint_query_params,
+        "userInfoEndpointHeaders": self.user_info_endpoint_headers,
+        "jwksURI": self.jwks_uri,
+        "oidcDiscoveryEndpoint": self.oidc_discovery_endpoint,
+        "userInfoMap": self.user_info_map.to_json()
+        if self.user_info_map is not None
+        else None,
+        "requireEmail": self.require_email,
+    }
+
+    return {k: v for k, v in res.items() if v is not None}
+
+
+
+
+
+class Provider +(id: str, config: ProviderConfigForClient) +
+
+
+
+ +Expand source code + +
class Provider:
+    def __init__(
+        self, id: str, config: ProviderConfigForClient
+    ):  # pylint: disable=redefined-builtin
+        self.id = id
+        self.config = config
+
+    async def get_config_for_client_type(  # pylint: disable=no-self-use
+        self, client_type: Optional[str], user_context: Dict[str, Any]
+    ) -> ProviderConfigForClient:
+        _ = client_type
+        __ = user_context
+        raise NotImplementedError()
+
+    async def get_authorisation_redirect_url(  # pylint: disable=no-self-use
+        self,
+        redirect_uri_on_provider_dashboard: str,
+        user_context: Dict[str, Any],
+    ) -> AuthorisationRedirect:
+        _ = redirect_uri_on_provider_dashboard
+        __ = user_context
+        raise NotImplementedError()
+
+    async def exchange_auth_code_for_oauth_tokens(  # pylint: disable=no-self-use
+        self,
+        redirect_uri_info: RedirectUriInfo,
+        user_context: Dict[str, Any],
+    ) -> Dict[str, Any]:
+        _ = redirect_uri_info
+        __ = user_context
+        raise NotImplementedError()
+
+    async def get_user_info(  # pylint: disable=no-self-use
+        self,
+        oauth_tokens: Dict[str, Any],
+        user_context: Dict[str, Any],
+    ) -> UserInfo:
+        _ = oauth_tokens
+        __ = user_context
+        raise NotImplementedError()
+
+

Subclasses

+ +

Methods

+
+
+async def exchange_auth_code_for_oauth_tokens(self, redirect_uri_info: RedirectUriInfo, user_context: Dict[str, Any]) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
async def exchange_auth_code_for_oauth_tokens(  # pylint: disable=no-self-use
+    self,
+    redirect_uri_info: RedirectUriInfo,
+    user_context: Dict[str, Any],
+) -> Dict[str, Any]:
+    _ = redirect_uri_info
+    __ = user_context
+    raise NotImplementedError()
+
+
+
+async def get_authorisation_redirect_url(self, redirect_uri_on_provider_dashboard: str, user_context: Dict[str, Any]) ‑> AuthorisationRedirect +
+
+
+
+ +Expand source code + +
async def get_authorisation_redirect_url(  # pylint: disable=no-self-use
+    self,
+    redirect_uri_on_provider_dashboard: str,
+    user_context: Dict[str, Any],
+) -> AuthorisationRedirect:
+    _ = redirect_uri_on_provider_dashboard
+    __ = user_context
+    raise NotImplementedError()
+
+
+
+async def get_config_for_client_type(self, client_type: Optional[str], user_context: Dict[str, Any]) ‑> ProviderConfigForClient +
+
+
+
+ +Expand source code + +
async def get_config_for_client_type(  # pylint: disable=no-self-use
+    self, client_type: Optional[str], user_context: Dict[str, Any]
+) -> ProviderConfigForClient:
+    _ = client_type
+    __ = user_context
+    raise NotImplementedError()
+
+
+
+async def get_user_info(self, oauth_tokens: Dict[str, Any], user_context: Dict[str, Any]) ‑> UserInfo +
+
+
+
+ +Expand source code + +
async def get_user_info(  # pylint: disable=no-self-use
+    self,
+    oauth_tokens: Dict[str, Any],
+    user_context: Dict[str, Any],
+) -> UserInfo:
+    _ = oauth_tokens
+    __ = user_context
+    raise NotImplementedError()
+
+
+
+
+
+class ProviderClientConfig +(client_id: str, client_secret: Optional[str] = None, client_type: Optional[str] = None, scope: Optional[List[str]] = None, force_pkce: Optional[bool] = None, additional_config: Optional[Dict[str, Any]] = None) +
+
+
+
+ +Expand source code + +
class ProviderClientConfig:
+    def __init__(
+        self,
+        client_id: str,
+        client_secret: Optional[str] = None,
+        client_type: Optional[str] = None,
+        scope: Optional[List[str]] = None,
+        force_pkce: Optional[bool] = None,
+        additional_config: Optional[Dict[str, Any]] = None,
+    ):
+        self.client_id = client_id
+        self.client_secret = client_secret
+        self.client_type = client_type
+        self.scope = scope
+        self.force_pkce = force_pkce
+        self.additional_config = additional_config
+
+    def to_json(self) -> Dict[str, Any]:
+        res = {
+            "clientId": self.client_id,
+            "clientSecret": self.client_secret,
+            "clientType": self.client_type,
+            "scope": self.scope,
+            "forcePKCE": self.force_pkce,
+            "additionalConfig": self.additional_config,
+        }
+
+        return {k: v for k, v in res.items() if v is not None}
+
+

Subclasses

+ +

Methods

+
+
+def to_json(self) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
def to_json(self) -> Dict[str, Any]:
+    res = {
+        "clientId": self.client_id,
+        "clientSecret": self.client_secret,
+        "clientType": self.client_type,
+        "scope": self.scope,
+        "forcePKCE": self.force_pkce,
+        "additionalConfig": self.additional_config,
+    }
+
+    return {k: v for k, v in res.items() if v is not None}
+
+
+
+
+
+class ProviderConfig +(third_party_id: str, name: Optional[str] = None, clients: Optional[List[ProviderClientConfig]] = None, authorization_endpoint: Optional[str] = None, authorization_endpoint_query_params: Optional[Dict[str, Union[str, None]]] = None, token_endpoint: Optional[str] = None, token_endpoint_body_params: Optional[Dict[str, Union[str, None]]] = None, user_info_endpoint: Optional[str] = None, user_info_endpoint_query_params: Optional[Dict[str, Union[str, None]]] = None, user_info_endpoint_headers: Optional[Dict[str, Union[str, None]]] = None, jwks_uri: Optional[str] = None, oidc_discovery_endpoint: Optional[str] = None, user_info_map: Optional[UserInfoMap] = None, require_email: Optional[bool] = None, validate_id_token_payload: Optional[Callable[[Dict[str, Any], ProviderConfigForClient, Dict[str, Any]], Awaitable[None]]] = None, generate_fake_email: Optional[Callable[[str, str, Dict[str, Any]], Awaitable[str]]] = None, validate_access_token: Optional[Callable[[str, ProviderConfigForClient, Dict[str, Any]], Awaitable[None]]] = None) +
+
+
+
+ +Expand source code + +
class ProviderConfig(CommonProviderConfig):
+    def __init__(
+        self,
+        third_party_id: str,
+        name: Optional[str] = None,
+        clients: Optional[List[ProviderClientConfig]] = None,
+        authorization_endpoint: Optional[str] = None,
+        authorization_endpoint_query_params: Optional[
+            Dict[str, Union[str, None]]
+        ] = None,
+        token_endpoint: Optional[str] = None,
+        token_endpoint_body_params: Optional[Dict[str, Union[str, None]]] = None,
+        user_info_endpoint: Optional[str] = None,
+        user_info_endpoint_query_params: Optional[Dict[str, Union[str, None]]] = None,
+        user_info_endpoint_headers: Optional[Dict[str, Union[str, None]]] = None,
+        jwks_uri: Optional[str] = None,
+        oidc_discovery_endpoint: Optional[str] = None,
+        user_info_map: Optional[UserInfoMap] = None,
+        require_email: Optional[bool] = None,
+        validate_id_token_payload: Optional[
+            Callable[
+                [Dict[str, Any], ProviderConfigForClient, Dict[str, Any]],
+                Awaitable[None],
+            ]
+        ] = None,
+        generate_fake_email: Optional[
+            Callable[[str, str, Dict[str, Any]], Awaitable[str]]
+        ] = None,
+        validate_access_token: Optional[
+            Callable[
+                [str, ProviderConfigForClient, Dict[str, Any]],
+                Awaitable[None],
+            ]
+        ] = None,
+    ):
+        super().__init__(
+            third_party_id,
+            name,
+            authorization_endpoint,
+            authorization_endpoint_query_params,
+            token_endpoint,
+            token_endpoint_body_params,
+            user_info_endpoint,
+            user_info_endpoint_query_params,
+            user_info_endpoint_headers,
+            jwks_uri,
+            oidc_discovery_endpoint,
+            user_info_map,
+            require_email,
+            validate_id_token_payload,
+            generate_fake_email,
+            validate_access_token,
+        )
+        self.clients = clients
+
+    def to_json(self) -> Dict[str, Any]:
+        d = CommonProviderConfig.to_json(self)
+
+        if self.clients is not None:
+            d["clients"] = [c.to_json() for c in self.clients]
+
+        return d
+
+

Ancestors

+ +

Methods

+
+
+def to_json(self) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
def to_json(self) -> Dict[str, Any]:
+    d = CommonProviderConfig.to_json(self)
+
+    if self.clients is not None:
+        d["clients"] = [c.to_json() for c in self.clients]
+
+    return d
+
+
+
+
+
+class ProviderConfigForClient +(client_id: str, client_secret: Optional[str] = None, client_type: Optional[str] = None, scope: Optional[List[str]] = None, force_pkce: Optional[bool] = None, additional_config: Optional[Dict[str, Any]] = None, third_party_id: str = 'temp', name: Optional[str] = None, authorization_endpoint: Optional[str] = None, authorization_endpoint_query_params: Optional[Dict[str, Union[str, None]]] = None, token_endpoint: Optional[str] = None, token_endpoint_body_params: Optional[Dict[str, Union[str, None]]] = None, user_info_endpoint: Optional[str] = None, user_info_endpoint_query_params: Optional[Dict[str, Union[str, None]]] = None, user_info_endpoint_headers: Optional[Dict[str, Union[str, None]]] = None, jwks_uri: Optional[str] = None, oidc_discovery_endpoint: Optional[str] = None, user_info_map: Optional[UserInfoMap] = None, require_email: Optional[bool] = None, validate_id_token_payload: Optional[Callable[[Dict[str, Any], ProviderConfigForClient, Dict[str, Any]], Awaitable[None]]] = None, generate_fake_email: Optional[Callable[[str, str, Dict[str, Any]], Awaitable[str]]] = None, validate_access_token: Optional[Callable[[str, ProviderConfigForClient, Dict[str, Any]], Awaitable[None]]] = None) +
+
+
+
+ +Expand source code + +
class ProviderConfigForClient(ProviderClientConfig, CommonProviderConfig):
+    def __init__(
+        self,
+        # ProviderClientConfig:
+        client_id: str,
+        client_secret: Optional[str] = None,
+        client_type: Optional[str] = None,
+        scope: Optional[List[str]] = None,
+        force_pkce: Optional[bool] = None,
+        additional_config: Optional[Dict[str, Any]] = None,
+        # CommonProviderConfig:
+        third_party_id: str = "temp",
+        name: Optional[str] = None,
+        authorization_endpoint: Optional[str] = None,
+        authorization_endpoint_query_params: Optional[
+            Dict[str, Union[str, None]]
+        ] = None,
+        token_endpoint: Optional[str] = None,
+        token_endpoint_body_params: Optional[Dict[str, Union[str, None]]] = None,
+        user_info_endpoint: Optional[str] = None,
+        user_info_endpoint_query_params: Optional[Dict[str, Union[str, None]]] = None,
+        user_info_endpoint_headers: Optional[Dict[str, Union[str, None]]] = None,
+        jwks_uri: Optional[str] = None,
+        oidc_discovery_endpoint: Optional[str] = None,
+        user_info_map: Optional[UserInfoMap] = None,
+        require_email: Optional[bool] = None,
+        validate_id_token_payload: Optional[
+            Callable[
+                [Dict[str, Any], ProviderConfigForClient, Dict[str, Any]],
+                Awaitable[None],
+            ]
+        ] = None,
+        generate_fake_email: Optional[
+            Callable[[str, str, Dict[str, Any]], Awaitable[str]]
+        ] = None,
+        validate_access_token: Optional[
+            Callable[
+                [str, ProviderConfigForClient, Dict[str, Any]],
+                Awaitable[None],
+            ]
+        ] = None,
+    ):
+        ProviderClientConfig.__init__(
+            self,
+            client_id,
+            client_secret,
+            client_type,
+            scope,
+            force_pkce,
+            additional_config,
+        )
+        CommonProviderConfig.__init__(
+            self,
+            third_party_id,
+            name,
+            authorization_endpoint,
+            authorization_endpoint_query_params,
+            token_endpoint,
+            token_endpoint_body_params,
+            user_info_endpoint,
+            user_info_endpoint_query_params,
+            user_info_endpoint_headers,
+            jwks_uri,
+            oidc_discovery_endpoint,
+            user_info_map,
+            require_email,
+            validate_id_token_payload,
+            generate_fake_email,
+            validate_access_token,
+        )
+
+    def to_json(self) -> Dict[str, Any]:
+        d1 = ProviderClientConfig.to_json(self)
+        d2 = CommonProviderConfig.to_json(self)
+        return {**d1, **d2}
+
+

Ancestors

+ +

Methods

+
+
+def to_json(self) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
def to_json(self) -> Dict[str, Any]:
+    d1 = ProviderClientConfig.to_json(self)
+    d2 = CommonProviderConfig.to_json(self)
+    return {**d1, **d2}
+
+
+
+
+
+class ProviderInput +(config: ProviderConfig, include_in_non_public_tenants_by_default: bool = False, override: Optional[Callable[[Provider], Provider]] = None) +
+
+
+
+ +Expand source code + +
class ProviderInput:
+    def __init__(
+        self,
+        config: ProviderConfig,
+        include_in_non_public_tenants_by_default: bool = False,
+        override: Optional[Callable[[Provider], Provider]] = None,
+    ):
+        self.config = config
+        self.include_in_non_public_tenants_by_default = (
+            include_in_non_public_tenants_by_default
+        )
+        self.override = override
+
+
+
+class RedirectUriInfo +(redirect_uri_on_provider_dashboard: str, redirect_uri_query_params: Dict[str, Any], pkce_code_verifier: Optional[str] = None) +
+
+
+
+ +Expand source code + +
class RedirectUriInfo:
+    def __init__(
+        self,
+        redirect_uri_on_provider_dashboard: str,
+        redirect_uri_query_params: Dict[str, Any],
+        pkce_code_verifier: Optional[str] = None,
+    ):
+        self.redirect_uri_on_provider_dashboard = redirect_uri_on_provider_dashboard
+        self.redirect_uri_query_params = redirect_uri_query_params
+        self.pkce_code_verifier = pkce_code_verifier
+
+
+
+class UserFields +(user_id: Optional[str] = None, email: Optional[str] = None, email_verified: Optional[str] = None) +
+
+
+
+ +Expand source code + +
class UserFields:
+    def __init__(
+        self,
+        user_id: Optional[str] = None,
+        email: Optional[str] = None,
+        email_verified: Optional[str] = None,
+    ):
+        self.user_id = user_id
+        self.email = email
+        self.email_verified = email_verified
+
+    def to_json(self) -> Dict[str, Any]:
+        res = {
+            "userId": self.user_id,
+            "email": self.email,
+            "emailVerified": self.email_verified,
+        }
+
+        return {k: v for k, v in res.items() if v is not None}
+
+

Methods

+
+
+def to_json(self) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
def to_json(self) -> Dict[str, Any]:
+    res = {
+        "userId": self.user_id,
+        "email": self.email,
+        "emailVerified": self.email_verified,
+    }
+
+    return {k: v for k, v in res.items() if v is not None}
+
+
+
+
+
+class UserInfoMap +(from_id_token_payload: Optional[UserFields] = None, from_user_info_api: Optional[UserFields] = None) +
+
+
+
+ +Expand source code + +
class UserInfoMap:
+    def __init__(
+        self,
+        from_id_token_payload: Optional[UserFields] = None,
+        from_user_info_api: Optional[UserFields] = None,
+    ):
+        self.from_id_token_payload = from_id_token_payload
+        self.from_user_info_api = from_user_info_api
+
+    def to_json(self) -> Dict[str, Any]:
+        res: Dict[str, Any] = {}
+        if self.from_id_token_payload:
+            res["fromIdTokenPayload"] = self.from_id_token_payload.to_json()
+        if self.from_user_info_api:
+            res["fromUserInfoAPI"] = self.from_user_info_api.to_json()
+        return res
+
+

Methods

+
+
+def to_json(self) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
def to_json(self) -> Dict[str, Any]:
+    res: Dict[str, Any] = {}
+    if self.from_id_token_payload:
+        res["fromIdTokenPayload"] = self.from_id_token_payload.to_json()
+    if self.from_user_info_api:
+        res["fromUserInfoAPI"] = self.from_user_info_api.to_json()
+    return res
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/thirdparty/providers/active_directory.html b/html/supertokens_python/recipe/thirdparty/providers/active_directory.html new file mode 100644 index 000000000..838fb2681 --- /dev/null +++ b/html/supertokens_python/recipe/thirdparty/providers/active_directory.html @@ -0,0 +1,245 @@ + + + + + + +supertokens_python.recipe.thirdparty.providers.active_directory API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.thirdparty.providers.active_directory

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from typing import Any, Dict, Optional
+from .custom import GenericProvider, NewProvider
+from ..provider import (
+    Provider,
+    ProviderConfigForClient,
+    ProviderInput,
+)
+from .utils import normalise_oidc_endpoint_to_include_well_known
+
+
+class ActiveDirectoryImpl(GenericProvider):
+    async def get_config_for_client_type(
+        self, client_type: Optional[str], user_context: Dict[str, Any]
+    ) -> ProviderConfigForClient:
+        config = await super().get_config_for_client_type(client_type, user_context)
+
+        if (
+            config.additional_config is None
+            or config.additional_config.get("directoryId") is None
+        ):
+            if not config.oidc_discovery_endpoint:
+                raise Exception(
+                    "Please provide the directoryId in the additionalConfig of the Active Directory provider."
+                )
+        else:
+            config.oidc_discovery_endpoint = f"https://login.microsoftonline.com/{config.additional_config['directoryId']}/v2.0/.well-known/openid-configuration"
+
+        # The config could be coming from core where we didn't add the well-known previously
+        config.oidc_discovery_endpoint = normalise_oidc_endpoint_to_include_well_known(
+            config.oidc_discovery_endpoint
+        )
+
+        if config.scope is None:
+            config.scope = ["openid", "email"]
+
+        # TODO: Implement client assertion if required
+
+        return config
+
+
+def ActiveDirectory(
+    input: ProviderInput,  # pylint: disable=redefined-builtin
+) -> Provider:
+    if not input.config.name:
+        input.config.name = "Active Directory"
+
+    return NewProvider(input, ActiveDirectoryImpl)
+
+
+
+
+
+
+
+

Functions

+
+
+def ActiveDirectory(input: ProviderInput) ‑> Provider +
+
+
+
+ +Expand source code + +
def ActiveDirectory(
+    input: ProviderInput,  # pylint: disable=redefined-builtin
+) -> Provider:
+    if not input.config.name:
+        input.config.name = "Active Directory"
+
+    return NewProvider(input, ActiveDirectoryImpl)
+
+
+
+
+
+

Classes

+
+
+class ActiveDirectoryImpl +(provider_config: ProviderConfig) +
+
+
+
+ +Expand source code + +
class ActiveDirectoryImpl(GenericProvider):
+    async def get_config_for_client_type(
+        self, client_type: Optional[str], user_context: Dict[str, Any]
+    ) -> ProviderConfigForClient:
+        config = await super().get_config_for_client_type(client_type, user_context)
+
+        if (
+            config.additional_config is None
+            or config.additional_config.get("directoryId") is None
+        ):
+            if not config.oidc_discovery_endpoint:
+                raise Exception(
+                    "Please provide the directoryId in the additionalConfig of the Active Directory provider."
+                )
+        else:
+            config.oidc_discovery_endpoint = f"https://login.microsoftonline.com/{config.additional_config['directoryId']}/v2.0/.well-known/openid-configuration"
+
+        # The config could be coming from core where we didn't add the well-known previously
+        config.oidc_discovery_endpoint = normalise_oidc_endpoint_to_include_well_known(
+            config.oidc_discovery_endpoint
+        )
+
+        if config.scope is None:
+            config.scope = ["openid", "email"]
+
+        # TODO: Implement client assertion if required
+
+        return config
+
+

Ancestors

+ +

Methods

+
+
+async def get_config_for_client_type(self, client_type: Optional[str], user_context: Dict[str, Any]) ‑> ProviderConfigForClient +
+
+
+
+ +Expand source code + +
async def get_config_for_client_type(
+    self, client_type: Optional[str], user_context: Dict[str, Any]
+) -> ProviderConfigForClient:
+    config = await super().get_config_for_client_type(client_type, user_context)
+
+    if (
+        config.additional_config is None
+        or config.additional_config.get("directoryId") is None
+    ):
+        if not config.oidc_discovery_endpoint:
+            raise Exception(
+                "Please provide the directoryId in the additionalConfig of the Active Directory provider."
+            )
+    else:
+        config.oidc_discovery_endpoint = f"https://login.microsoftonline.com/{config.additional_config['directoryId']}/v2.0/.well-known/openid-configuration"
+
+    # The config could be coming from core where we didn't add the well-known previously
+    config.oidc_discovery_endpoint = normalise_oidc_endpoint_to_include_well_known(
+        config.oidc_discovery_endpoint
+    )
+
+    if config.scope is None:
+        config.scope = ["openid", "email"]
+
+    # TODO: Implement client assertion if required
+
+    return config
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/thirdparty/providers/apple.html b/html/supertokens_python/recipe/thirdparty/providers/apple.html new file mode 100644 index 000000000..9985692b8 --- /dev/null +++ b/html/supertokens_python/recipe/thirdparty/providers/apple.html @@ -0,0 +1,299 @@ + + + + + + +supertokens_python.recipe.thirdparty.providers.apple API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.thirdparty.providers.apple

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from re import sub
+from typing import Any, Dict, Optional
+from jwt import encode  # type: ignore
+from time import time
+
+from .custom import GenericProvider, NewProvider
+from ..provider import Provider, ProviderConfigForClient, ProviderInput
+from .utils import (
+    get_actual_client_id_from_development_client_id,
+    normalise_oidc_endpoint_to_include_well_known,
+)
+
+
+class AppleImpl(GenericProvider):
+    async def get_config_for_client_type(
+        self, client_type: Optional[str], user_context: Dict[str, Any]
+    ) -> ProviderConfigForClient:
+        config = await super().get_config_for_client_type(client_type, user_context)
+
+        if config.scope is None:
+            config.scope = ["openid", "email"]
+
+        if not config.client_secret:
+            config.client_secret = await self._get_client_secret(config)
+
+        if not config.oidc_discovery_endpoint:
+            raise Exception("should never happen")
+
+        # The config could be coming from core where we didn't add the well-known previously
+        config.oidc_discovery_endpoint = normalise_oidc_endpoint_to_include_well_known(
+            config.oidc_discovery_endpoint
+        )
+
+        return config
+
+    async def _get_client_secret(  # pylint: disable=no-self-use
+        self, config: ProviderConfigForClient
+    ) -> str:
+        if (
+            config.additional_config is None
+            or config.additional_config.get("keyId") is None
+            or config.additional_config.get("teamId") is None
+            or config.additional_config.get("privateKey") is None
+        ):
+            raise Exception(
+                "Please ensure that keyId, teamId and privateKey are provided in the additionalConfig"
+            )
+
+        payload: Dict[str, Any] = {
+            "iss": config.additional_config.get("teamId"),
+            "iat": time(),
+            "exp": time() + (86400 * 180),  # 6 months
+            "aud": "https://appleid.apple.com",
+            "sub": get_actual_client_id_from_development_client_id(config.client_id),
+        }
+        headers = {"kid": config.additional_config.get("keyId")}
+        return encode(  # type: ignore
+            payload,
+            sub(r"\\n", "\n", config.additional_config.get("privateKey")),  # type: ignore
+            algorithm="ES256",
+            headers=headers,
+        )  # type: ignore
+
+
+def Apple(input: ProviderInput) -> Provider:  # pylint: disable=redefined-builtin
+    if not input.config.name:
+        input.config.name = "Apple"
+
+    if not input.config.oidc_discovery_endpoint:
+        input.config.oidc_discovery_endpoint = (
+            "https://appleid.apple.com/.well-known/openid-configuration"
+        )
+
+    input.config.authorization_endpoint_query_params = {
+        "response_mode": "form_post",
+        **(input.config.authorization_endpoint_query_params or {}),
+    }
+
+    return NewProvider(input, AppleImpl)
+
+
+
+
+
+
+
+

Functions

+
+
+def Apple(input: ProviderInput) ‑> Provider +
+
+
+
+ +Expand source code + +
def Apple(input: ProviderInput) -> Provider:  # pylint: disable=redefined-builtin
+    if not input.config.name:
+        input.config.name = "Apple"
+
+    if not input.config.oidc_discovery_endpoint:
+        input.config.oidc_discovery_endpoint = (
+            "https://appleid.apple.com/.well-known/openid-configuration"
+        )
+
+    input.config.authorization_endpoint_query_params = {
+        "response_mode": "form_post",
+        **(input.config.authorization_endpoint_query_params or {}),
+    }
+
+    return NewProvider(input, AppleImpl)
+
+
+
+
+
+

Classes

+
+
+class AppleImpl +(provider_config: ProviderConfig) +
+
+
+
+ +Expand source code + +
class AppleImpl(GenericProvider):
+    async def get_config_for_client_type(
+        self, client_type: Optional[str], user_context: Dict[str, Any]
+    ) -> ProviderConfigForClient:
+        config = await super().get_config_for_client_type(client_type, user_context)
+
+        if config.scope is None:
+            config.scope = ["openid", "email"]
+
+        if not config.client_secret:
+            config.client_secret = await self._get_client_secret(config)
+
+        if not config.oidc_discovery_endpoint:
+            raise Exception("should never happen")
+
+        # The config could be coming from core where we didn't add the well-known previously
+        config.oidc_discovery_endpoint = normalise_oidc_endpoint_to_include_well_known(
+            config.oidc_discovery_endpoint
+        )
+
+        return config
+
+    async def _get_client_secret(  # pylint: disable=no-self-use
+        self, config: ProviderConfigForClient
+    ) -> str:
+        if (
+            config.additional_config is None
+            or config.additional_config.get("keyId") is None
+            or config.additional_config.get("teamId") is None
+            or config.additional_config.get("privateKey") is None
+        ):
+            raise Exception(
+                "Please ensure that keyId, teamId and privateKey are provided in the additionalConfig"
+            )
+
+        payload: Dict[str, Any] = {
+            "iss": config.additional_config.get("teamId"),
+            "iat": time(),
+            "exp": time() + (86400 * 180),  # 6 months
+            "aud": "https://appleid.apple.com",
+            "sub": get_actual_client_id_from_development_client_id(config.client_id),
+        }
+        headers = {"kid": config.additional_config.get("keyId")}
+        return encode(  # type: ignore
+            payload,
+            sub(r"\\n", "\n", config.additional_config.get("privateKey")),  # type: ignore
+            algorithm="ES256",
+            headers=headers,
+        )  # type: ignore
+
+

Ancestors

+ +

Methods

+
+
+async def get_config_for_client_type(self, client_type: Optional[str], user_context: Dict[str, Any]) ‑> ProviderConfigForClient +
+
+
+
+ +Expand source code + +
async def get_config_for_client_type(
+    self, client_type: Optional[str], user_context: Dict[str, Any]
+) -> ProviderConfigForClient:
+    config = await super().get_config_for_client_type(client_type, user_context)
+
+    if config.scope is None:
+        config.scope = ["openid", "email"]
+
+    if not config.client_secret:
+        config.client_secret = await self._get_client_secret(config)
+
+    if not config.oidc_discovery_endpoint:
+        raise Exception("should never happen")
+
+    # The config could be coming from core where we didn't add the well-known previously
+    config.oidc_discovery_endpoint = normalise_oidc_endpoint_to_include_well_known(
+        config.oidc_discovery_endpoint
+    )
+
+    return config
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/thirdparty/providers/bitbucket.html b/html/supertokens_python/recipe/thirdparty/providers/bitbucket.html new file mode 100644 index 000000000..1dbde3000 --- /dev/null +++ b/html/supertokens_python/recipe/thirdparty/providers/bitbucket.html @@ -0,0 +1,378 @@ + + + + + + +supertokens_python.recipe.thirdparty.providers.bitbucket API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.thirdparty.providers.bitbucket

+
+
+
+ +Expand source code + +
# Copyright (c) 2023, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from __future__ import annotations
+
+from typing import Dict, Any, Optional
+
+from supertokens_python.recipe.thirdparty.provider import (
+    ProviderConfigForClient,
+    ProviderInput,
+    Provider,
+)
+from .custom import GenericProvider, NewProvider
+
+from .utils import do_get_request
+from ..types import RawUserInfoFromProvider, UserInfo, UserInfoEmail
+
+
+class BitbucketImpl(GenericProvider):
+    async def get_config_for_client_type(
+        self, client_type: Optional[str], user_context: Dict[str, Any]
+    ) -> ProviderConfigForClient:
+        config = await super().get_config_for_client_type(client_type, user_context)
+
+        if config.scope is None:
+            config.scope = ["account", "email"]
+
+        return config
+
+    async def get_user_info(
+        self, oauth_tokens: Dict[str, Any], user_context: Dict[str, Any]
+    ) -> UserInfo:
+        _ = user_context
+        access_token = oauth_tokens.get("access_token")
+        if access_token is None:
+            raise Exception("Access token not found")
+
+        headers = {
+            "Authorization": f"Bearer {access_token}",
+        }
+
+        raw_user_info_from_provider = RawUserInfoFromProvider({}, {})
+
+        user_info_from_access_token = await do_get_request(
+            "https://api.bitbucket.org/2.0/user",
+            query_params=None,
+            headers=headers,
+        )
+
+        raw_user_info_from_provider.from_user_info_api = user_info_from_access_token
+
+        user_info_from_email = await do_get_request(
+            "https://api.bitbucket.org/2.0/user/emails",
+            query_params=None,
+            headers=headers,
+        )
+
+        if raw_user_info_from_provider.from_id_token_payload is None:
+            # Actually this should never happen but python type
+            # checker is not agreeing so doing this:
+            raw_user_info_from_provider.from_id_token_payload = {}
+
+        raw_user_info_from_provider.from_id_token_payload[
+            "email"
+        ] = user_info_from_email
+
+        email = None
+        is_verified = False
+        for email_info in user_info_from_email["values"]:
+            if email_info["is_primary"]:
+                email = email_info["email"]
+                is_verified = email_info["is_confirmed"]
+
+        return UserInfo(
+            third_party_user_id=raw_user_info_from_provider.from_user_info_api["uuid"],
+            email=None if email is None else UserInfoEmail(email, is_verified),
+            raw_user_info_from_provider=raw_user_info_from_provider,
+        )
+
+
+def Bitbucket(input: ProviderInput) -> Provider:  # pylint: disable=redefined-builtin
+    if not input.config.name:
+        input.config.name = "Bitbucket"
+
+    if not input.config.authorization_endpoint:
+        input.config.authorization_endpoint = (
+            "https://bitbucket.org/site/oauth2/authorize"
+        )
+
+    if not input.config.token_endpoint:
+        input.config.token_endpoint = "https://bitbucket.org/site/oauth2/access_token"
+
+    if input.config.authorization_endpoint_query_params is None:
+        input.config.authorization_endpoint_query_params = {
+            "audience": "api.atlassian.com",
+        }
+
+    return NewProvider(input, BitbucketImpl)
+
+
+
+
+
+
+
+

Functions

+
+
+def Bitbucket(input: ProviderInput) ‑> Provider +
+
+
+
+ +Expand source code + +
def Bitbucket(input: ProviderInput) -> Provider:  # pylint: disable=redefined-builtin
+    if not input.config.name:
+        input.config.name = "Bitbucket"
+
+    if not input.config.authorization_endpoint:
+        input.config.authorization_endpoint = (
+            "https://bitbucket.org/site/oauth2/authorize"
+        )
+
+    if not input.config.token_endpoint:
+        input.config.token_endpoint = "https://bitbucket.org/site/oauth2/access_token"
+
+    if input.config.authorization_endpoint_query_params is None:
+        input.config.authorization_endpoint_query_params = {
+            "audience": "api.atlassian.com",
+        }
+
+    return NewProvider(input, BitbucketImpl)
+
+
+
+
+
+

Classes

+
+
+class BitbucketImpl +(provider_config: ProviderConfig) +
+
+
+
+ +Expand source code + +
class BitbucketImpl(GenericProvider):
+    async def get_config_for_client_type(
+        self, client_type: Optional[str], user_context: Dict[str, Any]
+    ) -> ProviderConfigForClient:
+        config = await super().get_config_for_client_type(client_type, user_context)
+
+        if config.scope is None:
+            config.scope = ["account", "email"]
+
+        return config
+
+    async def get_user_info(
+        self, oauth_tokens: Dict[str, Any], user_context: Dict[str, Any]
+    ) -> UserInfo:
+        _ = user_context
+        access_token = oauth_tokens.get("access_token")
+        if access_token is None:
+            raise Exception("Access token not found")
+
+        headers = {
+            "Authorization": f"Bearer {access_token}",
+        }
+
+        raw_user_info_from_provider = RawUserInfoFromProvider({}, {})
+
+        user_info_from_access_token = await do_get_request(
+            "https://api.bitbucket.org/2.0/user",
+            query_params=None,
+            headers=headers,
+        )
+
+        raw_user_info_from_provider.from_user_info_api = user_info_from_access_token
+
+        user_info_from_email = await do_get_request(
+            "https://api.bitbucket.org/2.0/user/emails",
+            query_params=None,
+            headers=headers,
+        )
+
+        if raw_user_info_from_provider.from_id_token_payload is None:
+            # Actually this should never happen but python type
+            # checker is not agreeing so doing this:
+            raw_user_info_from_provider.from_id_token_payload = {}
+
+        raw_user_info_from_provider.from_id_token_payload[
+            "email"
+        ] = user_info_from_email
+
+        email = None
+        is_verified = False
+        for email_info in user_info_from_email["values"]:
+            if email_info["is_primary"]:
+                email = email_info["email"]
+                is_verified = email_info["is_confirmed"]
+
+        return UserInfo(
+            third_party_user_id=raw_user_info_from_provider.from_user_info_api["uuid"],
+            email=None if email is None else UserInfoEmail(email, is_verified),
+            raw_user_info_from_provider=raw_user_info_from_provider,
+        )
+
+

Ancestors

+ +

Methods

+
+
+async def get_config_for_client_type(self, client_type: Optional[str], user_context: Dict[str, Any]) ‑> ProviderConfigForClient +
+
+
+
+ +Expand source code + +
async def get_config_for_client_type(
+    self, client_type: Optional[str], user_context: Dict[str, Any]
+) -> ProviderConfigForClient:
+    config = await super().get_config_for_client_type(client_type, user_context)
+
+    if config.scope is None:
+        config.scope = ["account", "email"]
+
+    return config
+
+
+
+async def get_user_info(self, oauth_tokens: Dict[str, Any], user_context: Dict[str, Any]) ‑> UserInfo +
+
+
+
+ +Expand source code + +
async def get_user_info(
+    self, oauth_tokens: Dict[str, Any], user_context: Dict[str, Any]
+) -> UserInfo:
+    _ = user_context
+    access_token = oauth_tokens.get("access_token")
+    if access_token is None:
+        raise Exception("Access token not found")
+
+    headers = {
+        "Authorization": f"Bearer {access_token}",
+    }
+
+    raw_user_info_from_provider = RawUserInfoFromProvider({}, {})
+
+    user_info_from_access_token = await do_get_request(
+        "https://api.bitbucket.org/2.0/user",
+        query_params=None,
+        headers=headers,
+    )
+
+    raw_user_info_from_provider.from_user_info_api = user_info_from_access_token
+
+    user_info_from_email = await do_get_request(
+        "https://api.bitbucket.org/2.0/user/emails",
+        query_params=None,
+        headers=headers,
+    )
+
+    if raw_user_info_from_provider.from_id_token_payload is None:
+        # Actually this should never happen but python type
+        # checker is not agreeing so doing this:
+        raw_user_info_from_provider.from_id_token_payload = {}
+
+    raw_user_info_from_provider.from_id_token_payload[
+        "email"
+    ] = user_info_from_email
+
+    email = None
+    is_verified = False
+    for email_info in user_info_from_email["values"]:
+        if email_info["is_primary"]:
+            email = email_info["email"]
+            is_verified = email_info["is_confirmed"]
+
+    return UserInfo(
+        third_party_user_id=raw_user_info_from_provider.from_user_info_api["uuid"],
+        email=None if email is None else UserInfoEmail(email, is_verified),
+        raw_user_info_from_provider=raw_user_info_from_provider,
+    )
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/thirdparty/providers/boxy_saml.html b/html/supertokens_python/recipe/thirdparty/providers/boxy_saml.html new file mode 100644 index 000000000..bd705ee2f --- /dev/null +++ b/html/supertokens_python/recipe/thirdparty/providers/boxy_saml.html @@ -0,0 +1,248 @@ + + + + + + +supertokens_python.recipe.thirdparty.providers.boxy_saml API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.thirdparty.providers.boxy_saml

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from typing import Any, Dict, Optional
+from .custom import GenericProvider, NewProvider
+from ..provider import (
+    Provider,
+    ProviderConfigForClient,
+    ProviderInput,
+    UserFields,
+    UserInfoMap,
+)
+
+
+class BoxySAMLImpl(GenericProvider):
+    async def get_config_for_client_type(
+        self, client_type: Optional[str], user_context: Dict[str, Any]
+    ) -> ProviderConfigForClient:
+        config = await super().get_config_for_client_type(client_type, user_context)
+
+        boxy_url = (
+            config.additional_config.get("boxyURL")
+            if config.additional_config is not None
+            else None
+        )
+        if boxy_url:
+            if not config.authorization_endpoint:
+                config.authorization_endpoint = f"{boxy_url}/api/oauth/authorize"
+
+            if not config.token_endpoint:
+                config.token_endpoint = f"{boxy_url}/api/oauth/token"
+
+            if not config.user_info_endpoint:
+                config.user_info_endpoint = f"{boxy_url}/api/oauth/userinfo"
+
+        return config
+
+
+def BoxySAML(input: ProviderInput) -> Provider:  # pylint: disable=redefined-builtin
+    if not input.config.name:
+        input.config.name = "SAML"
+
+    if input.config.user_info_map is None:
+        input.config.user_info_map = UserInfoMap(UserFields(), UserFields())
+
+    if input.config.user_info_map.from_user_info_api is None:
+        input.config.user_info_map.from_user_info_api = UserFields()
+
+    if input.config.user_info_map.from_user_info_api.user_id is None:
+        input.config.user_info_map.from_user_info_api.user_id = "id"
+
+    if input.config.user_info_map.from_user_info_api.email is None:
+        input.config.user_info_map.from_user_info_api.email = "email"
+
+    return NewProvider(input, BoxySAMLImpl)
+
+
+
+
+
+
+
+

Functions

+
+
+def BoxySAML(input: ProviderInput) ‑> Provider +
+
+
+
+ +Expand source code + +
def BoxySAML(input: ProviderInput) -> Provider:  # pylint: disable=redefined-builtin
+    if not input.config.name:
+        input.config.name = "SAML"
+
+    if input.config.user_info_map is None:
+        input.config.user_info_map = UserInfoMap(UserFields(), UserFields())
+
+    if input.config.user_info_map.from_user_info_api is None:
+        input.config.user_info_map.from_user_info_api = UserFields()
+
+    if input.config.user_info_map.from_user_info_api.user_id is None:
+        input.config.user_info_map.from_user_info_api.user_id = "id"
+
+    if input.config.user_info_map.from_user_info_api.email is None:
+        input.config.user_info_map.from_user_info_api.email = "email"
+
+    return NewProvider(input, BoxySAMLImpl)
+
+
+
+
+
+

Classes

+
+
+class BoxySAMLImpl +(provider_config: ProviderConfig) +
+
+
+
+ +Expand source code + +
class BoxySAMLImpl(GenericProvider):
+    async def get_config_for_client_type(
+        self, client_type: Optional[str], user_context: Dict[str, Any]
+    ) -> ProviderConfigForClient:
+        config = await super().get_config_for_client_type(client_type, user_context)
+
+        boxy_url = (
+            config.additional_config.get("boxyURL")
+            if config.additional_config is not None
+            else None
+        )
+        if boxy_url:
+            if not config.authorization_endpoint:
+                config.authorization_endpoint = f"{boxy_url}/api/oauth/authorize"
+
+            if not config.token_endpoint:
+                config.token_endpoint = f"{boxy_url}/api/oauth/token"
+
+            if not config.user_info_endpoint:
+                config.user_info_endpoint = f"{boxy_url}/api/oauth/userinfo"
+
+        return config
+
+

Ancestors

+ +

Methods

+
+
+async def get_config_for_client_type(self, client_type: Optional[str], user_context: Dict[str, Any]) ‑> ProviderConfigForClient +
+
+
+
+ +Expand source code + +
async def get_config_for_client_type(
+    self, client_type: Optional[str], user_context: Dict[str, Any]
+) -> ProviderConfigForClient:
+    config = await super().get_config_for_client_type(client_type, user_context)
+
+    boxy_url = (
+        config.additional_config.get("boxyURL")
+        if config.additional_config is not None
+        else None
+    )
+    if boxy_url:
+        if not config.authorization_endpoint:
+            config.authorization_endpoint = f"{boxy_url}/api/oauth/authorize"
+
+        if not config.token_endpoint:
+            config.token_endpoint = f"{boxy_url}/api/oauth/token"
+
+        if not config.user_info_endpoint:
+            config.user_info_endpoint = f"{boxy_url}/api/oauth/userinfo"
+
+    return config
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/thirdparty/providers/config_utils.html b/html/supertokens_python/recipe/thirdparty/providers/config_utils.html new file mode 100644 index 000000000..c348916d4 --- /dev/null +++ b/html/supertokens_python/recipe/thirdparty/providers/config_utils.html @@ -0,0 +1,684 @@ + + + + + + +supertokens_python.recipe.thirdparty.providers.config_utils API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.thirdparty.providers.config_utils

+
+
+
+ +Expand source code + +
from typing import List, Dict, Optional, Any
+
+from supertokens_python.normalised_url_domain import NormalisedURLDomain
+from supertokens_python.normalised_url_path import NormalisedURLPath
+from .active_directory import ActiveDirectory
+from .apple import Apple
+from .bitbucket import Bitbucket
+from .boxy_saml import BoxySAML
+from .discord import Discord
+from .facebook import Facebook
+from .github import Github
+from .gitlab import Gitlab
+from .google_workspaces import GoogleWorkspaces
+from .google import Google
+from .linkedin import Linkedin
+from .twitter import Twitter
+from .okta import Okta
+from .custom import NewProvider
+from .utils import do_get_request
+
+from ..provider import (
+    ProviderConfig,
+    ProviderConfigForClient,
+    ProviderInput,
+    Provider,
+    UserFields,
+    UserInfoMap,
+)
+
+
+def merge_config(
+    config_from_static: ProviderConfig, config_from_core: ProviderConfig
+) -> ProviderConfig:
+    result = ProviderConfig(
+        third_party_id=config_from_static.third_party_id,
+        name=(
+            config_from_static.name
+            if config_from_core.name is None
+            else config_from_core.name
+        ),
+        authorization_endpoint=(
+            config_from_static.authorization_endpoint
+            if config_from_core.authorization_endpoint is None
+            else config_from_core.authorization_endpoint
+        ),
+        authorization_endpoint_query_params=(
+            config_from_static.authorization_endpoint_query_params
+            if config_from_core.authorization_endpoint_query_params is None
+            else config_from_core.authorization_endpoint_query_params
+        ),
+        token_endpoint=(
+            config_from_static.token_endpoint
+            if config_from_core.token_endpoint is None
+            else config_from_core.token_endpoint
+        ),
+        token_endpoint_body_params=(
+            config_from_static.token_endpoint_body_params
+            if config_from_core.token_endpoint_body_params is None
+            else config_from_core.token_endpoint_body_params
+        ),
+        user_info_endpoint=(
+            config_from_static.user_info_endpoint
+            if config_from_core.user_info_endpoint is None
+            else config_from_core.user_info_endpoint
+        ),
+        user_info_endpoint_headers=(
+            config_from_static.user_info_endpoint_headers
+            if config_from_core.user_info_endpoint_headers is None
+            else config_from_core.user_info_endpoint_headers
+        ),
+        user_info_endpoint_query_params=(
+            config_from_static.user_info_endpoint_query_params
+            if config_from_core.user_info_endpoint_query_params is None
+            else config_from_core.user_info_endpoint_query_params
+        ),
+        jwks_uri=(
+            config_from_static.jwks_uri
+            if config_from_core.jwks_uri is None
+            else config_from_core.jwks_uri
+        ),
+        oidc_discovery_endpoint=(
+            config_from_static.oidc_discovery_endpoint
+            if config_from_core.oidc_discovery_endpoint is None
+            else config_from_core.oidc_discovery_endpoint
+        ),
+        require_email=config_from_static.require_email,
+        user_info_map=config_from_static.user_info_map,
+        generate_fake_email=config_from_static.generate_fake_email,
+        validate_id_token_payload=config_from_static.validate_id_token_payload,
+        validate_access_token=config_from_static.validate_access_token,
+    )
+
+    if result.user_info_map is None:
+        result.user_info_map = UserInfoMap(UserFields(), UserFields())
+
+    if result.user_info_map.from_user_info_api is None:
+        result.user_info_map.from_user_info_api = UserFields()
+    if result.user_info_map.from_id_token_payload is None:
+        result.user_info_map.from_id_token_payload = UserFields()
+
+    if config_from_core.user_info_map is not None:
+        if config_from_core.user_info_map.from_user_info_api is None:
+            config_from_core.user_info_map.from_user_info_api = UserFields()
+        if config_from_core.user_info_map.from_id_token_payload is None:
+            config_from_core.user_info_map.from_id_token_payload = UserFields()
+
+        if config_from_core.user_info_map.from_id_token_payload.user_id is not None:
+            result.user_info_map.from_id_token_payload.user_id = (
+                config_from_core.user_info_map.from_id_token_payload.user_id
+            )
+        if config_from_core.user_info_map.from_id_token_payload.email is not None:
+            result.user_info_map.from_id_token_payload.email = (
+                config_from_core.user_info_map.from_id_token_payload.email
+            )
+        if (
+            config_from_core.user_info_map.from_id_token_payload.email_verified
+            is not None
+        ):
+            result.user_info_map.from_id_token_payload.email_verified = (
+                config_from_core.user_info_map.from_id_token_payload.email_verified
+            )
+
+        if config_from_core.user_info_map.from_user_info_api.user_id is not None:
+            result.user_info_map.from_user_info_api.user_id = (
+                config_from_core.user_info_map.from_user_info_api.user_id
+            )
+        if config_from_core.user_info_map.from_user_info_api.email is not None:
+            result.user_info_map.from_user_info_api.email = (
+                config_from_core.user_info_map.from_user_info_api.email
+            )
+        if config_from_core.user_info_map.from_user_info_api.email_verified is not None:
+            result.user_info_map.from_user_info_api.email_verified = (
+                config_from_core.user_info_map.from_user_info_api.email_verified
+            )
+
+    merged_clients = (config_from_static.clients or [])[:]  # Make a copy
+    core_config_clients = config_from_core.clients or []
+
+    for core_client in core_config_clients:
+        found = False
+        for idx, static_client in enumerate(merged_clients):
+            if static_client.client_type == core_client.client_type:
+                merged_clients[idx] = core_client
+                found = True
+                break
+
+        if not found:
+            merged_clients.append(core_client)
+
+    result.clients = merged_clients
+
+    return result
+
+
+def merge_providers_from_core_and_static(
+    provider_configs_from_core: List[ProviderConfig],
+    provider_inputs_from_static: List[ProviderInput],
+    include_all_providers: bool,
+) -> List[ProviderInput]:
+    merged_providers: List[ProviderInput] = []
+
+    if len(provider_configs_from_core) == 0:
+        for config in filter(
+            lambda provider: include_all_providers
+            or provider.include_in_non_public_tenants_by_default,
+            provider_inputs_from_static,
+        ):
+            merged_providers.append(config)
+    else:
+        for provider_config_from_core in provider_configs_from_core:
+            merged_provider_input = ProviderInput(provider_config_from_core)
+
+            for provider_input_from_static in provider_inputs_from_static:
+                if (
+                    provider_input_from_static.config.third_party_id
+                    == provider_config_from_core.third_party_id
+                ):
+                    merged_provider_input.config = merge_config(
+                        provider_input_from_static.config, provider_config_from_core
+                    )
+                    merged_provider_input.override = provider_input_from_static.override
+                    break
+
+            merged_providers.append(merged_provider_input)
+
+    return merged_providers
+
+
+def create_provider(provider_input: ProviderInput) -> Provider:
+    if provider_input.config.third_party_id.startswith("active-directory"):
+        return ActiveDirectory(provider_input)
+    if provider_input.config.third_party_id.startswith("apple"):
+        return Apple(provider_input)
+    if provider_input.config.third_party_id.startswith("bitbucket"):
+        return Bitbucket(provider_input)
+    if provider_input.config.third_party_id.startswith("discord"):
+        return Discord(provider_input)
+    if provider_input.config.third_party_id.startswith("facebook"):
+        return Facebook(provider_input)
+    if provider_input.config.third_party_id.startswith("github"):
+        return Github(provider_input)
+    if provider_input.config.third_party_id.startswith("gitlab"):
+        return Gitlab(provider_input)
+    if provider_input.config.third_party_id.startswith("google-workspaces"):
+        return GoogleWorkspaces(provider_input)
+    if provider_input.config.third_party_id.startswith("google"):
+        return Google(provider_input)
+    if provider_input.config.third_party_id.startswith("okta"):
+        return Okta(provider_input)
+    if provider_input.config.third_party_id.startswith("linkedin"):
+        return Linkedin(provider_input)
+    if provider_input.config.third_party_id.startswith("twitter"):
+        return Twitter(provider_input)
+    if provider_input.config.third_party_id.startswith("boxy-saml"):
+        return BoxySAML(provider_input)
+
+    return NewProvider(provider_input)
+
+
+OIDC_INFO_MAP: Dict[str, Any] = {}
+
+
+async def get_oidc_discovery_info(issuer: str):
+    if issuer in OIDC_INFO_MAP:
+        return OIDC_INFO_MAP[issuer]
+
+    ndomain = NormalisedURLDomain(issuer)
+    npath = NormalisedURLPath(issuer)
+
+    oidc_info = await do_get_request(
+        ndomain.get_as_string_dangerous() + npath.get_as_string_dangerous()
+    )
+    OIDC_INFO_MAP[issuer] = oidc_info
+
+    return oidc_info
+
+
+async def discover_oidc_endpoints(
+    config: ProviderConfigForClient,
+) -> ProviderConfigForClient:
+    if config.oidc_discovery_endpoint is None:
+        return config
+
+    oidc_info = await get_oidc_discovery_info(config.oidc_discovery_endpoint)
+    if (
+        oidc_info.get("authorization_endpoint") is not None
+        and config.authorization_endpoint is None
+    ):
+        config.authorization_endpoint = oidc_info["authorization_endpoint"]
+
+    if oidc_info.get("token_endpoint") is not None and config.token_endpoint is None:
+        config.token_endpoint = oidc_info["token_endpoint"]
+
+    if (
+        oidc_info.get("userinfo_endpoint") is not None
+        and config.user_info_endpoint is None
+    ):
+        config.user_info_endpoint = oidc_info["userinfo_endpoint"]
+
+    if oidc_info.get("jwks_uri") is not None and config.jwks_uri is None:
+        config.jwks_uri = oidc_info["jwks_uri"]
+
+    return config
+
+
+async def fetch_and_set_config(
+    provider_instance: Provider,
+    client_type: Optional[str],
+    user_context: Dict[str, Any],
+):
+    config = await provider_instance.get_config_for_client_type(
+        client_type, user_context
+    )
+    config = await discover_oidc_endpoints(config)
+    provider_instance.config = config
+
+
+async def find_and_create_provider_instance(
+    providers: List[ProviderInput],
+    third_party_id: str,
+    client_type: Optional[str],
+    user_context: Dict[str, Any],
+) -> Optional[Provider]:
+    for provider_input in providers:
+        if provider_input.config.third_party_id == third_party_id:
+            provider_instance = create_provider(provider_input)
+            await fetch_and_set_config(provider_instance, client_type, user_context)
+            return provider_instance
+
+    return None
+
+
+
+
+
+
+
+

Functions

+
+
+def create_provider(provider_input: ProviderInput) ‑> Provider +
+
+
+
+ +Expand source code + +
def create_provider(provider_input: ProviderInput) -> Provider:
+    if provider_input.config.third_party_id.startswith("active-directory"):
+        return ActiveDirectory(provider_input)
+    if provider_input.config.third_party_id.startswith("apple"):
+        return Apple(provider_input)
+    if provider_input.config.third_party_id.startswith("bitbucket"):
+        return Bitbucket(provider_input)
+    if provider_input.config.third_party_id.startswith("discord"):
+        return Discord(provider_input)
+    if provider_input.config.third_party_id.startswith("facebook"):
+        return Facebook(provider_input)
+    if provider_input.config.third_party_id.startswith("github"):
+        return Github(provider_input)
+    if provider_input.config.third_party_id.startswith("gitlab"):
+        return Gitlab(provider_input)
+    if provider_input.config.third_party_id.startswith("google-workspaces"):
+        return GoogleWorkspaces(provider_input)
+    if provider_input.config.third_party_id.startswith("google"):
+        return Google(provider_input)
+    if provider_input.config.third_party_id.startswith("okta"):
+        return Okta(provider_input)
+    if provider_input.config.third_party_id.startswith("linkedin"):
+        return Linkedin(provider_input)
+    if provider_input.config.third_party_id.startswith("twitter"):
+        return Twitter(provider_input)
+    if provider_input.config.third_party_id.startswith("boxy-saml"):
+        return BoxySAML(provider_input)
+
+    return NewProvider(provider_input)
+
+
+
+async def discover_oidc_endpoints(config: ProviderConfigForClient) ‑> ProviderConfigForClient +
+
+
+
+ +Expand source code + +
async def discover_oidc_endpoints(
+    config: ProviderConfigForClient,
+) -> ProviderConfigForClient:
+    if config.oidc_discovery_endpoint is None:
+        return config
+
+    oidc_info = await get_oidc_discovery_info(config.oidc_discovery_endpoint)
+    if (
+        oidc_info.get("authorization_endpoint") is not None
+        and config.authorization_endpoint is None
+    ):
+        config.authorization_endpoint = oidc_info["authorization_endpoint"]
+
+    if oidc_info.get("token_endpoint") is not None and config.token_endpoint is None:
+        config.token_endpoint = oidc_info["token_endpoint"]
+
+    if (
+        oidc_info.get("userinfo_endpoint") is not None
+        and config.user_info_endpoint is None
+    ):
+        config.user_info_endpoint = oidc_info["userinfo_endpoint"]
+
+    if oidc_info.get("jwks_uri") is not None and config.jwks_uri is None:
+        config.jwks_uri = oidc_info["jwks_uri"]
+
+    return config
+
+
+
+async def fetch_and_set_config(provider_instance: Provider, client_type: Optional[str], user_context: Dict[str, Any]) +
+
+
+
+ +Expand source code + +
async def fetch_and_set_config(
+    provider_instance: Provider,
+    client_type: Optional[str],
+    user_context: Dict[str, Any],
+):
+    config = await provider_instance.get_config_for_client_type(
+        client_type, user_context
+    )
+    config = await discover_oidc_endpoints(config)
+    provider_instance.config = config
+
+
+
+async def find_and_create_provider_instance(providers: List[ProviderInput], third_party_id: str, client_type: Optional[str], user_context: Dict[str, Any]) ‑> Optional[Provider] +
+
+
+
+ +Expand source code + +
async def find_and_create_provider_instance(
+    providers: List[ProviderInput],
+    third_party_id: str,
+    client_type: Optional[str],
+    user_context: Dict[str, Any],
+) -> Optional[Provider]:
+    for provider_input in providers:
+        if provider_input.config.third_party_id == third_party_id:
+            provider_instance = create_provider(provider_input)
+            await fetch_and_set_config(provider_instance, client_type, user_context)
+            return provider_instance
+
+    return None
+
+
+
+async def get_oidc_discovery_info(issuer: str) +
+
+
+
+ +Expand source code + +
async def get_oidc_discovery_info(issuer: str):
+    if issuer in OIDC_INFO_MAP:
+        return OIDC_INFO_MAP[issuer]
+
+    ndomain = NormalisedURLDomain(issuer)
+    npath = NormalisedURLPath(issuer)
+
+    oidc_info = await do_get_request(
+        ndomain.get_as_string_dangerous() + npath.get_as_string_dangerous()
+    )
+    OIDC_INFO_MAP[issuer] = oidc_info
+
+    return oidc_info
+
+
+
+def merge_config(config_from_static: ProviderConfig, config_from_core: ProviderConfig) ‑> ProviderConfig +
+
+
+
+ +Expand source code + +
def merge_config(
+    config_from_static: ProviderConfig, config_from_core: ProviderConfig
+) -> ProviderConfig:
+    result = ProviderConfig(
+        third_party_id=config_from_static.third_party_id,
+        name=(
+            config_from_static.name
+            if config_from_core.name is None
+            else config_from_core.name
+        ),
+        authorization_endpoint=(
+            config_from_static.authorization_endpoint
+            if config_from_core.authorization_endpoint is None
+            else config_from_core.authorization_endpoint
+        ),
+        authorization_endpoint_query_params=(
+            config_from_static.authorization_endpoint_query_params
+            if config_from_core.authorization_endpoint_query_params is None
+            else config_from_core.authorization_endpoint_query_params
+        ),
+        token_endpoint=(
+            config_from_static.token_endpoint
+            if config_from_core.token_endpoint is None
+            else config_from_core.token_endpoint
+        ),
+        token_endpoint_body_params=(
+            config_from_static.token_endpoint_body_params
+            if config_from_core.token_endpoint_body_params is None
+            else config_from_core.token_endpoint_body_params
+        ),
+        user_info_endpoint=(
+            config_from_static.user_info_endpoint
+            if config_from_core.user_info_endpoint is None
+            else config_from_core.user_info_endpoint
+        ),
+        user_info_endpoint_headers=(
+            config_from_static.user_info_endpoint_headers
+            if config_from_core.user_info_endpoint_headers is None
+            else config_from_core.user_info_endpoint_headers
+        ),
+        user_info_endpoint_query_params=(
+            config_from_static.user_info_endpoint_query_params
+            if config_from_core.user_info_endpoint_query_params is None
+            else config_from_core.user_info_endpoint_query_params
+        ),
+        jwks_uri=(
+            config_from_static.jwks_uri
+            if config_from_core.jwks_uri is None
+            else config_from_core.jwks_uri
+        ),
+        oidc_discovery_endpoint=(
+            config_from_static.oidc_discovery_endpoint
+            if config_from_core.oidc_discovery_endpoint is None
+            else config_from_core.oidc_discovery_endpoint
+        ),
+        require_email=config_from_static.require_email,
+        user_info_map=config_from_static.user_info_map,
+        generate_fake_email=config_from_static.generate_fake_email,
+        validate_id_token_payload=config_from_static.validate_id_token_payload,
+        validate_access_token=config_from_static.validate_access_token,
+    )
+
+    if result.user_info_map is None:
+        result.user_info_map = UserInfoMap(UserFields(), UserFields())
+
+    if result.user_info_map.from_user_info_api is None:
+        result.user_info_map.from_user_info_api = UserFields()
+    if result.user_info_map.from_id_token_payload is None:
+        result.user_info_map.from_id_token_payload = UserFields()
+
+    if config_from_core.user_info_map is not None:
+        if config_from_core.user_info_map.from_user_info_api is None:
+            config_from_core.user_info_map.from_user_info_api = UserFields()
+        if config_from_core.user_info_map.from_id_token_payload is None:
+            config_from_core.user_info_map.from_id_token_payload = UserFields()
+
+        if config_from_core.user_info_map.from_id_token_payload.user_id is not None:
+            result.user_info_map.from_id_token_payload.user_id = (
+                config_from_core.user_info_map.from_id_token_payload.user_id
+            )
+        if config_from_core.user_info_map.from_id_token_payload.email is not None:
+            result.user_info_map.from_id_token_payload.email = (
+                config_from_core.user_info_map.from_id_token_payload.email
+            )
+        if (
+            config_from_core.user_info_map.from_id_token_payload.email_verified
+            is not None
+        ):
+            result.user_info_map.from_id_token_payload.email_verified = (
+                config_from_core.user_info_map.from_id_token_payload.email_verified
+            )
+
+        if config_from_core.user_info_map.from_user_info_api.user_id is not None:
+            result.user_info_map.from_user_info_api.user_id = (
+                config_from_core.user_info_map.from_user_info_api.user_id
+            )
+        if config_from_core.user_info_map.from_user_info_api.email is not None:
+            result.user_info_map.from_user_info_api.email = (
+                config_from_core.user_info_map.from_user_info_api.email
+            )
+        if config_from_core.user_info_map.from_user_info_api.email_verified is not None:
+            result.user_info_map.from_user_info_api.email_verified = (
+                config_from_core.user_info_map.from_user_info_api.email_verified
+            )
+
+    merged_clients = (config_from_static.clients or [])[:]  # Make a copy
+    core_config_clients = config_from_core.clients or []
+
+    for core_client in core_config_clients:
+        found = False
+        for idx, static_client in enumerate(merged_clients):
+            if static_client.client_type == core_client.client_type:
+                merged_clients[idx] = core_client
+                found = True
+                break
+
+        if not found:
+            merged_clients.append(core_client)
+
+    result.clients = merged_clients
+
+    return result
+
+
+
+def merge_providers_from_core_and_static(provider_configs_from_core: List[ProviderConfig], provider_inputs_from_static: List[ProviderInput], include_all_providers: bool) ‑> List[ProviderInput] +
+
+
+
+ +Expand source code + +
def merge_providers_from_core_and_static(
+    provider_configs_from_core: List[ProviderConfig],
+    provider_inputs_from_static: List[ProviderInput],
+    include_all_providers: bool,
+) -> List[ProviderInput]:
+    merged_providers: List[ProviderInput] = []
+
+    if len(provider_configs_from_core) == 0:
+        for config in filter(
+            lambda provider: include_all_providers
+            or provider.include_in_non_public_tenants_by_default,
+            provider_inputs_from_static,
+        ):
+            merged_providers.append(config)
+    else:
+        for provider_config_from_core in provider_configs_from_core:
+            merged_provider_input = ProviderInput(provider_config_from_core)
+
+            for provider_input_from_static in provider_inputs_from_static:
+                if (
+                    provider_input_from_static.config.third_party_id
+                    == provider_config_from_core.third_party_id
+                ):
+                    merged_provider_input.config = merge_config(
+                        provider_input_from_static.config, provider_config_from_core
+                    )
+                    merged_provider_input.override = provider_input_from_static.override
+                    break
+
+            merged_providers.append(merged_provider_input)
+
+    return merged_providers
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/thirdparty/providers/custom.html b/html/supertokens_python/recipe/thirdparty/providers/custom.html new file mode 100644 index 000000000..a533be40f --- /dev/null +++ b/html/supertokens_python/recipe/thirdparty/providers/custom.html @@ -0,0 +1,1253 @@ + + + + + + +supertokens_python.recipe.thirdparty.providers.custom API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.thirdparty.providers.custom

+
+
+
+ +Expand source code + +
from typing import Any, Callable, Dict, List, Optional, Union
+from urllib.parse import parse_qs, urlencode, urlparse
+
+from httpx import AsyncClient
+
+from jwt import decode  # type: ignore
+from jwt.algorithms import RSAAlgorithm
+import pkce
+
+from supertokens_python.recipe.thirdparty.exceptions import ClientTypeNotFoundError
+from supertokens_python.recipe.thirdparty.providers.utils import (
+    DEV_OAUTH_AUTHORIZATION_URL,
+    DEV_OAUTH_REDIRECT_URL,
+    do_get_request,
+    do_post_request,
+    get_actual_client_id_from_development_client_id,
+    is_using_oauth_development_client_id,
+    DEV_KEY_IDENTIFIER,
+    DEV_OAUTH_CLIENT_IDS,
+)
+
+from ..types import RawUserInfoFromProvider, UserInfo, UserInfoEmail
+from ..provider import (
+    AuthorisationRedirect,
+    Provider,
+    ProviderClientConfig,
+    ProviderConfig,
+    ProviderConfigForClient,
+    ProviderInput,
+    RedirectUriInfo,
+    UserFields,
+    UserInfoMap,
+)
+
+
+def get_provider_config_for_client(
+    config: ProviderConfig, client_config: ProviderClientConfig
+) -> ProviderConfigForClient:
+    return ProviderConfigForClient(
+        # ProviderClientConfig
+        client_id=client_config.client_id,
+        client_secret=client_config.client_secret,
+        client_type=client_config.client_type,
+        scope=client_config.scope,
+        force_pkce=client_config.force_pkce,
+        additional_config=client_config.additional_config,
+        # CommonProviderConfig
+        third_party_id=config.third_party_id,
+        name=config.name,
+        authorization_endpoint=config.authorization_endpoint,
+        authorization_endpoint_query_params=config.authorization_endpoint_query_params,
+        token_endpoint=config.token_endpoint,
+        token_endpoint_body_params=config.token_endpoint_body_params,
+        user_info_endpoint=config.user_info_endpoint,
+        user_info_endpoint_query_params=config.user_info_endpoint_query_params,
+        user_info_endpoint_headers=config.user_info_endpoint_headers,
+        jwks_uri=config.jwks_uri,
+        oidc_discovery_endpoint=config.oidc_discovery_endpoint,
+        user_info_map=config.user_info_map,
+        require_email=config.require_email,
+        validate_id_token_payload=config.validate_id_token_payload,
+        generate_fake_email=config.generate_fake_email,
+        validate_access_token=config.validate_access_token,
+    )
+
+
+def access_field(obj: Any, key: str) -> Any:
+    key_parts = key.split(".")
+    for part in key_parts:
+        if isinstance(obj, dict):
+            obj = obj.get(part)  # type: ignore
+        else:
+            return None
+
+    return obj
+
+
+def get_supertokens_user_info_result_from_raw_user_info(
+    config: ProviderConfigForClient,
+    raw_user_info_from_provider: RawUserInfoFromProvider,
+) -> UserInfo:
+    third_party_user_id = ""
+
+    if config.user_info_map is None:
+        raise Exception("user info map is missing")
+
+    if config.user_info_map.from_user_info_api is None:
+        config.user_info_map.from_user_info_api = UserFields()
+    if config.user_info_map.from_id_token_payload is None:
+        config.user_info_map.from_id_token_payload = UserFields()
+
+    if config.user_info_map.from_user_info_api.user_id is not None:
+        user_id = access_field(
+            raw_user_info_from_provider.from_user_info_api,
+            config.user_info_map.from_user_info_api.user_id,
+        )
+        if user_id is not None:
+            third_party_user_id = str(user_id)
+
+    if config.user_info_map.from_id_token_payload.user_id is not None:
+        user_id = access_field(
+            raw_user_info_from_provider.from_id_token_payload,
+            config.user_info_map.from_id_token_payload.user_id,
+        )
+        if user_id is not None:
+            third_party_user_id = str(user_id)
+
+    if third_party_user_id == "":
+        raise Exception("third party user id is missing")
+
+    result = UserInfo(
+        third_party_user_id=third_party_user_id,
+    )
+
+    email = ""
+
+    if config.user_info_map.from_user_info_api.email is not None:
+        email_val = access_field(
+            raw_user_info_from_provider.from_user_info_api,
+            config.user_info_map.from_user_info_api.email,
+        )
+        if email_val is not None:
+            email = email_val
+
+    if config.user_info_map.from_id_token_payload.email is not None:
+        email_val = access_field(
+            raw_user_info_from_provider.from_id_token_payload,
+            config.user_info_map.from_id_token_payload.email,
+        )
+        if email_val is not None:
+            email = email_val
+
+    if email != "":
+        result.email = UserInfoEmail(email, False)
+
+        if config.user_info_map.from_user_info_api.email_verified is not None:
+            email_verified = access_field(
+                raw_user_info_from_provider.from_user_info_api,
+                config.user_info_map.from_user_info_api.email_verified,
+            )
+            if email_verified is not None:
+                result.email.is_verified = str(email_verified).lower() == "true"
+
+        if config.user_info_map.from_id_token_payload.email_verified is not None:
+            email_verified = access_field(
+                raw_user_info_from_provider.from_id_token_payload,
+                config.user_info_map.from_id_token_payload.email_verified,
+            )
+            if email_verified is not None:
+                result.email.is_verified = str(email_verified).lower() == "true"
+
+    return result
+
+
+async def verify_id_token_from_jwks_endpoint_and_get_payload(
+    id_token: str, jwks_uri: str, audience: str
+):
+    public_keys: List[RSAAlgorithm] = []
+    async with AsyncClient(timeout=30.0) as client:
+        response = await client.get(jwks_uri)  # type:ignore
+        key_payload = response.json()
+        for key in key_payload["keys"]:
+            public_keys.append(RSAAlgorithm.from_jwk(key))  # type: ignore
+
+    err = Exception("id token verification failed")
+    for key in public_keys:
+        try:
+            return decode(jwt=id_token, key=key, audience=[audience], algorithms=["RS256"])  # type: ignore
+        except Exception as e:
+            err = e
+    raise err
+
+
+def merge_into_dict(src: Dict[str, Any], dest: Dict[str, Any]) -> Dict[str, Any]:
+    res = dest.copy()
+    for k, v in src.items():
+        if v is None:
+            if k in res:
+                del res[k]
+        else:
+            res[k] = v
+
+    return res
+
+
+def is_using_development_client_id(client_id: str) -> bool:
+    return client_id.startswith(DEV_KEY_IDENTIFIER) or client_id in DEV_OAUTH_CLIENT_IDS
+
+
+class GenericProvider(Provider):
+    def __init__(self, provider_config: ProviderConfig):
+        self.input_config = input_config = self._normalize_input(provider_config)
+
+        provider_config_for_client = ProviderConfigForClient(
+            # Will automatically get replaced with correct value
+            # in get_provider_config_for_client
+            # when fetch_and_set_config function runs
+            client_id="temp",
+            client_secret=None,
+            client_type=None,
+            scope=None,
+            force_pkce=False,
+            additional_config=None,
+            name=input_config.name,
+            authorization_endpoint=input_config.authorization_endpoint,
+            authorization_endpoint_query_params=input_config.authorization_endpoint_query_params,
+            token_endpoint=input_config.token_endpoint,
+            token_endpoint_body_params=input_config.token_endpoint_body_params,
+            user_info_endpoint=input_config.user_info_endpoint,
+            user_info_endpoint_query_params=input_config.user_info_endpoint_query_params,
+            user_info_endpoint_headers=input_config.user_info_endpoint_headers,
+            jwks_uri=input_config.jwks_uri,
+            oidc_discovery_endpoint=input_config.oidc_discovery_endpoint,
+            user_info_map=input_config.user_info_map,
+            require_email=input_config.require_email,
+            validate_id_token_payload=input_config.validate_id_token_payload,
+            generate_fake_email=input_config.generate_fake_email,
+        )
+        super().__init__(input_config.third_party_id, provider_config_for_client)
+
+    def _normalize_input(  # pylint: disable=no-self-use
+        self, input_config: ProviderConfig
+    ) -> ProviderConfig:
+        if input_config.user_info_map is None:
+            input_config.user_info_map = UserInfoMap(
+                from_id_token_payload=UserFields(),
+                from_user_info_api=UserFields(),
+            )
+        if input_config.user_info_map.from_user_info_api is None:
+            input_config.user_info_map.from_user_info_api = UserFields()
+        if input_config.user_info_map.from_id_token_payload is None:
+            input_config.user_info_map.from_id_token_payload = UserFields()
+
+        # These are safe defaults common to most providers. Each provider
+        # implementations override these as necessary
+        if input_config.user_info_map.from_id_token_payload.user_id is None:
+            input_config.user_info_map.from_id_token_payload.user_id = "sub"
+
+        if input_config.user_info_map.from_id_token_payload.email is None:
+            input_config.user_info_map.from_id_token_payload.email = "email"
+
+        if input_config.user_info_map.from_id_token_payload.email_verified is None:
+            input_config.user_info_map.from_id_token_payload.email_verified = (
+                "email_verified"
+            )
+
+        if input_config.user_info_map.from_user_info_api.user_id is None:
+            input_config.user_info_map.from_user_info_api.user_id = "sub"
+
+        if input_config.user_info_map.from_user_info_api.email is None:
+            input_config.user_info_map.from_user_info_api.email = "email"
+
+        if input_config.user_info_map.from_user_info_api.email_verified is None:
+            input_config.user_info_map.from_user_info_api.email_verified = (
+                "email_verified"
+            )
+
+        if input_config.generate_fake_email is None:
+
+            async def default_generate_fake_email(
+                _tenant_id: str, third_party_user_id: str, _: Dict[str, Any]
+            ) -> str:
+                return f"{third_party_user_id}.{input_config.third_party_id}@stfakeemail.supertokens.com"
+
+            input_config.generate_fake_email = default_generate_fake_email
+
+        return input_config
+
+    async def get_config_for_client_type(
+        self, client_type: Optional[str], user_context: Dict[str, Any]
+    ) -> ProviderConfigForClient:
+        if client_type is None:
+            if self.input_config.clients is None or len(self.input_config.clients) != 1:
+                raise ClientTypeNotFoundError(
+                    "please provide exactly one client config or pass clientType or tenantId"
+                )
+
+            return get_provider_config_for_client(
+                self.input_config, self.input_config.clients[0]
+            )
+
+        if self.input_config.clients is not None:
+            for client in self.input_config.clients:
+                if client.client_type == client_type:
+                    return get_provider_config_for_client(self.input_config, client)
+
+        raise ClientTypeNotFoundError(
+            f"Could not find client config for clientType: {client_type}"
+        )
+
+    async def get_authorisation_redirect_url(
+        self,
+        redirect_uri_on_provider_dashboard: str,
+        user_context: Dict[str, Any],
+    ) -> AuthorisationRedirect:
+        query_params: Dict[str, str] = {
+            "client_id": self.config.client_id,
+            "redirect_uri": redirect_uri_on_provider_dashboard,
+            "response_type": "code",
+        }
+
+        if self.config.scope is not None:
+            query_params["scope"] = " ".join(self.config.scope)
+
+        pkce_code_verifier: Union[str, None] = None
+
+        if self.config.client_secret is None or self.config.force_pkce:
+            code_verifier, code_challenge = pkce.generate_pkce_pair(64)
+            query_params["code_challenge"] = code_challenge
+            query_params["code_challenge_method"] = "S256"
+            pkce_code_verifier = code_verifier
+
+        if self.config.authorization_endpoint_query_params is not None:
+            for k, v in self.config.authorization_endpoint_query_params.items():
+                if v is None:
+                    del query_params[k]
+                else:
+                    query_params[k] = v
+
+        if self.config.authorization_endpoint is None:
+            raise Exception(
+                "ThirdParty provider's authorizationEndpoint is not configured."
+            )
+
+        url: str = self.config.authorization_endpoint
+
+        # Transformation needed for dev keys BEGIN
+        if is_using_oauth_development_client_id(self.config.client_id):
+            query_params["client_id"] = get_actual_client_id_from_development_client_id(
+                self.config.client_id
+            )
+            query_params["actual_redirect_uri"] = url
+            url = DEV_OAUTH_AUTHORIZATION_URL
+        # Transformation needed for dev keys END
+
+        url_obj = urlparse(url)
+        qparams = parse_qs(url_obj.query)
+        for k, v in query_params.items():
+            qparams[k] = [v]
+
+        url = url_obj._replace(query=urlencode(qparams, doseq=True)).geturl()
+
+        return AuthorisationRedirect(url, pkce_code_verifier)
+
+    async def exchange_auth_code_for_oauth_tokens(
+        self, redirect_uri_info: RedirectUriInfo, user_context: Dict[str, Any]
+    ) -> Dict[str, Any]:
+        if self.config.token_endpoint is None:
+            raise Exception("ThirdParty provider's tokenEndpoint is not configured.")
+
+        token_api_url = self.config.token_endpoint
+        access_token_params: Dict[str, str] = {
+            "client_id": self.config.client_id,
+            "redirect_uri": redirect_uri_info.redirect_uri_on_provider_dashboard,
+            "code": redirect_uri_info.redirect_uri_query_params["code"],
+            "grant_type": "authorization_code",
+        }
+        if self.config.client_secret is not None:
+            access_token_params["client_secret"] = self.config.client_secret
+
+        if redirect_uri_info.pkce_code_verifier is not None:
+            access_token_params["code_verifier"] = redirect_uri_info.pkce_code_verifier
+
+        if self.config.token_endpoint_body_params is not None:
+            access_token_params = merge_into_dict(
+                self.config.token_endpoint_body_params, access_token_params
+            )
+
+        # Transformation needed for dev keys BEGIN
+        if is_using_oauth_development_client_id(self.config.client_id):
+            access_token_params[
+                "client_id"
+            ] = get_actual_client_id_from_development_client_id(self.config.client_id)
+            access_token_params["redirect_uri"] = DEV_OAUTH_REDIRECT_URL
+        # Transformation needed for dev keys END
+
+        _, body = await do_post_request(token_api_url, access_token_params)
+        return body
+
+    async def get_user_info(
+        self, oauth_tokens: Dict[str, Any], user_context: Dict[str, Any]
+    ) -> UserInfo:
+        access_token: Union[str, None] = oauth_tokens.get("access_token")
+        id_token: Union[str, None] = oauth_tokens.get("id_token")
+
+        raw_user_info_from_provider = RawUserInfoFromProvider({}, {})
+        if id_token is not None and self.config.jwks_uri is not None:
+            raw_user_info_from_provider.from_id_token_payload = (
+                await verify_id_token_from_jwks_endpoint_and_get_payload(
+                    id_token,
+                    self.config.jwks_uri,
+                    get_actual_client_id_from_development_client_id(
+                        self.config.client_id
+                    ),
+                )
+            )
+
+            if self.config.validate_id_token_payload is not None:
+                await self.config.validate_id_token_payload(
+                    raw_user_info_from_provider.from_id_token_payload,
+                    self.config,
+                    user_context,
+                )
+
+        if self.config.validate_access_token is not None and access_token is not None:
+            await self.config.validate_access_token(
+                access_token, self.config, user_context
+            )
+
+        if access_token is not None and self.config.user_info_endpoint is not None:
+            headers: Dict[str, str] = {"Authorization": f"Bearer {access_token}"}
+            query_params: Dict[str, str] = {}
+
+            if self.config.user_info_endpoint_headers is not None:
+                headers = merge_into_dict(
+                    self.config.user_info_endpoint_headers, headers
+                )
+
+            if self.config.user_info_endpoint_query_params is not None:
+                query_params = merge_into_dict(
+                    self.config.user_info_endpoint_query_params, query_params
+                )
+
+            raw_user_info_from_provider.from_user_info_api = await do_get_request(
+                self.config.user_info_endpoint, query_params, headers
+            )
+
+        user_info_result = get_supertokens_user_info_result_from_raw_user_info(
+            self.config, raw_user_info_from_provider
+        )
+
+        return UserInfo(
+            third_party_user_id=user_info_result.third_party_user_id,
+            email=user_info_result.email,
+            raw_user_info_from_provider=raw_user_info_from_provider,
+        )
+
+
+def NewProvider(
+    input: ProviderInput,  # pylint: disable=redefined-builtin
+    base_class: Callable[[ProviderConfig], Provider] = GenericProvider,
+) -> Provider:
+    provider_instance = base_class(input.config)
+    if input.override is not None:
+        provider_instance = input.override(provider_instance)
+
+    return provider_instance
+
+
+
+
+
+
+
+

Functions

+
+
+def NewProvider(input: ProviderInput, base_class: Callable[[ProviderConfig], Provider] = supertokens_python.recipe.thirdparty.providers.custom.GenericProvider) ‑> Provider +
+
+
+
+ +Expand source code + +
def NewProvider(
+    input: ProviderInput,  # pylint: disable=redefined-builtin
+    base_class: Callable[[ProviderConfig], Provider] = GenericProvider,
+) -> Provider:
+    provider_instance = base_class(input.config)
+    if input.override is not None:
+        provider_instance = input.override(provider_instance)
+
+    return provider_instance
+
+
+
+def access_field(obj: Any, key: str) ‑> Any +
+
+
+
+ +Expand source code + +
def access_field(obj: Any, key: str) -> Any:
+    key_parts = key.split(".")
+    for part in key_parts:
+        if isinstance(obj, dict):
+            obj = obj.get(part)  # type: ignore
+        else:
+            return None
+
+    return obj
+
+
+
+def get_provider_config_for_client(config: ProviderConfig, client_config: ProviderClientConfig) ‑> ProviderConfigForClient +
+
+
+
+ +Expand source code + +
def get_provider_config_for_client(
+    config: ProviderConfig, client_config: ProviderClientConfig
+) -> ProviderConfigForClient:
+    return ProviderConfigForClient(
+        # ProviderClientConfig
+        client_id=client_config.client_id,
+        client_secret=client_config.client_secret,
+        client_type=client_config.client_type,
+        scope=client_config.scope,
+        force_pkce=client_config.force_pkce,
+        additional_config=client_config.additional_config,
+        # CommonProviderConfig
+        third_party_id=config.third_party_id,
+        name=config.name,
+        authorization_endpoint=config.authorization_endpoint,
+        authorization_endpoint_query_params=config.authorization_endpoint_query_params,
+        token_endpoint=config.token_endpoint,
+        token_endpoint_body_params=config.token_endpoint_body_params,
+        user_info_endpoint=config.user_info_endpoint,
+        user_info_endpoint_query_params=config.user_info_endpoint_query_params,
+        user_info_endpoint_headers=config.user_info_endpoint_headers,
+        jwks_uri=config.jwks_uri,
+        oidc_discovery_endpoint=config.oidc_discovery_endpoint,
+        user_info_map=config.user_info_map,
+        require_email=config.require_email,
+        validate_id_token_payload=config.validate_id_token_payload,
+        generate_fake_email=config.generate_fake_email,
+        validate_access_token=config.validate_access_token,
+    )
+
+
+
+def get_supertokens_user_info_result_from_raw_user_info(config: ProviderConfigForClient, raw_user_info_from_provider: RawUserInfoFromProvider) ‑> UserInfo +
+
+
+
+ +Expand source code + +
def get_supertokens_user_info_result_from_raw_user_info(
+    config: ProviderConfigForClient,
+    raw_user_info_from_provider: RawUserInfoFromProvider,
+) -> UserInfo:
+    third_party_user_id = ""
+
+    if config.user_info_map is None:
+        raise Exception("user info map is missing")
+
+    if config.user_info_map.from_user_info_api is None:
+        config.user_info_map.from_user_info_api = UserFields()
+    if config.user_info_map.from_id_token_payload is None:
+        config.user_info_map.from_id_token_payload = UserFields()
+
+    if config.user_info_map.from_user_info_api.user_id is not None:
+        user_id = access_field(
+            raw_user_info_from_provider.from_user_info_api,
+            config.user_info_map.from_user_info_api.user_id,
+        )
+        if user_id is not None:
+            third_party_user_id = str(user_id)
+
+    if config.user_info_map.from_id_token_payload.user_id is not None:
+        user_id = access_field(
+            raw_user_info_from_provider.from_id_token_payload,
+            config.user_info_map.from_id_token_payload.user_id,
+        )
+        if user_id is not None:
+            third_party_user_id = str(user_id)
+
+    if third_party_user_id == "":
+        raise Exception("third party user id is missing")
+
+    result = UserInfo(
+        third_party_user_id=third_party_user_id,
+    )
+
+    email = ""
+
+    if config.user_info_map.from_user_info_api.email is not None:
+        email_val = access_field(
+            raw_user_info_from_provider.from_user_info_api,
+            config.user_info_map.from_user_info_api.email,
+        )
+        if email_val is not None:
+            email = email_val
+
+    if config.user_info_map.from_id_token_payload.email is not None:
+        email_val = access_field(
+            raw_user_info_from_provider.from_id_token_payload,
+            config.user_info_map.from_id_token_payload.email,
+        )
+        if email_val is not None:
+            email = email_val
+
+    if email != "":
+        result.email = UserInfoEmail(email, False)
+
+        if config.user_info_map.from_user_info_api.email_verified is not None:
+            email_verified = access_field(
+                raw_user_info_from_provider.from_user_info_api,
+                config.user_info_map.from_user_info_api.email_verified,
+            )
+            if email_verified is not None:
+                result.email.is_verified = str(email_verified).lower() == "true"
+
+        if config.user_info_map.from_id_token_payload.email_verified is not None:
+            email_verified = access_field(
+                raw_user_info_from_provider.from_id_token_payload,
+                config.user_info_map.from_id_token_payload.email_verified,
+            )
+            if email_verified is not None:
+                result.email.is_verified = str(email_verified).lower() == "true"
+
+    return result
+
+
+
+def is_using_development_client_id(client_id: str) ‑> bool +
+
+
+
+ +Expand source code + +
def is_using_development_client_id(client_id: str) -> bool:
+    return client_id.startswith(DEV_KEY_IDENTIFIER) or client_id in DEV_OAUTH_CLIENT_IDS
+
+
+
+def merge_into_dict(src: Dict[str, Any], dest: Dict[str, Any]) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
def merge_into_dict(src: Dict[str, Any], dest: Dict[str, Any]) -> Dict[str, Any]:
+    res = dest.copy()
+    for k, v in src.items():
+        if v is None:
+            if k in res:
+                del res[k]
+        else:
+            res[k] = v
+
+    return res
+
+
+
+async def verify_id_token_from_jwks_endpoint_and_get_payload(id_token: str, jwks_uri: str, audience: str) +
+
+
+
+ +Expand source code + +
async def verify_id_token_from_jwks_endpoint_and_get_payload(
+    id_token: str, jwks_uri: str, audience: str
+):
+    public_keys: List[RSAAlgorithm] = []
+    async with AsyncClient(timeout=30.0) as client:
+        response = await client.get(jwks_uri)  # type:ignore
+        key_payload = response.json()
+        for key in key_payload["keys"]:
+            public_keys.append(RSAAlgorithm.from_jwk(key))  # type: ignore
+
+    err = Exception("id token verification failed")
+    for key in public_keys:
+        try:
+            return decode(jwt=id_token, key=key, audience=[audience], algorithms=["RS256"])  # type: ignore
+        except Exception as e:
+            err = e
+    raise err
+
+
+
+
+
+

Classes

+
+
+class GenericProvider +(provider_config: ProviderConfig) +
+
+
+
+ +Expand source code + +
class GenericProvider(Provider):
+    def __init__(self, provider_config: ProviderConfig):
+        self.input_config = input_config = self._normalize_input(provider_config)
+
+        provider_config_for_client = ProviderConfigForClient(
+            # Will automatically get replaced with correct value
+            # in get_provider_config_for_client
+            # when fetch_and_set_config function runs
+            client_id="temp",
+            client_secret=None,
+            client_type=None,
+            scope=None,
+            force_pkce=False,
+            additional_config=None,
+            name=input_config.name,
+            authorization_endpoint=input_config.authorization_endpoint,
+            authorization_endpoint_query_params=input_config.authorization_endpoint_query_params,
+            token_endpoint=input_config.token_endpoint,
+            token_endpoint_body_params=input_config.token_endpoint_body_params,
+            user_info_endpoint=input_config.user_info_endpoint,
+            user_info_endpoint_query_params=input_config.user_info_endpoint_query_params,
+            user_info_endpoint_headers=input_config.user_info_endpoint_headers,
+            jwks_uri=input_config.jwks_uri,
+            oidc_discovery_endpoint=input_config.oidc_discovery_endpoint,
+            user_info_map=input_config.user_info_map,
+            require_email=input_config.require_email,
+            validate_id_token_payload=input_config.validate_id_token_payload,
+            generate_fake_email=input_config.generate_fake_email,
+        )
+        super().__init__(input_config.third_party_id, provider_config_for_client)
+
+    def _normalize_input(  # pylint: disable=no-self-use
+        self, input_config: ProviderConfig
+    ) -> ProviderConfig:
+        if input_config.user_info_map is None:
+            input_config.user_info_map = UserInfoMap(
+                from_id_token_payload=UserFields(),
+                from_user_info_api=UserFields(),
+            )
+        if input_config.user_info_map.from_user_info_api is None:
+            input_config.user_info_map.from_user_info_api = UserFields()
+        if input_config.user_info_map.from_id_token_payload is None:
+            input_config.user_info_map.from_id_token_payload = UserFields()
+
+        # These are safe defaults common to most providers. Each provider
+        # implementations override these as necessary
+        if input_config.user_info_map.from_id_token_payload.user_id is None:
+            input_config.user_info_map.from_id_token_payload.user_id = "sub"
+
+        if input_config.user_info_map.from_id_token_payload.email is None:
+            input_config.user_info_map.from_id_token_payload.email = "email"
+
+        if input_config.user_info_map.from_id_token_payload.email_verified is None:
+            input_config.user_info_map.from_id_token_payload.email_verified = (
+                "email_verified"
+            )
+
+        if input_config.user_info_map.from_user_info_api.user_id is None:
+            input_config.user_info_map.from_user_info_api.user_id = "sub"
+
+        if input_config.user_info_map.from_user_info_api.email is None:
+            input_config.user_info_map.from_user_info_api.email = "email"
+
+        if input_config.user_info_map.from_user_info_api.email_verified is None:
+            input_config.user_info_map.from_user_info_api.email_verified = (
+                "email_verified"
+            )
+
+        if input_config.generate_fake_email is None:
+
+            async def default_generate_fake_email(
+                _tenant_id: str, third_party_user_id: str, _: Dict[str, Any]
+            ) -> str:
+                return f"{third_party_user_id}.{input_config.third_party_id}@stfakeemail.supertokens.com"
+
+            input_config.generate_fake_email = default_generate_fake_email
+
+        return input_config
+
+    async def get_config_for_client_type(
+        self, client_type: Optional[str], user_context: Dict[str, Any]
+    ) -> ProviderConfigForClient:
+        if client_type is None:
+            if self.input_config.clients is None or len(self.input_config.clients) != 1:
+                raise ClientTypeNotFoundError(
+                    "please provide exactly one client config or pass clientType or tenantId"
+                )
+
+            return get_provider_config_for_client(
+                self.input_config, self.input_config.clients[0]
+            )
+
+        if self.input_config.clients is not None:
+            for client in self.input_config.clients:
+                if client.client_type == client_type:
+                    return get_provider_config_for_client(self.input_config, client)
+
+        raise ClientTypeNotFoundError(
+            f"Could not find client config for clientType: {client_type}"
+        )
+
+    async def get_authorisation_redirect_url(
+        self,
+        redirect_uri_on_provider_dashboard: str,
+        user_context: Dict[str, Any],
+    ) -> AuthorisationRedirect:
+        query_params: Dict[str, str] = {
+            "client_id": self.config.client_id,
+            "redirect_uri": redirect_uri_on_provider_dashboard,
+            "response_type": "code",
+        }
+
+        if self.config.scope is not None:
+            query_params["scope"] = " ".join(self.config.scope)
+
+        pkce_code_verifier: Union[str, None] = None
+
+        if self.config.client_secret is None or self.config.force_pkce:
+            code_verifier, code_challenge = pkce.generate_pkce_pair(64)
+            query_params["code_challenge"] = code_challenge
+            query_params["code_challenge_method"] = "S256"
+            pkce_code_verifier = code_verifier
+
+        if self.config.authorization_endpoint_query_params is not None:
+            for k, v in self.config.authorization_endpoint_query_params.items():
+                if v is None:
+                    del query_params[k]
+                else:
+                    query_params[k] = v
+
+        if self.config.authorization_endpoint is None:
+            raise Exception(
+                "ThirdParty provider's authorizationEndpoint is not configured."
+            )
+
+        url: str = self.config.authorization_endpoint
+
+        # Transformation needed for dev keys BEGIN
+        if is_using_oauth_development_client_id(self.config.client_id):
+            query_params["client_id"] = get_actual_client_id_from_development_client_id(
+                self.config.client_id
+            )
+            query_params["actual_redirect_uri"] = url
+            url = DEV_OAUTH_AUTHORIZATION_URL
+        # Transformation needed for dev keys END
+
+        url_obj = urlparse(url)
+        qparams = parse_qs(url_obj.query)
+        for k, v in query_params.items():
+            qparams[k] = [v]
+
+        url = url_obj._replace(query=urlencode(qparams, doseq=True)).geturl()
+
+        return AuthorisationRedirect(url, pkce_code_verifier)
+
+    async def exchange_auth_code_for_oauth_tokens(
+        self, redirect_uri_info: RedirectUriInfo, user_context: Dict[str, Any]
+    ) -> Dict[str, Any]:
+        if self.config.token_endpoint is None:
+            raise Exception("ThirdParty provider's tokenEndpoint is not configured.")
+
+        token_api_url = self.config.token_endpoint
+        access_token_params: Dict[str, str] = {
+            "client_id": self.config.client_id,
+            "redirect_uri": redirect_uri_info.redirect_uri_on_provider_dashboard,
+            "code": redirect_uri_info.redirect_uri_query_params["code"],
+            "grant_type": "authorization_code",
+        }
+        if self.config.client_secret is not None:
+            access_token_params["client_secret"] = self.config.client_secret
+
+        if redirect_uri_info.pkce_code_verifier is not None:
+            access_token_params["code_verifier"] = redirect_uri_info.pkce_code_verifier
+
+        if self.config.token_endpoint_body_params is not None:
+            access_token_params = merge_into_dict(
+                self.config.token_endpoint_body_params, access_token_params
+            )
+
+        # Transformation needed for dev keys BEGIN
+        if is_using_oauth_development_client_id(self.config.client_id):
+            access_token_params[
+                "client_id"
+            ] = get_actual_client_id_from_development_client_id(self.config.client_id)
+            access_token_params["redirect_uri"] = DEV_OAUTH_REDIRECT_URL
+        # Transformation needed for dev keys END
+
+        _, body = await do_post_request(token_api_url, access_token_params)
+        return body
+
+    async def get_user_info(
+        self, oauth_tokens: Dict[str, Any], user_context: Dict[str, Any]
+    ) -> UserInfo:
+        access_token: Union[str, None] = oauth_tokens.get("access_token")
+        id_token: Union[str, None] = oauth_tokens.get("id_token")
+
+        raw_user_info_from_provider = RawUserInfoFromProvider({}, {})
+        if id_token is not None and self.config.jwks_uri is not None:
+            raw_user_info_from_provider.from_id_token_payload = (
+                await verify_id_token_from_jwks_endpoint_and_get_payload(
+                    id_token,
+                    self.config.jwks_uri,
+                    get_actual_client_id_from_development_client_id(
+                        self.config.client_id
+                    ),
+                )
+            )
+
+            if self.config.validate_id_token_payload is not None:
+                await self.config.validate_id_token_payload(
+                    raw_user_info_from_provider.from_id_token_payload,
+                    self.config,
+                    user_context,
+                )
+
+        if self.config.validate_access_token is not None and access_token is not None:
+            await self.config.validate_access_token(
+                access_token, self.config, user_context
+            )
+
+        if access_token is not None and self.config.user_info_endpoint is not None:
+            headers: Dict[str, str] = {"Authorization": f"Bearer {access_token}"}
+            query_params: Dict[str, str] = {}
+
+            if self.config.user_info_endpoint_headers is not None:
+                headers = merge_into_dict(
+                    self.config.user_info_endpoint_headers, headers
+                )
+
+            if self.config.user_info_endpoint_query_params is not None:
+                query_params = merge_into_dict(
+                    self.config.user_info_endpoint_query_params, query_params
+                )
+
+            raw_user_info_from_provider.from_user_info_api = await do_get_request(
+                self.config.user_info_endpoint, query_params, headers
+            )
+
+        user_info_result = get_supertokens_user_info_result_from_raw_user_info(
+            self.config, raw_user_info_from_provider
+        )
+
+        return UserInfo(
+            third_party_user_id=user_info_result.third_party_user_id,
+            email=user_info_result.email,
+            raw_user_info_from_provider=raw_user_info_from_provider,
+        )
+
+

Ancestors

+ +

Subclasses

+ +

Methods

+
+
+async def exchange_auth_code_for_oauth_tokens(self, redirect_uri_info: RedirectUriInfo, user_context: Dict[str, Any]) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
async def exchange_auth_code_for_oauth_tokens(
+    self, redirect_uri_info: RedirectUriInfo, user_context: Dict[str, Any]
+) -> Dict[str, Any]:
+    if self.config.token_endpoint is None:
+        raise Exception("ThirdParty provider's tokenEndpoint is not configured.")
+
+    token_api_url = self.config.token_endpoint
+    access_token_params: Dict[str, str] = {
+        "client_id": self.config.client_id,
+        "redirect_uri": redirect_uri_info.redirect_uri_on_provider_dashboard,
+        "code": redirect_uri_info.redirect_uri_query_params["code"],
+        "grant_type": "authorization_code",
+    }
+    if self.config.client_secret is not None:
+        access_token_params["client_secret"] = self.config.client_secret
+
+    if redirect_uri_info.pkce_code_verifier is not None:
+        access_token_params["code_verifier"] = redirect_uri_info.pkce_code_verifier
+
+    if self.config.token_endpoint_body_params is not None:
+        access_token_params = merge_into_dict(
+            self.config.token_endpoint_body_params, access_token_params
+        )
+
+    # Transformation needed for dev keys BEGIN
+    if is_using_oauth_development_client_id(self.config.client_id):
+        access_token_params[
+            "client_id"
+        ] = get_actual_client_id_from_development_client_id(self.config.client_id)
+        access_token_params["redirect_uri"] = DEV_OAUTH_REDIRECT_URL
+    # Transformation needed for dev keys END
+
+    _, body = await do_post_request(token_api_url, access_token_params)
+    return body
+
+
+
+async def get_authorisation_redirect_url(self, redirect_uri_on_provider_dashboard: str, user_context: Dict[str, Any]) ‑> AuthorisationRedirect +
+
+
+
+ +Expand source code + +
async def get_authorisation_redirect_url(
+    self,
+    redirect_uri_on_provider_dashboard: str,
+    user_context: Dict[str, Any],
+) -> AuthorisationRedirect:
+    query_params: Dict[str, str] = {
+        "client_id": self.config.client_id,
+        "redirect_uri": redirect_uri_on_provider_dashboard,
+        "response_type": "code",
+    }
+
+    if self.config.scope is not None:
+        query_params["scope"] = " ".join(self.config.scope)
+
+    pkce_code_verifier: Union[str, None] = None
+
+    if self.config.client_secret is None or self.config.force_pkce:
+        code_verifier, code_challenge = pkce.generate_pkce_pair(64)
+        query_params["code_challenge"] = code_challenge
+        query_params["code_challenge_method"] = "S256"
+        pkce_code_verifier = code_verifier
+
+    if self.config.authorization_endpoint_query_params is not None:
+        for k, v in self.config.authorization_endpoint_query_params.items():
+            if v is None:
+                del query_params[k]
+            else:
+                query_params[k] = v
+
+    if self.config.authorization_endpoint is None:
+        raise Exception(
+            "ThirdParty provider's authorizationEndpoint is not configured."
+        )
+
+    url: str = self.config.authorization_endpoint
+
+    # Transformation needed for dev keys BEGIN
+    if is_using_oauth_development_client_id(self.config.client_id):
+        query_params["client_id"] = get_actual_client_id_from_development_client_id(
+            self.config.client_id
+        )
+        query_params["actual_redirect_uri"] = url
+        url = DEV_OAUTH_AUTHORIZATION_URL
+    # Transformation needed for dev keys END
+
+    url_obj = urlparse(url)
+    qparams = parse_qs(url_obj.query)
+    for k, v in query_params.items():
+        qparams[k] = [v]
+
+    url = url_obj._replace(query=urlencode(qparams, doseq=True)).geturl()
+
+    return AuthorisationRedirect(url, pkce_code_verifier)
+
+
+
+async def get_config_for_client_type(self, client_type: Optional[str], user_context: Dict[str, Any]) ‑> ProviderConfigForClient +
+
+
+
+ +Expand source code + +
async def get_config_for_client_type(
+    self, client_type: Optional[str], user_context: Dict[str, Any]
+) -> ProviderConfigForClient:
+    if client_type is None:
+        if self.input_config.clients is None or len(self.input_config.clients) != 1:
+            raise ClientTypeNotFoundError(
+                "please provide exactly one client config or pass clientType or tenantId"
+            )
+
+        return get_provider_config_for_client(
+            self.input_config, self.input_config.clients[0]
+        )
+
+    if self.input_config.clients is not None:
+        for client in self.input_config.clients:
+            if client.client_type == client_type:
+                return get_provider_config_for_client(self.input_config, client)
+
+    raise ClientTypeNotFoundError(
+        f"Could not find client config for clientType: {client_type}"
+    )
+
+
+
+async def get_user_info(self, oauth_tokens: Dict[str, Any], user_context: Dict[str, Any]) ‑> UserInfo +
+
+
+
+ +Expand source code + +
async def get_user_info(
+    self, oauth_tokens: Dict[str, Any], user_context: Dict[str, Any]
+) -> UserInfo:
+    access_token: Union[str, None] = oauth_tokens.get("access_token")
+    id_token: Union[str, None] = oauth_tokens.get("id_token")
+
+    raw_user_info_from_provider = RawUserInfoFromProvider({}, {})
+    if id_token is not None and self.config.jwks_uri is not None:
+        raw_user_info_from_provider.from_id_token_payload = (
+            await verify_id_token_from_jwks_endpoint_and_get_payload(
+                id_token,
+                self.config.jwks_uri,
+                get_actual_client_id_from_development_client_id(
+                    self.config.client_id
+                ),
+            )
+        )
+
+        if self.config.validate_id_token_payload is not None:
+            await self.config.validate_id_token_payload(
+                raw_user_info_from_provider.from_id_token_payload,
+                self.config,
+                user_context,
+            )
+
+    if self.config.validate_access_token is not None and access_token is not None:
+        await self.config.validate_access_token(
+            access_token, self.config, user_context
+        )
+
+    if access_token is not None and self.config.user_info_endpoint is not None:
+        headers: Dict[str, str] = {"Authorization": f"Bearer {access_token}"}
+        query_params: Dict[str, str] = {}
+
+        if self.config.user_info_endpoint_headers is not None:
+            headers = merge_into_dict(
+                self.config.user_info_endpoint_headers, headers
+            )
+
+        if self.config.user_info_endpoint_query_params is not None:
+            query_params = merge_into_dict(
+                self.config.user_info_endpoint_query_params, query_params
+            )
+
+        raw_user_info_from_provider.from_user_info_api = await do_get_request(
+            self.config.user_info_endpoint, query_params, headers
+        )
+
+    user_info_result = get_supertokens_user_info_result_from_raw_user_info(
+        self.config, raw_user_info_from_provider
+    )
+
+    return UserInfo(
+        third_party_user_id=user_info_result.third_party_user_id,
+        email=user_info_result.email,
+        raw_user_info_from_provider=raw_user_info_from_provider,
+    )
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/thirdparty/providers/discord.html b/html/supertokens_python/recipe/thirdparty/providers/discord.html new file mode 100644 index 000000000..1a71d6e81 --- /dev/null +++ b/html/supertokens_python/recipe/thirdparty/providers/discord.html @@ -0,0 +1,240 @@ + + + + + + +supertokens_python.recipe.thirdparty.providers.discord API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.thirdparty.providers.discord

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from typing import Any, Dict, Optional
+from ..provider import (
+    Provider,
+    ProviderConfigForClient,
+    ProviderInput,
+    UserFields,
+    UserInfoMap,
+)
+
+from .custom import (
+    GenericProvider,
+    NewProvider,
+)
+
+
+class DiscordImpl(GenericProvider):
+    async def get_config_for_client_type(
+        self, client_type: Optional[str], user_context: Dict[str, Any]
+    ) -> ProviderConfigForClient:
+        config = await super().get_config_for_client_type(client_type, user_context)
+
+        if config.scope is None:
+            config.scope = ["identify", "email"]
+
+        return config
+
+
+def Discord(input: ProviderInput) -> Provider:  # pylint: disable=redefined-builtin
+    if not input.config.name:
+        input.config.name = "Discord"
+
+    if not input.config.authorization_endpoint:
+        input.config.authorization_endpoint = "https://discord.com/api/oauth2/authorize"
+
+    if not input.config.token_endpoint:
+        input.config.token_endpoint = "https://discord.com/api/oauth2/token"
+
+    if not input.config.user_info_endpoint:
+        input.config.user_info_endpoint = "https://discord.com/api/users/@me"
+
+    if input.config.user_info_map is None:
+        input.config.user_info_map = UserInfoMap(UserFields(), UserFields())
+
+    if input.config.user_info_map.from_user_info_api is None:
+        input.config.user_info_map.from_user_info_api = UserFields()
+
+    if input.config.user_info_map.from_user_info_api.user_id is None:
+        input.config.user_info_map.from_user_info_api.user_id = "id"
+
+    if input.config.user_info_map.from_user_info_api.email is None:
+        input.config.user_info_map.from_user_info_api.email = "email"
+
+    if input.config.user_info_map.from_user_info_api.email_verified is None:
+        input.config.user_info_map.from_user_info_api.email_verified = "verified"
+
+    return NewProvider(input, DiscordImpl)
+
+
+
+
+
+
+
+

Functions

+
+
+def Discord(input: ProviderInput) ‑> Provider +
+
+
+
+ +Expand source code + +
def Discord(input: ProviderInput) -> Provider:  # pylint: disable=redefined-builtin
+    if not input.config.name:
+        input.config.name = "Discord"
+
+    if not input.config.authorization_endpoint:
+        input.config.authorization_endpoint = "https://discord.com/api/oauth2/authorize"
+
+    if not input.config.token_endpoint:
+        input.config.token_endpoint = "https://discord.com/api/oauth2/token"
+
+    if not input.config.user_info_endpoint:
+        input.config.user_info_endpoint = "https://discord.com/api/users/@me"
+
+    if input.config.user_info_map is None:
+        input.config.user_info_map = UserInfoMap(UserFields(), UserFields())
+
+    if input.config.user_info_map.from_user_info_api is None:
+        input.config.user_info_map.from_user_info_api = UserFields()
+
+    if input.config.user_info_map.from_user_info_api.user_id is None:
+        input.config.user_info_map.from_user_info_api.user_id = "id"
+
+    if input.config.user_info_map.from_user_info_api.email is None:
+        input.config.user_info_map.from_user_info_api.email = "email"
+
+    if input.config.user_info_map.from_user_info_api.email_verified is None:
+        input.config.user_info_map.from_user_info_api.email_verified = "verified"
+
+    return NewProvider(input, DiscordImpl)
+
+
+
+
+
+

Classes

+
+
+class DiscordImpl +(provider_config: ProviderConfig) +
+
+
+
+ +Expand source code + +
class DiscordImpl(GenericProvider):
+    async def get_config_for_client_type(
+        self, client_type: Optional[str], user_context: Dict[str, Any]
+    ) -> ProviderConfigForClient:
+        config = await super().get_config_for_client_type(client_type, user_context)
+
+        if config.scope is None:
+            config.scope = ["identify", "email"]
+
+        return config
+
+

Ancestors

+ +

Methods

+
+
+async def get_config_for_client_type(self, client_type: Optional[str], user_context: Dict[str, Any]) ‑> ProviderConfigForClient +
+
+
+
+ +Expand source code + +
async def get_config_for_client_type(
+    self, client_type: Optional[str], user_context: Dict[str, Any]
+) -> ProviderConfigForClient:
+    config = await super().get_config_for_client_type(client_type, user_context)
+
+    if config.scope is None:
+        config.scope = ["identify", "email"]
+
+    return config
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/thirdparty/providers/facebook.html b/html/supertokens_python/recipe/thirdparty/providers/facebook.html new file mode 100644 index 000000000..a31e496b4 --- /dev/null +++ b/html/supertokens_python/recipe/thirdparty/providers/facebook.html @@ -0,0 +1,282 @@ + + + + + + +supertokens_python.recipe.thirdparty.providers.facebook API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.thirdparty.providers.facebook

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from typing import Any, Dict, Optional
+
+from supertokens_python.recipe.thirdparty.types import UserInfo
+from ..provider import (
+    Provider,
+    ProviderConfigForClient,
+    ProviderInput,
+    UserFields,
+    UserInfoMap,
+)
+
+from .custom import (
+    GenericProvider,
+    NewProvider,
+)
+
+
+class FacebookImpl(GenericProvider):
+    async def get_config_for_client_type(
+        self, client_type: Optional[str], user_context: Dict[str, Any]
+    ) -> ProviderConfigForClient:
+        config = await super().get_config_for_client_type(client_type, user_context)
+
+        if config.scope is None:
+            config.scope = ["email"]
+
+        return config
+
+    async def get_user_info(
+        self, oauth_tokens: Dict[str, Any], user_context: Dict[str, Any]
+    ) -> UserInfo:
+        self.config.user_info_endpoint_query_params = {
+            "access_token": str(oauth_tokens["access_token"]),
+            "fields": "id,email",
+            "format": "json",
+            **(self.config.user_info_endpoint_query_params or {}),
+        }
+        return await super().get_user_info(oauth_tokens, user_context)
+
+
+def Facebook(input: ProviderInput) -> Provider:  # pylint: disable=redefined-builtin
+    if not input.config.name:
+        input.config.name = "Facebook"
+
+    if not input.config.authorization_endpoint:
+        input.config.authorization_endpoint = (
+            "https://www.facebook.com/v12.0/dialog/oauth"
+        )
+
+    if not input.config.token_endpoint:
+        input.config.token_endpoint = (
+            "https://graph.facebook.com/v12.0/oauth/access_token"
+        )
+
+    if not input.config.user_info_endpoint:
+        input.config.user_info_endpoint = "https://graph.facebook.com/me"
+
+    if input.config.user_info_map is None:
+        input.config.user_info_map = UserInfoMap(UserFields(), UserFields())
+
+    if input.config.user_info_map.from_user_info_api is None:
+        input.config.user_info_map.from_user_info_api = UserFields()
+
+    if input.config.user_info_map.from_user_info_api.user_id is None:
+        input.config.user_info_map.from_user_info_api.user_id = "id"
+
+    return NewProvider(input, FacebookImpl)
+
+
+
+
+
+
+
+

Functions

+
+
+def Facebook(input: ProviderInput) ‑> Provider +
+
+
+
+ +Expand source code + +
def Facebook(input: ProviderInput) -> Provider:  # pylint: disable=redefined-builtin
+    if not input.config.name:
+        input.config.name = "Facebook"
+
+    if not input.config.authorization_endpoint:
+        input.config.authorization_endpoint = (
+            "https://www.facebook.com/v12.0/dialog/oauth"
+        )
+
+    if not input.config.token_endpoint:
+        input.config.token_endpoint = (
+            "https://graph.facebook.com/v12.0/oauth/access_token"
+        )
+
+    if not input.config.user_info_endpoint:
+        input.config.user_info_endpoint = "https://graph.facebook.com/me"
+
+    if input.config.user_info_map is None:
+        input.config.user_info_map = UserInfoMap(UserFields(), UserFields())
+
+    if input.config.user_info_map.from_user_info_api is None:
+        input.config.user_info_map.from_user_info_api = UserFields()
+
+    if input.config.user_info_map.from_user_info_api.user_id is None:
+        input.config.user_info_map.from_user_info_api.user_id = "id"
+
+    return NewProvider(input, FacebookImpl)
+
+
+
+
+
+

Classes

+
+
+class FacebookImpl +(provider_config: ProviderConfig) +
+
+
+
+ +Expand source code + +
class FacebookImpl(GenericProvider):
+    async def get_config_for_client_type(
+        self, client_type: Optional[str], user_context: Dict[str, Any]
+    ) -> ProviderConfigForClient:
+        config = await super().get_config_for_client_type(client_type, user_context)
+
+        if config.scope is None:
+            config.scope = ["email"]
+
+        return config
+
+    async def get_user_info(
+        self, oauth_tokens: Dict[str, Any], user_context: Dict[str, Any]
+    ) -> UserInfo:
+        self.config.user_info_endpoint_query_params = {
+            "access_token": str(oauth_tokens["access_token"]),
+            "fields": "id,email",
+            "format": "json",
+            **(self.config.user_info_endpoint_query_params or {}),
+        }
+        return await super().get_user_info(oauth_tokens, user_context)
+
+

Ancestors

+ +

Methods

+
+
+async def get_config_for_client_type(self, client_type: Optional[str], user_context: Dict[str, Any]) ‑> ProviderConfigForClient +
+
+
+
+ +Expand source code + +
async def get_config_for_client_type(
+    self, client_type: Optional[str], user_context: Dict[str, Any]
+) -> ProviderConfigForClient:
+    config = await super().get_config_for_client_type(client_type, user_context)
+
+    if config.scope is None:
+        config.scope = ["email"]
+
+    return config
+
+
+
+async def get_user_info(self, oauth_tokens: Dict[str, Any], user_context: Dict[str, Any]) ‑> UserInfo +
+
+
+
+ +Expand source code + +
async def get_user_info(
+    self, oauth_tokens: Dict[str, Any], user_context: Dict[str, Any]
+) -> UserInfo:
+    self.config.user_info_endpoint_query_params = {
+        "access_token": str(oauth_tokens["access_token"]),
+        "fields": "id,email",
+        "format": "json",
+        **(self.config.user_info_endpoint_query_params or {}),
+    }
+    return await super().get_user_info(oauth_tokens, user_context)
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/thirdparty/providers/github.html b/html/supertokens_python/recipe/thirdparty/providers/github.html new file mode 100644 index 000000000..d7d727ef2 --- /dev/null +++ b/html/supertokens_python/recipe/thirdparty/providers/github.html @@ -0,0 +1,357 @@ + + + + + + +supertokens_python.recipe.thirdparty.providers.github API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.thirdparty.providers.github

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+import base64
+from typing import Any, Dict, List, Optional
+
+from supertokens_python.recipe.thirdparty.providers.utils import (
+    do_get_request,
+    do_post_request,
+)
+from supertokens_python.recipe.thirdparty.types import UserInfo, UserInfoEmail
+
+from .custom import GenericProvider, NewProvider
+from ..provider import Provider, ProviderConfigForClient, ProviderInput
+
+
+class GithubImpl(GenericProvider):
+    async def get_config_for_client_type(
+        self, client_type: Optional[str], user_context: Dict[str, Any]
+    ) -> ProviderConfigForClient:
+        config = await super().get_config_for_client_type(client_type, user_context)
+
+        if config.scope is None:
+            config.scope = ["read:user", "user:email"]
+
+        return config
+
+    async def get_user_info(
+        self, oauth_tokens: Dict[str, Any], user_context: Dict[str, Any]
+    ) -> UserInfo:
+        headers = {
+            "Authorization": f"Bearer {oauth_tokens.get('access_token')}",
+            "Accept": "application/vnd.github.v3+json",
+        }
+
+        raw_response = {}
+
+        email_info: List[Any] = await do_get_request("https://api.github.com/user/emails", headers=headers)  # type: ignore
+        user_info = await do_get_request("https://api.github.com/user", headers=headers)
+
+        raw_response["emails"] = email_info
+        raw_response["user"] = user_info
+
+        result = UserInfo(
+            third_party_user_id=str(user_info.get("id")),
+        )
+
+        for info in email_info:
+            if info.get("primary"):
+                result.email = UserInfoEmail(
+                    email=info.get("email"), is_verified=info.get("verified")
+                )
+
+        return result
+
+
+def Github(input: ProviderInput) -> Provider:  # pylint: disable=redefined-builtin
+    if not input.config.name:
+        input.config.name = "Github"
+
+    if not input.config.authorization_endpoint:
+        input.config.authorization_endpoint = "https://github.com/login/oauth/authorize"
+
+    if not input.config.token_endpoint:
+        input.config.token_endpoint = "https://github.com/login/oauth/access_token"
+
+    if input.config.validate_access_token is None:
+        input.config.validate_access_token = validate_access_token
+
+    return NewProvider(input, GithubImpl)
+
+
+async def validate_access_token(
+    access_token: str, config: ProviderConfigForClient, _: Dict[str, Any]
+):
+    client_secret = "" if config.client_secret is None else config.client_secret
+    basic_auth_token = base64.b64encode(
+        f"{config.client_id}:{client_secret}".encode()
+    ).decode()
+
+    url = f"https://api.github.com/applications/{config.client_id}/token"
+    headers = {
+        "Authorization": f"Basic {basic_auth_token}",
+        "Content-Type": "application/json",
+    }
+
+    status, body = await do_post_request(url, {"access_token": access_token}, headers)
+    if status != 200:
+        raise ValueError("Invalid access token")
+
+    if "app" not in body or body["app"].get("client_id") != config.client_id:
+        raise ValueError("Access token does not belong to your application")
+
+
+
+
+
+
+
+

Functions

+
+
+def Github(input: ProviderInput) ‑> Provider +
+
+
+
+ +Expand source code + +
def Github(input: ProviderInput) -> Provider:  # pylint: disable=redefined-builtin
+    if not input.config.name:
+        input.config.name = "Github"
+
+    if not input.config.authorization_endpoint:
+        input.config.authorization_endpoint = "https://github.com/login/oauth/authorize"
+
+    if not input.config.token_endpoint:
+        input.config.token_endpoint = "https://github.com/login/oauth/access_token"
+
+    if input.config.validate_access_token is None:
+        input.config.validate_access_token = validate_access_token
+
+    return NewProvider(input, GithubImpl)
+
+
+
+async def validate_access_token(access_token: str, config: ProviderConfigForClient, _: Dict[str, Any]) +
+
+
+
+ +Expand source code + +
async def validate_access_token(
+    access_token: str, config: ProviderConfigForClient, _: Dict[str, Any]
+):
+    client_secret = "" if config.client_secret is None else config.client_secret
+    basic_auth_token = base64.b64encode(
+        f"{config.client_id}:{client_secret}".encode()
+    ).decode()
+
+    url = f"https://api.github.com/applications/{config.client_id}/token"
+    headers = {
+        "Authorization": f"Basic {basic_auth_token}",
+        "Content-Type": "application/json",
+    }
+
+    status, body = await do_post_request(url, {"access_token": access_token}, headers)
+    if status != 200:
+        raise ValueError("Invalid access token")
+
+    if "app" not in body or body["app"].get("client_id") != config.client_id:
+        raise ValueError("Access token does not belong to your application")
+
+
+
+
+
+

Classes

+
+
+class GithubImpl +(provider_config: ProviderConfig) +
+
+
+
+ +Expand source code + +
class GithubImpl(GenericProvider):
+    async def get_config_for_client_type(
+        self, client_type: Optional[str], user_context: Dict[str, Any]
+    ) -> ProviderConfigForClient:
+        config = await super().get_config_for_client_type(client_type, user_context)
+
+        if config.scope is None:
+            config.scope = ["read:user", "user:email"]
+
+        return config
+
+    async def get_user_info(
+        self, oauth_tokens: Dict[str, Any], user_context: Dict[str, Any]
+    ) -> UserInfo:
+        headers = {
+            "Authorization": f"Bearer {oauth_tokens.get('access_token')}",
+            "Accept": "application/vnd.github.v3+json",
+        }
+
+        raw_response = {}
+
+        email_info: List[Any] = await do_get_request("https://api.github.com/user/emails", headers=headers)  # type: ignore
+        user_info = await do_get_request("https://api.github.com/user", headers=headers)
+
+        raw_response["emails"] = email_info
+        raw_response["user"] = user_info
+
+        result = UserInfo(
+            third_party_user_id=str(user_info.get("id")),
+        )
+
+        for info in email_info:
+            if info.get("primary"):
+                result.email = UserInfoEmail(
+                    email=info.get("email"), is_verified=info.get("verified")
+                )
+
+        return result
+
+

Ancestors

+ +

Methods

+
+
+async def get_config_for_client_type(self, client_type: Optional[str], user_context: Dict[str, Any]) ‑> ProviderConfigForClient +
+
+
+
+ +Expand source code + +
async def get_config_for_client_type(
+    self, client_type: Optional[str], user_context: Dict[str, Any]
+) -> ProviderConfigForClient:
+    config = await super().get_config_for_client_type(client_type, user_context)
+
+    if config.scope is None:
+        config.scope = ["read:user", "user:email"]
+
+    return config
+
+
+
+async def get_user_info(self, oauth_tokens: Dict[str, Any], user_context: Dict[str, Any]) ‑> UserInfo +
+
+
+
+ +Expand source code + +
async def get_user_info(
+    self, oauth_tokens: Dict[str, Any], user_context: Dict[str, Any]
+) -> UserInfo:
+    headers = {
+        "Authorization": f"Bearer {oauth_tokens.get('access_token')}",
+        "Accept": "application/vnd.github.v3+json",
+    }
+
+    raw_response = {}
+
+    email_info: List[Any] = await do_get_request("https://api.github.com/user/emails", headers=headers)  # type: ignore
+    user_info = await do_get_request("https://api.github.com/user", headers=headers)
+
+    raw_response["emails"] = email_info
+    raw_response["user"] = user_info
+
+    result = UserInfo(
+        third_party_user_id=str(user_info.get("id")),
+    )
+
+    for info in email_info:
+        if info.get("primary"):
+            result.email = UserInfoEmail(
+                email=info.get("email"), is_verified=info.get("verified")
+            )
+
+    return result
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/thirdparty/providers/gitlab.html b/html/supertokens_python/recipe/thirdparty/providers/gitlab.html new file mode 100644 index 000000000..8e3b51155 --- /dev/null +++ b/html/supertokens_python/recipe/thirdparty/providers/gitlab.html @@ -0,0 +1,261 @@ + + + + + + +supertokens_python.recipe.thirdparty.providers.gitlab API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.thirdparty.providers.gitlab

+
+
+
+ +Expand source code + +
# Copyright (c) 2023, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from __future__ import annotations
+
+from typing import Optional, Dict, Any
+
+from supertokens_python.recipe.thirdparty.provider import (
+    Provider,
+    ProviderConfigForClient,
+)
+from .custom import GenericProvider, NewProvider
+from ..provider import Provider, ProviderInput
+from .utils import normalise_oidc_endpoint_to_include_well_known
+from supertokens_python.normalised_url_domain import NormalisedURLDomain
+from supertokens_python.normalised_url_path import NormalisedURLPath
+
+
+class GitlabImpl(GenericProvider):
+    async def get_config_for_client_type(
+        self, client_type: Optional[str], user_context: Dict[str, Any]
+    ) -> ProviderConfigForClient:
+        config = await super().get_config_for_client_type(client_type, user_context)
+
+        if config.scope is None:
+            config.scope = ["openid", "email"]
+
+        if (
+            config.additional_config is not None
+            and config.additional_config.get("gitlabBaseUrl") is not None
+        ):
+            gitlab_base_url = config.additional_config["gitlabBaseUrl"]
+            oidc_domain = NormalisedURLDomain(gitlab_base_url)
+            oidc_path = NormalisedURLPath("/.well-known/openid-configuration")
+            config.oidc_discovery_endpoint = (
+                oidc_domain.get_as_string_dangerous()
+                + oidc_path.get_as_string_dangerous()
+            )
+
+        if not config.oidc_discovery_endpoint:
+            raise Exception("should never come here")
+
+        # The config could be coming from core where we didn't add the well-known previously
+        config.oidc_discovery_endpoint = normalise_oidc_endpoint_to_include_well_known(
+            config.oidc_discovery_endpoint
+        )
+
+        return config
+
+
+def Gitlab(input: ProviderInput) -> Provider:  # pylint: disable=redefined-builtin
+    if not input.config.name:
+        input.config.name = "Gitlab"
+
+    if not input.config.oidc_discovery_endpoint:
+        input.config.oidc_discovery_endpoint = (
+            "https://gitlab.com/.well-known/openid-configuration"
+        )
+
+    return NewProvider(input, GitlabImpl)
+
+
+
+
+
+
+
+

Functions

+
+
+def Gitlab(input: ProviderInput) ‑> Provider +
+
+
+
+ +Expand source code + +
def Gitlab(input: ProviderInput) -> Provider:  # pylint: disable=redefined-builtin
+    if not input.config.name:
+        input.config.name = "Gitlab"
+
+    if not input.config.oidc_discovery_endpoint:
+        input.config.oidc_discovery_endpoint = (
+            "https://gitlab.com/.well-known/openid-configuration"
+        )
+
+    return NewProvider(input, GitlabImpl)
+
+
+
+
+
+

Classes

+
+
+class GitlabImpl +(provider_config: ProviderConfig) +
+
+
+
+ +Expand source code + +
class GitlabImpl(GenericProvider):
+    async def get_config_for_client_type(
+        self, client_type: Optional[str], user_context: Dict[str, Any]
+    ) -> ProviderConfigForClient:
+        config = await super().get_config_for_client_type(client_type, user_context)
+
+        if config.scope is None:
+            config.scope = ["openid", "email"]
+
+        if (
+            config.additional_config is not None
+            and config.additional_config.get("gitlabBaseUrl") is not None
+        ):
+            gitlab_base_url = config.additional_config["gitlabBaseUrl"]
+            oidc_domain = NormalisedURLDomain(gitlab_base_url)
+            oidc_path = NormalisedURLPath("/.well-known/openid-configuration")
+            config.oidc_discovery_endpoint = (
+                oidc_domain.get_as_string_dangerous()
+                + oidc_path.get_as_string_dangerous()
+            )
+
+        if not config.oidc_discovery_endpoint:
+            raise Exception("should never come here")
+
+        # The config could be coming from core where we didn't add the well-known previously
+        config.oidc_discovery_endpoint = normalise_oidc_endpoint_to_include_well_known(
+            config.oidc_discovery_endpoint
+        )
+
+        return config
+
+

Ancestors

+ +

Methods

+
+
+async def get_config_for_client_type(self, client_type: Optional[str], user_context: Dict[str, Any]) ‑> ProviderConfigForClient +
+
+
+
+ +Expand source code + +
async def get_config_for_client_type(
+    self, client_type: Optional[str], user_context: Dict[str, Any]
+) -> ProviderConfigForClient:
+    config = await super().get_config_for_client_type(client_type, user_context)
+
+    if config.scope is None:
+        config.scope = ["openid", "email"]
+
+    if (
+        config.additional_config is not None
+        and config.additional_config.get("gitlabBaseUrl") is not None
+    ):
+        gitlab_base_url = config.additional_config["gitlabBaseUrl"]
+        oidc_domain = NormalisedURLDomain(gitlab_base_url)
+        oidc_path = NormalisedURLPath("/.well-known/openid-configuration")
+        config.oidc_discovery_endpoint = (
+            oidc_domain.get_as_string_dangerous()
+            + oidc_path.get_as_string_dangerous()
+        )
+
+    if not config.oidc_discovery_endpoint:
+        raise Exception("should never come here")
+
+    # The config could be coming from core where we didn't add the well-known previously
+    config.oidc_discovery_endpoint = normalise_oidc_endpoint_to_include_well_known(
+        config.oidc_discovery_endpoint
+    )
+
+    return config
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/thirdparty/providers/google.html b/html/supertokens_python/recipe/thirdparty/providers/google.html new file mode 100644 index 000000000..dec9afe5e --- /dev/null +++ b/html/supertokens_python/recipe/thirdparty/providers/google.html @@ -0,0 +1,262 @@ + + + + + + +supertokens_python.recipe.thirdparty.providers.google API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.thirdparty.providers.google

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+from typing import Any, Callable, Dict, Optional
+
+from ..provider import (
+    Provider,
+    ProviderConfig,
+    ProviderConfigForClient,
+    ProviderInput,
+    UserFields,
+    UserInfoMap,
+)
+
+from .custom import (
+    GenericProvider,
+    NewProvider,
+)
+from .utils import normalise_oidc_endpoint_to_include_well_known
+
+
+class GoogleImpl(GenericProvider):
+    async def get_config_for_client_type(
+        self, client_type: Optional[str], user_context: Dict[str, Any]
+    ) -> ProviderConfigForClient:
+        config = await super().get_config_for_client_type(client_type, user_context)
+
+        if config.scope is None:
+            config.scope = ["openid", "email"]
+
+        if not config.oidc_discovery_endpoint:
+            raise Exception("should never happen")
+
+        # The config could be coming from core where we didn't add the well-known previously
+        config.oidc_discovery_endpoint = normalise_oidc_endpoint_to_include_well_known(
+            config.oidc_discovery_endpoint
+        )
+
+        return config
+
+
+def Google(
+    input: ProviderInput,  # pylint: disable=redefined-builtin
+    base_class: Callable[[ProviderConfig], GoogleImpl] = GoogleImpl,
+) -> Provider:
+    if not input.config.name:
+        input.config.name = "Google"
+
+    if not input.config.oidc_discovery_endpoint:
+        input.config.oidc_discovery_endpoint = (
+            "https://accounts.google.com/.well-known/openid-configuration"
+        )
+
+    if input.config.user_info_map is None:
+        input.config.user_info_map = UserInfoMap(UserFields(), UserFields())
+
+    if input.config.authorization_endpoint_query_params is None:
+        input.config.authorization_endpoint_query_params = {}
+
+    input.config.authorization_endpoint_query_params = {
+        "included_grant_scopes": "true",
+        "access_type": "offline",
+        **input.config.authorization_endpoint_query_params,
+    }
+
+    return NewProvider(input, base_class)
+
+
+
+
+
+
+
+

Functions

+
+
+def Google(input: ProviderInput, base_class: Callable[[ProviderConfig], GoogleImpl] = supertokens_python.recipe.thirdparty.providers.google.GoogleImpl) ‑> Provider +
+
+
+
+ +Expand source code + +
def Google(
+    input: ProviderInput,  # pylint: disable=redefined-builtin
+    base_class: Callable[[ProviderConfig], GoogleImpl] = GoogleImpl,
+) -> Provider:
+    if not input.config.name:
+        input.config.name = "Google"
+
+    if not input.config.oidc_discovery_endpoint:
+        input.config.oidc_discovery_endpoint = (
+            "https://accounts.google.com/.well-known/openid-configuration"
+        )
+
+    if input.config.user_info_map is None:
+        input.config.user_info_map = UserInfoMap(UserFields(), UserFields())
+
+    if input.config.authorization_endpoint_query_params is None:
+        input.config.authorization_endpoint_query_params = {}
+
+    input.config.authorization_endpoint_query_params = {
+        "included_grant_scopes": "true",
+        "access_type": "offline",
+        **input.config.authorization_endpoint_query_params,
+    }
+
+    return NewProvider(input, base_class)
+
+
+
+
+
+

Classes

+
+
+class GoogleImpl +(provider_config: ProviderConfig) +
+
+
+
+ +Expand source code + +
class GoogleImpl(GenericProvider):
+    async def get_config_for_client_type(
+        self, client_type: Optional[str], user_context: Dict[str, Any]
+    ) -> ProviderConfigForClient:
+        config = await super().get_config_for_client_type(client_type, user_context)
+
+        if config.scope is None:
+            config.scope = ["openid", "email"]
+
+        if not config.oidc_discovery_endpoint:
+            raise Exception("should never happen")
+
+        # The config could be coming from core where we didn't add the well-known previously
+        config.oidc_discovery_endpoint = normalise_oidc_endpoint_to_include_well_known(
+            config.oidc_discovery_endpoint
+        )
+
+        return config
+
+

Ancestors

+ +

Subclasses

+ +

Methods

+
+
+async def get_config_for_client_type(self, client_type: Optional[str], user_context: Dict[str, Any]) ‑> ProviderConfigForClient +
+
+
+
+ +Expand source code + +
async def get_config_for_client_type(
+    self, client_type: Optional[str], user_context: Dict[str, Any]
+) -> ProviderConfigForClient:
+    config = await super().get_config_for_client_type(client_type, user_context)
+
+    if config.scope is None:
+        config.scope = ["openid", "email"]
+
+    if not config.oidc_discovery_endpoint:
+        raise Exception("should never happen")
+
+    # The config could be coming from core where we didn't add the well-known previously
+    config.oidc_discovery_endpoint = normalise_oidc_endpoint_to_include_well_known(
+        config.oidc_discovery_endpoint
+    )
+
+    return config
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/thirdparty/providers/google_workspaces.html b/html/supertokens_python/recipe/thirdparty/providers/google_workspaces.html new file mode 100644 index 000000000..8a0176aac --- /dev/null +++ b/html/supertokens_python/recipe/thirdparty/providers/google_workspaces.html @@ -0,0 +1,241 @@ + + + + + + +supertokens_python.recipe.thirdparty.providers.google_workspaces API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.thirdparty.providers.google_workspaces

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+from typing import Any, Dict, Optional
+
+from .google import Google, GoogleImpl
+
+from ..provider import (
+    Provider,
+    ProviderConfigForClient,
+    ProviderInput,
+)
+
+
+class GoogleWorkspacesImpl(GoogleImpl):
+    async def get_config_for_client_type(
+        self, client_type: Optional[str], user_context: Dict[str, Any]
+    ) -> ProviderConfigForClient:
+        config = await super().get_config_for_client_type(client_type, user_context)
+
+        if config.additional_config is None:
+            config.additional_config = {}
+
+        config.authorization_endpoint_query_params = {
+            "hd": str(config.additional_config.get("hd", "*")),
+            **(config.authorization_endpoint_query_params or {}),
+        }
+
+        return config
+
+
+def GoogleWorkspaces(
+    input: ProviderInput,  # pylint: disable=redefined-builtin
+) -> Provider:
+    if not input.config.name:
+        input.config.name = "Google Workspaces"
+
+    if input.config.validate_id_token_payload is None:
+
+        async def default_validate_id_token_payload(
+            id_token_payload: Dict[str, Any],
+            config: ProviderConfigForClient,
+            _user_context: Dict[str, Any],
+        ):
+            if (config.additional_config or {}).get("hd", "*") != "*":
+                if (config.additional_config or {}).get("hd") != id_token_payload.get(
+                    "hd"
+                ):
+                    raise Exception(
+                        "the value for hd claim in the id token does not match the value provided in the config"
+                    )
+
+        input.config.validate_id_token_payload = default_validate_id_token_payload
+
+    return Google(input, GoogleWorkspacesImpl)
+
+
+
+
+
+
+
+

Functions

+
+
+def GoogleWorkspaces(input: ProviderInput) ‑> Provider +
+
+
+
+ +Expand source code + +
def GoogleWorkspaces(
+    input: ProviderInput,  # pylint: disable=redefined-builtin
+) -> Provider:
+    if not input.config.name:
+        input.config.name = "Google Workspaces"
+
+    if input.config.validate_id_token_payload is None:
+
+        async def default_validate_id_token_payload(
+            id_token_payload: Dict[str, Any],
+            config: ProviderConfigForClient,
+            _user_context: Dict[str, Any],
+        ):
+            if (config.additional_config or {}).get("hd", "*") != "*":
+                if (config.additional_config or {}).get("hd") != id_token_payload.get(
+                    "hd"
+                ):
+                    raise Exception(
+                        "the value for hd claim in the id token does not match the value provided in the config"
+                    )
+
+        input.config.validate_id_token_payload = default_validate_id_token_payload
+
+    return Google(input, GoogleWorkspacesImpl)
+
+
+
+
+
+

Classes

+
+
+class GoogleWorkspacesImpl +(provider_config: ProviderConfig) +
+
+
+
+ +Expand source code + +
class GoogleWorkspacesImpl(GoogleImpl):
+    async def get_config_for_client_type(
+        self, client_type: Optional[str], user_context: Dict[str, Any]
+    ) -> ProviderConfigForClient:
+        config = await super().get_config_for_client_type(client_type, user_context)
+
+        if config.additional_config is None:
+            config.additional_config = {}
+
+        config.authorization_endpoint_query_params = {
+            "hd": str(config.additional_config.get("hd", "*")),
+            **(config.authorization_endpoint_query_params or {}),
+        }
+
+        return config
+
+

Ancestors

+ +

Methods

+
+
+async def get_config_for_client_type(self, client_type: Optional[str], user_context: Dict[str, Any]) ‑> ProviderConfigForClient +
+
+
+
+ +Expand source code + +
async def get_config_for_client_type(
+    self, client_type: Optional[str], user_context: Dict[str, Any]
+) -> ProviderConfigForClient:
+    config = await super().get_config_for_client_type(client_type, user_context)
+
+    if config.additional_config is None:
+        config.additional_config = {}
+
+    config.authorization_endpoint_query_params = {
+        "hd": str(config.additional_config.get("hd", "*")),
+        **(config.authorization_endpoint_query_params or {}),
+    }
+
+    return config
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/thirdparty/providers/index.html b/html/supertokens_python/recipe/thirdparty/providers/index.html new file mode 100644 index 000000000..35af277d9 --- /dev/null +++ b/html/supertokens_python/recipe/thirdparty/providers/index.html @@ -0,0 +1,158 @@ + + + + + + +supertokens_python.recipe.thirdparty.providers API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.thirdparty.providers

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+
+
+

Sub-modules

+
+
supertokens_python.recipe.thirdparty.providers.active_directory
+
+
+
+
supertokens_python.recipe.thirdparty.providers.apple
+
+
+
+
supertokens_python.recipe.thirdparty.providers.bitbucket
+
+
+
+
supertokens_python.recipe.thirdparty.providers.boxy_saml
+
+
+
+
supertokens_python.recipe.thirdparty.providers.config_utils
+
+
+
+
supertokens_python.recipe.thirdparty.providers.custom
+
+
+
+
supertokens_python.recipe.thirdparty.providers.discord
+
+
+
+
supertokens_python.recipe.thirdparty.providers.facebook
+
+
+
+
supertokens_python.recipe.thirdparty.providers.github
+
+
+
+
supertokens_python.recipe.thirdparty.providers.gitlab
+
+
+
+
supertokens_python.recipe.thirdparty.providers.google
+
+
+
+
supertokens_python.recipe.thirdparty.providers.google_workspaces
+
+
+
+
supertokens_python.recipe.thirdparty.providers.linkedin
+
+
+
+
supertokens_python.recipe.thirdparty.providers.okta
+
+
+
+
supertokens_python.recipe.thirdparty.providers.twitter
+
+
+
+
supertokens_python.recipe.thirdparty.providers.utils
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/thirdparty/providers/linkedin.html b/html/supertokens_python/recipe/thirdparty/providers/linkedin.html new file mode 100644 index 000000000..ac96778d6 --- /dev/null +++ b/html/supertokens_python/recipe/thirdparty/providers/linkedin.html @@ -0,0 +1,340 @@ + + + + + + +supertokens_python.recipe.thirdparty.providers.linkedin API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.thirdparty.providers.linkedin

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from typing import Any, Dict, Optional, Union
+from supertokens_python.recipe.thirdparty.providers.utils import do_get_request
+
+from supertokens_python.recipe.thirdparty.types import (
+    RawUserInfoFromProvider,
+    UserInfo,
+    UserInfoEmail,
+)
+from ..provider import (
+    Provider,
+    ProviderConfigForClient,
+    ProviderInput,
+    UserFields,
+    UserInfoMap,
+)
+
+from .custom import (
+    GenericProvider,
+    NewProvider,
+)
+
+
+class LinkedinImpl(GenericProvider):
+    async def get_config_for_client_type(
+        self, client_type: Optional[str], user_context: Dict[str, Any]
+    ) -> ProviderConfigForClient:
+        config = await super().get_config_for_client_type(client_type, user_context)
+
+        if config.scope is None:
+            # https://learn.microsoft.com/en-us/linkedin/consumer/integrations/self-serve/sign-in-with-linkedin-v2?context=linkedin%2Fconsumer%2Fcontext#authenticating-members
+            config.scope = ["openid", "profile", "email"]
+
+        return config
+
+    async def get_user_info(
+        self, oauth_tokens: Dict[str, Any], user_context: Dict[str, Any]
+    ) -> UserInfo:
+        access_token: Union[str, None] = oauth_tokens.get("access_token")
+
+        if access_token is None:
+            raise Exception("Access token not found")
+
+        headers = {
+            "Authorization": f"Bearer {access_token}",
+        }
+
+        raw_user_info_from_provider = RawUserInfoFromProvider({}, {})
+        # https://learn.microsoft.com/en-us/linkedin/consumer/integrations/self-serve/sign-in-with-linkedin-v2?context=linkedin%2Fconsumer%2Fcontext#sample-api-response
+        user_info = await do_get_request(
+            "https://api.linkedin.com/v2/userinfo", headers=headers
+        )
+        raw_user_info_from_provider.from_user_info_api = user_info
+
+        return UserInfo(
+            third_party_user_id=raw_user_info_from_provider.from_user_info_api.get("sub"),  # type: ignore
+            email=UserInfoEmail(
+                email=raw_user_info_from_provider.from_user_info_api.get("email"),  # type: ignore
+                is_verified=raw_user_info_from_provider.from_user_info_api.get("email_verified"),  # type: ignore
+            ),
+        )
+
+
+def Linkedin(input: ProviderInput) -> Provider:  # pylint: disable=redefined-builtin
+    if not input.config.name:
+        input.config.name = "Linkedin"
+
+    if not input.config.authorization_endpoint:
+        input.config.authorization_endpoint = (
+            "https://www.linkedin.com/oauth/v2/authorization"
+        )
+
+    if not input.config.token_endpoint:
+        input.config.token_endpoint = "https://www.linkedin.com/oauth/v2/accessToken"
+
+    if input.config.user_info_map is None:
+        input.config.user_info_map = UserInfoMap(UserFields(), UserFields())
+
+    if input.config.user_info_map.from_user_info_api is None:
+        input.config.user_info_map.from_user_info_api = UserFields()
+
+    if input.config.user_info_map.from_user_info_api.user_id is None:
+        input.config.user_info_map.from_user_info_api.user_id = "id"
+
+    if input.config.user_info_map.from_user_info_api.email is None:
+        input.config.user_info_map.from_user_info_api.email = "email"
+
+    if input.config.user_info_map.from_user_info_api.email_verified is None:
+        input.config.user_info_map.from_user_info_api.email = "verified"
+
+    return NewProvider(input, LinkedinImpl)
+
+
+
+
+
+
+
+

Functions

+
+
+def Linkedin(input: ProviderInput) ‑> Provider +
+
+
+
+ +Expand source code + +
def Linkedin(input: ProviderInput) -> Provider:  # pylint: disable=redefined-builtin
+    if not input.config.name:
+        input.config.name = "Linkedin"
+
+    if not input.config.authorization_endpoint:
+        input.config.authorization_endpoint = (
+            "https://www.linkedin.com/oauth/v2/authorization"
+        )
+
+    if not input.config.token_endpoint:
+        input.config.token_endpoint = "https://www.linkedin.com/oauth/v2/accessToken"
+
+    if input.config.user_info_map is None:
+        input.config.user_info_map = UserInfoMap(UserFields(), UserFields())
+
+    if input.config.user_info_map.from_user_info_api is None:
+        input.config.user_info_map.from_user_info_api = UserFields()
+
+    if input.config.user_info_map.from_user_info_api.user_id is None:
+        input.config.user_info_map.from_user_info_api.user_id = "id"
+
+    if input.config.user_info_map.from_user_info_api.email is None:
+        input.config.user_info_map.from_user_info_api.email = "email"
+
+    if input.config.user_info_map.from_user_info_api.email_verified is None:
+        input.config.user_info_map.from_user_info_api.email = "verified"
+
+    return NewProvider(input, LinkedinImpl)
+
+
+
+
+
+

Classes

+
+
+class LinkedinImpl +(provider_config: ProviderConfig) +
+
+
+
+ +Expand source code + +
class LinkedinImpl(GenericProvider):
+    async def get_config_for_client_type(
+        self, client_type: Optional[str], user_context: Dict[str, Any]
+    ) -> ProviderConfigForClient:
+        config = await super().get_config_for_client_type(client_type, user_context)
+
+        if config.scope is None:
+            # https://learn.microsoft.com/en-us/linkedin/consumer/integrations/self-serve/sign-in-with-linkedin-v2?context=linkedin%2Fconsumer%2Fcontext#authenticating-members
+            config.scope = ["openid", "profile", "email"]
+
+        return config
+
+    async def get_user_info(
+        self, oauth_tokens: Dict[str, Any], user_context: Dict[str, Any]
+    ) -> UserInfo:
+        access_token: Union[str, None] = oauth_tokens.get("access_token")
+
+        if access_token is None:
+            raise Exception("Access token not found")
+
+        headers = {
+            "Authorization": f"Bearer {access_token}",
+        }
+
+        raw_user_info_from_provider = RawUserInfoFromProvider({}, {})
+        # https://learn.microsoft.com/en-us/linkedin/consumer/integrations/self-serve/sign-in-with-linkedin-v2?context=linkedin%2Fconsumer%2Fcontext#sample-api-response
+        user_info = await do_get_request(
+            "https://api.linkedin.com/v2/userinfo", headers=headers
+        )
+        raw_user_info_from_provider.from_user_info_api = user_info
+
+        return UserInfo(
+            third_party_user_id=raw_user_info_from_provider.from_user_info_api.get("sub"),  # type: ignore
+            email=UserInfoEmail(
+                email=raw_user_info_from_provider.from_user_info_api.get("email"),  # type: ignore
+                is_verified=raw_user_info_from_provider.from_user_info_api.get("email_verified"),  # type: ignore
+            ),
+        )
+
+

Ancestors

+ +

Methods

+
+
+async def get_config_for_client_type(self, client_type: Optional[str], user_context: Dict[str, Any]) ‑> ProviderConfigForClient +
+
+
+
+ +Expand source code + +
async def get_config_for_client_type(
+    self, client_type: Optional[str], user_context: Dict[str, Any]
+) -> ProviderConfigForClient:
+    config = await super().get_config_for_client_type(client_type, user_context)
+
+    if config.scope is None:
+        # https://learn.microsoft.com/en-us/linkedin/consumer/integrations/self-serve/sign-in-with-linkedin-v2?context=linkedin%2Fconsumer%2Fcontext#authenticating-members
+        config.scope = ["openid", "profile", "email"]
+
+    return config
+
+
+
+async def get_user_info(self, oauth_tokens: Dict[str, Any], user_context: Dict[str, Any]) ‑> UserInfo +
+
+
+
+ +Expand source code + +
async def get_user_info(
+    self, oauth_tokens: Dict[str, Any], user_context: Dict[str, Any]
+) -> UserInfo:
+    access_token: Union[str, None] = oauth_tokens.get("access_token")
+
+    if access_token is None:
+        raise Exception("Access token not found")
+
+    headers = {
+        "Authorization": f"Bearer {access_token}",
+    }
+
+    raw_user_info_from_provider = RawUserInfoFromProvider({}, {})
+    # https://learn.microsoft.com/en-us/linkedin/consumer/integrations/self-serve/sign-in-with-linkedin-v2?context=linkedin%2Fconsumer%2Fcontext#sample-api-response
+    user_info = await do_get_request(
+        "https://api.linkedin.com/v2/userinfo", headers=headers
+    )
+    raw_user_info_from_provider.from_user_info_api = user_info
+
+    return UserInfo(
+        third_party_user_id=raw_user_info_from_provider.from_user_info_api.get("sub"),  # type: ignore
+        email=UserInfoEmail(
+            email=raw_user_info_from_provider.from_user_info_api.get("email"),  # type: ignore
+            is_verified=raw_user_info_from_provider.from_user_info_api.get("email_verified"),  # type: ignore
+        ),
+    )
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/thirdparty/providers/okta.html b/html/supertokens_python/recipe/thirdparty/providers/okta.html new file mode 100644 index 000000000..e5e6b16b6 --- /dev/null +++ b/html/supertokens_python/recipe/thirdparty/providers/okta.html @@ -0,0 +1,270 @@ + + + + + + +supertokens_python.recipe.thirdparty.providers.okta API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.thirdparty.providers.okta

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from typing import Any, Dict, Optional
+from .custom import GenericProvider, NewProvider
+from ..provider import (
+    Provider,
+    ProviderConfigForClient,
+    ProviderInput,
+)
+from .utils import normalise_oidc_endpoint_to_include_well_known
+from supertokens_python.normalised_url_domain import NormalisedURLDomain
+from supertokens_python.normalised_url_path import NormalisedURLPath
+
+
+class OktaImpl(GenericProvider):
+    async def get_config_for_client_type(
+        self, client_type: Optional[str], user_context: Dict[str, Any]
+    ) -> ProviderConfigForClient:
+        config = await super().get_config_for_client_type(client_type, user_context)
+
+        if (
+            config.additional_config is None
+            or config.additional_config.get("oktaDomain") is None
+        ):
+            if not config.oidc_discovery_endpoint:
+                raise Exception(
+                    "Please provide the oktaDomain in the additionalConfig of the Okta provider."
+                )
+        else:
+            okta_domain = config.additional_config["oktaDomain"]
+            oidc_domain = NormalisedURLDomain(okta_domain)
+            oidc_path = NormalisedURLPath("/.well-known/openid-configuration")
+            config.oidc_discovery_endpoint = (
+                oidc_domain.get_as_string_dangerous()
+                + oidc_path.get_as_string_dangerous()
+            )
+
+        if not config.oidc_discovery_endpoint:
+            raise Exception("should never happen")
+
+        # The config could be coming from core where we didn't add the well-known previously
+        config.oidc_discovery_endpoint = normalise_oidc_endpoint_to_include_well_known(
+            config.oidc_discovery_endpoint
+        )
+
+        if config.scope is None:
+            config.scope = ["openid", "email"]
+
+        # TODO later if required, client assertion impl
+
+        return config
+
+
+def Okta(input: ProviderInput) -> Provider:  # pylint: disable=redefined-builtin
+    if not input.config.name:
+        input.config.name = "Okta"
+
+    return NewProvider(input, OktaImpl)
+
+
+
+
+
+
+
+

Functions

+
+
+def Okta(input: ProviderInput) ‑> Provider +
+
+
+
+ +Expand source code + +
def Okta(input: ProviderInput) -> Provider:  # pylint: disable=redefined-builtin
+    if not input.config.name:
+        input.config.name = "Okta"
+
+    return NewProvider(input, OktaImpl)
+
+
+
+
+
+

Classes

+
+
+class OktaImpl +(provider_config: ProviderConfig) +
+
+
+
+ +Expand source code + +
class OktaImpl(GenericProvider):
+    async def get_config_for_client_type(
+        self, client_type: Optional[str], user_context: Dict[str, Any]
+    ) -> ProviderConfigForClient:
+        config = await super().get_config_for_client_type(client_type, user_context)
+
+        if (
+            config.additional_config is None
+            or config.additional_config.get("oktaDomain") is None
+        ):
+            if not config.oidc_discovery_endpoint:
+                raise Exception(
+                    "Please provide the oktaDomain in the additionalConfig of the Okta provider."
+                )
+        else:
+            okta_domain = config.additional_config["oktaDomain"]
+            oidc_domain = NormalisedURLDomain(okta_domain)
+            oidc_path = NormalisedURLPath("/.well-known/openid-configuration")
+            config.oidc_discovery_endpoint = (
+                oidc_domain.get_as_string_dangerous()
+                + oidc_path.get_as_string_dangerous()
+            )
+
+        if not config.oidc_discovery_endpoint:
+            raise Exception("should never happen")
+
+        # The config could be coming from core where we didn't add the well-known previously
+        config.oidc_discovery_endpoint = normalise_oidc_endpoint_to_include_well_known(
+            config.oidc_discovery_endpoint
+        )
+
+        if config.scope is None:
+            config.scope = ["openid", "email"]
+
+        # TODO later if required, client assertion impl
+
+        return config
+
+

Ancestors

+ +

Methods

+
+
+async def get_config_for_client_type(self, client_type: Optional[str], user_context: Dict[str, Any]) ‑> ProviderConfigForClient +
+
+
+
+ +Expand source code + +
async def get_config_for_client_type(
+    self, client_type: Optional[str], user_context: Dict[str, Any]
+) -> ProviderConfigForClient:
+    config = await super().get_config_for_client_type(client_type, user_context)
+
+    if (
+        config.additional_config is None
+        or config.additional_config.get("oktaDomain") is None
+    ):
+        if not config.oidc_discovery_endpoint:
+            raise Exception(
+                "Please provide the oktaDomain in the additionalConfig of the Okta provider."
+            )
+    else:
+        okta_domain = config.additional_config["oktaDomain"]
+        oidc_domain = NormalisedURLDomain(okta_domain)
+        oidc_path = NormalisedURLPath("/.well-known/openid-configuration")
+        config.oidc_discovery_endpoint = (
+            oidc_domain.get_as_string_dangerous()
+            + oidc_path.get_as_string_dangerous()
+        )
+
+    if not config.oidc_discovery_endpoint:
+        raise Exception("should never happen")
+
+    # The config could be coming from core where we didn't add the well-known previously
+    config.oidc_discovery_endpoint = normalise_oidc_endpoint_to_include_well_known(
+        config.oidc_discovery_endpoint
+    )
+
+    if config.scope is None:
+        config.scope = ["openid", "email"]
+
+    # TODO later if required, client assertion impl
+
+    return config
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/thirdparty/providers/twitter.html b/html/supertokens_python/recipe/thirdparty/providers/twitter.html new file mode 100644 index 000000000..85a5b2435 --- /dev/null +++ b/html/supertokens_python/recipe/thirdparty/providers/twitter.html @@ -0,0 +1,385 @@ + + + + + + +supertokens_python.recipe.thirdparty.providers.twitter API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.thirdparty.providers.twitter

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from base64 import b64encode
+from typing import Any, Dict, Optional
+from supertokens_python.recipe.thirdparty.provider import RedirectUriInfo
+from supertokens_python.recipe.thirdparty.providers.utils import (
+    do_post_request,
+    DEV_OAUTH_REDIRECT_URL,
+    get_actual_client_id_from_development_client_id,
+)
+from ..provider import (
+    Provider,
+    ProviderConfigForClient,
+    ProviderInput,
+    UserFields,
+    UserInfoMap,
+)
+
+from .custom import (
+    GenericProvider,
+    NewProvider,
+    is_using_development_client_id,
+)
+
+
+class TwitterImpl(GenericProvider):
+    async def get_config_for_client_type(
+        self, client_type: Optional[str], user_context: Dict[str, Any]
+    ) -> ProviderConfigForClient:
+        config = await super().get_config_for_client_type(client_type, user_context)
+
+        if config.scope is None:
+            config.scope = ["users.read", "tweet.read"]
+
+        if config.force_pkce is None:
+            config.force_pkce = True
+
+        return config
+
+    async def exchange_auth_code_for_oauth_tokens(
+        self, redirect_uri_info: RedirectUriInfo, user_context: Dict[str, Any]
+    ) -> Dict[str, Any]:
+
+        client_id = self.config.client_id
+        redirect_uri = redirect_uri_info.redirect_uri_on_provider_dashboard
+
+        # We need to do this because we don't call the original implementation
+        # Transformation needed for dev keys BEGIN
+        if is_using_development_client_id(self.config.client_id):
+            client_id = get_actual_client_id_from_development_client_id(
+                self.config.client_id
+            )
+            redirect_uri = DEV_OAUTH_REDIRECT_URL
+        # Transformation needed for dev keys END
+
+        credentials = client_id + ":" + (self.config.client_secret or "")
+        auth_token = b64encode(credentials.encode()).decode()
+
+        twitter_oauth_tokens_params: Dict[str, Any] = {
+            "grant_type": "authorization_code",
+            "client_id": client_id,
+            "code_verifier": redirect_uri_info.pkce_code_verifier,
+            "redirect_uri": redirect_uri,
+            "code": redirect_uri_info.redirect_uri_query_params["code"],
+        }
+
+        twitter_oauth_tokens_params = {
+            **twitter_oauth_tokens_params,
+            **(self.config.token_endpoint_body_params or {}),
+        }
+
+        assert self.config.token_endpoint is not None
+
+        _, body = await do_post_request(
+            self.config.token_endpoint,
+            body_params=twitter_oauth_tokens_params,
+            headers={"Authorization": f"Basic {auth_token}"},
+        )
+        return body
+
+
+def Twitter(input: ProviderInput) -> Provider:  # pylint: disable=redefined-builtin
+    if not input.config.name:
+        input.config.name = "Twitter"
+
+    if not input.config.authorization_endpoint:
+        input.config.authorization_endpoint = "https://twitter.com/i/oauth2/authorize"
+
+    if not input.config.token_endpoint:
+        input.config.token_endpoint = "https://api.twitter.com/2/oauth2/token"
+
+    if not input.config.user_info_endpoint:
+        input.config.user_info_endpoint = "https://api.twitter.com/2/users/me"
+
+    if input.config.require_email is None:
+        input.config.require_email = False
+
+    if input.config.user_info_map is None:
+        input.config.user_info_map = UserInfoMap(UserFields(), UserFields())
+
+    if input.config.user_info_map.from_user_info_api is None:
+        input.config.user_info_map.from_user_info_api = UserFields()
+
+    if input.config.user_info_map.from_user_info_api.user_id is None:
+        input.config.user_info_map.from_user_info_api.user_id = "data.id"
+
+    return NewProvider(input, TwitterImpl)
+
+
+
+
+
+
+
+

Functions

+
+
+def Twitter(input: ProviderInput) ‑> Provider +
+
+
+
+ +Expand source code + +
def Twitter(input: ProviderInput) -> Provider:  # pylint: disable=redefined-builtin
+    if not input.config.name:
+        input.config.name = "Twitter"
+
+    if not input.config.authorization_endpoint:
+        input.config.authorization_endpoint = "https://twitter.com/i/oauth2/authorize"
+
+    if not input.config.token_endpoint:
+        input.config.token_endpoint = "https://api.twitter.com/2/oauth2/token"
+
+    if not input.config.user_info_endpoint:
+        input.config.user_info_endpoint = "https://api.twitter.com/2/users/me"
+
+    if input.config.require_email is None:
+        input.config.require_email = False
+
+    if input.config.user_info_map is None:
+        input.config.user_info_map = UserInfoMap(UserFields(), UserFields())
+
+    if input.config.user_info_map.from_user_info_api is None:
+        input.config.user_info_map.from_user_info_api = UserFields()
+
+    if input.config.user_info_map.from_user_info_api.user_id is None:
+        input.config.user_info_map.from_user_info_api.user_id = "data.id"
+
+    return NewProvider(input, TwitterImpl)
+
+
+
+
+
+

Classes

+
+
+class TwitterImpl +(provider_config: ProviderConfig) +
+
+
+
+ +Expand source code + +
class TwitterImpl(GenericProvider):
+    async def get_config_for_client_type(
+        self, client_type: Optional[str], user_context: Dict[str, Any]
+    ) -> ProviderConfigForClient:
+        config = await super().get_config_for_client_type(client_type, user_context)
+
+        if config.scope is None:
+            config.scope = ["users.read", "tweet.read"]
+
+        if config.force_pkce is None:
+            config.force_pkce = True
+
+        return config
+
+    async def exchange_auth_code_for_oauth_tokens(
+        self, redirect_uri_info: RedirectUriInfo, user_context: Dict[str, Any]
+    ) -> Dict[str, Any]:
+
+        client_id = self.config.client_id
+        redirect_uri = redirect_uri_info.redirect_uri_on_provider_dashboard
+
+        # We need to do this because we don't call the original implementation
+        # Transformation needed for dev keys BEGIN
+        if is_using_development_client_id(self.config.client_id):
+            client_id = get_actual_client_id_from_development_client_id(
+                self.config.client_id
+            )
+            redirect_uri = DEV_OAUTH_REDIRECT_URL
+        # Transformation needed for dev keys END
+
+        credentials = client_id + ":" + (self.config.client_secret or "")
+        auth_token = b64encode(credentials.encode()).decode()
+
+        twitter_oauth_tokens_params: Dict[str, Any] = {
+            "grant_type": "authorization_code",
+            "client_id": client_id,
+            "code_verifier": redirect_uri_info.pkce_code_verifier,
+            "redirect_uri": redirect_uri,
+            "code": redirect_uri_info.redirect_uri_query_params["code"],
+        }
+
+        twitter_oauth_tokens_params = {
+            **twitter_oauth_tokens_params,
+            **(self.config.token_endpoint_body_params or {}),
+        }
+
+        assert self.config.token_endpoint is not None
+
+        _, body = await do_post_request(
+            self.config.token_endpoint,
+            body_params=twitter_oauth_tokens_params,
+            headers={"Authorization": f"Basic {auth_token}"},
+        )
+        return body
+
+

Ancestors

+ +

Methods

+
+
+async def exchange_auth_code_for_oauth_tokens(self, redirect_uri_info: RedirectUriInfo, user_context: Dict[str, Any]) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
async def exchange_auth_code_for_oauth_tokens(
+    self, redirect_uri_info: RedirectUriInfo, user_context: Dict[str, Any]
+) -> Dict[str, Any]:
+
+    client_id = self.config.client_id
+    redirect_uri = redirect_uri_info.redirect_uri_on_provider_dashboard
+
+    # We need to do this because we don't call the original implementation
+    # Transformation needed for dev keys BEGIN
+    if is_using_development_client_id(self.config.client_id):
+        client_id = get_actual_client_id_from_development_client_id(
+            self.config.client_id
+        )
+        redirect_uri = DEV_OAUTH_REDIRECT_URL
+    # Transformation needed for dev keys END
+
+    credentials = client_id + ":" + (self.config.client_secret or "")
+    auth_token = b64encode(credentials.encode()).decode()
+
+    twitter_oauth_tokens_params: Dict[str, Any] = {
+        "grant_type": "authorization_code",
+        "client_id": client_id,
+        "code_verifier": redirect_uri_info.pkce_code_verifier,
+        "redirect_uri": redirect_uri,
+        "code": redirect_uri_info.redirect_uri_query_params["code"],
+    }
+
+    twitter_oauth_tokens_params = {
+        **twitter_oauth_tokens_params,
+        **(self.config.token_endpoint_body_params or {}),
+    }
+
+    assert self.config.token_endpoint is not None
+
+    _, body = await do_post_request(
+        self.config.token_endpoint,
+        body_params=twitter_oauth_tokens_params,
+        headers={"Authorization": f"Basic {auth_token}"},
+    )
+    return body
+
+
+
+async def get_config_for_client_type(self, client_type: Optional[str], user_context: Dict[str, Any]) ‑> ProviderConfigForClient +
+
+
+
+ +Expand source code + +
async def get_config_for_client_type(
+    self, client_type: Optional[str], user_context: Dict[str, Any]
+) -> ProviderConfigForClient:
+    config = await super().get_config_for_client_type(client_type, user_context)
+
+    if config.scope is None:
+        config.scope = ["users.read", "tweet.read"]
+
+    if config.force_pkce is None:
+        config.force_pkce = True
+
+    return config
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/thirdparty/providers/utils.html b/html/supertokens_python/recipe/thirdparty/providers/utils.html new file mode 100644 index 000000000..3655535d2 --- /dev/null +++ b/html/supertokens_python/recipe/thirdparty/providers/utils.html @@ -0,0 +1,277 @@ + + + + + + +supertokens_python.recipe.thirdparty.providers.utils API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.thirdparty.providers.utils

+
+
+
+ +Expand source code + +
from typing import Any, Dict, Optional, Tuple
+
+from httpx import AsyncClient
+
+from supertokens_python.logger import log_debug_message
+from supertokens_python.normalised_url_domain import NormalisedURLDomain
+from supertokens_python.normalised_url_path import NormalisedURLPath
+
+DEV_OAUTH_CLIENT_IDS = [
+    "1060725074195-kmeum4crr01uirfl2op9kd5acmi9jutn.apps.googleusercontent.com",
+    # google client id
+    "467101b197249757c71f",  # github client id
+]
+DEV_KEY_IDENTIFIER = "4398792-"
+DEV_OAUTH_AUTHORIZATION_URL = "https://supertokens.io/dev/oauth/redirect-to-provider"
+DEV_OAUTH_REDIRECT_URL = "https://supertokens.io/dev/oauth/redirect-to-app"
+
+
+def is_using_oauth_development_client_id(client_id: str):
+    return client_id.startswith(DEV_KEY_IDENTIFIER) or client_id in DEV_OAUTH_CLIENT_IDS
+
+
+def get_actual_client_id_from_development_client_id(client_id: str) -> str:
+    if client_id.startswith(DEV_KEY_IDENTIFIER):
+        return client_id.split(DEV_KEY_IDENTIFIER, 1)[1]
+    return client_id
+
+
+async def do_get_request(
+    url: str,
+    query_params: Optional[Dict[str, str]] = None,
+    headers: Optional[Dict[str, str]] = None,
+) -> Dict[str, Any]:
+    if query_params is None:
+        query_params = {}
+    if headers is None:
+        headers = {}
+
+    async with AsyncClient(timeout=30.0) as client:
+        res = await client.get(url, params=query_params, headers=headers)  # type:ignore
+
+        log_debug_message(
+            "Received response with status %s and body %s", res.status_code, res.text
+        )
+
+        return res.json()
+
+
+async def do_post_request(
+    url: str,
+    body_params: Optional[Dict[str, str]] = None,
+    headers: Optional[Dict[str, str]] = None,
+) -> Tuple[int, Dict[str, Any]]:
+    if body_params is None:
+        body_params = {}
+    if headers is None:
+        headers = {}
+
+    headers["content-type"] = "application/x-www-form-urlencoded"
+    headers["accept"] = "application/json"
+
+    async with AsyncClient(timeout=30.0) as client:
+        res = await client.post(url, data=body_params, headers=headers)  # type:ignore
+        log_debug_message(
+            "Received response with status %s and body %s", res.status_code, res.text
+        )
+        return res.status_code, res.json()
+
+
+def normalise_oidc_endpoint_to_include_well_known(url: str) -> str:
+    # We call this only for built-in providers that use OIDC.
+    # We no longer generically add well-known in the custom provider
+    if url.endswith("/.well-known/openid-configuration"):
+        return url
+
+    try:
+        normalised_domain = NormalisedURLDomain(url)
+        normalised_path = NormalisedURLPath(url)
+
+        normalised_path = normalised_path.append(
+            NormalisedURLPath("/.well-known/openid-configuration")
+        )
+    except Exception:
+        return url  # Return original URL if normalization fails
+
+    return (
+        normalised_domain.get_as_string_dangerous()
+        + normalised_path.get_as_string_dangerous()
+    )
+
+
+
+
+
+
+
+

Functions

+
+
+async def do_get_request(url: str, query_params: Optional[Dict[str, str]] = None, headers: Optional[Dict[str, str]] = None) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
async def do_get_request(
+    url: str,
+    query_params: Optional[Dict[str, str]] = None,
+    headers: Optional[Dict[str, str]] = None,
+) -> Dict[str, Any]:
+    if query_params is None:
+        query_params = {}
+    if headers is None:
+        headers = {}
+
+    async with AsyncClient(timeout=30.0) as client:
+        res = await client.get(url, params=query_params, headers=headers)  # type:ignore
+
+        log_debug_message(
+            "Received response with status %s and body %s", res.status_code, res.text
+        )
+
+        return res.json()
+
+
+
+async def do_post_request(url: str, body_params: Optional[Dict[str, str]] = None, headers: Optional[Dict[str, str]] = None) ‑> Tuple[int, Dict[str, Any]] +
+
+
+
+ +Expand source code + +
async def do_post_request(
+    url: str,
+    body_params: Optional[Dict[str, str]] = None,
+    headers: Optional[Dict[str, str]] = None,
+) -> Tuple[int, Dict[str, Any]]:
+    if body_params is None:
+        body_params = {}
+    if headers is None:
+        headers = {}
+
+    headers["content-type"] = "application/x-www-form-urlencoded"
+    headers["accept"] = "application/json"
+
+    async with AsyncClient(timeout=30.0) as client:
+        res = await client.post(url, data=body_params, headers=headers)  # type:ignore
+        log_debug_message(
+            "Received response with status %s and body %s", res.status_code, res.text
+        )
+        return res.status_code, res.json()
+
+
+
+def get_actual_client_id_from_development_client_id(client_id: str) ‑> str +
+
+
+
+ +Expand source code + +
def get_actual_client_id_from_development_client_id(client_id: str) -> str:
+    if client_id.startswith(DEV_KEY_IDENTIFIER):
+        return client_id.split(DEV_KEY_IDENTIFIER, 1)[1]
+    return client_id
+
+
+
+def is_using_oauth_development_client_id(client_id: str) +
+
+
+
+ +Expand source code + +
def is_using_oauth_development_client_id(client_id: str):
+    return client_id.startswith(DEV_KEY_IDENTIFIER) or client_id in DEV_OAUTH_CLIENT_IDS
+
+
+
+def normalise_oidc_endpoint_to_include_well_known(url: str) ‑> str +
+
+
+
+ +Expand source code + +
def normalise_oidc_endpoint_to_include_well_known(url: str) -> str:
+    # We call this only for built-in providers that use OIDC.
+    # We no longer generically add well-known in the custom provider
+    if url.endswith("/.well-known/openid-configuration"):
+        return url
+
+    try:
+        normalised_domain = NormalisedURLDomain(url)
+        normalised_path = NormalisedURLPath(url)
+
+        normalised_path = normalised_path.append(
+            NormalisedURLPath("/.well-known/openid-configuration")
+        )
+    except Exception:
+        return url  # Return original URL if normalization fails
+
+    return (
+        normalised_domain.get_as_string_dangerous()
+        + normalised_path.get_as_string_dangerous()
+    )
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/thirdparty/recipe.html b/html/supertokens_python/recipe/thirdparty/recipe.html new file mode 100644 index 000000000..04bb9cd8f --- /dev/null +++ b/html/supertokens_python/recipe/thirdparty/recipe.html @@ -0,0 +1,711 @@ + + + + + + +supertokens_python.recipe.thirdparty.recipe API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.thirdparty.recipe

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from os import environ
+from typing import TYPE_CHECKING, Any, Dict, List, Union
+
+from supertokens_python.normalised_url_path import NormalisedURLPath
+from supertokens_python.querier import Querier
+from supertokens_python.recipe_module import APIHandled, RecipeModule
+
+from .api.implementation import APIImplementation
+from .interfaces import APIInterface, APIOptions, RecipeInterface
+from .recipe_implementation import RecipeImplementation
+from ..emailverification.interfaces import GetEmailForUserIdOkResult, UnknownUserIdError
+from ...post_init_callbacks import PostSTInitCallbacks
+
+if TYPE_CHECKING:
+    from supertokens_python.framework.request import BaseRequest
+    from supertokens_python.framework.response import BaseResponse
+    from supertokens_python.supertokens import AppInfo
+    from .utils import SignInAndUpFeature, InputOverrideConfig
+
+from supertokens_python.exceptions import SuperTokensError, raise_general_exception
+from supertokens_python.recipe.emailverification.recipe import EmailVerificationRecipe
+from supertokens_python.recipe.multitenancy.recipe import MultitenancyRecipe
+
+from .api import (
+    handle_apple_redirect_api,
+    handle_authorisation_url_api,
+    handle_sign_in_up_api,
+)
+from .constants import APPLE_REDIRECT_HANDLER, AUTHORISATIONURL, SIGNINUP
+from .exceptions import SuperTokensThirdPartyError
+from .types import ThirdPartyIngredients
+from .utils import validate_and_normalise_user_input
+
+
+class ThirdPartyRecipe(RecipeModule):
+    recipe_id = "thirdparty"
+    __instance = None
+
+    def __init__(
+        self,
+        recipe_id: str,
+        app_info: AppInfo,
+        sign_in_and_up_feature: SignInAndUpFeature,
+        _ingredients: ThirdPartyIngredients,
+        override: Union[InputOverrideConfig, None] = None,
+    ):
+        super().__init__(recipe_id, app_info)
+        self.config = validate_and_normalise_user_input(
+            sign_in_and_up_feature,
+            override,
+        )
+        self.providers = self.config.sign_in_and_up_feature.providers
+        recipe_implementation = RecipeImplementation(
+            Querier.get_instance(recipe_id), self.providers
+        )
+        self.recipe_implementation: RecipeInterface = (
+            recipe_implementation
+            if self.config.override.functions is None
+            else self.config.override.functions(recipe_implementation)
+        )
+        api_implementation = APIImplementation()
+        self.api_implementation: APIInterface = (
+            api_implementation
+            if self.config.override.apis is None
+            else self.config.override.apis(api_implementation)
+        )
+
+        def callback():
+            ev_recipe = EmailVerificationRecipe.get_instance_optional()
+            if ev_recipe:
+                ev_recipe.add_get_email_for_user_id_func(self.get_email_for_user_id)
+
+            mt_recipe = MultitenancyRecipe.get_instance_optional()
+            if mt_recipe:
+                mt_recipe.static_third_party_providers = self.providers
+
+        PostSTInitCallbacks.add_post_init_callback(callback)
+
+    def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
+        return isinstance(err, SuperTokensError) and (
+            isinstance(err, SuperTokensThirdPartyError)
+        )
+
+    def get_apis_handled(self) -> List[APIHandled]:
+        return [
+            APIHandled(
+                NormalisedURLPath(SIGNINUP),
+                "post",
+                SIGNINUP,
+                self.api_implementation.disable_sign_in_up_post,
+            ),
+            APIHandled(
+                NormalisedURLPath(AUTHORISATIONURL),
+                "get",
+                AUTHORISATIONURL,
+                self.api_implementation.disable_authorisation_url_get,
+            ),
+            APIHandled(
+                NormalisedURLPath(APPLE_REDIRECT_HANDLER),
+                "post",
+                APPLE_REDIRECT_HANDLER,
+                self.api_implementation.disable_apple_redirect_handler_post,
+            ),
+        ]
+
+    async def handle_api_request(
+        self,
+        request_id: str,
+        tenant_id: str,
+        request: BaseRequest,
+        path: NormalisedURLPath,
+        method: str,
+        response: BaseResponse,
+        user_context: Dict[str, Any],
+    ):
+        api_options = APIOptions(
+            request,
+            response,
+            self.recipe_id,
+            self.config,
+            self.recipe_implementation,
+            self.providers,
+            self.app_info,
+        )
+
+        if request_id == SIGNINUP:
+            return await handle_sign_in_up_api(
+                self.api_implementation, tenant_id, api_options, user_context
+            )
+        if request_id == AUTHORISATIONURL:
+            return await handle_authorisation_url_api(
+                self.api_implementation, tenant_id, api_options, user_context
+            )
+        if request_id == APPLE_REDIRECT_HANDLER:
+            return await handle_apple_redirect_api(
+                self.api_implementation, api_options, user_context
+            )
+
+        return None
+
+    async def handle_error(
+        self,
+        request: BaseRequest,
+        err: SuperTokensError,
+        response: BaseResponse,
+        user_context: Dict[str, Any],
+    ) -> BaseResponse:  # type: ignore
+        if isinstance(err, SuperTokensThirdPartyError):
+            raise err
+
+    def get_all_cors_headers(self) -> List[str]:
+        return []
+
+    @staticmethod
+    def init(
+        sign_in_and_up_feature: SignInAndUpFeature,
+        override: Union[InputOverrideConfig, None] = None,
+    ):
+        def func(app_info: AppInfo):
+            if ThirdPartyRecipe.__instance is None:
+                ingredients = ThirdPartyIngredients()
+                ThirdPartyRecipe.__instance = ThirdPartyRecipe(
+                    ThirdPartyRecipe.recipe_id,
+                    app_info,
+                    sign_in_and_up_feature,
+                    ingredients,
+                    override,
+                )
+                return ThirdPartyRecipe.__instance
+            raise_general_exception(
+                "ThirdParty recipe has already been initialised. Please check your code for bugs."
+            )
+
+        return func
+
+    @staticmethod
+    def get_instance() -> ThirdPartyRecipe:
+        if ThirdPartyRecipe.__instance is not None:
+            return ThirdPartyRecipe.__instance
+        raise_general_exception(
+            "Initialisation not done. Did you forget to call the SuperTokens.init function?"
+        )
+
+    @staticmethod
+    def reset():
+        if ("SUPERTOKENS_ENV" not in environ) or (
+            environ["SUPERTOKENS_ENV"] != "testing"
+        ):
+            raise_general_exception("calling testing function in non testing env")
+        ThirdPartyRecipe.__instance = None
+
+    # instance functions below...............
+
+    async def get_email_for_user_id(self, user_id: str, user_context: Dict[str, Any]):
+        user_info = await self.recipe_implementation.get_user_by_id(
+            user_id, user_context
+        )
+        if user_info is not None:
+            return GetEmailForUserIdOkResult(user_info.email)
+
+        return UnknownUserIdError()
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class ThirdPartyRecipe +(recipe_id: str, app_info: AppInfo, sign_in_and_up_feature: SignInAndUpFeature, _ingredients: ThirdPartyIngredients, override: Union[InputOverrideConfig, None] = None) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class ThirdPartyRecipe(RecipeModule):
+    recipe_id = "thirdparty"
+    __instance = None
+
+    def __init__(
+        self,
+        recipe_id: str,
+        app_info: AppInfo,
+        sign_in_and_up_feature: SignInAndUpFeature,
+        _ingredients: ThirdPartyIngredients,
+        override: Union[InputOverrideConfig, None] = None,
+    ):
+        super().__init__(recipe_id, app_info)
+        self.config = validate_and_normalise_user_input(
+            sign_in_and_up_feature,
+            override,
+        )
+        self.providers = self.config.sign_in_and_up_feature.providers
+        recipe_implementation = RecipeImplementation(
+            Querier.get_instance(recipe_id), self.providers
+        )
+        self.recipe_implementation: RecipeInterface = (
+            recipe_implementation
+            if self.config.override.functions is None
+            else self.config.override.functions(recipe_implementation)
+        )
+        api_implementation = APIImplementation()
+        self.api_implementation: APIInterface = (
+            api_implementation
+            if self.config.override.apis is None
+            else self.config.override.apis(api_implementation)
+        )
+
+        def callback():
+            ev_recipe = EmailVerificationRecipe.get_instance_optional()
+            if ev_recipe:
+                ev_recipe.add_get_email_for_user_id_func(self.get_email_for_user_id)
+
+            mt_recipe = MultitenancyRecipe.get_instance_optional()
+            if mt_recipe:
+                mt_recipe.static_third_party_providers = self.providers
+
+        PostSTInitCallbacks.add_post_init_callback(callback)
+
+    def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
+        return isinstance(err, SuperTokensError) and (
+            isinstance(err, SuperTokensThirdPartyError)
+        )
+
+    def get_apis_handled(self) -> List[APIHandled]:
+        return [
+            APIHandled(
+                NormalisedURLPath(SIGNINUP),
+                "post",
+                SIGNINUP,
+                self.api_implementation.disable_sign_in_up_post,
+            ),
+            APIHandled(
+                NormalisedURLPath(AUTHORISATIONURL),
+                "get",
+                AUTHORISATIONURL,
+                self.api_implementation.disable_authorisation_url_get,
+            ),
+            APIHandled(
+                NormalisedURLPath(APPLE_REDIRECT_HANDLER),
+                "post",
+                APPLE_REDIRECT_HANDLER,
+                self.api_implementation.disable_apple_redirect_handler_post,
+            ),
+        ]
+
+    async def handle_api_request(
+        self,
+        request_id: str,
+        tenant_id: str,
+        request: BaseRequest,
+        path: NormalisedURLPath,
+        method: str,
+        response: BaseResponse,
+        user_context: Dict[str, Any],
+    ):
+        api_options = APIOptions(
+            request,
+            response,
+            self.recipe_id,
+            self.config,
+            self.recipe_implementation,
+            self.providers,
+            self.app_info,
+        )
+
+        if request_id == SIGNINUP:
+            return await handle_sign_in_up_api(
+                self.api_implementation, tenant_id, api_options, user_context
+            )
+        if request_id == AUTHORISATIONURL:
+            return await handle_authorisation_url_api(
+                self.api_implementation, tenant_id, api_options, user_context
+            )
+        if request_id == APPLE_REDIRECT_HANDLER:
+            return await handle_apple_redirect_api(
+                self.api_implementation, api_options, user_context
+            )
+
+        return None
+
+    async def handle_error(
+        self,
+        request: BaseRequest,
+        err: SuperTokensError,
+        response: BaseResponse,
+        user_context: Dict[str, Any],
+    ) -> BaseResponse:  # type: ignore
+        if isinstance(err, SuperTokensThirdPartyError):
+            raise err
+
+    def get_all_cors_headers(self) -> List[str]:
+        return []
+
+    @staticmethod
+    def init(
+        sign_in_and_up_feature: SignInAndUpFeature,
+        override: Union[InputOverrideConfig, None] = None,
+    ):
+        def func(app_info: AppInfo):
+            if ThirdPartyRecipe.__instance is None:
+                ingredients = ThirdPartyIngredients()
+                ThirdPartyRecipe.__instance = ThirdPartyRecipe(
+                    ThirdPartyRecipe.recipe_id,
+                    app_info,
+                    sign_in_and_up_feature,
+                    ingredients,
+                    override,
+                )
+                return ThirdPartyRecipe.__instance
+            raise_general_exception(
+                "ThirdParty recipe has already been initialised. Please check your code for bugs."
+            )
+
+        return func
+
+    @staticmethod
+    def get_instance() -> ThirdPartyRecipe:
+        if ThirdPartyRecipe.__instance is not None:
+            return ThirdPartyRecipe.__instance
+        raise_general_exception(
+            "Initialisation not done. Did you forget to call the SuperTokens.init function?"
+        )
+
+    @staticmethod
+    def reset():
+        if ("SUPERTOKENS_ENV" not in environ) or (
+            environ["SUPERTOKENS_ENV"] != "testing"
+        ):
+            raise_general_exception("calling testing function in non testing env")
+        ThirdPartyRecipe.__instance = None
+
+    # instance functions below...............
+
+    async def get_email_for_user_id(self, user_id: str, user_context: Dict[str, Any]):
+        user_info = await self.recipe_implementation.get_user_by_id(
+            user_id, user_context
+        )
+        if user_info is not None:
+            return GetEmailForUserIdOkResult(user_info.email)
+
+        return UnknownUserIdError()
+
+

Ancestors

+ +

Class variables

+
+
var get_tenant_id : Optional[Callable[[str, Dict[str, Any]], Awaitable[str]]]
+
+
+
+
var recipe_id
+
+
+
+
+

Static methods

+
+
+def get_instance() ‑> ThirdPartyRecipe +
+
+
+
+ +Expand source code + +
@staticmethod
+def get_instance() -> ThirdPartyRecipe:
+    if ThirdPartyRecipe.__instance is not None:
+        return ThirdPartyRecipe.__instance
+    raise_general_exception(
+        "Initialisation not done. Did you forget to call the SuperTokens.init function?"
+    )
+
+
+
+def init(sign_in_and_up_feature: SignInAndUpFeature, override: Union[InputOverrideConfig, None] = None) +
+
+
+
+ +Expand source code + +
@staticmethod
+def init(
+    sign_in_and_up_feature: SignInAndUpFeature,
+    override: Union[InputOverrideConfig, None] = None,
+):
+    def func(app_info: AppInfo):
+        if ThirdPartyRecipe.__instance is None:
+            ingredients = ThirdPartyIngredients()
+            ThirdPartyRecipe.__instance = ThirdPartyRecipe(
+                ThirdPartyRecipe.recipe_id,
+                app_info,
+                sign_in_and_up_feature,
+                ingredients,
+                override,
+            )
+            return ThirdPartyRecipe.__instance
+        raise_general_exception(
+            "ThirdParty recipe has already been initialised. Please check your code for bugs."
+        )
+
+    return func
+
+
+
+def reset() +
+
+
+
+ +Expand source code + +
@staticmethod
+def reset():
+    if ("SUPERTOKENS_ENV" not in environ) or (
+        environ["SUPERTOKENS_ENV"] != "testing"
+    ):
+        raise_general_exception("calling testing function in non testing env")
+    ThirdPartyRecipe.__instance = None
+
+
+
+

Methods

+
+
+def get_all_cors_headers(self) ‑> List[str] +
+
+
+
+ +Expand source code + +
def get_all_cors_headers(self) -> List[str]:
+    return []
+
+
+
+def get_apis_handled(self) ‑> List[APIHandled] +
+
+
+
+ +Expand source code + +
def get_apis_handled(self) -> List[APIHandled]:
+    return [
+        APIHandled(
+            NormalisedURLPath(SIGNINUP),
+            "post",
+            SIGNINUP,
+            self.api_implementation.disable_sign_in_up_post,
+        ),
+        APIHandled(
+            NormalisedURLPath(AUTHORISATIONURL),
+            "get",
+            AUTHORISATIONURL,
+            self.api_implementation.disable_authorisation_url_get,
+        ),
+        APIHandled(
+            NormalisedURLPath(APPLE_REDIRECT_HANDLER),
+            "post",
+            APPLE_REDIRECT_HANDLER,
+            self.api_implementation.disable_apple_redirect_handler_post,
+        ),
+    ]
+
+
+
+async def get_email_for_user_id(self, user_id: str, user_context: Dict[str, Any]) +
+
+
+
+ +Expand source code + +
async def get_email_for_user_id(self, user_id: str, user_context: Dict[str, Any]):
+    user_info = await self.recipe_implementation.get_user_by_id(
+        user_id, user_context
+    )
+    if user_info is not None:
+        return GetEmailForUserIdOkResult(user_info.email)
+
+    return UnknownUserIdError()
+
+
+
+async def handle_api_request(self, request_id: str, tenant_id: str, request: BaseRequest, path: NormalisedURLPath, method: str, response: BaseResponse, user_context: Dict[str, Any]) +
+
+
+
+ +Expand source code + +
async def handle_api_request(
+    self,
+    request_id: str,
+    tenant_id: str,
+    request: BaseRequest,
+    path: NormalisedURLPath,
+    method: str,
+    response: BaseResponse,
+    user_context: Dict[str, Any],
+):
+    api_options = APIOptions(
+        request,
+        response,
+        self.recipe_id,
+        self.config,
+        self.recipe_implementation,
+        self.providers,
+        self.app_info,
+    )
+
+    if request_id == SIGNINUP:
+        return await handle_sign_in_up_api(
+            self.api_implementation, tenant_id, api_options, user_context
+        )
+    if request_id == AUTHORISATIONURL:
+        return await handle_authorisation_url_api(
+            self.api_implementation, tenant_id, api_options, user_context
+        )
+    if request_id == APPLE_REDIRECT_HANDLER:
+        return await handle_apple_redirect_api(
+            self.api_implementation, api_options, user_context
+        )
+
+    return None
+
+
+
+async def handle_error(self, request: BaseRequest, err: SuperTokensError, response: BaseResponse, user_context: Dict[str, Any]) ‑> BaseResponse +
+
+
+
+ +Expand source code + +
async def handle_error(
+    self,
+    request: BaseRequest,
+    err: SuperTokensError,
+    response: BaseResponse,
+    user_context: Dict[str, Any],
+) -> BaseResponse:  # type: ignore
+    if isinstance(err, SuperTokensThirdPartyError):
+        raise err
+
+
+
+def is_error_from_this_recipe_based_on_instance(self, err: Exception) ‑> bool +
+
+
+
+ +Expand source code + +
def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
+    return isinstance(err, SuperTokensError) and (
+        isinstance(err, SuperTokensThirdPartyError)
+    )
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/thirdparty/recipe_implementation.html b/html/supertokens_python/recipe/thirdparty/recipe_implementation.html new file mode 100644 index 000000000..bc2bf5f56 --- /dev/null +++ b/html/supertokens_python/recipe/thirdparty/recipe_implementation.html @@ -0,0 +1,723 @@ + + + + + + +supertokens_python.recipe.thirdparty.recipe_implementation API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.thirdparty.recipe_implementation

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union
+
+from supertokens_python.normalised_url_path import NormalisedURLPath
+from supertokens_python.recipe.multitenancy.constants import DEFAULT_TENANT_ID
+from supertokens_python.recipe.multitenancy.recipe import MultitenancyRecipe
+from supertokens_python.recipe.thirdparty.provider import ProviderInput
+from supertokens_python.recipe.thirdparty.providers.config_utils import (
+    find_and_create_provider_instance,
+    merge_providers_from_core_and_static,
+)
+
+if TYPE_CHECKING:
+    from supertokens_python.querier import Querier
+
+from .interfaces import (
+    ManuallyCreateOrUpdateUserOkResult,
+    RecipeInterface,
+    SignInUpOkResult,
+)
+from .types import RawUserInfoFromProvider, ThirdPartyInfo, User
+
+
+class RecipeImplementation(RecipeInterface):
+    def __init__(self, querier: Querier, providers: List[ProviderInput]):
+        super().__init__()
+        self.querier = querier
+        self.providers = providers
+
+    async def get_user_by_id(
+        self, user_id: str, user_context: Dict[str, Any]
+    ) -> Union[User, None]:
+        params = {"userId": user_id}
+        response = await self.querier.send_get_request(
+            NormalisedURLPath("/recipe/user"),
+            params,
+            user_context=user_context,
+        )
+        if "status" in response and response["status"] == "OK":
+            return User(
+                response["user"]["id"],
+                response["user"]["email"],
+                response["user"]["timeJoined"],
+                response["user"]["tenantIds"],
+                ThirdPartyInfo(
+                    response["user"]["thirdParty"]["userId"],
+                    response["user"]["thirdParty"]["id"],
+                ),
+            )
+        return None
+
+    async def get_users_by_email(
+        self, email: str, tenant_id: str, user_context: Dict[str, Any]
+    ) -> List[User]:
+        response = await self.querier.send_get_request(
+            NormalisedURLPath(f"{tenant_id}/recipe/users/by-email"),
+            {"email": email},
+            user_context=user_context,
+        )
+        users: List[User] = []
+        users_list: List[Dict[str, Any]] = (
+            response["users"] if "users" in response else []
+        )
+        for user in users_list:
+            users.append(
+                User(
+                    user["id"],
+                    user["email"],
+                    user["timeJoined"],
+                    user["tenantIds"],
+                    ThirdPartyInfo(
+                        user["thirdParty"]["userId"], user["thirdParty"]["id"]
+                    ),
+                )
+            )
+        return users
+
+    async def get_user_by_thirdparty_info(
+        self,
+        third_party_id: str,
+        third_party_user_id: str,
+        tenant_id: str,
+        user_context: Dict[str, Any],
+    ) -> Union[User, None]:
+        params = {
+            "thirdPartyId": third_party_id,
+            "thirdPartyUserId": third_party_user_id,
+        }
+        response = await self.querier.send_get_request(
+            NormalisedURLPath(f"{tenant_id}/recipe/user"),
+            params,
+            user_context=user_context,
+        )
+        if "status" in response and response["status"] == "OK":
+            return User(
+                response["user"]["id"],
+                response["user"]["email"],
+                response["user"]["timeJoined"],
+                response["user"]["tenantIds"],
+                ThirdPartyInfo(
+                    response["user"]["thirdParty"]["userId"],
+                    response["user"]["thirdParty"]["id"],
+                ),
+            )
+        return None
+
+    async def sign_in_up(
+        self,
+        third_party_id: str,
+        third_party_user_id: str,
+        email: str,
+        oauth_tokens: Dict[str, Any],
+        raw_user_info_from_provider: RawUserInfoFromProvider,
+        tenant_id: str,
+        user_context: Dict[str, Any],
+    ) -> SignInUpOkResult:
+        data = {
+            "thirdPartyId": third_party_id,
+            "thirdPartyUserId": third_party_user_id,
+            "email": {"id": email},
+        }
+        response = await self.querier.send_post_request(
+            NormalisedURLPath(f"{tenant_id}/recipe/signinup"),
+            data,
+            user_context=user_context,
+        )
+        return SignInUpOkResult(
+            User(
+                response["user"]["id"],
+                response["user"]["email"],
+                response["user"]["timeJoined"],
+                response["user"]["tenantIds"],
+                ThirdPartyInfo(
+                    response["user"]["thirdParty"]["userId"],
+                    response["user"]["thirdParty"]["id"],
+                ),
+            ),
+            response["createdNewUser"],
+            oauth_tokens,
+            raw_user_info_from_provider,
+        )
+
+    async def manually_create_or_update_user(
+        self,
+        third_party_id: str,
+        third_party_user_id: str,
+        email: str,
+        tenant_id: str,
+        user_context: Dict[str, Any],
+    ) -> ManuallyCreateOrUpdateUserOkResult:
+        data = {
+            "thirdPartyId": third_party_id,
+            "thirdPartyUserId": third_party_user_id,
+            "email": {"id": email},
+        }
+        response = await self.querier.send_post_request(
+            NormalisedURLPath(f"{tenant_id}/recipe/signinup"),
+            data,
+            user_context=user_context,
+        )
+        return ManuallyCreateOrUpdateUserOkResult(
+            User(
+                response["user"]["id"],
+                response["user"]["email"],
+                response["user"]["timeJoined"],
+                response["user"]["tenantIds"],
+                ThirdPartyInfo(
+                    response["user"]["thirdParty"]["userId"],
+                    response["user"]["thirdParty"]["id"],
+                ),
+            ),
+            response["createdNewUser"],
+        )
+
+    async def get_provider(
+        self,
+        third_party_id: str,
+        client_type: Optional[str],
+        tenant_id: str,
+        user_context: Dict[str, Any],
+    ):
+        mt_recipe = MultitenancyRecipe.get_instance()
+        tenant_config = await mt_recipe.recipe_implementation.get_tenant(
+            tenant_id=tenant_id,
+            user_context=user_context,
+        )
+
+        if tenant_config is None:
+            raise Exception("Tenant not found")
+
+        merged_providers = merge_providers_from_core_and_static(
+            provider_configs_from_core=tenant_config.third_party.providers,
+            provider_inputs_from_static=self.providers,
+            include_all_providers=tenant_id == DEFAULT_TENANT_ID,
+        )
+
+        provider = await find_and_create_provider_instance(
+            merged_providers, third_party_id, client_type, user_context
+        )
+
+        return provider
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class RecipeImplementation +(querier: Querier, providers: List[ProviderInput]) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class RecipeImplementation(RecipeInterface):
+    def __init__(self, querier: Querier, providers: List[ProviderInput]):
+        super().__init__()
+        self.querier = querier
+        self.providers = providers
+
+    async def get_user_by_id(
+        self, user_id: str, user_context: Dict[str, Any]
+    ) -> Union[User, None]:
+        params = {"userId": user_id}
+        response = await self.querier.send_get_request(
+            NormalisedURLPath("/recipe/user"),
+            params,
+            user_context=user_context,
+        )
+        if "status" in response and response["status"] == "OK":
+            return User(
+                response["user"]["id"],
+                response["user"]["email"],
+                response["user"]["timeJoined"],
+                response["user"]["tenantIds"],
+                ThirdPartyInfo(
+                    response["user"]["thirdParty"]["userId"],
+                    response["user"]["thirdParty"]["id"],
+                ),
+            )
+        return None
+
+    async def get_users_by_email(
+        self, email: str, tenant_id: str, user_context: Dict[str, Any]
+    ) -> List[User]:
+        response = await self.querier.send_get_request(
+            NormalisedURLPath(f"{tenant_id}/recipe/users/by-email"),
+            {"email": email},
+            user_context=user_context,
+        )
+        users: List[User] = []
+        users_list: List[Dict[str, Any]] = (
+            response["users"] if "users" in response else []
+        )
+        for user in users_list:
+            users.append(
+                User(
+                    user["id"],
+                    user["email"],
+                    user["timeJoined"],
+                    user["tenantIds"],
+                    ThirdPartyInfo(
+                        user["thirdParty"]["userId"], user["thirdParty"]["id"]
+                    ),
+                )
+            )
+        return users
+
+    async def get_user_by_thirdparty_info(
+        self,
+        third_party_id: str,
+        third_party_user_id: str,
+        tenant_id: str,
+        user_context: Dict[str, Any],
+    ) -> Union[User, None]:
+        params = {
+            "thirdPartyId": third_party_id,
+            "thirdPartyUserId": third_party_user_id,
+        }
+        response = await self.querier.send_get_request(
+            NormalisedURLPath(f"{tenant_id}/recipe/user"),
+            params,
+            user_context=user_context,
+        )
+        if "status" in response and response["status"] == "OK":
+            return User(
+                response["user"]["id"],
+                response["user"]["email"],
+                response["user"]["timeJoined"],
+                response["user"]["tenantIds"],
+                ThirdPartyInfo(
+                    response["user"]["thirdParty"]["userId"],
+                    response["user"]["thirdParty"]["id"],
+                ),
+            )
+        return None
+
+    async def sign_in_up(
+        self,
+        third_party_id: str,
+        third_party_user_id: str,
+        email: str,
+        oauth_tokens: Dict[str, Any],
+        raw_user_info_from_provider: RawUserInfoFromProvider,
+        tenant_id: str,
+        user_context: Dict[str, Any],
+    ) -> SignInUpOkResult:
+        data = {
+            "thirdPartyId": third_party_id,
+            "thirdPartyUserId": third_party_user_id,
+            "email": {"id": email},
+        }
+        response = await self.querier.send_post_request(
+            NormalisedURLPath(f"{tenant_id}/recipe/signinup"),
+            data,
+            user_context=user_context,
+        )
+        return SignInUpOkResult(
+            User(
+                response["user"]["id"],
+                response["user"]["email"],
+                response["user"]["timeJoined"],
+                response["user"]["tenantIds"],
+                ThirdPartyInfo(
+                    response["user"]["thirdParty"]["userId"],
+                    response["user"]["thirdParty"]["id"],
+                ),
+            ),
+            response["createdNewUser"],
+            oauth_tokens,
+            raw_user_info_from_provider,
+        )
+
+    async def manually_create_or_update_user(
+        self,
+        third_party_id: str,
+        third_party_user_id: str,
+        email: str,
+        tenant_id: str,
+        user_context: Dict[str, Any],
+    ) -> ManuallyCreateOrUpdateUserOkResult:
+        data = {
+            "thirdPartyId": third_party_id,
+            "thirdPartyUserId": third_party_user_id,
+            "email": {"id": email},
+        }
+        response = await self.querier.send_post_request(
+            NormalisedURLPath(f"{tenant_id}/recipe/signinup"),
+            data,
+            user_context=user_context,
+        )
+        return ManuallyCreateOrUpdateUserOkResult(
+            User(
+                response["user"]["id"],
+                response["user"]["email"],
+                response["user"]["timeJoined"],
+                response["user"]["tenantIds"],
+                ThirdPartyInfo(
+                    response["user"]["thirdParty"]["userId"],
+                    response["user"]["thirdParty"]["id"],
+                ),
+            ),
+            response["createdNewUser"],
+        )
+
+    async def get_provider(
+        self,
+        third_party_id: str,
+        client_type: Optional[str],
+        tenant_id: str,
+        user_context: Dict[str, Any],
+    ):
+        mt_recipe = MultitenancyRecipe.get_instance()
+        tenant_config = await mt_recipe.recipe_implementation.get_tenant(
+            tenant_id=tenant_id,
+            user_context=user_context,
+        )
+
+        if tenant_config is None:
+            raise Exception("Tenant not found")
+
+        merged_providers = merge_providers_from_core_and_static(
+            provider_configs_from_core=tenant_config.third_party.providers,
+            provider_inputs_from_static=self.providers,
+            include_all_providers=tenant_id == DEFAULT_TENANT_ID,
+        )
+
+        provider = await find_and_create_provider_instance(
+            merged_providers, third_party_id, client_type, user_context
+        )
+
+        return provider
+
+

Ancestors

+ +

Methods

+
+
+async def get_provider(self, third_party_id: str, client_type: Optional[str], tenant_id: str, user_context: Dict[str, Any]) +
+
+
+
+ +Expand source code + +
async def get_provider(
+    self,
+    third_party_id: str,
+    client_type: Optional[str],
+    tenant_id: str,
+    user_context: Dict[str, Any],
+):
+    mt_recipe = MultitenancyRecipe.get_instance()
+    tenant_config = await mt_recipe.recipe_implementation.get_tenant(
+        tenant_id=tenant_id,
+        user_context=user_context,
+    )
+
+    if tenant_config is None:
+        raise Exception("Tenant not found")
+
+    merged_providers = merge_providers_from_core_and_static(
+        provider_configs_from_core=tenant_config.third_party.providers,
+        provider_inputs_from_static=self.providers,
+        include_all_providers=tenant_id == DEFAULT_TENANT_ID,
+    )
+
+    provider = await find_and_create_provider_instance(
+        merged_providers, third_party_id, client_type, user_context
+    )
+
+    return provider
+
+
+
+async def get_user_by_id(self, user_id: str, user_context: Dict[str, Any]) ‑> Optional[User] +
+
+
+
+ +Expand source code + +
async def get_user_by_id(
+    self, user_id: str, user_context: Dict[str, Any]
+) -> Union[User, None]:
+    params = {"userId": user_id}
+    response = await self.querier.send_get_request(
+        NormalisedURLPath("/recipe/user"),
+        params,
+        user_context=user_context,
+    )
+    if "status" in response and response["status"] == "OK":
+        return User(
+            response["user"]["id"],
+            response["user"]["email"],
+            response["user"]["timeJoined"],
+            response["user"]["tenantIds"],
+            ThirdPartyInfo(
+                response["user"]["thirdParty"]["userId"],
+                response["user"]["thirdParty"]["id"],
+            ),
+        )
+    return None
+
+
+
+async def get_user_by_thirdparty_info(self, third_party_id: str, third_party_user_id: str, tenant_id: str, user_context: Dict[str, Any]) ‑> Optional[User] +
+
+
+
+ +Expand source code + +
async def get_user_by_thirdparty_info(
+    self,
+    third_party_id: str,
+    third_party_user_id: str,
+    tenant_id: str,
+    user_context: Dict[str, Any],
+) -> Union[User, None]:
+    params = {
+        "thirdPartyId": third_party_id,
+        "thirdPartyUserId": third_party_user_id,
+    }
+    response = await self.querier.send_get_request(
+        NormalisedURLPath(f"{tenant_id}/recipe/user"),
+        params,
+        user_context=user_context,
+    )
+    if "status" in response and response["status"] == "OK":
+        return User(
+            response["user"]["id"],
+            response["user"]["email"],
+            response["user"]["timeJoined"],
+            response["user"]["tenantIds"],
+            ThirdPartyInfo(
+                response["user"]["thirdParty"]["userId"],
+                response["user"]["thirdParty"]["id"],
+            ),
+        )
+    return None
+
+
+
+async def get_users_by_email(self, email: str, tenant_id: str, user_context: Dict[str, Any]) ‑> List[User] +
+
+
+
+ +Expand source code + +
async def get_users_by_email(
+    self, email: str, tenant_id: str, user_context: Dict[str, Any]
+) -> List[User]:
+    response = await self.querier.send_get_request(
+        NormalisedURLPath(f"{tenant_id}/recipe/users/by-email"),
+        {"email": email},
+        user_context=user_context,
+    )
+    users: List[User] = []
+    users_list: List[Dict[str, Any]] = (
+        response["users"] if "users" in response else []
+    )
+    for user in users_list:
+        users.append(
+            User(
+                user["id"],
+                user["email"],
+                user["timeJoined"],
+                user["tenantIds"],
+                ThirdPartyInfo(
+                    user["thirdParty"]["userId"], user["thirdParty"]["id"]
+                ),
+            )
+        )
+    return users
+
+
+
+async def manually_create_or_update_user(self, third_party_id: str, third_party_user_id: str, email: str, tenant_id: str, user_context: Dict[str, Any]) ‑> ManuallyCreateOrUpdateUserOkResult +
+
+
+
+ +Expand source code + +
async def manually_create_or_update_user(
+    self,
+    third_party_id: str,
+    third_party_user_id: str,
+    email: str,
+    tenant_id: str,
+    user_context: Dict[str, Any],
+) -> ManuallyCreateOrUpdateUserOkResult:
+    data = {
+        "thirdPartyId": third_party_id,
+        "thirdPartyUserId": third_party_user_id,
+        "email": {"id": email},
+    }
+    response = await self.querier.send_post_request(
+        NormalisedURLPath(f"{tenant_id}/recipe/signinup"),
+        data,
+        user_context=user_context,
+    )
+    return ManuallyCreateOrUpdateUserOkResult(
+        User(
+            response["user"]["id"],
+            response["user"]["email"],
+            response["user"]["timeJoined"],
+            response["user"]["tenantIds"],
+            ThirdPartyInfo(
+                response["user"]["thirdParty"]["userId"],
+                response["user"]["thirdParty"]["id"],
+            ),
+        ),
+        response["createdNewUser"],
+    )
+
+
+
+async def sign_in_up(self, third_party_id: str, third_party_user_id: str, email: str, oauth_tokens: Dict[str, Any], raw_user_info_from_provider: RawUserInfoFromProvider, tenant_id: str, user_context: Dict[str, Any]) ‑> SignInUpOkResult +
+
+
+
+ +Expand source code + +
async def sign_in_up(
+    self,
+    third_party_id: str,
+    third_party_user_id: str,
+    email: str,
+    oauth_tokens: Dict[str, Any],
+    raw_user_info_from_provider: RawUserInfoFromProvider,
+    tenant_id: str,
+    user_context: Dict[str, Any],
+) -> SignInUpOkResult:
+    data = {
+        "thirdPartyId": third_party_id,
+        "thirdPartyUserId": third_party_user_id,
+        "email": {"id": email},
+    }
+    response = await self.querier.send_post_request(
+        NormalisedURLPath(f"{tenant_id}/recipe/signinup"),
+        data,
+        user_context=user_context,
+    )
+    return SignInUpOkResult(
+        User(
+            response["user"]["id"],
+            response["user"]["email"],
+            response["user"]["timeJoined"],
+            response["user"]["tenantIds"],
+            ThirdPartyInfo(
+                response["user"]["thirdParty"]["userId"],
+                response["user"]["thirdParty"]["id"],
+            ),
+        ),
+        response["createdNewUser"],
+        oauth_tokens,
+        raw_user_info_from_provider,
+    )
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/thirdparty/syncio/index.html b/html/supertokens_python/recipe/thirdparty/syncio/index.html new file mode 100644 index 000000000..85ca6a152 --- /dev/null +++ b/html/supertokens_python/recipe/thirdparty/syncio/index.html @@ -0,0 +1,261 @@ + + + + + + +supertokens_python.recipe.thirdparty.syncio API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.thirdparty.syncio

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from typing import Any, Dict, List, Optional, Union
+
+from supertokens_python.async_to_sync_wrapper import sync
+
+from ..types import User
+
+
+def get_user_by_id(
+    user_id: str, user_context: Union[None, Dict[str, Any]] = None
+) -> Union[User, None]:
+    from supertokens_python.recipe.thirdparty.asyncio import get_user_by_id
+
+    return sync(get_user_by_id(user_id, user_context))
+
+
+def get_users_by_email(
+    tenant_id: str,
+    email: str,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> List[User]:
+    from supertokens_python.recipe.thirdparty.asyncio import get_users_by_email
+
+    return sync(get_users_by_email(tenant_id, email, user_context))
+
+
+def get_user_by_third_party_info(
+    tenant_id: str,
+    third_party_id: str,
+    third_party_user_id: str,
+    user_context: Union[None, Dict[str, Any]] = None,
+):
+    from supertokens_python.recipe.thirdparty.asyncio import (
+        get_user_by_third_party_info,
+    )
+
+    return sync(
+        get_user_by_third_party_info(
+            tenant_id, third_party_id, third_party_user_id, user_context
+        )
+    )
+
+
+def manually_create_or_update_user(
+    tenant_id: str,
+    third_party_id: str,
+    third_party_user_id: str,
+    email: str,
+    user_context: Union[None, Dict[str, Any]] = None,
+):
+    from supertokens_python.recipe.thirdparty.asyncio import (
+        manually_create_or_update_user,
+    )
+
+    return sync(
+        manually_create_or_update_user(
+            tenant_id, third_party_id, third_party_user_id, email, user_context
+        )
+    )
+
+
+def get_provider(
+    tenant_id: str,
+    third_party_id: str,
+    client_type: Optional[str] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+):
+    from supertokens_python.recipe.thirdparty.asyncio import get_provider
+
+    return sync(get_provider(tenant_id, third_party_id, client_type, user_context))
+
+
+
+
+
+
+
+

Functions

+
+
+def get_provider(tenant_id: str, third_party_id: str, client_type: Optional[str] = None, user_context: Optional[Dict[str, Any]] = None) +
+
+
+
+ +Expand source code + +
def get_provider(
+    tenant_id: str,
+    third_party_id: str,
+    client_type: Optional[str] = None,
+    user_context: Union[None, Dict[str, Any]] = None,
+):
+    from supertokens_python.recipe.thirdparty.asyncio import get_provider
+
+    return sync(get_provider(tenant_id, third_party_id, client_type, user_context))
+
+
+
+def get_user_by_id(user_id: str, user_context: Optional[Dict[str, Any]] = None) ‑> Optional[User] +
+
+
+
+ +Expand source code + +
def get_user_by_id(
+    user_id: str, user_context: Union[None, Dict[str, Any]] = None
+) -> Union[User, None]:
+    from supertokens_python.recipe.thirdparty.asyncio import get_user_by_id
+
+    return sync(get_user_by_id(user_id, user_context))
+
+
+
+def get_user_by_third_party_info(tenant_id: str, third_party_id: str, third_party_user_id: str, user_context: Optional[Dict[str, Any]] = None) +
+
+
+
+ +Expand source code + +
def get_user_by_third_party_info(
+    tenant_id: str,
+    third_party_id: str,
+    third_party_user_id: str,
+    user_context: Union[None, Dict[str, Any]] = None,
+):
+    from supertokens_python.recipe.thirdparty.asyncio import (
+        get_user_by_third_party_info,
+    )
+
+    return sync(
+        get_user_by_third_party_info(
+            tenant_id, third_party_id, third_party_user_id, user_context
+        )
+    )
+
+
+
+def get_users_by_email(tenant_id: str, email: str, user_context: Optional[Dict[str, Any]] = None) ‑> List[User] +
+
+
+
+ +Expand source code + +
def get_users_by_email(
+    tenant_id: str,
+    email: str,
+    user_context: Union[None, Dict[str, Any]] = None,
+) -> List[User]:
+    from supertokens_python.recipe.thirdparty.asyncio import get_users_by_email
+
+    return sync(get_users_by_email(tenant_id, email, user_context))
+
+
+
+def manually_create_or_update_user(tenant_id: str, third_party_id: str, third_party_user_id: str, email: str, user_context: Optional[Dict[str, Any]] = None) +
+
+
+
+ +Expand source code + +
def manually_create_or_update_user(
+    tenant_id: str,
+    third_party_id: str,
+    third_party_user_id: str,
+    email: str,
+    user_context: Union[None, Dict[str, Any]] = None,
+):
+    from supertokens_python.recipe.thirdparty.asyncio import (
+        manually_create_or_update_user,
+    )
+
+    return sync(
+        manually_create_or_update_user(
+            tenant_id, third_party_id, third_party_user_id, email, user_context
+        )
+    )
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/thirdparty/types.html b/html/supertokens_python/recipe/thirdparty/types.html new file mode 100644 index 000000000..f1f46712c --- /dev/null +++ b/html/supertokens_python/recipe/thirdparty/types.html @@ -0,0 +1,408 @@ + + + + + + +supertokens_python.recipe.thirdparty.types API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.thirdparty.types

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from typing import Any, Callable, Dict, List, Union, Optional
+
+from supertokens_python.framework.request import BaseRequest
+
+
+class ThirdPartyInfo:
+    def __init__(self, third_party_user_id: str, third_party_id: str):
+        self.user_id = third_party_user_id
+        self.id = third_party_id
+
+    def __eq__(self, other: object) -> bool:
+        return (
+            isinstance(other, self.__class__)
+            and self.user_id == other.user_id
+            and self.id == other.id
+        )
+
+
+class RawUserInfoFromProvider:
+    def __init__(
+        self,
+        from_id_token_payload: Optional[Dict[str, Any]],
+        from_user_info_api: Optional[Dict[str, Any]],
+    ):
+        self.from_id_token_payload = from_id_token_payload
+        self.from_user_info_api = from_user_info_api
+
+
+class User:
+    def __init__(
+        self,
+        user_id: str,
+        email: str,
+        time_joined: int,
+        tenant_ids: List[str],
+        third_party_info: ThirdPartyInfo,
+    ):
+        self.user_id: str = user_id
+        self.email: str = email
+        self.time_joined: int = time_joined
+        self.tenant_ids = tenant_ids
+        self.third_party_info: ThirdPartyInfo = third_party_info
+
+    def __eq__(self, other: object) -> bool:
+        return (
+            isinstance(other, self.__class__)
+            and self.user_id == other.user_id
+            and self.email == other.email
+            and self.time_joined == other.time_joined
+            and self.tenant_ids == other.tenant_ids
+            and self.third_party_info == other.third_party_info
+        )
+
+
+class UserInfoEmail:
+    def __init__(self, email: str, is_verified: bool):
+        self.id: str = email
+        self.is_verified: bool = is_verified
+
+
+class UserInfo:
+    def __init__(
+        self,
+        third_party_user_id: str,
+        email: Union[UserInfoEmail, None] = None,
+        raw_user_info_from_provider: Optional[RawUserInfoFromProvider] = None,
+    ):
+        self.third_party_user_id: str = third_party_user_id
+        self.email: Union[UserInfoEmail, None] = email
+        self.raw_user_info_from_provider = (
+            raw_user_info_from_provider or RawUserInfoFromProvider({}, {})
+        )
+
+
+class AccessTokenAPI:
+    def __init__(self, url: str, params: Dict[str, str]):
+        self.url = url
+        self.params = params
+
+
+class AuthorisationRedirectAPI:
+    def __init__(
+        self, url: str, params: Dict[str, Union[Callable[[BaseRequest], str], str]]
+    ):
+        self.url = url
+        self.params = params
+
+
+class SignInUpResponse:
+    def __init__(self, user: User, is_new_user: bool):
+        self.user = user
+        self.is_new_user = is_new_user
+
+
+class UsersResponse:
+    def __init__(self, users: List[User], next_pagination_token: Union[str, None]):
+        self.users = users
+        self.next_pagination_token = next_pagination_token
+
+
+class ThirdPartyIngredients:
+    pass
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class AccessTokenAPI +(url: str, params: Dict[str, str]) +
+
+
+
+ +Expand source code + +
class AccessTokenAPI:
+    def __init__(self, url: str, params: Dict[str, str]):
+        self.url = url
+        self.params = params
+
+
+
+class AuthorisationRedirectAPI +(url: str, params: Dict[str, Union[Callable[[BaseRequest], str], str]]) +
+
+
+
+ +Expand source code + +
class AuthorisationRedirectAPI:
+    def __init__(
+        self, url: str, params: Dict[str, Union[Callable[[BaseRequest], str], str]]
+    ):
+        self.url = url
+        self.params = params
+
+
+
+class RawUserInfoFromProvider +(from_id_token_payload: Optional[Dict[str, Any]], from_user_info_api: Optional[Dict[str, Any]]) +
+
+
+
+ +Expand source code + +
class RawUserInfoFromProvider:
+    def __init__(
+        self,
+        from_id_token_payload: Optional[Dict[str, Any]],
+        from_user_info_api: Optional[Dict[str, Any]],
+    ):
+        self.from_id_token_payload = from_id_token_payload
+        self.from_user_info_api = from_user_info_api
+
+
+
+class SignInUpResponse +(user: User, is_new_user: bool) +
+
+
+
+ +Expand source code + +
class SignInUpResponse:
+    def __init__(self, user: User, is_new_user: bool):
+        self.user = user
+        self.is_new_user = is_new_user
+
+
+
+class ThirdPartyInfo +(third_party_user_id: str, third_party_id: str) +
+
+
+
+ +Expand source code + +
class ThirdPartyInfo:
+    def __init__(self, third_party_user_id: str, third_party_id: str):
+        self.user_id = third_party_user_id
+        self.id = third_party_id
+
+    def __eq__(self, other: object) -> bool:
+        return (
+            isinstance(other, self.__class__)
+            and self.user_id == other.user_id
+            and self.id == other.id
+        )
+
+
+
+class ThirdPartyIngredients +
+
+
+
+ +Expand source code + +
class ThirdPartyIngredients:
+    pass
+
+
+
+class User +(user_id: str, email: str, time_joined: int, tenant_ids: List[str], third_party_info: ThirdPartyInfo) +
+
+
+
+ +Expand source code + +
class User:
+    def __init__(
+        self,
+        user_id: str,
+        email: str,
+        time_joined: int,
+        tenant_ids: List[str],
+        third_party_info: ThirdPartyInfo,
+    ):
+        self.user_id: str = user_id
+        self.email: str = email
+        self.time_joined: int = time_joined
+        self.tenant_ids = tenant_ids
+        self.third_party_info: ThirdPartyInfo = third_party_info
+
+    def __eq__(self, other: object) -> bool:
+        return (
+            isinstance(other, self.__class__)
+            and self.user_id == other.user_id
+            and self.email == other.email
+            and self.time_joined == other.time_joined
+            and self.tenant_ids == other.tenant_ids
+            and self.third_party_info == other.third_party_info
+        )
+
+
+
+class UserInfo +(third_party_user_id: str, email: Optional[UserInfoEmail] = None, raw_user_info_from_provider: Optional[RawUserInfoFromProvider] = None) +
+
+
+
+ +Expand source code + +
class UserInfo:
+    def __init__(
+        self,
+        third_party_user_id: str,
+        email: Union[UserInfoEmail, None] = None,
+        raw_user_info_from_provider: Optional[RawUserInfoFromProvider] = None,
+    ):
+        self.third_party_user_id: str = third_party_user_id
+        self.email: Union[UserInfoEmail, None] = email
+        self.raw_user_info_from_provider = (
+            raw_user_info_from_provider or RawUserInfoFromProvider({}, {})
+        )
+
+
+
+class UserInfoEmail +(email: str, is_verified: bool) +
+
+
+
+ +Expand source code + +
class UserInfoEmail:
+    def __init__(self, email: str, is_verified: bool):
+        self.id: str = email
+        self.is_verified: bool = is_verified
+
+
+
+class UsersResponse +(users: List[User], next_pagination_token: Optional[str]) +
+
+
+
+ +Expand source code + +
class UsersResponse:
+    def __init__(self, users: List[User], next_pagination_token: Union[str, None]):
+        self.users = users
+        self.next_pagination_token = next_pagination_token
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/thirdparty/utils.html b/html/supertokens_python/recipe/thirdparty/utils.html new file mode 100644 index 000000000..7c576643c --- /dev/null +++ b/html/supertokens_python/recipe/thirdparty/utils.html @@ -0,0 +1,362 @@ + + + + + + +supertokens_python.recipe.thirdparty.utils API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.thirdparty.utils

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Any, Callable, Dict, List, Set, Union, Optional
+
+from supertokens_python.exceptions import raise_bad_input_exception
+from supertokens_python.recipe.thirdparty.provider import ProviderInput
+
+from .interfaces import APIInterface, RecipeInterface
+
+if TYPE_CHECKING:
+    from .provider import ProviderInput
+
+from jwt import PyJWKClient, decode  # type: ignore
+
+
+class SignInAndUpFeature:
+    def __init__(self, providers: Optional[List[ProviderInput]] = None):
+        if providers is None:
+            providers = []
+
+        third_party_id_set: Set[str] = set()
+
+        for provider in providers:
+            third_party_id = provider.config.third_party_id
+
+            if third_party_id in third_party_id_set:
+                raise_bad_input_exception(
+                    "The providers array has multiple entries for the same third party provider."
+                )
+
+            third_party_id_set.add(third_party_id)
+
+        self.providers = providers
+
+
+class InputOverrideConfig:
+    def __init__(
+        self,
+        functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None,
+        apis: Union[Callable[[APIInterface], APIInterface], None] = None,
+    ):
+        self.functions = functions
+        self.apis = apis
+
+
+class OverrideConfig:
+    def __init__(
+        self,
+        functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None,
+        apis: Union[Callable[[APIInterface], APIInterface], None] = None,
+    ):
+        self.functions = functions
+        self.apis = apis
+
+
+class ThirdPartyConfig:
+    def __init__(
+        self,
+        sign_in_and_up_feature: SignInAndUpFeature,
+        override: OverrideConfig,
+    ):
+        self.sign_in_and_up_feature = sign_in_and_up_feature
+        self.override = override
+
+
+def validate_and_normalise_user_input(
+    sign_in_and_up_feature: SignInAndUpFeature,
+    override: Union[InputOverrideConfig, None] = None,
+) -> ThirdPartyConfig:
+    if not isinstance(sign_in_and_up_feature, SignInAndUpFeature):  # type: ignore
+        raise ValueError(
+            "sign_in_and_up_feature must be an instance of SignInAndUpFeature"
+        )
+
+    if override is not None and not isinstance(override, InputOverrideConfig):  # type: ignore
+        raise ValueError("override must be an instance of InputOverrideConfig or None")
+
+    if override is None:
+        override = InputOverrideConfig()
+
+    return ThirdPartyConfig(
+        sign_in_and_up_feature,
+        OverrideConfig(functions=override.functions, apis=override.apis),
+    )
+
+
+def verify_id_token_from_jwks_endpoint(
+    id_token: str, jwks_uri: str, audience: str, issuers: List[str]
+) -> Dict[str, Any]:
+    jwks_client = PyJWKClient(jwks_uri)
+    signing_key = jwks_client.get_signing_key_from_jwt(id_token)
+
+    data: Dict[str, Any] = decode(  # type: ignore
+        id_token,
+        signing_key.key,  # type: ignore
+        algorithms=["RS256"],
+        audience=audience,
+        options={"verify_exp": False},
+    )
+
+    issuer_found = False
+    for issuer in issuers:
+        if data["iss"] == issuer:
+            issuer_found = True
+
+    if not issuer_found:
+        raise Exception("no required issuer found")
+
+    return data
+
+
+
+
+
+
+
+

Functions

+
+
+def validate_and_normalise_user_input(sign_in_and_up_feature: SignInAndUpFeature, override: Union[InputOverrideConfig, None] = None) ‑> ThirdPartyConfig +
+
+
+
+ +Expand source code + +
def validate_and_normalise_user_input(
+    sign_in_and_up_feature: SignInAndUpFeature,
+    override: Union[InputOverrideConfig, None] = None,
+) -> ThirdPartyConfig:
+    if not isinstance(sign_in_and_up_feature, SignInAndUpFeature):  # type: ignore
+        raise ValueError(
+            "sign_in_and_up_feature must be an instance of SignInAndUpFeature"
+        )
+
+    if override is not None and not isinstance(override, InputOverrideConfig):  # type: ignore
+        raise ValueError("override must be an instance of InputOverrideConfig or None")
+
+    if override is None:
+        override = InputOverrideConfig()
+
+    return ThirdPartyConfig(
+        sign_in_and_up_feature,
+        OverrideConfig(functions=override.functions, apis=override.apis),
+    )
+
+
+
+def verify_id_token_from_jwks_endpoint(id_token: str, jwks_uri: str, audience: str, issuers: List[str]) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
def verify_id_token_from_jwks_endpoint(
+    id_token: str, jwks_uri: str, audience: str, issuers: List[str]
+) -> Dict[str, Any]:
+    jwks_client = PyJWKClient(jwks_uri)
+    signing_key = jwks_client.get_signing_key_from_jwt(id_token)
+
+    data: Dict[str, Any] = decode(  # type: ignore
+        id_token,
+        signing_key.key,  # type: ignore
+        algorithms=["RS256"],
+        audience=audience,
+        options={"verify_exp": False},
+    )
+
+    issuer_found = False
+    for issuer in issuers:
+        if data["iss"] == issuer:
+            issuer_found = True
+
+    if not issuer_found:
+        raise Exception("no required issuer found")
+
+    return data
+
+
+
+
+
+

Classes

+
+
+class InputOverrideConfig +(functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None, apis: Union[Callable[[APIInterface], APIInterface], None] = None) +
+
+
+
+ +Expand source code + +
class InputOverrideConfig:
+    def __init__(
+        self,
+        functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None,
+        apis: Union[Callable[[APIInterface], APIInterface], None] = None,
+    ):
+        self.functions = functions
+        self.apis = apis
+
+
+
+class OverrideConfig +(functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None, apis: Union[Callable[[APIInterface], APIInterface], None] = None) +
+
+
+
+ +Expand source code + +
class OverrideConfig:
+    def __init__(
+        self,
+        functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None,
+        apis: Union[Callable[[APIInterface], APIInterface], None] = None,
+    ):
+        self.functions = functions
+        self.apis = apis
+
+
+
+class SignInAndUpFeature +(providers: Optional[List[ProviderInput]] = None) +
+
+
+
+ +Expand source code + +
class SignInAndUpFeature:
+    def __init__(self, providers: Optional[List[ProviderInput]] = None):
+        if providers is None:
+            providers = []
+
+        third_party_id_set: Set[str] = set()
+
+        for provider in providers:
+            third_party_id = provider.config.third_party_id
+
+            if third_party_id in third_party_id_set:
+                raise_bad_input_exception(
+                    "The providers array has multiple entries for the same third party provider."
+                )
+
+            third_party_id_set.add(third_party_id)
+
+        self.providers = providers
+
+
+
+class ThirdPartyConfig +(sign_in_and_up_feature: SignInAndUpFeature, override: OverrideConfig) +
+
+
+
+ +Expand source code + +
class ThirdPartyConfig:
+    def __init__(
+        self,
+        sign_in_and_up_feature: SignInAndUpFeature,
+        override: OverrideConfig,
+    ):
+        self.sign_in_and_up_feature = sign_in_and_up_feature
+        self.override = override
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/usermetadata/asyncio/index.html b/html/supertokens_python/recipe/usermetadata/asyncio/index.html new file mode 100644 index 000000000..6c8452ec1 --- /dev/null +++ b/html/supertokens_python/recipe/usermetadata/asyncio/index.html @@ -0,0 +1,166 @@ + + + + + + +supertokens_python.recipe.usermetadata.asyncio API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.usermetadata.asyncio

+
+
+
+ +Expand source code + +
from typing import Any, Dict, Union
+
+from supertokens_python.recipe.usermetadata.recipe import UserMetadataRecipe
+
+
+async def get_user_metadata(
+    user_id: str, user_context: Union[Dict[str, Any], None] = None
+):
+    if user_context is None:
+        user_context = {}
+    return (
+        await UserMetadataRecipe.get_instance().recipe_implementation.get_user_metadata(
+            user_id, user_context
+        )
+    )
+
+
+async def update_user_metadata(
+    user_id: str,
+    metadata_update: Dict[str, Any],
+    user_context: Union[Dict[str, Any], None] = None,
+):
+    if user_context is None:
+        user_context = {}
+    return await UserMetadataRecipe.get_instance().recipe_implementation.update_user_metadata(
+        user_id, metadata_update, user_context
+    )
+
+
+async def clear_user_metadata(
+    user_id: str, user_context: Union[Dict[str, Any], None] = None
+):
+    if user_context is None:
+        user_context = {}
+    return await UserMetadataRecipe.get_instance().recipe_implementation.clear_user_metadata(
+        user_id, user_context
+    )
+
+
+
+
+
+
+
+

Functions

+
+
+async def clear_user_metadata(user_id: str, user_context: Optional[Dict[str, Any]] = None) +
+
+
+
+ +Expand source code + +
async def clear_user_metadata(
+    user_id: str, user_context: Union[Dict[str, Any], None] = None
+):
+    if user_context is None:
+        user_context = {}
+    return await UserMetadataRecipe.get_instance().recipe_implementation.clear_user_metadata(
+        user_id, user_context
+    )
+
+
+
+async def get_user_metadata(user_id: str, user_context: Optional[Dict[str, Any]] = None) +
+
+
+
+ +Expand source code + +
async def get_user_metadata(
+    user_id: str, user_context: Union[Dict[str, Any], None] = None
+):
+    if user_context is None:
+        user_context = {}
+    return (
+        await UserMetadataRecipe.get_instance().recipe_implementation.get_user_metadata(
+            user_id, user_context
+        )
+    )
+
+
+
+async def update_user_metadata(user_id: str, metadata_update: Dict[str, Any], user_context: Optional[Dict[str, Any]] = None) +
+
+
+
+ +Expand source code + +
async def update_user_metadata(
+    user_id: str,
+    metadata_update: Dict[str, Any],
+    user_context: Union[Dict[str, Any], None] = None,
+):
+    if user_context is None:
+        user_context = {}
+    return await UserMetadataRecipe.get_instance().recipe_implementation.update_user_metadata(
+        user_id, metadata_update, user_context
+    )
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/usermetadata/exceptions.html b/html/supertokens_python/recipe/usermetadata/exceptions.html new file mode 100644 index 000000000..0bfbdfd01 --- /dev/null +++ b/html/supertokens_python/recipe/usermetadata/exceptions.html @@ -0,0 +1,107 @@ + + + + + + +supertokens_python.recipe.usermetadata.exceptions API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.usermetadata.exceptions

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from supertokens_python.exceptions import SuperTokensError
+
+
+class SuperTokensUserMetadataError(SuperTokensError):
+    pass
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class SuperTokensUserMetadataError +(*args, **kwargs) +
+
+

Common base class for all non-exit exceptions.

+
+ +Expand source code + +
class SuperTokensUserMetadataError(SuperTokensError):
+    pass
+
+

Ancestors

+ +
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/usermetadata/index.html b/html/supertokens_python/recipe/usermetadata/index.html new file mode 100644 index 000000000..f2b634495 --- /dev/null +++ b/html/supertokens_python/recipe/usermetadata/index.html @@ -0,0 +1,153 @@ + + + + + + +supertokens_python.recipe.usermetadata API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.usermetadata

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Callable, Union
+
+from . import utils
+from .recipe import UserMetadataRecipe
+
+if TYPE_CHECKING:
+    from supertokens_python.supertokens import AppInfo
+
+    from ...recipe_module import RecipeModule
+
+
+def init(
+    override: Union[utils.InputOverrideConfig, None] = None
+) -> Callable[[AppInfo], RecipeModule]:
+    return UserMetadataRecipe.init(override)
+
+
+
+

Sub-modules

+
+
supertokens_python.recipe.usermetadata.asyncio
+
+
+
+
supertokens_python.recipe.usermetadata.exceptions
+
+
+
+
supertokens_python.recipe.usermetadata.interfaces
+
+
+
+
supertokens_python.recipe.usermetadata.recipe
+
+
+
+
supertokens_python.recipe.usermetadata.recipe_implementation
+
+
+
+
supertokens_python.recipe.usermetadata.syncio
+
+
+
+
supertokens_python.recipe.usermetadata.utils
+
+
+
+
+
+
+
+
+

Functions

+
+
+def init(override: Union[InputOverrideConfig, None] = None) ‑> Callable[[AppInfo], RecipeModule] +
+
+
+
+ +Expand source code + +
def init(
+    override: Union[utils.InputOverrideConfig, None] = None
+) -> Callable[[AppInfo], RecipeModule]:
+    return UserMetadataRecipe.init(override)
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/usermetadata/interfaces.html b/html/supertokens_python/recipe/usermetadata/interfaces.html new file mode 100644 index 000000000..93724418b --- /dev/null +++ b/html/supertokens_python/recipe/usermetadata/interfaces.html @@ -0,0 +1,266 @@ + + + + + + +supertokens_python.recipe.usermetadata.interfaces API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.usermetadata.interfaces

+
+
+
+ +Expand source code + +
from abc import ABC, abstractmethod
+from typing import Any, Dict
+
+
+class MetadataResult(ABC):
+    def __init__(self, metadata: Dict[str, Any]):
+        self.metadata = metadata
+
+
+class ClearUserMetadataResult:
+    pass
+
+
+class RecipeInterface(ABC):
+    @abstractmethod
+    async def get_user_metadata(
+        self, user_id: str, user_context: Dict[str, Any]
+    ) -> MetadataResult:
+        pass
+
+    @abstractmethod
+    async def update_user_metadata(
+        self,
+        user_id: str,
+        metadata_update: Dict[str, Any],
+        user_context: Dict[str, Any],
+    ) -> MetadataResult:
+        pass
+
+    @abstractmethod
+    async def clear_user_metadata(
+        self, user_id: str, user_context: Dict[str, Any]
+    ) -> ClearUserMetadataResult:
+        pass
+
+
+class APIInterface(ABC):
+    pass
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class APIInterface +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class APIInterface(ABC):
+    pass
+
+

Ancestors

+
    +
  • abc.ABC
  • +
+
+
+class ClearUserMetadataResult +
+
+
+
+ +Expand source code + +
class ClearUserMetadataResult:
+    pass
+
+
+
+class MetadataResult +(metadata: Dict[str, Any]) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class MetadataResult(ABC):
+    def __init__(self, metadata: Dict[str, Any]):
+        self.metadata = metadata
+
+

Ancestors

+
    +
  • abc.ABC
  • +
+
+
+class RecipeInterface +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class RecipeInterface(ABC):
+    @abstractmethod
+    async def get_user_metadata(
+        self, user_id: str, user_context: Dict[str, Any]
+    ) -> MetadataResult:
+        pass
+
+    @abstractmethod
+    async def update_user_metadata(
+        self,
+        user_id: str,
+        metadata_update: Dict[str, Any],
+        user_context: Dict[str, Any],
+    ) -> MetadataResult:
+        pass
+
+    @abstractmethod
+    async def clear_user_metadata(
+        self, user_id: str, user_context: Dict[str, Any]
+    ) -> ClearUserMetadataResult:
+        pass
+
+

Ancestors

+
    +
  • abc.ABC
  • +
+

Subclasses

+ +

Methods

+
+
+async def clear_user_metadata(self, user_id: str, user_context: Dict[str, Any]) ‑> ClearUserMetadataResult +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def clear_user_metadata(
+    self, user_id: str, user_context: Dict[str, Any]
+) -> ClearUserMetadataResult:
+    pass
+
+
+
+async def get_user_metadata(self, user_id: str, user_context: Dict[str, Any]) ‑> MetadataResult +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def get_user_metadata(
+    self, user_id: str, user_context: Dict[str, Any]
+) -> MetadataResult:
+    pass
+
+
+
+async def update_user_metadata(self, user_id: str, metadata_update: Dict[str, Any], user_context: Dict[str, Any]) ‑> MetadataResult +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def update_user_metadata(
+    self,
+    user_id: str,
+    metadata_update: Dict[str, Any],
+    user_context: Dict[str, Any],
+) -> MetadataResult:
+    pass
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/usermetadata/recipe.html b/html/supertokens_python/recipe/usermetadata/recipe.html new file mode 100644 index 000000000..246077d73 --- /dev/null +++ b/html/supertokens_python/recipe/usermetadata/recipe.html @@ -0,0 +1,458 @@ + + + + + + +supertokens_python.recipe.usermetadata.recipe API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.usermetadata.recipe

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from __future__ import annotations
+
+from os import environ
+from typing import List, Union, Optional, Dict, Any
+
+from supertokens_python.exceptions import SuperTokensError, raise_general_exception
+from supertokens_python.framework import BaseRequest, BaseResponse
+from supertokens_python.normalised_url_path import NormalisedURLPath
+from supertokens_python.querier import Querier
+from supertokens_python.recipe.usermetadata.exceptions import (
+    SuperTokensUserMetadataError,
+)
+from supertokens_python.recipe.usermetadata.recipe_implementation import (
+    RecipeImplementation,
+)
+from supertokens_python.recipe.usermetadata.utils import (
+    validate_and_normalise_user_input,
+)
+from supertokens_python.recipe_module import APIHandled, RecipeModule
+from supertokens_python.supertokens import AppInfo
+
+from .utils import InputOverrideConfig
+
+
+class UserMetadataRecipe(RecipeModule):
+    recipe_id = "usermetadata"
+    __instance = None
+
+    def __init__(
+        self,
+        recipe_id: str,
+        app_info: AppInfo,
+        override: Union[InputOverrideConfig, None] = None,
+    ):
+        super().__init__(recipe_id, app_info)
+        self.config = validate_and_normalise_user_input(self, app_info, override)
+        recipe_implementation = RecipeImplementation(Querier.get_instance(recipe_id))
+        self.recipe_implementation = (
+            recipe_implementation
+            if self.config.override.functions is None
+            else self.config.override.functions(recipe_implementation)
+        )
+
+    def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
+        return isinstance(err, SuperTokensError) and (
+            isinstance(err, SuperTokensUserMetadataError)
+        )
+
+    def get_apis_handled(self) -> List[APIHandled]:
+        return []
+
+    async def handle_api_request(
+        self,
+        request_id: str,
+        tenant_id: Optional[str],
+        request: BaseRequest,
+        path: NormalisedURLPath,
+        method: str,
+        response: BaseResponse,
+        user_context: Dict[str, Any],
+    ) -> Union[BaseResponse, None]:
+        raise Exception("Should never come here")
+
+    async def handle_error(
+        self,
+        request: BaseRequest,
+        err: SuperTokensError,
+        response: BaseResponse,
+        user_context: Dict[str, Any],
+    ) -> BaseResponse:
+        raise err
+
+    def get_all_cors_headers(self) -> List[str]:
+        return []
+
+    @staticmethod
+    def init(override: Union[InputOverrideConfig, None] = None):
+        def func(app_info: AppInfo):
+            if UserMetadataRecipe.__instance is None:
+                UserMetadataRecipe.__instance = UserMetadataRecipe(
+                    UserMetadataRecipe.recipe_id, app_info, override
+                )
+                return UserMetadataRecipe.__instance
+            raise Exception(
+                None,
+                "Usermetadata recipe has already been initialised. Please check your code for bugs.",
+            )
+
+        return func
+
+    @staticmethod
+    def reset():
+        if ("SUPERTOKENS_ENV" not in environ) or (
+            environ["SUPERTOKENS_ENV"] != "testing"
+        ):
+            raise_general_exception("calling testing function in non testing env")
+        UserMetadataRecipe.__instance = None
+
+    @staticmethod
+    def get_instance() -> UserMetadataRecipe:
+        if UserMetadataRecipe.__instance is not None:
+            return UserMetadataRecipe.__instance
+        raise_general_exception(
+            "Initialisation not done. Did you forget to call the SuperTokens.init function?"
+        )
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class UserMetadataRecipe +(recipe_id: str, app_info: AppInfo, override: Union[InputOverrideConfig, None] = None) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class UserMetadataRecipe(RecipeModule):
+    recipe_id = "usermetadata"
+    __instance = None
+
+    def __init__(
+        self,
+        recipe_id: str,
+        app_info: AppInfo,
+        override: Union[InputOverrideConfig, None] = None,
+    ):
+        super().__init__(recipe_id, app_info)
+        self.config = validate_and_normalise_user_input(self, app_info, override)
+        recipe_implementation = RecipeImplementation(Querier.get_instance(recipe_id))
+        self.recipe_implementation = (
+            recipe_implementation
+            if self.config.override.functions is None
+            else self.config.override.functions(recipe_implementation)
+        )
+
+    def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
+        return isinstance(err, SuperTokensError) and (
+            isinstance(err, SuperTokensUserMetadataError)
+        )
+
+    def get_apis_handled(self) -> List[APIHandled]:
+        return []
+
+    async def handle_api_request(
+        self,
+        request_id: str,
+        tenant_id: Optional[str],
+        request: BaseRequest,
+        path: NormalisedURLPath,
+        method: str,
+        response: BaseResponse,
+        user_context: Dict[str, Any],
+    ) -> Union[BaseResponse, None]:
+        raise Exception("Should never come here")
+
+    async def handle_error(
+        self,
+        request: BaseRequest,
+        err: SuperTokensError,
+        response: BaseResponse,
+        user_context: Dict[str, Any],
+    ) -> BaseResponse:
+        raise err
+
+    def get_all_cors_headers(self) -> List[str]:
+        return []
+
+    @staticmethod
+    def init(override: Union[InputOverrideConfig, None] = None):
+        def func(app_info: AppInfo):
+            if UserMetadataRecipe.__instance is None:
+                UserMetadataRecipe.__instance = UserMetadataRecipe(
+                    UserMetadataRecipe.recipe_id, app_info, override
+                )
+                return UserMetadataRecipe.__instance
+            raise Exception(
+                None,
+                "Usermetadata recipe has already been initialised. Please check your code for bugs.",
+            )
+
+        return func
+
+    @staticmethod
+    def reset():
+        if ("SUPERTOKENS_ENV" not in environ) or (
+            environ["SUPERTOKENS_ENV"] != "testing"
+        ):
+            raise_general_exception("calling testing function in non testing env")
+        UserMetadataRecipe.__instance = None
+
+    @staticmethod
+    def get_instance() -> UserMetadataRecipe:
+        if UserMetadataRecipe.__instance is not None:
+            return UserMetadataRecipe.__instance
+        raise_general_exception(
+            "Initialisation not done. Did you forget to call the SuperTokens.init function?"
+        )
+
+

Ancestors

+ +

Class variables

+
+
var get_tenant_id : Optional[Callable[[str, Dict[str, Any]], Awaitable[str]]]
+
+
+
+
var recipe_id
+
+
+
+
+

Static methods

+
+
+def get_instance() ‑> UserMetadataRecipe +
+
+
+
+ +Expand source code + +
@staticmethod
+def get_instance() -> UserMetadataRecipe:
+    if UserMetadataRecipe.__instance is not None:
+        return UserMetadataRecipe.__instance
+    raise_general_exception(
+        "Initialisation not done. Did you forget to call the SuperTokens.init function?"
+    )
+
+
+
+def init(override: Union[InputOverrideConfig, None] = None) +
+
+
+
+ +Expand source code + +
@staticmethod
+def init(override: Union[InputOverrideConfig, None] = None):
+    def func(app_info: AppInfo):
+        if UserMetadataRecipe.__instance is None:
+            UserMetadataRecipe.__instance = UserMetadataRecipe(
+                UserMetadataRecipe.recipe_id, app_info, override
+            )
+            return UserMetadataRecipe.__instance
+        raise Exception(
+            None,
+            "Usermetadata recipe has already been initialised. Please check your code for bugs.",
+        )
+
+    return func
+
+
+
+def reset() +
+
+
+
+ +Expand source code + +
@staticmethod
+def reset():
+    if ("SUPERTOKENS_ENV" not in environ) or (
+        environ["SUPERTOKENS_ENV"] != "testing"
+    ):
+        raise_general_exception("calling testing function in non testing env")
+    UserMetadataRecipe.__instance = None
+
+
+
+

Methods

+
+
+def get_all_cors_headers(self) ‑> List[str] +
+
+
+
+ +Expand source code + +
def get_all_cors_headers(self) -> List[str]:
+    return []
+
+
+
+def get_apis_handled(self) ‑> List[APIHandled] +
+
+
+
+ +Expand source code + +
def get_apis_handled(self) -> List[APIHandled]:
+    return []
+
+
+
+async def handle_api_request(self, request_id: str, tenant_id: Optional[str], request: BaseRequest, path: NormalisedURLPath, method: str, response: BaseResponse, user_context: Dict[str, Any]) ‑> Optional[BaseResponse] +
+
+
+
+ +Expand source code + +
async def handle_api_request(
+    self,
+    request_id: str,
+    tenant_id: Optional[str],
+    request: BaseRequest,
+    path: NormalisedURLPath,
+    method: str,
+    response: BaseResponse,
+    user_context: Dict[str, Any],
+) -> Union[BaseResponse, None]:
+    raise Exception("Should never come here")
+
+
+
+async def handle_error(self, request: BaseRequest, err: SuperTokensError, response: BaseResponse, user_context: Dict[str, Any]) ‑> BaseResponse +
+
+
+
+ +Expand source code + +
async def handle_error(
+    self,
+    request: BaseRequest,
+    err: SuperTokensError,
+    response: BaseResponse,
+    user_context: Dict[str, Any],
+) -> BaseResponse:
+    raise err
+
+
+
+def is_error_from_this_recipe_based_on_instance(self, err: Exception) ‑> bool +
+
+
+
+ +Expand source code + +
def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
+    return isinstance(err, SuperTokensError) and (
+        isinstance(err, SuperTokensUserMetadataError)
+    )
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/usermetadata/recipe_implementation.html b/html/supertokens_python/recipe/usermetadata/recipe_implementation.html new file mode 100644 index 000000000..77a612637 --- /dev/null +++ b/html/supertokens_python/recipe/usermetadata/recipe_implementation.html @@ -0,0 +1,263 @@ + + + + + + +supertokens_python.recipe.usermetadata.recipe_implementation API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.usermetadata.recipe_implementation

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+
+from typing import Any, Dict
+
+from supertokens_python.normalised_url_path import NormalisedURLPath
+from supertokens_python.querier import Querier
+
+from .interfaces import ClearUserMetadataResult, MetadataResult, RecipeInterface
+
+
+class RecipeImplementation(RecipeInterface):
+    def __init__(self, querier: Querier):
+        super().__init__()
+        self.querier = querier
+
+    async def get_user_metadata(
+        self, user_id: str, user_context: Dict[str, Any]
+    ) -> MetadataResult:
+        params = {"userId": user_id}
+        response = await self.querier.send_get_request(
+            NormalisedURLPath("/recipe/user/metadata"),
+            params,
+            user_context=user_context,
+        )
+        return MetadataResult(metadata=response["metadata"])
+
+    async def update_user_metadata(
+        self,
+        user_id: str,
+        metadata_update: Dict[str, Any],
+        user_context: Dict[str, Any],
+    ) -> MetadataResult:
+        params = {"userId": user_id, "metadataUpdate": metadata_update}
+        response = await self.querier.send_put_request(
+            NormalisedURLPath("/recipe/user/metadata"),
+            params,
+            user_context=user_context,
+        )
+        return MetadataResult(metadata=response["metadata"])
+
+    async def clear_user_metadata(
+        self, user_id: str, user_context: Dict[str, Any]
+    ) -> ClearUserMetadataResult:
+        params = {"userId": user_id}
+        await self.querier.send_post_request(
+            NormalisedURLPath("/recipe/user/metadata/remove"),
+            params,
+            user_context=user_context,
+        )
+        return ClearUserMetadataResult()
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class RecipeImplementation +(querier: Querier) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class RecipeImplementation(RecipeInterface):
+    def __init__(self, querier: Querier):
+        super().__init__()
+        self.querier = querier
+
+    async def get_user_metadata(
+        self, user_id: str, user_context: Dict[str, Any]
+    ) -> MetadataResult:
+        params = {"userId": user_id}
+        response = await self.querier.send_get_request(
+            NormalisedURLPath("/recipe/user/metadata"),
+            params,
+            user_context=user_context,
+        )
+        return MetadataResult(metadata=response["metadata"])
+
+    async def update_user_metadata(
+        self,
+        user_id: str,
+        metadata_update: Dict[str, Any],
+        user_context: Dict[str, Any],
+    ) -> MetadataResult:
+        params = {"userId": user_id, "metadataUpdate": metadata_update}
+        response = await self.querier.send_put_request(
+            NormalisedURLPath("/recipe/user/metadata"),
+            params,
+            user_context=user_context,
+        )
+        return MetadataResult(metadata=response["metadata"])
+
+    async def clear_user_metadata(
+        self, user_id: str, user_context: Dict[str, Any]
+    ) -> ClearUserMetadataResult:
+        params = {"userId": user_id}
+        await self.querier.send_post_request(
+            NormalisedURLPath("/recipe/user/metadata/remove"),
+            params,
+            user_context=user_context,
+        )
+        return ClearUserMetadataResult()
+
+

Ancestors

+ +

Methods

+
+
+async def clear_user_metadata(self, user_id: str, user_context: Dict[str, Any]) ‑> ClearUserMetadataResult +
+
+
+
+ +Expand source code + +
async def clear_user_metadata(
+    self, user_id: str, user_context: Dict[str, Any]
+) -> ClearUserMetadataResult:
+    params = {"userId": user_id}
+    await self.querier.send_post_request(
+        NormalisedURLPath("/recipe/user/metadata/remove"),
+        params,
+        user_context=user_context,
+    )
+    return ClearUserMetadataResult()
+
+
+
+async def get_user_metadata(self, user_id: str, user_context: Dict[str, Any]) ‑> MetadataResult +
+
+
+
+ +Expand source code + +
async def get_user_metadata(
+    self, user_id: str, user_context: Dict[str, Any]
+) -> MetadataResult:
+    params = {"userId": user_id}
+    response = await self.querier.send_get_request(
+        NormalisedURLPath("/recipe/user/metadata"),
+        params,
+        user_context=user_context,
+    )
+    return MetadataResult(metadata=response["metadata"])
+
+
+
+async def update_user_metadata(self, user_id: str, metadata_update: Dict[str, Any], user_context: Dict[str, Any]) ‑> MetadataResult +
+
+
+
+ +Expand source code + +
async def update_user_metadata(
+    self,
+    user_id: str,
+    metadata_update: Dict[str, Any],
+    user_context: Dict[str, Any],
+) -> MetadataResult:
+    params = {"userId": user_id, "metadataUpdate": metadata_update}
+    response = await self.querier.send_put_request(
+        NormalisedURLPath("/recipe/user/metadata"),
+        params,
+        user_context=user_context,
+    )
+    return MetadataResult(metadata=response["metadata"])
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/usermetadata/syncio/index.html b/html/supertokens_python/recipe/usermetadata/syncio/index.html new file mode 100644 index 000000000..9422e3bc1 --- /dev/null +++ b/html/supertokens_python/recipe/usermetadata/syncio/index.html @@ -0,0 +1,142 @@ + + + + + + +supertokens_python.recipe.usermetadata.syncio API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.usermetadata.syncio

+
+
+
+ +Expand source code + +
from typing import Any, Dict, Union
+
+from supertokens_python.async_to_sync_wrapper import sync
+
+
+def get_user_metadata(user_id: str, user_context: Union[Dict[str, Any], None] = None):
+    from supertokens_python.recipe.usermetadata.asyncio import get_user_metadata
+
+    return sync(get_user_metadata(user_id, user_context))
+
+
+def update_user_metadata(
+    user_id: str,
+    metadata_update: Dict[str, Any],
+    user_context: Union[Dict[str, Any], None] = None,
+):
+    from supertokens_python.recipe.usermetadata.asyncio import update_user_metadata
+
+    return sync(update_user_metadata(user_id, metadata_update, user_context))
+
+
+def clear_user_metadata(user_id: str, user_context: Union[Dict[str, Any], None] = None):
+    from supertokens_python.recipe.usermetadata.asyncio import clear_user_metadata
+
+    return sync(clear_user_metadata(user_id, user_context))
+
+
+
+
+
+
+
+

Functions

+
+
+def clear_user_metadata(user_id: str, user_context: Optional[Dict[str, Any]] = None) +
+
+
+
+ +Expand source code + +
def clear_user_metadata(user_id: str, user_context: Union[Dict[str, Any], None] = None):
+    from supertokens_python.recipe.usermetadata.asyncio import clear_user_metadata
+
+    return sync(clear_user_metadata(user_id, user_context))
+
+
+
+def get_user_metadata(user_id: str, user_context: Optional[Dict[str, Any]] = None) +
+
+
+
+ +Expand source code + +
def get_user_metadata(user_id: str, user_context: Union[Dict[str, Any], None] = None):
+    from supertokens_python.recipe.usermetadata.asyncio import get_user_metadata
+
+    return sync(get_user_metadata(user_id, user_context))
+
+
+
+def update_user_metadata(user_id: str, metadata_update: Dict[str, Any], user_context: Optional[Dict[str, Any]] = None) +
+
+
+
+ +Expand source code + +
def update_user_metadata(
+    user_id: str,
+    metadata_update: Dict[str, Any],
+    user_context: Union[Dict[str, Any], None] = None,
+):
+    from supertokens_python.recipe.usermetadata.asyncio import update_user_metadata
+
+    return sync(update_user_metadata(user_id, metadata_update, user_context))
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/usermetadata/utils.html b/html/supertokens_python/recipe/usermetadata/utils.html new file mode 100644 index 000000000..691fd3763 --- /dev/null +++ b/html/supertokens_python/recipe/usermetadata/utils.html @@ -0,0 +1,192 @@ + + + + + + +supertokens_python.recipe.usermetadata.utils API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.usermetadata.utils

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Callable, Union
+
+from supertokens_python.recipe.usermetadata.interfaces import (
+    APIInterface,
+    RecipeInterface,
+)
+from supertokens_python.supertokens import AppInfo
+
+if TYPE_CHECKING:
+    from supertokens_python.recipe.usermetadata.recipe import UserMetadataRecipe
+
+
+class InputOverrideConfig:
+    def __init__(
+        self,
+        functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None,
+        apis: Union[Callable[[APIInterface], APIInterface], None] = None,
+    ):
+        self.functions = functions
+        self.apis = apis
+
+
+class UserMetadataConfig:
+    def __init__(self, override: InputOverrideConfig) -> None:
+        self.override = override
+
+
+def validate_and_normalise_user_input(
+    _recipe: UserMetadataRecipe,
+    _app_info: AppInfo,
+    override: Union[InputOverrideConfig, None] = None,
+) -> UserMetadataConfig:
+    if override is not None and not isinstance(override, InputOverrideConfig):  # type: ignore
+        raise ValueError("override must be an instance of InputOverrideConfig or None")
+
+    if override is None:
+        override = InputOverrideConfig()
+
+    return UserMetadataConfig(override=override)
+
+
+
+
+
+
+
+

Functions

+
+
+def validate_and_normalise_user_input(_recipe: UserMetadataRecipe, _app_info: AppInfo, override: Union[InputOverrideConfig, None] = None) ‑> UserMetadataConfig +
+
+
+
+ +Expand source code + +
def validate_and_normalise_user_input(
+    _recipe: UserMetadataRecipe,
+    _app_info: AppInfo,
+    override: Union[InputOverrideConfig, None] = None,
+) -> UserMetadataConfig:
+    if override is not None and not isinstance(override, InputOverrideConfig):  # type: ignore
+        raise ValueError("override must be an instance of InputOverrideConfig or None")
+
+    if override is None:
+        override = InputOverrideConfig()
+
+    return UserMetadataConfig(override=override)
+
+
+
+
+
+

Classes

+
+
+class InputOverrideConfig +(functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None, apis: Union[Callable[[APIInterface], APIInterface], None] = None) +
+
+
+
+ +Expand source code + +
class InputOverrideConfig:
+    def __init__(
+        self,
+        functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None,
+        apis: Union[Callable[[APIInterface], APIInterface], None] = None,
+    ):
+        self.functions = functions
+        self.apis = apis
+
+
+
+class UserMetadataConfig +(override: InputOverrideConfig) +
+
+
+
+ +Expand source code + +
class UserMetadataConfig:
+    def __init__(self, override: InputOverrideConfig) -> None:
+        self.override = override
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/userroles/asyncio/index.html b/html/supertokens_python/recipe/userroles/asyncio/index.html new file mode 100644 index 000000000..402fcd11f --- /dev/null +++ b/html/supertokens_python/recipe/userroles/asyncio/index.html @@ -0,0 +1,397 @@ + + + + + + +supertokens_python.recipe.userroles.asyncio API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.userroles.asyncio

+
+
+
+ +Expand source code + +
from typing import Any, Dict, List, Union
+
+from supertokens_python.recipe.userroles.interfaces import (
+    AddRoleToUserOkResult,
+    CreateNewRoleOrAddPermissionsOkResult,
+    DeleteRoleOkResult,
+    GetAllRolesOkResult,
+    GetPermissionsForRoleOkResult,
+    GetRolesForUserOkResult,
+    GetRolesThatHavePermissionOkResult,
+    GetUsersThatHaveRoleOkResult,
+    RemovePermissionsFromRoleOkResult,
+    RemoveUserRoleOkResult,
+    UnknownRoleError,
+)
+from supertokens_python.recipe.userroles.recipe import UserRolesRecipe
+
+
+async def add_role_to_user(
+    tenant_id: str,
+    user_id: str,
+    role: str,
+    user_context: Union[Dict[str, Any], None] = None,
+) -> Union[AddRoleToUserOkResult, UnknownRoleError]:
+    if user_context is None:
+        user_context = {}
+    return await UserRolesRecipe.get_instance().recipe_implementation.add_role_to_user(
+        user_id, role, tenant_id, user_context
+    )
+
+
+async def remove_user_role(
+    tenant_id: str,
+    user_id: str,
+    role: str,
+    user_context: Union[Dict[str, Any], None] = None,
+) -> Union[RemoveUserRoleOkResult, UnknownRoleError]:
+    if user_context is None:
+        user_context = {}
+    return await UserRolesRecipe.get_instance().recipe_implementation.remove_user_role(
+        user_id, role, tenant_id, user_context
+    )
+
+
+async def get_roles_for_user(
+    tenant_id: str, user_id: str, user_context: Union[Dict[str, Any], None] = None
+) -> GetRolesForUserOkResult:
+    if user_context is None:
+        user_context = {}
+    return (
+        await UserRolesRecipe.get_instance().recipe_implementation.get_roles_for_user(
+            user_id, tenant_id, user_context
+        )
+    )
+
+
+async def get_users_that_have_role(
+    tenant_id: str, role: str, user_context: Union[Dict[str, Any], None] = None
+) -> Union[GetUsersThatHaveRoleOkResult, UnknownRoleError]:
+    if user_context is None:
+        user_context = {}
+    return await UserRolesRecipe.get_instance().recipe_implementation.get_users_that_have_role(
+        role, tenant_id, user_context
+    )
+
+
+async def create_new_role_or_add_permissions(
+    role: str, permissions: List[str], user_context: Union[Dict[str, Any], None] = None
+) -> CreateNewRoleOrAddPermissionsOkResult:
+    if user_context is None:
+        user_context = {}
+    return await UserRolesRecipe.get_instance().recipe_implementation.create_new_role_or_add_permissions(
+        role, permissions, user_context
+    )
+
+
+async def get_permissions_for_role(
+    role: str, user_context: Union[Dict[str, Any], None] = None
+) -> Union[GetPermissionsForRoleOkResult, UnknownRoleError]:
+    if user_context is None:
+        user_context = {}
+    return await UserRolesRecipe.get_instance().recipe_implementation.get_permissions_for_role(
+        role, user_context
+    )
+
+
+async def remove_permissions_from_role(
+    role: str, permissions: List[str], user_context: Union[Dict[str, Any], None] = None
+) -> Union[RemovePermissionsFromRoleOkResult, UnknownRoleError]:
+    if user_context is None:
+        user_context = {}
+    return await UserRolesRecipe.get_instance().recipe_implementation.remove_permissions_from_role(
+        role, permissions, user_context
+    )
+
+
+async def get_roles_that_have_permission(
+    permission: str, user_context: Union[Dict[str, Any], None] = None
+) -> GetRolesThatHavePermissionOkResult:
+    if user_context is None:
+        user_context = {}
+    return await UserRolesRecipe.get_instance().recipe_implementation.get_roles_that_have_permission(
+        permission, user_context
+    )
+
+
+async def delete_role(
+    role: str, user_context: Union[Dict[str, Any], None] = None
+) -> DeleteRoleOkResult:
+    if user_context is None:
+        user_context = {}
+    return await UserRolesRecipe.get_instance().recipe_implementation.delete_role(
+        role, user_context
+    )
+
+
+async def get_all_roles(
+    user_context: Union[Dict[str, Any], None] = None
+) -> GetAllRolesOkResult:
+    if user_context is None:
+        user_context = {}
+    return await UserRolesRecipe.get_instance().recipe_implementation.get_all_roles(
+        user_context
+    )
+
+
+
+
+
+
+
+

Functions

+
+
+async def add_role_to_user(tenant_id: str, user_id: str, role: str, user_context: Optional[Dict[str, Any]] = None) ‑> Union[AddRoleToUserOkResultUnknownRoleError] +
+
+
+
+ +Expand source code + +
async def add_role_to_user(
+    tenant_id: str,
+    user_id: str,
+    role: str,
+    user_context: Union[Dict[str, Any], None] = None,
+) -> Union[AddRoleToUserOkResult, UnknownRoleError]:
+    if user_context is None:
+        user_context = {}
+    return await UserRolesRecipe.get_instance().recipe_implementation.add_role_to_user(
+        user_id, role, tenant_id, user_context
+    )
+
+
+
+async def create_new_role_or_add_permissions(role: str, permissions: List[str], user_context: Optional[Dict[str, Any]] = None) ‑> CreateNewRoleOrAddPermissionsOkResult +
+
+
+
+ +Expand source code + +
async def create_new_role_or_add_permissions(
+    role: str, permissions: List[str], user_context: Union[Dict[str, Any], None] = None
+) -> CreateNewRoleOrAddPermissionsOkResult:
+    if user_context is None:
+        user_context = {}
+    return await UserRolesRecipe.get_instance().recipe_implementation.create_new_role_or_add_permissions(
+        role, permissions, user_context
+    )
+
+
+
+async def delete_role(role: str, user_context: Optional[Dict[str, Any]] = None) ‑> DeleteRoleOkResult +
+
+
+
+ +Expand source code + +
async def delete_role(
+    role: str, user_context: Union[Dict[str, Any], None] = None
+) -> DeleteRoleOkResult:
+    if user_context is None:
+        user_context = {}
+    return await UserRolesRecipe.get_instance().recipe_implementation.delete_role(
+        role, user_context
+    )
+
+
+
+async def get_all_roles(user_context: Optional[Dict[str, Any]] = None) ‑> GetAllRolesOkResult +
+
+
+
+ +Expand source code + +
async def get_all_roles(
+    user_context: Union[Dict[str, Any], None] = None
+) -> GetAllRolesOkResult:
+    if user_context is None:
+        user_context = {}
+    return await UserRolesRecipe.get_instance().recipe_implementation.get_all_roles(
+        user_context
+    )
+
+
+
+async def get_permissions_for_role(role: str, user_context: Optional[Dict[str, Any]] = None) ‑> Union[GetPermissionsForRoleOkResultUnknownRoleError] +
+
+
+
+ +Expand source code + +
async def get_permissions_for_role(
+    role: str, user_context: Union[Dict[str, Any], None] = None
+) -> Union[GetPermissionsForRoleOkResult, UnknownRoleError]:
+    if user_context is None:
+        user_context = {}
+    return await UserRolesRecipe.get_instance().recipe_implementation.get_permissions_for_role(
+        role, user_context
+    )
+
+
+
+async def get_roles_for_user(tenant_id: str, user_id: str, user_context: Optional[Dict[str, Any]] = None) ‑> GetRolesForUserOkResult +
+
+
+
+ +Expand source code + +
async def get_roles_for_user(
+    tenant_id: str, user_id: str, user_context: Union[Dict[str, Any], None] = None
+) -> GetRolesForUserOkResult:
+    if user_context is None:
+        user_context = {}
+    return (
+        await UserRolesRecipe.get_instance().recipe_implementation.get_roles_for_user(
+            user_id, tenant_id, user_context
+        )
+    )
+
+
+
+async def get_roles_that_have_permission(permission: str, user_context: Optional[Dict[str, Any]] = None) ‑> GetRolesThatHavePermissionOkResult +
+
+
+
+ +Expand source code + +
async def get_roles_that_have_permission(
+    permission: str, user_context: Union[Dict[str, Any], None] = None
+) -> GetRolesThatHavePermissionOkResult:
+    if user_context is None:
+        user_context = {}
+    return await UserRolesRecipe.get_instance().recipe_implementation.get_roles_that_have_permission(
+        permission, user_context
+    )
+
+
+
+async def get_users_that_have_role(tenant_id: str, role: str, user_context: Optional[Dict[str, Any]] = None) ‑> Union[GetUsersThatHaveRoleOkResultUnknownRoleError] +
+
+
+
+ +Expand source code + +
async def get_users_that_have_role(
+    tenant_id: str, role: str, user_context: Union[Dict[str, Any], None] = None
+) -> Union[GetUsersThatHaveRoleOkResult, UnknownRoleError]:
+    if user_context is None:
+        user_context = {}
+    return await UserRolesRecipe.get_instance().recipe_implementation.get_users_that_have_role(
+        role, tenant_id, user_context
+    )
+
+
+
+async def remove_permissions_from_role(role: str, permissions: List[str], user_context: Optional[Dict[str, Any]] = None) ‑> Union[RemovePermissionsFromRoleOkResultUnknownRoleError] +
+
+
+
+ +Expand source code + +
async def remove_permissions_from_role(
+    role: str, permissions: List[str], user_context: Union[Dict[str, Any], None] = None
+) -> Union[RemovePermissionsFromRoleOkResult, UnknownRoleError]:
+    if user_context is None:
+        user_context = {}
+    return await UserRolesRecipe.get_instance().recipe_implementation.remove_permissions_from_role(
+        role, permissions, user_context
+    )
+
+
+
+async def remove_user_role(tenant_id: str, user_id: str, role: str, user_context: Optional[Dict[str, Any]] = None) ‑> Union[RemoveUserRoleOkResultUnknownRoleError] +
+
+
+
+ +Expand source code + +
async def remove_user_role(
+    tenant_id: str,
+    user_id: str,
+    role: str,
+    user_context: Union[Dict[str, Any], None] = None,
+) -> Union[RemoveUserRoleOkResult, UnknownRoleError]:
+    if user_context is None:
+        user_context = {}
+    return await UserRolesRecipe.get_instance().recipe_implementation.remove_user_role(
+        user_id, role, tenant_id, user_context
+    )
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/userroles/exceptions.html b/html/supertokens_python/recipe/userroles/exceptions.html new file mode 100644 index 000000000..d10b26e96 --- /dev/null +++ b/html/supertokens_python/recipe/userroles/exceptions.html @@ -0,0 +1,107 @@ + + + + + + +supertokens_python.recipe.userroles.exceptions API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.userroles.exceptions

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from supertokens_python.exceptions import SuperTokensError
+
+
+class SuperTokensUserRolesError(SuperTokensError):
+    pass
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class SuperTokensUserRolesError +(*args, **kwargs) +
+
+

Common base class for all non-exit exceptions.

+
+ +Expand source code + +
class SuperTokensUserRolesError(SuperTokensError):
+    pass
+
+

Ancestors

+ +
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/userroles/index.html b/html/supertokens_python/recipe/userroles/index.html new file mode 100644 index 000000000..791eb9c3b --- /dev/null +++ b/html/supertokens_python/recipe/userroles/index.html @@ -0,0 +1,169 @@ + + + + + + +supertokens_python.recipe.userroles API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.userroles

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Callable, Union, Optional
+
+from . import utils
+from .recipe import UserRolesRecipe
+from . import recipe
+
+PermissionClaim = recipe.PermissionClaim
+UserRoleClaim = recipe.UserRoleClaim
+
+if TYPE_CHECKING:
+    from supertokens_python.supertokens import AppInfo
+
+    from ...recipe_module import RecipeModule
+
+
+def init(
+    skip_adding_roles_to_access_token: Optional[bool] = None,
+    skip_adding_permissions_to_access_token: Optional[bool] = None,
+    override: Union[utils.InputOverrideConfig, None] = None,
+) -> Callable[[AppInfo], RecipeModule]:
+    return UserRolesRecipe.init(
+        skip_adding_roles_to_access_token,
+        skip_adding_permissions_to_access_token,
+        override,
+    )
+
+
+
+

Sub-modules

+
+
supertokens_python.recipe.userroles.asyncio
+
+
+
+
supertokens_python.recipe.userroles.exceptions
+
+
+
+
supertokens_python.recipe.userroles.interfaces
+
+
+
+
supertokens_python.recipe.userroles.recipe
+
+
+
+
supertokens_python.recipe.userroles.recipe_implementation
+
+
+
+
supertokens_python.recipe.userroles.syncio
+
+
+
+
supertokens_python.recipe.userroles.utils
+
+
+
+
+
+
+
+
+

Functions

+
+
+def init(skip_adding_roles_to_access_token: Optional[bool] = None, skip_adding_permissions_to_access_token: Optional[bool] = None, override: Union[InputOverrideConfig, None] = None) ‑> Callable[[AppInfo], RecipeModule] +
+
+
+
+ +Expand source code + +
def init(
+    skip_adding_roles_to_access_token: Optional[bool] = None,
+    skip_adding_permissions_to_access_token: Optional[bool] = None,
+    override: Union[utils.InputOverrideConfig, None] = None,
+) -> Callable[[AppInfo], RecipeModule]:
+    return UserRolesRecipe.init(
+        skip_adding_roles_to_access_token,
+        skip_adding_permissions_to_access_token,
+        override,
+    )
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/userroles/interfaces.html b/html/supertokens_python/recipe/userroles/interfaces.html new file mode 100644 index 000000000..b3db4927f --- /dev/null +++ b/html/supertokens_python/recipe/userroles/interfaces.html @@ -0,0 +1,677 @@ + + + + + + +supertokens_python.recipe.userroles.interfaces API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.userroles.interfaces

+
+
+
+ +Expand source code + +
from abc import ABC, abstractmethod
+from typing import Any, Dict, List, Union
+
+
+class AddRoleToUserOkResult:
+    def __init__(self, did_user_already_have_role: bool):
+        self.did_user_already_have_role = did_user_already_have_role
+
+
+class UnknownRoleError:
+    pass
+
+
+class RemoveUserRoleOkResult:
+    def __init__(self, did_user_have_role: bool):
+        self.did_user_have_role = did_user_have_role
+
+
+class GetRolesForUserOkResult:
+    def __init__(self, roles: List[str]):
+        self.roles = roles
+
+
+class GetUsersThatHaveRoleOkResult:
+    def __init__(self, users: List[str]):
+        self.users = users
+
+
+class CreateNewRoleOrAddPermissionsOkResult:
+    def __init__(self, created_new_role: bool):
+        self.created_new_role = created_new_role
+
+
+class GetPermissionsForRoleOkResult:
+    def __init__(self, permissions: List[str]):
+        self.permissions = permissions
+
+
+class RemovePermissionsFromRoleOkResult:
+    pass
+
+
+class GetRolesThatHavePermissionOkResult:
+    def __init__(self, roles: List[str]):
+        self.roles = roles
+
+
+class DeleteRoleOkResult:
+    def __init__(self, did_role_exist: bool):
+        self.did_role_exist = did_role_exist
+
+
+class GetAllRolesOkResult:
+    def __init__(self, roles: List[str]):
+        self.roles = roles
+
+
+class RecipeInterface(ABC):
+    @abstractmethod
+    async def add_role_to_user(
+        self,
+        user_id: str,
+        role: str,
+        tenant_id: str,
+        user_context: Dict[str, Any],
+    ) -> Union[AddRoleToUserOkResult, UnknownRoleError]:
+        pass
+
+    @abstractmethod
+    async def remove_user_role(
+        self,
+        user_id: str,
+        role: str,
+        tenant_id: str,
+        user_context: Dict[str, Any],
+    ) -> Union[RemoveUserRoleOkResult, UnknownRoleError]:
+        pass
+
+    @abstractmethod
+    async def get_roles_for_user(
+        self, user_id: str, tenant_id: str, user_context: Dict[str, Any]
+    ) -> GetRolesForUserOkResult:
+        pass
+
+    @abstractmethod
+    async def get_users_that_have_role(
+        self, role: str, tenant_id: str, user_context: Dict[str, Any]
+    ) -> Union[GetUsersThatHaveRoleOkResult, UnknownRoleError]:
+        pass
+
+    @abstractmethod
+    async def create_new_role_or_add_permissions(
+        self, role: str, permissions: List[str], user_context: Dict[str, Any]
+    ) -> CreateNewRoleOrAddPermissionsOkResult:
+        pass
+
+    @abstractmethod
+    async def get_permissions_for_role(
+        self, role: str, user_context: Dict[str, Any]
+    ) -> Union[GetPermissionsForRoleOkResult, UnknownRoleError]:
+        pass
+
+    @abstractmethod
+    async def remove_permissions_from_role(
+        self, role: str, permissions: List[str], user_context: Dict[str, Any]
+    ) -> Union[RemovePermissionsFromRoleOkResult, UnknownRoleError]:
+        pass
+
+    @abstractmethod
+    async def get_roles_that_have_permission(
+        self, permission: str, user_context: Dict[str, Any]
+    ) -> GetRolesThatHavePermissionOkResult:
+        pass
+
+    @abstractmethod
+    async def delete_role(
+        self, role: str, user_context: Dict[str, Any]
+    ) -> DeleteRoleOkResult:
+        pass
+
+    @abstractmethod
+    async def get_all_roles(self, user_context: Dict[str, Any]) -> GetAllRolesOkResult:
+        pass
+
+
+class APIInterface(ABC):
+    pass
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class APIInterface +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class APIInterface(ABC):
+    pass
+
+

Ancestors

+
    +
  • abc.ABC
  • +
+
+
+class AddRoleToUserOkResult +(did_user_already_have_role: bool) +
+
+
+
+ +Expand source code + +
class AddRoleToUserOkResult:
+    def __init__(self, did_user_already_have_role: bool):
+        self.did_user_already_have_role = did_user_already_have_role
+
+
+
+class CreateNewRoleOrAddPermissionsOkResult +(created_new_role: bool) +
+
+
+
+ +Expand source code + +
class CreateNewRoleOrAddPermissionsOkResult:
+    def __init__(self, created_new_role: bool):
+        self.created_new_role = created_new_role
+
+
+
+class DeleteRoleOkResult +(did_role_exist: bool) +
+
+
+
+ +Expand source code + +
class DeleteRoleOkResult:
+    def __init__(self, did_role_exist: bool):
+        self.did_role_exist = did_role_exist
+
+
+
+class GetAllRolesOkResult +(roles: List[str]) +
+
+
+
+ +Expand source code + +
class GetAllRolesOkResult:
+    def __init__(self, roles: List[str]):
+        self.roles = roles
+
+
+
+class GetPermissionsForRoleOkResult +(permissions: List[str]) +
+
+
+
+ +Expand source code + +
class GetPermissionsForRoleOkResult:
+    def __init__(self, permissions: List[str]):
+        self.permissions = permissions
+
+
+
+class GetRolesForUserOkResult +(roles: List[str]) +
+
+
+
+ +Expand source code + +
class GetRolesForUserOkResult:
+    def __init__(self, roles: List[str]):
+        self.roles = roles
+
+
+
+class GetRolesThatHavePermissionOkResult +(roles: List[str]) +
+
+
+
+ +Expand source code + +
class GetRolesThatHavePermissionOkResult:
+    def __init__(self, roles: List[str]):
+        self.roles = roles
+
+
+
+class GetUsersThatHaveRoleOkResult +(users: List[str]) +
+
+
+
+ +Expand source code + +
class GetUsersThatHaveRoleOkResult:
+    def __init__(self, users: List[str]):
+        self.users = users
+
+
+
+class RecipeInterface +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class RecipeInterface(ABC):
+    @abstractmethod
+    async def add_role_to_user(
+        self,
+        user_id: str,
+        role: str,
+        tenant_id: str,
+        user_context: Dict[str, Any],
+    ) -> Union[AddRoleToUserOkResult, UnknownRoleError]:
+        pass
+
+    @abstractmethod
+    async def remove_user_role(
+        self,
+        user_id: str,
+        role: str,
+        tenant_id: str,
+        user_context: Dict[str, Any],
+    ) -> Union[RemoveUserRoleOkResult, UnknownRoleError]:
+        pass
+
+    @abstractmethod
+    async def get_roles_for_user(
+        self, user_id: str, tenant_id: str, user_context: Dict[str, Any]
+    ) -> GetRolesForUserOkResult:
+        pass
+
+    @abstractmethod
+    async def get_users_that_have_role(
+        self, role: str, tenant_id: str, user_context: Dict[str, Any]
+    ) -> Union[GetUsersThatHaveRoleOkResult, UnknownRoleError]:
+        pass
+
+    @abstractmethod
+    async def create_new_role_or_add_permissions(
+        self, role: str, permissions: List[str], user_context: Dict[str, Any]
+    ) -> CreateNewRoleOrAddPermissionsOkResult:
+        pass
+
+    @abstractmethod
+    async def get_permissions_for_role(
+        self, role: str, user_context: Dict[str, Any]
+    ) -> Union[GetPermissionsForRoleOkResult, UnknownRoleError]:
+        pass
+
+    @abstractmethod
+    async def remove_permissions_from_role(
+        self, role: str, permissions: List[str], user_context: Dict[str, Any]
+    ) -> Union[RemovePermissionsFromRoleOkResult, UnknownRoleError]:
+        pass
+
+    @abstractmethod
+    async def get_roles_that_have_permission(
+        self, permission: str, user_context: Dict[str, Any]
+    ) -> GetRolesThatHavePermissionOkResult:
+        pass
+
+    @abstractmethod
+    async def delete_role(
+        self, role: str, user_context: Dict[str, Any]
+    ) -> DeleteRoleOkResult:
+        pass
+
+    @abstractmethod
+    async def get_all_roles(self, user_context: Dict[str, Any]) -> GetAllRolesOkResult:
+        pass
+
+

Ancestors

+
    +
  • abc.ABC
  • +
+

Subclasses

+ +

Methods

+
+
+async def add_role_to_user(self, user_id: str, role: str, tenant_id: str, user_context: Dict[str, Any]) ‑> Union[AddRoleToUserOkResultUnknownRoleError] +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def add_role_to_user(
+    self,
+    user_id: str,
+    role: str,
+    tenant_id: str,
+    user_context: Dict[str, Any],
+) -> Union[AddRoleToUserOkResult, UnknownRoleError]:
+    pass
+
+
+
+async def create_new_role_or_add_permissions(self, role: str, permissions: List[str], user_context: Dict[str, Any]) ‑> CreateNewRoleOrAddPermissionsOkResult +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def create_new_role_or_add_permissions(
+    self, role: str, permissions: List[str], user_context: Dict[str, Any]
+) -> CreateNewRoleOrAddPermissionsOkResult:
+    pass
+
+
+
+async def delete_role(self, role: str, user_context: Dict[str, Any]) ‑> DeleteRoleOkResult +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def delete_role(
+    self, role: str, user_context: Dict[str, Any]
+) -> DeleteRoleOkResult:
+    pass
+
+
+
+async def get_all_roles(self, user_context: Dict[str, Any]) ‑> GetAllRolesOkResult +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def get_all_roles(self, user_context: Dict[str, Any]) -> GetAllRolesOkResult:
+    pass
+
+
+
+async def get_permissions_for_role(self, role: str, user_context: Dict[str, Any]) ‑> Union[GetPermissionsForRoleOkResultUnknownRoleError] +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def get_permissions_for_role(
+    self, role: str, user_context: Dict[str, Any]
+) -> Union[GetPermissionsForRoleOkResult, UnknownRoleError]:
+    pass
+
+
+
+async def get_roles_for_user(self, user_id: str, tenant_id: str, user_context: Dict[str, Any]) ‑> GetRolesForUserOkResult +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def get_roles_for_user(
+    self, user_id: str, tenant_id: str, user_context: Dict[str, Any]
+) -> GetRolesForUserOkResult:
+    pass
+
+
+
+async def get_roles_that_have_permission(self, permission: str, user_context: Dict[str, Any]) ‑> GetRolesThatHavePermissionOkResult +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def get_roles_that_have_permission(
+    self, permission: str, user_context: Dict[str, Any]
+) -> GetRolesThatHavePermissionOkResult:
+    pass
+
+
+
+async def get_users_that_have_role(self, role: str, tenant_id: str, user_context: Dict[str, Any]) ‑> Union[GetUsersThatHaveRoleOkResultUnknownRoleError] +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def get_users_that_have_role(
+    self, role: str, tenant_id: str, user_context: Dict[str, Any]
+) -> Union[GetUsersThatHaveRoleOkResult, UnknownRoleError]:
+    pass
+
+
+
+async def remove_permissions_from_role(self, role: str, permissions: List[str], user_context: Dict[str, Any]) ‑> Union[RemovePermissionsFromRoleOkResultUnknownRoleError] +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def remove_permissions_from_role(
+    self, role: str, permissions: List[str], user_context: Dict[str, Any]
+) -> Union[RemovePermissionsFromRoleOkResult, UnknownRoleError]:
+    pass
+
+
+
+async def remove_user_role(self, user_id: str, role: str, tenant_id: str, user_context: Dict[str, Any]) ‑> Union[RemoveUserRoleOkResultUnknownRoleError] +
+
+
+
+ +Expand source code + +
@abstractmethod
+async def remove_user_role(
+    self,
+    user_id: str,
+    role: str,
+    tenant_id: str,
+    user_context: Dict[str, Any],
+) -> Union[RemoveUserRoleOkResult, UnknownRoleError]:
+    pass
+
+
+
+
+
+class RemovePermissionsFromRoleOkResult +
+
+
+
+ +Expand source code + +
class RemovePermissionsFromRoleOkResult:
+    pass
+
+
+
+class RemoveUserRoleOkResult +(did_user_have_role: bool) +
+
+
+
+ +Expand source code + +
class RemoveUserRoleOkResult:
+    def __init__(self, did_user_have_role: bool):
+        self.did_user_have_role = did_user_have_role
+
+
+
+class UnknownRoleError +
+
+
+
+ +Expand source code + +
class UnknownRoleError:
+    pass
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/userroles/recipe.html b/html/supertokens_python/recipe/userroles/recipe.html new file mode 100644 index 000000000..ddceffa24 --- /dev/null +++ b/html/supertokens_python/recipe/userroles/recipe.html @@ -0,0 +1,704 @@ + + + + + + +supertokens_python.recipe.userroles.recipe API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.userroles.recipe

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from __future__ import annotations
+
+from os import environ
+from typing import Any, Dict, List, Optional, Set, Union
+
+from supertokens_python.exceptions import SuperTokensError, raise_general_exception
+from supertokens_python.framework import BaseRequest, BaseResponse
+from supertokens_python.normalised_url_path import NormalisedURLPath
+from supertokens_python.querier import Querier
+from supertokens_python.recipe.userroles.recipe_implementation import (
+    RecipeImplementation,
+)
+from supertokens_python.recipe.userroles.utils import validate_and_normalise_user_input
+from supertokens_python.recipe_module import APIHandled, RecipeModule
+from supertokens_python.supertokens import AppInfo
+
+from ...post_init_callbacks import PostSTInitCallbacks
+from ..session import SessionRecipe
+from ..session.claim_base_classes.primitive_array_claim import PrimitiveArrayClaim
+from .exceptions import SuperTokensUserRolesError
+from .interfaces import GetPermissionsForRoleOkResult
+from .utils import InputOverrideConfig
+
+
+class UserRolesRecipe(RecipeModule):
+    recipe_id = "userroles"
+    __instance = None
+
+    def __init__(
+        self,
+        recipe_id: str,
+        app_info: AppInfo,
+        skip_adding_roles_to_access_token: Optional[bool] = None,
+        skip_adding_permissions_to_access_token: Optional[bool] = None,
+        override: Union[InputOverrideConfig, None] = None,
+    ):
+        super().__init__(recipe_id, app_info)
+        self.config = validate_and_normalise_user_input(
+            self,
+            app_info,
+            skip_adding_roles_to_access_token,
+            skip_adding_permissions_to_access_token,
+            override,
+        )
+        recipe_implementation = RecipeImplementation(Querier.get_instance(recipe_id))
+        self.recipe_implementation = (
+            recipe_implementation
+            if self.config.override.functions is None
+            else self.config.override.functions(recipe_implementation)
+        )
+
+        def callback():
+            if self.config.skip_adding_roles_to_access_token is False:
+                SessionRecipe.get_instance().add_claim_from_other_recipe(UserRoleClaim)
+            if self.config.skip_adding_permissions_to_access_token is False:
+                SessionRecipe.get_instance().add_claim_from_other_recipe(
+                    PermissionClaim
+                )
+
+        PostSTInitCallbacks.add_post_init_callback(callback)
+
+    def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
+        return isinstance(err, SuperTokensError) and (
+            isinstance(err, SuperTokensUserRolesError)
+        )
+
+    def get_apis_handled(self) -> List[APIHandled]:
+        return []
+
+    async def handle_api_request(
+        self,
+        request_id: str,
+        tenant_id: Optional[str],
+        request: BaseRequest,
+        path: NormalisedURLPath,
+        method: str,
+        response: BaseResponse,
+        user_context: Dict[str, Any],
+    ) -> Union[BaseResponse, None]:
+        raise Exception("Should never come here")
+
+    async def handle_error(
+        self,
+        request: BaseRequest,
+        err: SuperTokensError,
+        response: BaseResponse,
+        user_context: Dict[str, Any],
+    ) -> BaseResponse:
+        raise err
+
+    def get_all_cors_headers(self) -> List[str]:
+        return []
+
+    @staticmethod
+    def init(
+        skip_adding_roles_to_access_token: Optional[bool] = None,
+        skip_adding_permissions_to_access_token: Optional[bool] = None,
+        override: Union[InputOverrideConfig, None] = None,
+    ):
+        def func(app_info: AppInfo):
+            if UserRolesRecipe.__instance is None:
+                UserRolesRecipe.__instance = UserRolesRecipe(
+                    UserRolesRecipe.recipe_id,
+                    app_info,
+                    skip_adding_roles_to_access_token,
+                    skip_adding_permissions_to_access_token,
+                    override,
+                )
+                return UserRolesRecipe.__instance
+            raise Exception(
+                None,
+                "UserRoles recipe has already been initialised. Please check your code for bugs.",
+            )
+
+        return func
+
+    @staticmethod
+    def reset():
+        if ("SUPERTOKENS_ENV" not in environ) or (
+            environ["SUPERTOKENS_ENV"] != "testing"
+        ):
+            raise_general_exception("calling testing function in non testing env")
+        UserRolesRecipe.__instance = None
+
+    @staticmethod
+    def get_instance() -> UserRolesRecipe:
+        if UserRolesRecipe.__instance is not None:
+            return UserRolesRecipe.__instance
+        raise_general_exception(
+            "Initialisation not done. Did you forget to call the SuperTokens.init or UserRoles.init function?"
+        )
+
+
+class PermissionClaimClass(PrimitiveArrayClaim[List[str]]):
+    def __init__(self) -> None:
+        key = "st-perm"
+        default_max_age_in_sec = 300
+
+        async def fetch_value(
+            user_id: str, tenant_id: str, user_context: Dict[str, Any]
+        ) -> List[str]:
+            recipe = UserRolesRecipe.get_instance()
+
+            user_roles = await recipe.recipe_implementation.get_roles_for_user(
+                user_id, tenant_id, user_context
+            )
+
+            user_permissions: Set[str] = set()
+
+            for role in user_roles.roles:
+                role_permissions = (
+                    await recipe.recipe_implementation.get_permissions_for_role(
+                        role, user_context
+                    )
+                )
+
+                if isinstance(role_permissions, GetPermissionsForRoleOkResult):
+                    for permission in role_permissions.permissions:
+                        user_permissions.add(permission)
+
+            return list(user_permissions)
+
+        super().__init__(key, fetch_value, default_max_age_in_sec)
+
+
+PermissionClaim = PermissionClaimClass()
+
+
+class UserRoleClaimClass(PrimitiveArrayClaim[List[str]]):
+    def __init__(self) -> None:
+        key = "st-role"
+        default_max_age_in_sec = 300
+
+        async def fetch_value(
+            user_id: str, tenant_id: str, user_context: Dict[str, Any]
+        ) -> List[str]:
+            recipe = UserRolesRecipe.get_instance()
+            res = await recipe.recipe_implementation.get_roles_for_user(
+                user_id, tenant_id, user_context
+            )
+            return res.roles
+
+        super().__init__(key, fetch_value, default_max_age_in_sec)
+
+
+UserRoleClaim = UserRoleClaimClass()
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class PermissionClaimClass +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+

Args

+
+
key
+
The key to use when storing the claim in the payload.
+
fetch_value
+
a method that fetches the current value of this claim for the user. +A None return value signifies that we don't want to update the claim payload and or the claim value is +not present in the database. For example, this can happen with a second factor auth claim, where we +don't want to add the claim to the session automatically
+
+
+ +Expand source code + +
class PermissionClaimClass(PrimitiveArrayClaim[List[str]]):
+    def __init__(self) -> None:
+        key = "st-perm"
+        default_max_age_in_sec = 300
+
+        async def fetch_value(
+            user_id: str, tenant_id: str, user_context: Dict[str, Any]
+        ) -> List[str]:
+            recipe = UserRolesRecipe.get_instance()
+
+            user_roles = await recipe.recipe_implementation.get_roles_for_user(
+                user_id, tenant_id, user_context
+            )
+
+            user_permissions: Set[str] = set()
+
+            for role in user_roles.roles:
+                role_permissions = (
+                    await recipe.recipe_implementation.get_permissions_for_role(
+                        role, user_context
+                    )
+                )
+
+                if isinstance(role_permissions, GetPermissionsForRoleOkResult):
+                    for permission in role_permissions.permissions:
+                        user_permissions.add(permission)
+
+            return list(user_permissions)
+
+        super().__init__(key, fetch_value, default_max_age_in_sec)
+
+

Ancestors

+ +

Inherited members

+ +
+
+class UserRoleClaimClass +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+

Args

+
+
key
+
The key to use when storing the claim in the payload.
+
fetch_value
+
a method that fetches the current value of this claim for the user. +A None return value signifies that we don't want to update the claim payload and or the claim value is +not present in the database. For example, this can happen with a second factor auth claim, where we +don't want to add the claim to the session automatically
+
+
+ +Expand source code + +
class UserRoleClaimClass(PrimitiveArrayClaim[List[str]]):
+    def __init__(self) -> None:
+        key = "st-role"
+        default_max_age_in_sec = 300
+
+        async def fetch_value(
+            user_id: str, tenant_id: str, user_context: Dict[str, Any]
+        ) -> List[str]:
+            recipe = UserRolesRecipe.get_instance()
+            res = await recipe.recipe_implementation.get_roles_for_user(
+                user_id, tenant_id, user_context
+            )
+            return res.roles
+
+        super().__init__(key, fetch_value, default_max_age_in_sec)
+
+

Ancestors

+ +

Inherited members

+ +
+
+class UserRolesRecipe +(recipe_id: str, app_info: AppInfo, skip_adding_roles_to_access_token: Optional[bool] = None, skip_adding_permissions_to_access_token: Optional[bool] = None, override: Union[InputOverrideConfig, None] = None) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class UserRolesRecipe(RecipeModule):
+    recipe_id = "userroles"
+    __instance = None
+
+    def __init__(
+        self,
+        recipe_id: str,
+        app_info: AppInfo,
+        skip_adding_roles_to_access_token: Optional[bool] = None,
+        skip_adding_permissions_to_access_token: Optional[bool] = None,
+        override: Union[InputOverrideConfig, None] = None,
+    ):
+        super().__init__(recipe_id, app_info)
+        self.config = validate_and_normalise_user_input(
+            self,
+            app_info,
+            skip_adding_roles_to_access_token,
+            skip_adding_permissions_to_access_token,
+            override,
+        )
+        recipe_implementation = RecipeImplementation(Querier.get_instance(recipe_id))
+        self.recipe_implementation = (
+            recipe_implementation
+            if self.config.override.functions is None
+            else self.config.override.functions(recipe_implementation)
+        )
+
+        def callback():
+            if self.config.skip_adding_roles_to_access_token is False:
+                SessionRecipe.get_instance().add_claim_from_other_recipe(UserRoleClaim)
+            if self.config.skip_adding_permissions_to_access_token is False:
+                SessionRecipe.get_instance().add_claim_from_other_recipe(
+                    PermissionClaim
+                )
+
+        PostSTInitCallbacks.add_post_init_callback(callback)
+
+    def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
+        return isinstance(err, SuperTokensError) and (
+            isinstance(err, SuperTokensUserRolesError)
+        )
+
+    def get_apis_handled(self) -> List[APIHandled]:
+        return []
+
+    async def handle_api_request(
+        self,
+        request_id: str,
+        tenant_id: Optional[str],
+        request: BaseRequest,
+        path: NormalisedURLPath,
+        method: str,
+        response: BaseResponse,
+        user_context: Dict[str, Any],
+    ) -> Union[BaseResponse, None]:
+        raise Exception("Should never come here")
+
+    async def handle_error(
+        self,
+        request: BaseRequest,
+        err: SuperTokensError,
+        response: BaseResponse,
+        user_context: Dict[str, Any],
+    ) -> BaseResponse:
+        raise err
+
+    def get_all_cors_headers(self) -> List[str]:
+        return []
+
+    @staticmethod
+    def init(
+        skip_adding_roles_to_access_token: Optional[bool] = None,
+        skip_adding_permissions_to_access_token: Optional[bool] = None,
+        override: Union[InputOverrideConfig, None] = None,
+    ):
+        def func(app_info: AppInfo):
+            if UserRolesRecipe.__instance is None:
+                UserRolesRecipe.__instance = UserRolesRecipe(
+                    UserRolesRecipe.recipe_id,
+                    app_info,
+                    skip_adding_roles_to_access_token,
+                    skip_adding_permissions_to_access_token,
+                    override,
+                )
+                return UserRolesRecipe.__instance
+            raise Exception(
+                None,
+                "UserRoles recipe has already been initialised. Please check your code for bugs.",
+            )
+
+        return func
+
+    @staticmethod
+    def reset():
+        if ("SUPERTOKENS_ENV" not in environ) or (
+            environ["SUPERTOKENS_ENV"] != "testing"
+        ):
+            raise_general_exception("calling testing function in non testing env")
+        UserRolesRecipe.__instance = None
+
+    @staticmethod
+    def get_instance() -> UserRolesRecipe:
+        if UserRolesRecipe.__instance is not None:
+            return UserRolesRecipe.__instance
+        raise_general_exception(
+            "Initialisation not done. Did you forget to call the SuperTokens.init or UserRoles.init function?"
+        )
+
+

Ancestors

+ +

Class variables

+
+
var get_tenant_id : Optional[Callable[[str, Dict[str, Any]], Awaitable[str]]]
+
+
+
+
var recipe_id
+
+
+
+
+

Static methods

+
+
+def get_instance() ‑> UserRolesRecipe +
+
+
+
+ +Expand source code + +
@staticmethod
+def get_instance() -> UserRolesRecipe:
+    if UserRolesRecipe.__instance is not None:
+        return UserRolesRecipe.__instance
+    raise_general_exception(
+        "Initialisation not done. Did you forget to call the SuperTokens.init or UserRoles.init function?"
+    )
+
+
+
+def init(skip_adding_roles_to_access_token: Optional[bool] = None, skip_adding_permissions_to_access_token: Optional[bool] = None, override: Union[InputOverrideConfig, None] = None) +
+
+
+
+ +Expand source code + +
@staticmethod
+def init(
+    skip_adding_roles_to_access_token: Optional[bool] = None,
+    skip_adding_permissions_to_access_token: Optional[bool] = None,
+    override: Union[InputOverrideConfig, None] = None,
+):
+    def func(app_info: AppInfo):
+        if UserRolesRecipe.__instance is None:
+            UserRolesRecipe.__instance = UserRolesRecipe(
+                UserRolesRecipe.recipe_id,
+                app_info,
+                skip_adding_roles_to_access_token,
+                skip_adding_permissions_to_access_token,
+                override,
+            )
+            return UserRolesRecipe.__instance
+        raise Exception(
+            None,
+            "UserRoles recipe has already been initialised. Please check your code for bugs.",
+        )
+
+    return func
+
+
+
+def reset() +
+
+
+
+ +Expand source code + +
@staticmethod
+def reset():
+    if ("SUPERTOKENS_ENV" not in environ) or (
+        environ["SUPERTOKENS_ENV"] != "testing"
+    ):
+        raise_general_exception("calling testing function in non testing env")
+    UserRolesRecipe.__instance = None
+
+
+
+

Methods

+
+
+def get_all_cors_headers(self) ‑> List[str] +
+
+
+
+ +Expand source code + +
def get_all_cors_headers(self) -> List[str]:
+    return []
+
+
+
+def get_apis_handled(self) ‑> List[APIHandled] +
+
+
+
+ +Expand source code + +
def get_apis_handled(self) -> List[APIHandled]:
+    return []
+
+
+
+async def handle_api_request(self, request_id: str, tenant_id: Optional[str], request: BaseRequest, path: NormalisedURLPath, method: str, response: BaseResponse, user_context: Dict[str, Any]) ‑> Optional[BaseResponse] +
+
+
+
+ +Expand source code + +
async def handle_api_request(
+    self,
+    request_id: str,
+    tenant_id: Optional[str],
+    request: BaseRequest,
+    path: NormalisedURLPath,
+    method: str,
+    response: BaseResponse,
+    user_context: Dict[str, Any],
+) -> Union[BaseResponse, None]:
+    raise Exception("Should never come here")
+
+
+
+async def handle_error(self, request: BaseRequest, err: SuperTokensError, response: BaseResponse, user_context: Dict[str, Any]) ‑> BaseResponse +
+
+
+
+ +Expand source code + +
async def handle_error(
+    self,
+    request: BaseRequest,
+    err: SuperTokensError,
+    response: BaseResponse,
+    user_context: Dict[str, Any],
+) -> BaseResponse:
+    raise err
+
+
+
+def is_error_from_this_recipe_based_on_instance(self, err: Exception) ‑> bool +
+
+
+
+ +Expand source code + +
def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
+    return isinstance(err, SuperTokensError) and (
+        isinstance(err, SuperTokensUserRolesError)
+    )
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/userroles/recipe_implementation.html b/html/supertokens_python/recipe/userroles/recipe_implementation.html new file mode 100644 index 000000000..cdb6d6f7f --- /dev/null +++ b/html/supertokens_python/recipe/userroles/recipe_implementation.html @@ -0,0 +1,641 @@ + + + + + + +supertokens_python.recipe.userroles.recipe_implementation API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.userroles.recipe_implementation

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+
+from typing import Any, Dict, List, Union
+
+from supertokens_python.normalised_url_path import NormalisedURLPath
+from supertokens_python.querier import Querier
+
+from .interfaces import (
+    AddRoleToUserOkResult,
+    CreateNewRoleOrAddPermissionsOkResult,
+    DeleteRoleOkResult,
+    GetAllRolesOkResult,
+    GetPermissionsForRoleOkResult,
+    GetRolesForUserOkResult,
+    GetRolesThatHavePermissionOkResult,
+    GetUsersThatHaveRoleOkResult,
+    RecipeInterface,
+    RemovePermissionsFromRoleOkResult,
+    RemoveUserRoleOkResult,
+    UnknownRoleError,
+)
+
+
+class RecipeImplementation(RecipeInterface):
+    def __init__(self, querier: Querier):
+        super().__init__()
+        self.querier = querier
+
+    async def add_role_to_user(
+        self,
+        user_id: str,
+        role: str,
+        tenant_id: str,
+        user_context: Dict[str, Any],
+    ) -> Union[AddRoleToUserOkResult, UnknownRoleError]:
+        params = {"userId": user_id, "role": role}
+        response = await self.querier.send_put_request(
+            NormalisedURLPath(f"{tenant_id}/recipe/user/role"),
+            params,
+            user_context=user_context,
+        )
+        if response["status"] == "OK":
+            return AddRoleToUserOkResult(
+                did_user_already_have_role=response["didUserAlreadyHaveRole"]
+            )
+        return UnknownRoleError()
+
+    async def remove_user_role(
+        self,
+        user_id: str,
+        role: str,
+        tenant_id: str,
+        user_context: Dict[str, Any],
+    ) -> Union[RemoveUserRoleOkResult, UnknownRoleError]:
+        params = {"userId": user_id, "role": role}
+        response = await self.querier.send_post_request(
+            NormalisedURLPath(f"{tenant_id}/recipe/user/role/remove"),
+            params,
+            user_context=user_context,
+        )
+        if response["status"] == "OK":
+            return RemoveUserRoleOkResult(
+                did_user_have_role=response["didUserHaveRole"]
+            )
+        return UnknownRoleError()
+
+    async def get_roles_for_user(
+        self, user_id: str, tenant_id: str, user_context: Dict[str, Any]
+    ) -> GetRolesForUserOkResult:
+        params = {"userId": user_id}
+        response = await self.querier.send_get_request(
+            NormalisedURLPath(f"{tenant_id}/recipe/user/roles"),
+            params,
+            user_context=user_context,
+        )
+        return GetRolesForUserOkResult(roles=response["roles"])
+
+    async def get_users_that_have_role(
+        self, role: str, tenant_id: str, user_context: Dict[str, Any]
+    ) -> Union[GetUsersThatHaveRoleOkResult, UnknownRoleError]:
+        params = {"role": role}
+        response = await self.querier.send_get_request(
+            NormalisedURLPath(f"{tenant_id}/recipe/role/users"),
+            params,
+            user_context=user_context,
+        )
+        if response["status"] == "OK":
+            return GetUsersThatHaveRoleOkResult(users=response["users"])
+        return UnknownRoleError()
+
+    async def create_new_role_or_add_permissions(
+        self, role: str, permissions: List[str], user_context: Dict[str, Any]
+    ) -> CreateNewRoleOrAddPermissionsOkResult:
+        params = {"role": role, "permissions": permissions}
+        response = await self.querier.send_put_request(
+            NormalisedURLPath("/recipe/role"),
+            params,
+            user_context=user_context,
+        )
+        return CreateNewRoleOrAddPermissionsOkResult(
+            created_new_role=response["createdNewRole"]
+        )
+
+    async def get_permissions_for_role(
+        self, role: str, user_context: Dict[str, Any]
+    ) -> Union[GetPermissionsForRoleOkResult, UnknownRoleError]:
+        params = {"role": role}
+        response = await self.querier.send_get_request(
+            NormalisedURLPath("/recipe/role/permissions"),
+            params,
+            user_context=user_context,
+        )
+        if response["status"] == "OK":
+            return GetPermissionsForRoleOkResult(permissions=response["permissions"])
+        return UnknownRoleError()
+
+    async def remove_permissions_from_role(
+        self, role: str, permissions: List[str], user_context: Dict[str, Any]
+    ) -> Union[RemovePermissionsFromRoleOkResult, UnknownRoleError]:
+        params = {"role": role, "permissions": permissions}
+        response = await self.querier.send_post_request(
+            NormalisedURLPath("/recipe/role/permissions/remove"),
+            params,
+            user_context=user_context,
+        )
+        if response["status"] == "OK":
+            return RemovePermissionsFromRoleOkResult()
+        return UnknownRoleError()
+
+    async def get_roles_that_have_permission(
+        self, permission: str, user_context: Dict[str, Any]
+    ) -> GetRolesThatHavePermissionOkResult:
+        params = {"permission": permission}
+        response = await self.querier.send_get_request(
+            NormalisedURLPath("/recipe/permission/roles"),
+            params,
+            user_context=user_context,
+        )
+        return GetRolesThatHavePermissionOkResult(roles=response["roles"])
+
+    async def delete_role(
+        self, role: str, user_context: Dict[str, Any]
+    ) -> DeleteRoleOkResult:
+        params = {"role": role}
+        response = await self.querier.send_post_request(
+            NormalisedURLPath("/recipe/role/remove"),
+            params,
+            user_context=user_context,
+        )
+        return DeleteRoleOkResult(did_role_exist=response["didRoleExist"])
+
+    async def get_all_roles(self, user_context: Dict[str, Any]) -> GetAllRolesOkResult:
+        params = {}
+        response = await self.querier.send_get_request(
+            NormalisedURLPath("/recipe/roles"),
+            params,
+            user_context=user_context,
+        )
+        return GetAllRolesOkResult(roles=response["roles"])
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class RecipeImplementation +(querier: Querier) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class RecipeImplementation(RecipeInterface):
+    def __init__(self, querier: Querier):
+        super().__init__()
+        self.querier = querier
+
+    async def add_role_to_user(
+        self,
+        user_id: str,
+        role: str,
+        tenant_id: str,
+        user_context: Dict[str, Any],
+    ) -> Union[AddRoleToUserOkResult, UnknownRoleError]:
+        params = {"userId": user_id, "role": role}
+        response = await self.querier.send_put_request(
+            NormalisedURLPath(f"{tenant_id}/recipe/user/role"),
+            params,
+            user_context=user_context,
+        )
+        if response["status"] == "OK":
+            return AddRoleToUserOkResult(
+                did_user_already_have_role=response["didUserAlreadyHaveRole"]
+            )
+        return UnknownRoleError()
+
+    async def remove_user_role(
+        self,
+        user_id: str,
+        role: str,
+        tenant_id: str,
+        user_context: Dict[str, Any],
+    ) -> Union[RemoveUserRoleOkResult, UnknownRoleError]:
+        params = {"userId": user_id, "role": role}
+        response = await self.querier.send_post_request(
+            NormalisedURLPath(f"{tenant_id}/recipe/user/role/remove"),
+            params,
+            user_context=user_context,
+        )
+        if response["status"] == "OK":
+            return RemoveUserRoleOkResult(
+                did_user_have_role=response["didUserHaveRole"]
+            )
+        return UnknownRoleError()
+
+    async def get_roles_for_user(
+        self, user_id: str, tenant_id: str, user_context: Dict[str, Any]
+    ) -> GetRolesForUserOkResult:
+        params = {"userId": user_id}
+        response = await self.querier.send_get_request(
+            NormalisedURLPath(f"{tenant_id}/recipe/user/roles"),
+            params,
+            user_context=user_context,
+        )
+        return GetRolesForUserOkResult(roles=response["roles"])
+
+    async def get_users_that_have_role(
+        self, role: str, tenant_id: str, user_context: Dict[str, Any]
+    ) -> Union[GetUsersThatHaveRoleOkResult, UnknownRoleError]:
+        params = {"role": role}
+        response = await self.querier.send_get_request(
+            NormalisedURLPath(f"{tenant_id}/recipe/role/users"),
+            params,
+            user_context=user_context,
+        )
+        if response["status"] == "OK":
+            return GetUsersThatHaveRoleOkResult(users=response["users"])
+        return UnknownRoleError()
+
+    async def create_new_role_or_add_permissions(
+        self, role: str, permissions: List[str], user_context: Dict[str, Any]
+    ) -> CreateNewRoleOrAddPermissionsOkResult:
+        params = {"role": role, "permissions": permissions}
+        response = await self.querier.send_put_request(
+            NormalisedURLPath("/recipe/role"),
+            params,
+            user_context=user_context,
+        )
+        return CreateNewRoleOrAddPermissionsOkResult(
+            created_new_role=response["createdNewRole"]
+        )
+
+    async def get_permissions_for_role(
+        self, role: str, user_context: Dict[str, Any]
+    ) -> Union[GetPermissionsForRoleOkResult, UnknownRoleError]:
+        params = {"role": role}
+        response = await self.querier.send_get_request(
+            NormalisedURLPath("/recipe/role/permissions"),
+            params,
+            user_context=user_context,
+        )
+        if response["status"] == "OK":
+            return GetPermissionsForRoleOkResult(permissions=response["permissions"])
+        return UnknownRoleError()
+
+    async def remove_permissions_from_role(
+        self, role: str, permissions: List[str], user_context: Dict[str, Any]
+    ) -> Union[RemovePermissionsFromRoleOkResult, UnknownRoleError]:
+        params = {"role": role, "permissions": permissions}
+        response = await self.querier.send_post_request(
+            NormalisedURLPath("/recipe/role/permissions/remove"),
+            params,
+            user_context=user_context,
+        )
+        if response["status"] == "OK":
+            return RemovePermissionsFromRoleOkResult()
+        return UnknownRoleError()
+
+    async def get_roles_that_have_permission(
+        self, permission: str, user_context: Dict[str, Any]
+    ) -> GetRolesThatHavePermissionOkResult:
+        params = {"permission": permission}
+        response = await self.querier.send_get_request(
+            NormalisedURLPath("/recipe/permission/roles"),
+            params,
+            user_context=user_context,
+        )
+        return GetRolesThatHavePermissionOkResult(roles=response["roles"])
+
+    async def delete_role(
+        self, role: str, user_context: Dict[str, Any]
+    ) -> DeleteRoleOkResult:
+        params = {"role": role}
+        response = await self.querier.send_post_request(
+            NormalisedURLPath("/recipe/role/remove"),
+            params,
+            user_context=user_context,
+        )
+        return DeleteRoleOkResult(did_role_exist=response["didRoleExist"])
+
+    async def get_all_roles(self, user_context: Dict[str, Any]) -> GetAllRolesOkResult:
+        params = {}
+        response = await self.querier.send_get_request(
+            NormalisedURLPath("/recipe/roles"),
+            params,
+            user_context=user_context,
+        )
+        return GetAllRolesOkResult(roles=response["roles"])
+
+

Ancestors

+ +

Methods

+
+
+async def add_role_to_user(self, user_id: str, role: str, tenant_id: str, user_context: Dict[str, Any]) ‑> Union[AddRoleToUserOkResultUnknownRoleError] +
+
+
+
+ +Expand source code + +
async def add_role_to_user(
+    self,
+    user_id: str,
+    role: str,
+    tenant_id: str,
+    user_context: Dict[str, Any],
+) -> Union[AddRoleToUserOkResult, UnknownRoleError]:
+    params = {"userId": user_id, "role": role}
+    response = await self.querier.send_put_request(
+        NormalisedURLPath(f"{tenant_id}/recipe/user/role"),
+        params,
+        user_context=user_context,
+    )
+    if response["status"] == "OK":
+        return AddRoleToUserOkResult(
+            did_user_already_have_role=response["didUserAlreadyHaveRole"]
+        )
+    return UnknownRoleError()
+
+
+
+async def create_new_role_or_add_permissions(self, role: str, permissions: List[str], user_context: Dict[str, Any]) ‑> CreateNewRoleOrAddPermissionsOkResult +
+
+
+
+ +Expand source code + +
async def create_new_role_or_add_permissions(
+    self, role: str, permissions: List[str], user_context: Dict[str, Any]
+) -> CreateNewRoleOrAddPermissionsOkResult:
+    params = {"role": role, "permissions": permissions}
+    response = await self.querier.send_put_request(
+        NormalisedURLPath("/recipe/role"),
+        params,
+        user_context=user_context,
+    )
+    return CreateNewRoleOrAddPermissionsOkResult(
+        created_new_role=response["createdNewRole"]
+    )
+
+
+
+async def delete_role(self, role: str, user_context: Dict[str, Any]) ‑> DeleteRoleOkResult +
+
+
+
+ +Expand source code + +
async def delete_role(
+    self, role: str, user_context: Dict[str, Any]
+) -> DeleteRoleOkResult:
+    params = {"role": role}
+    response = await self.querier.send_post_request(
+        NormalisedURLPath("/recipe/role/remove"),
+        params,
+        user_context=user_context,
+    )
+    return DeleteRoleOkResult(did_role_exist=response["didRoleExist"])
+
+
+
+async def get_all_roles(self, user_context: Dict[str, Any]) ‑> GetAllRolesOkResult +
+
+
+
+ +Expand source code + +
async def get_all_roles(self, user_context: Dict[str, Any]) -> GetAllRolesOkResult:
+    params = {}
+    response = await self.querier.send_get_request(
+        NormalisedURLPath("/recipe/roles"),
+        params,
+        user_context=user_context,
+    )
+    return GetAllRolesOkResult(roles=response["roles"])
+
+
+
+async def get_permissions_for_role(self, role: str, user_context: Dict[str, Any]) ‑> Union[GetPermissionsForRoleOkResultUnknownRoleError] +
+
+
+
+ +Expand source code + +
async def get_permissions_for_role(
+    self, role: str, user_context: Dict[str, Any]
+) -> Union[GetPermissionsForRoleOkResult, UnknownRoleError]:
+    params = {"role": role}
+    response = await self.querier.send_get_request(
+        NormalisedURLPath("/recipe/role/permissions"),
+        params,
+        user_context=user_context,
+    )
+    if response["status"] == "OK":
+        return GetPermissionsForRoleOkResult(permissions=response["permissions"])
+    return UnknownRoleError()
+
+
+
+async def get_roles_for_user(self, user_id: str, tenant_id: str, user_context: Dict[str, Any]) ‑> GetRolesForUserOkResult +
+
+
+
+ +Expand source code + +
async def get_roles_for_user(
+    self, user_id: str, tenant_id: str, user_context: Dict[str, Any]
+) -> GetRolesForUserOkResult:
+    params = {"userId": user_id}
+    response = await self.querier.send_get_request(
+        NormalisedURLPath(f"{tenant_id}/recipe/user/roles"),
+        params,
+        user_context=user_context,
+    )
+    return GetRolesForUserOkResult(roles=response["roles"])
+
+
+
+async def get_roles_that_have_permission(self, permission: str, user_context: Dict[str, Any]) ‑> GetRolesThatHavePermissionOkResult +
+
+
+
+ +Expand source code + +
async def get_roles_that_have_permission(
+    self, permission: str, user_context: Dict[str, Any]
+) -> GetRolesThatHavePermissionOkResult:
+    params = {"permission": permission}
+    response = await self.querier.send_get_request(
+        NormalisedURLPath("/recipe/permission/roles"),
+        params,
+        user_context=user_context,
+    )
+    return GetRolesThatHavePermissionOkResult(roles=response["roles"])
+
+
+
+async def get_users_that_have_role(self, role: str, tenant_id: str, user_context: Dict[str, Any]) ‑> Union[GetUsersThatHaveRoleOkResultUnknownRoleError] +
+
+
+
+ +Expand source code + +
async def get_users_that_have_role(
+    self, role: str, tenant_id: str, user_context: Dict[str, Any]
+) -> Union[GetUsersThatHaveRoleOkResult, UnknownRoleError]:
+    params = {"role": role}
+    response = await self.querier.send_get_request(
+        NormalisedURLPath(f"{tenant_id}/recipe/role/users"),
+        params,
+        user_context=user_context,
+    )
+    if response["status"] == "OK":
+        return GetUsersThatHaveRoleOkResult(users=response["users"])
+    return UnknownRoleError()
+
+
+
+async def remove_permissions_from_role(self, role: str, permissions: List[str], user_context: Dict[str, Any]) ‑> Union[RemovePermissionsFromRoleOkResultUnknownRoleError] +
+
+
+
+ +Expand source code + +
async def remove_permissions_from_role(
+    self, role: str, permissions: List[str], user_context: Dict[str, Any]
+) -> Union[RemovePermissionsFromRoleOkResult, UnknownRoleError]:
+    params = {"role": role, "permissions": permissions}
+    response = await self.querier.send_post_request(
+        NormalisedURLPath("/recipe/role/permissions/remove"),
+        params,
+        user_context=user_context,
+    )
+    if response["status"] == "OK":
+        return RemovePermissionsFromRoleOkResult()
+    return UnknownRoleError()
+
+
+
+async def remove_user_role(self, user_id: str, role: str, tenant_id: str, user_context: Dict[str, Any]) ‑> Union[RemoveUserRoleOkResultUnknownRoleError] +
+
+
+
+ +Expand source code + +
async def remove_user_role(
+    self,
+    user_id: str,
+    role: str,
+    tenant_id: str,
+    user_context: Dict[str, Any],
+) -> Union[RemoveUserRoleOkResult, UnknownRoleError]:
+    params = {"userId": user_id, "role": role}
+    response = await self.querier.send_post_request(
+        NormalisedURLPath(f"{tenant_id}/recipe/user/role/remove"),
+        params,
+        user_context=user_context,
+    )
+    if response["status"] == "OK":
+        return RemoveUserRoleOkResult(
+            did_user_have_role=response["didUserHaveRole"]
+        )
+    return UnknownRoleError()
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/userroles/syncio/index.html b/html/supertokens_python/recipe/userroles/syncio/index.html new file mode 100644 index 000000000..3760819c0 --- /dev/null +++ b/html/supertokens_python/recipe/userroles/syncio/index.html @@ -0,0 +1,375 @@ + + + + + + +supertokens_python.recipe.userroles.syncio API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.userroles.syncio

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from typing import Any, Dict, List, Union
+
+from supertokens_python.async_to_sync_wrapper import sync
+from supertokens_python.recipe.userroles.interfaces import (
+    AddRoleToUserOkResult,
+    CreateNewRoleOrAddPermissionsOkResult,
+    DeleteRoleOkResult,
+    GetAllRolesOkResult,
+    GetPermissionsForRoleOkResult,
+    GetRolesForUserOkResult,
+    GetRolesThatHavePermissionOkResult,
+    GetUsersThatHaveRoleOkResult,
+    RemovePermissionsFromRoleOkResult,
+    RemoveUserRoleOkResult,
+    UnknownRoleError,
+)
+
+
+def add_role_to_user(
+    tenant_id: str,
+    user_id: str,
+    role: str,
+    user_context: Union[Dict[str, Any], None] = None,
+) -> Union[AddRoleToUserOkResult, UnknownRoleError]:
+    from supertokens_python.recipe.userroles.asyncio import add_role_to_user
+
+    return sync(add_role_to_user(tenant_id, user_id, role, user_context))
+
+
+def remove_user_role(
+    tenant_id: str,
+    user_id: str,
+    role: str,
+    user_context: Union[Dict[str, Any], None] = None,
+) -> Union[RemoveUserRoleOkResult, UnknownRoleError]:
+    from supertokens_python.recipe.userroles.asyncio import remove_user_role
+
+    return sync(remove_user_role(tenant_id, user_id, role, user_context))
+
+
+def get_roles_for_user(
+    tenant_id: str, user_id: str, user_context: Union[Dict[str, Any], None] = None
+) -> GetRolesForUserOkResult:
+    from supertokens_python.recipe.userroles.asyncio import get_roles_for_user
+
+    return sync(get_roles_for_user(tenant_id, user_id, user_context))
+
+
+def get_users_that_have_role(
+    tenant_id: str, role: str, user_context: Union[Dict[str, Any], None] = None
+) -> Union[GetUsersThatHaveRoleOkResult, UnknownRoleError]:
+    from supertokens_python.recipe.userroles.asyncio import get_users_that_have_role
+
+    return sync(get_users_that_have_role(tenant_id, role, user_context))
+
+
+def create_new_role_or_add_permissions(
+    role: str, permissions: List[str], user_context: Union[Dict[str, Any], None] = None
+) -> CreateNewRoleOrAddPermissionsOkResult:
+    from supertokens_python.recipe.userroles.asyncio import (
+        create_new_role_or_add_permissions,
+    )
+
+    return sync(create_new_role_or_add_permissions(role, permissions, user_context))
+
+
+def get_permissions_for_role(
+    role: str, user_context: Union[Dict[str, Any], None] = None
+) -> Union[GetPermissionsForRoleOkResult, UnknownRoleError]:
+    from supertokens_python.recipe.userroles.asyncio import get_permissions_for_role
+
+    return sync(get_permissions_for_role(role, user_context))
+
+
+def remove_permissions_from_role(
+    role: str, permissions: List[str], user_context: Union[Dict[str, Any], None] = None
+) -> Union[RemovePermissionsFromRoleOkResult, UnknownRoleError]:
+    from supertokens_python.recipe.userroles.asyncio import remove_permissions_from_role
+
+    return sync(remove_permissions_from_role(role, permissions, user_context))
+
+
+def get_roles_that_have_permission(
+    permission: str, user_context: Union[Dict[str, Any], None] = None
+) -> GetRolesThatHavePermissionOkResult:
+    from supertokens_python.recipe.userroles.asyncio import (
+        get_roles_that_have_permission,
+    )
+
+    return sync(get_roles_that_have_permission(permission, user_context))
+
+
+def delete_role(
+    role: str, user_context: Union[Dict[str, Any], None] = None
+) -> DeleteRoleOkResult:
+    from supertokens_python.recipe.userroles.asyncio import delete_role
+
+    return sync(delete_role(role, user_context))
+
+
+def get_all_roles(
+    user_context: Union[Dict[str, Any], None] = None
+) -> GetAllRolesOkResult:
+    from supertokens_python.recipe.userroles.asyncio import get_all_roles
+
+    return sync(get_all_roles(user_context))
+
+
+
+
+
+
+
+

Functions

+
+
+def add_role_to_user(tenant_id: str, user_id: str, role: str, user_context: Optional[Dict[str, Any]] = None) ‑> Union[AddRoleToUserOkResultUnknownRoleError] +
+
+
+
+ +Expand source code + +
def add_role_to_user(
+    tenant_id: str,
+    user_id: str,
+    role: str,
+    user_context: Union[Dict[str, Any], None] = None,
+) -> Union[AddRoleToUserOkResult, UnknownRoleError]:
+    from supertokens_python.recipe.userroles.asyncio import add_role_to_user
+
+    return sync(add_role_to_user(tenant_id, user_id, role, user_context))
+
+
+
+def create_new_role_or_add_permissions(role: str, permissions: List[str], user_context: Optional[Dict[str, Any]] = None) ‑> CreateNewRoleOrAddPermissionsOkResult +
+
+
+
+ +Expand source code + +
def create_new_role_or_add_permissions(
+    role: str, permissions: List[str], user_context: Union[Dict[str, Any], None] = None
+) -> CreateNewRoleOrAddPermissionsOkResult:
+    from supertokens_python.recipe.userroles.asyncio import (
+        create_new_role_or_add_permissions,
+    )
+
+    return sync(create_new_role_or_add_permissions(role, permissions, user_context))
+
+
+
+def delete_role(role: str, user_context: Optional[Dict[str, Any]] = None) ‑> DeleteRoleOkResult +
+
+
+
+ +Expand source code + +
def delete_role(
+    role: str, user_context: Union[Dict[str, Any], None] = None
+) -> DeleteRoleOkResult:
+    from supertokens_python.recipe.userroles.asyncio import delete_role
+
+    return sync(delete_role(role, user_context))
+
+
+
+def get_all_roles(user_context: Optional[Dict[str, Any]] = None) ‑> GetAllRolesOkResult +
+
+
+
+ +Expand source code + +
def get_all_roles(
+    user_context: Union[Dict[str, Any], None] = None
+) -> GetAllRolesOkResult:
+    from supertokens_python.recipe.userroles.asyncio import get_all_roles
+
+    return sync(get_all_roles(user_context))
+
+
+
+def get_permissions_for_role(role: str, user_context: Optional[Dict[str, Any]] = None) ‑> Union[GetPermissionsForRoleOkResultUnknownRoleError] +
+
+
+
+ +Expand source code + +
def get_permissions_for_role(
+    role: str, user_context: Union[Dict[str, Any], None] = None
+) -> Union[GetPermissionsForRoleOkResult, UnknownRoleError]:
+    from supertokens_python.recipe.userroles.asyncio import get_permissions_for_role
+
+    return sync(get_permissions_for_role(role, user_context))
+
+
+
+def get_roles_for_user(tenant_id: str, user_id: str, user_context: Optional[Dict[str, Any]] = None) ‑> GetRolesForUserOkResult +
+
+
+
+ +Expand source code + +
def get_roles_for_user(
+    tenant_id: str, user_id: str, user_context: Union[Dict[str, Any], None] = None
+) -> GetRolesForUserOkResult:
+    from supertokens_python.recipe.userroles.asyncio import get_roles_for_user
+
+    return sync(get_roles_for_user(tenant_id, user_id, user_context))
+
+
+
+def get_roles_that_have_permission(permission: str, user_context: Optional[Dict[str, Any]] = None) ‑> GetRolesThatHavePermissionOkResult +
+
+
+
+ +Expand source code + +
def get_roles_that_have_permission(
+    permission: str, user_context: Union[Dict[str, Any], None] = None
+) -> GetRolesThatHavePermissionOkResult:
+    from supertokens_python.recipe.userroles.asyncio import (
+        get_roles_that_have_permission,
+    )
+
+    return sync(get_roles_that_have_permission(permission, user_context))
+
+
+
+def get_users_that_have_role(tenant_id: str, role: str, user_context: Optional[Dict[str, Any]] = None) ‑> Union[GetUsersThatHaveRoleOkResultUnknownRoleError] +
+
+
+
+ +Expand source code + +
def get_users_that_have_role(
+    tenant_id: str, role: str, user_context: Union[Dict[str, Any], None] = None
+) -> Union[GetUsersThatHaveRoleOkResult, UnknownRoleError]:
+    from supertokens_python.recipe.userroles.asyncio import get_users_that_have_role
+
+    return sync(get_users_that_have_role(tenant_id, role, user_context))
+
+
+
+def remove_permissions_from_role(role: str, permissions: List[str], user_context: Optional[Dict[str, Any]] = None) ‑> Union[RemovePermissionsFromRoleOkResultUnknownRoleError] +
+
+
+
+ +Expand source code + +
def remove_permissions_from_role(
+    role: str, permissions: List[str], user_context: Union[Dict[str, Any], None] = None
+) -> Union[RemovePermissionsFromRoleOkResult, UnknownRoleError]:
+    from supertokens_python.recipe.userroles.asyncio import remove_permissions_from_role
+
+    return sync(remove_permissions_from_role(role, permissions, user_context))
+
+
+
+def remove_user_role(tenant_id: str, user_id: str, role: str, user_context: Optional[Dict[str, Any]] = None) ‑> Union[RemoveUserRoleOkResultUnknownRoleError] +
+
+
+
+ +Expand source code + +
def remove_user_role(
+    tenant_id: str,
+    user_id: str,
+    role: str,
+    user_context: Union[Dict[str, Any], None] = None,
+) -> Union[RemoveUserRoleOkResult, UnknownRoleError]:
+    from supertokens_python.recipe.userroles.asyncio import remove_user_role
+
+    return sync(remove_user_role(tenant_id, user_id, role, user_context))
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe/userroles/utils.html b/html/supertokens_python/recipe/userroles/utils.html new file mode 100644 index 000000000..41d2f37f5 --- /dev/null +++ b/html/supertokens_python/recipe/userroles/utils.html @@ -0,0 +1,229 @@ + + + + + + +supertokens_python.recipe.userroles.utils API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe.userroles.utils

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Callable, Union, Optional
+
+from supertokens_python.recipe.userroles.interfaces import APIInterface, RecipeInterface
+from supertokens_python.supertokens import AppInfo
+
+if TYPE_CHECKING:
+    from supertokens_python.recipe.userroles.recipe import UserRolesRecipe
+
+
+class InputOverrideConfig:
+    def __init__(
+        self,
+        functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None,
+        apis: Union[Callable[[APIInterface], APIInterface], None] = None,
+    ):
+        self.functions = functions
+        self.apis = apis
+
+
+class UserRolesConfig:
+    def __init__(
+        self,
+        skip_adding_roles_to_access_token: bool,
+        skip_adding_permissions_to_access_token: bool,
+        override: InputOverrideConfig,
+    ) -> None:
+        self.skip_adding_roles_to_access_token = skip_adding_roles_to_access_token
+        self.skip_adding_permissions_to_access_token = (
+            skip_adding_permissions_to_access_token
+        )
+        self.override = override
+
+
+def validate_and_normalise_user_input(
+    _recipe: UserRolesRecipe,
+    _app_info: AppInfo,
+    skip_adding_roles_to_access_token: Optional[bool] = None,
+    skip_adding_permissions_to_access_token: Optional[bool] = None,
+    override: Union[InputOverrideConfig, None] = None,
+) -> UserRolesConfig:
+    if override is not None and not isinstance(override, InputOverrideConfig):  # type: ignore
+        raise ValueError("override must be an instance of InputOverrideConfig or None")
+
+    if override is None:
+        override = InputOverrideConfig()
+
+    if skip_adding_roles_to_access_token is None:
+        skip_adding_roles_to_access_token = False
+    if skip_adding_permissions_to_access_token is None:
+        skip_adding_permissions_to_access_token = False
+
+    return UserRolesConfig(
+        skip_adding_roles_to_access_token=skip_adding_roles_to_access_token,
+        skip_adding_permissions_to_access_token=skip_adding_permissions_to_access_token,
+        override=override,
+    )
+
+
+
+
+
+
+
+

Functions

+
+
+def validate_and_normalise_user_input(_recipe: UserRolesRecipe, _app_info: AppInfo, skip_adding_roles_to_access_token: Optional[bool] = None, skip_adding_permissions_to_access_token: Optional[bool] = None, override: Union[InputOverrideConfig, None] = None) ‑> UserRolesConfig +
+
+
+
+ +Expand source code + +
def validate_and_normalise_user_input(
+    _recipe: UserRolesRecipe,
+    _app_info: AppInfo,
+    skip_adding_roles_to_access_token: Optional[bool] = None,
+    skip_adding_permissions_to_access_token: Optional[bool] = None,
+    override: Union[InputOverrideConfig, None] = None,
+) -> UserRolesConfig:
+    if override is not None and not isinstance(override, InputOverrideConfig):  # type: ignore
+        raise ValueError("override must be an instance of InputOverrideConfig or None")
+
+    if override is None:
+        override = InputOverrideConfig()
+
+    if skip_adding_roles_to_access_token is None:
+        skip_adding_roles_to_access_token = False
+    if skip_adding_permissions_to_access_token is None:
+        skip_adding_permissions_to_access_token = False
+
+    return UserRolesConfig(
+        skip_adding_roles_to_access_token=skip_adding_roles_to_access_token,
+        skip_adding_permissions_to_access_token=skip_adding_permissions_to_access_token,
+        override=override,
+    )
+
+
+
+
+
+

Classes

+
+
+class InputOverrideConfig +(functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None, apis: Union[Callable[[APIInterface], APIInterface], None] = None) +
+
+
+
+ +Expand source code + +
class InputOverrideConfig:
+    def __init__(
+        self,
+        functions: Union[Callable[[RecipeInterface], RecipeInterface], None] = None,
+        apis: Union[Callable[[APIInterface], APIInterface], None] = None,
+    ):
+        self.functions = functions
+        self.apis = apis
+
+
+
+class UserRolesConfig +(skip_adding_roles_to_access_token: bool, skip_adding_permissions_to_access_token: bool, override: InputOverrideConfig) +
+
+
+
+ +Expand source code + +
class UserRolesConfig:
+    def __init__(
+        self,
+        skip_adding_roles_to_access_token: bool,
+        skip_adding_permissions_to_access_token: bool,
+        override: InputOverrideConfig,
+    ) -> None:
+        self.skip_adding_roles_to_access_token = skip_adding_roles_to_access_token
+        self.skip_adding_permissions_to_access_token = (
+            skip_adding_permissions_to_access_token
+        )
+        self.override = override
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/recipe_module.html b/html/supertokens_python/recipe_module.html new file mode 100644 index 000000000..f91df4351 --- /dev/null +++ b/html/supertokens_python/recipe_module.html @@ -0,0 +1,580 @@ + + + + + + +supertokens_python.recipe_module API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.recipe_module

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from __future__ import annotations
+
+import abc
+import re
+from typing import TYPE_CHECKING, List, Union, Optional, Dict, Any, Callable, Awaitable
+from typing_extensions import Literal
+
+from .framework.response import BaseResponse
+
+if TYPE_CHECKING:
+    from supertokens_python.framework.request import BaseRequest
+    from .supertokens import AppInfo
+
+
+from .exceptions import SuperTokensError
+from .normalised_url_path import NormalisedURLPath
+
+
+class ApiIdWithTenantId:
+    def __init__(self, api_id: str, tenant_id: str):
+        self.api_id = api_id
+        self.tenant_id = tenant_id
+
+
+class RecipeModule(abc.ABC):
+    get_tenant_id: Optional[Callable[[str, Dict[str, Any]], Awaitable[str]]] = None
+
+    def __init__(self, recipe_id: str, app_info: AppInfo):
+        self.recipe_id = recipe_id
+        self.app_info = app_info
+
+    def get_recipe_id(self):
+        return self.recipe_id
+
+    def get_app_info(self):
+        return self.app_info
+
+    async def return_api_id_if_can_handle_request(
+        self, path: NormalisedURLPath, method: str, user_context: Dict[str, Any]
+    ) -> Union[ApiIdWithTenantId, None]:
+        from supertokens_python.recipe.multitenancy.constants import DEFAULT_TENANT_ID
+
+        apis_handled = self.get_apis_handled()
+
+        base_path_str = self.app_info.api_base_path.get_as_string_dangerous()
+        path_str = path.get_as_string_dangerous()
+        regex = rf"^{base_path_str}(?:/([a-zA-Z0-9-]+))?(/.*)$"
+
+        match = re.match(regex, path_str)
+        match_group_1 = match.group(1) if match is not None else None
+        match_group_2 = match.group(2) if match is not None else None
+
+        tenant_id: str = DEFAULT_TENANT_ID
+        remaining_path: Optional[NormalisedURLPath] = None
+
+        if (
+            match is not None
+            and isinstance(match_group_1, str)
+            and isinstance(match_group_2, str)
+        ):
+            tenant_id = match_group_1
+            remaining_path = NormalisedURLPath(match_group_2)
+
+        assert RecipeModule.get_tenant_id is not None
+        assert callable(RecipeModule.get_tenant_id)
+
+        for current_api in apis_handled:
+            if not current_api.disabled and current_api.method == method:
+                if self.app_info.api_base_path.append(
+                    current_api.path_without_api_base_path
+                ).equals(path):
+                    final_tenant_id = await RecipeModule.get_tenant_id(  # pylint: disable=not-callable
+                        DEFAULT_TENANT_ID, user_context
+                    )
+                    return ApiIdWithTenantId(current_api.request_id, final_tenant_id)
+
+                if remaining_path is not None and self.app_info.api_base_path.append(
+                    current_api.path_without_api_base_path
+                ).equals(self.app_info.api_base_path.append(remaining_path)):
+                    final_tenant_id = await RecipeModule.get_tenant_id(  # pylint: disable=not-callable
+                        tenant_id, user_context
+                    )
+                    return ApiIdWithTenantId(current_api.request_id, final_tenant_id)
+
+        return None
+
+    @abc.abstractmethod
+    def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
+        pass
+
+    @abc.abstractmethod
+    def get_apis_handled(self) -> List[APIHandled]:
+        pass
+
+    @abc.abstractmethod
+    async def handle_api_request(
+        self,
+        request_id: str,
+        tenant_id: str,
+        request: BaseRequest,
+        path: NormalisedURLPath,
+        method: str,
+        response: BaseResponse,
+        user_context: Dict[str, Any],
+    ) -> Union[BaseResponse, None]:
+        pass
+
+    @abc.abstractmethod
+    async def handle_error(
+        self,
+        request: BaseRequest,
+        err: SuperTokensError,
+        response: BaseResponse,
+        user_context: Dict[str, Any],
+    ) -> BaseResponse:
+        pass
+
+    @abc.abstractmethod
+    def get_all_cors_headers(self) -> List[str]:
+        pass
+
+
+class APIHandled:
+    def __init__(
+        self,
+        path_without_api_base_path: NormalisedURLPath,
+        method: Literal["post", "get", "delete", "put", "options", "trace"],
+        request_id: str,
+        disabled: bool,
+    ):
+        self.path_without_api_base_path = path_without_api_base_path
+        self.method = method
+        self.request_id = request_id
+        self.disabled = disabled
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class APIHandled +(path_without_api_base_path: NormalisedURLPath, method: "Literal[('post', 'get', 'delete', 'put', 'options', 'trace')]", request_id: str, disabled: bool) +
+
+
+
+ +Expand source code + +
class APIHandled:
+    def __init__(
+        self,
+        path_without_api_base_path: NormalisedURLPath,
+        method: Literal["post", "get", "delete", "put", "options", "trace"],
+        request_id: str,
+        disabled: bool,
+    ):
+        self.path_without_api_base_path = path_without_api_base_path
+        self.method = method
+        self.request_id = request_id
+        self.disabled = disabled
+
+
+
+class ApiIdWithTenantId +(api_id: str, tenant_id: str) +
+
+
+
+ +Expand source code + +
class ApiIdWithTenantId:
+    def __init__(self, api_id: str, tenant_id: str):
+        self.api_id = api_id
+        self.tenant_id = tenant_id
+
+
+
+class RecipeModule +(recipe_id: str, app_info: AppInfo) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class RecipeModule(abc.ABC):
+    get_tenant_id: Optional[Callable[[str, Dict[str, Any]], Awaitable[str]]] = None
+
+    def __init__(self, recipe_id: str, app_info: AppInfo):
+        self.recipe_id = recipe_id
+        self.app_info = app_info
+
+    def get_recipe_id(self):
+        return self.recipe_id
+
+    def get_app_info(self):
+        return self.app_info
+
+    async def return_api_id_if_can_handle_request(
+        self, path: NormalisedURLPath, method: str, user_context: Dict[str, Any]
+    ) -> Union[ApiIdWithTenantId, None]:
+        from supertokens_python.recipe.multitenancy.constants import DEFAULT_TENANT_ID
+
+        apis_handled = self.get_apis_handled()
+
+        base_path_str = self.app_info.api_base_path.get_as_string_dangerous()
+        path_str = path.get_as_string_dangerous()
+        regex = rf"^{base_path_str}(?:/([a-zA-Z0-9-]+))?(/.*)$"
+
+        match = re.match(regex, path_str)
+        match_group_1 = match.group(1) if match is not None else None
+        match_group_2 = match.group(2) if match is not None else None
+
+        tenant_id: str = DEFAULT_TENANT_ID
+        remaining_path: Optional[NormalisedURLPath] = None
+
+        if (
+            match is not None
+            and isinstance(match_group_1, str)
+            and isinstance(match_group_2, str)
+        ):
+            tenant_id = match_group_1
+            remaining_path = NormalisedURLPath(match_group_2)
+
+        assert RecipeModule.get_tenant_id is not None
+        assert callable(RecipeModule.get_tenant_id)
+
+        for current_api in apis_handled:
+            if not current_api.disabled and current_api.method == method:
+                if self.app_info.api_base_path.append(
+                    current_api.path_without_api_base_path
+                ).equals(path):
+                    final_tenant_id = await RecipeModule.get_tenant_id(  # pylint: disable=not-callable
+                        DEFAULT_TENANT_ID, user_context
+                    )
+                    return ApiIdWithTenantId(current_api.request_id, final_tenant_id)
+
+                if remaining_path is not None and self.app_info.api_base_path.append(
+                    current_api.path_without_api_base_path
+                ).equals(self.app_info.api_base_path.append(remaining_path)):
+                    final_tenant_id = await RecipeModule.get_tenant_id(  # pylint: disable=not-callable
+                        tenant_id, user_context
+                    )
+                    return ApiIdWithTenantId(current_api.request_id, final_tenant_id)
+
+        return None
+
+    @abc.abstractmethod
+    def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
+        pass
+
+    @abc.abstractmethod
+    def get_apis_handled(self) -> List[APIHandled]:
+        pass
+
+    @abc.abstractmethod
+    async def handle_api_request(
+        self,
+        request_id: str,
+        tenant_id: str,
+        request: BaseRequest,
+        path: NormalisedURLPath,
+        method: str,
+        response: BaseResponse,
+        user_context: Dict[str, Any],
+    ) -> Union[BaseResponse, None]:
+        pass
+
+    @abc.abstractmethod
+    async def handle_error(
+        self,
+        request: BaseRequest,
+        err: SuperTokensError,
+        response: BaseResponse,
+        user_context: Dict[str, Any],
+    ) -> BaseResponse:
+        pass
+
+    @abc.abstractmethod
+    def get_all_cors_headers(self) -> List[str]:
+        pass
+
+

Ancestors

+
    +
  • abc.ABC
  • +
+

Subclasses

+ +

Class variables

+
+
var get_tenant_id : Optional[Callable[[str, Dict[str, Any]], Awaitable[str]]]
+
+
+
+
+

Methods

+
+
+def get_all_cors_headers(self) ‑> List[str] +
+
+
+
+ +Expand source code + +
@abc.abstractmethod
+def get_all_cors_headers(self) -> List[str]:
+    pass
+
+
+
+def get_apis_handled(self) ‑> List[APIHandled] +
+
+
+
+ +Expand source code + +
@abc.abstractmethod
+def get_apis_handled(self) -> List[APIHandled]:
+    pass
+
+
+
+def get_app_info(self) +
+
+
+
+ +Expand source code + +
def get_app_info(self):
+    return self.app_info
+
+
+
+def get_recipe_id(self) +
+
+
+
+ +Expand source code + +
def get_recipe_id(self):
+    return self.recipe_id
+
+
+
+async def handle_api_request(self, request_id: str, tenant_id: str, request: BaseRequest, path: NormalisedURLPath, method: str, response: BaseResponse, user_context: Dict[str, Any]) ‑> Union[BaseResponse, None] +
+
+
+
+ +Expand source code + +
@abc.abstractmethod
+async def handle_api_request(
+    self,
+    request_id: str,
+    tenant_id: str,
+    request: BaseRequest,
+    path: NormalisedURLPath,
+    method: str,
+    response: BaseResponse,
+    user_context: Dict[str, Any],
+) -> Union[BaseResponse, None]:
+    pass
+
+
+
+async def handle_error(self, request: BaseRequest, err: SuperTokensError, response: BaseResponse, user_context: Dict[str, Any]) ‑> BaseResponse +
+
+
+
+ +Expand source code + +
@abc.abstractmethod
+async def handle_error(
+    self,
+    request: BaseRequest,
+    err: SuperTokensError,
+    response: BaseResponse,
+    user_context: Dict[str, Any],
+) -> BaseResponse:
+    pass
+
+
+
+def is_error_from_this_recipe_based_on_instance(self, err: Exception) ‑> bool +
+
+
+
+ +Expand source code + +
@abc.abstractmethod
+def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
+    pass
+
+
+
+async def return_api_id_if_can_handle_request(self, path: NormalisedURLPath, method: str, user_context: Dict[str, Any]) ‑> Optional[ApiIdWithTenantId] +
+
+
+
+ +Expand source code + +
async def return_api_id_if_can_handle_request(
+    self, path: NormalisedURLPath, method: str, user_context: Dict[str, Any]
+) -> Union[ApiIdWithTenantId, None]:
+    from supertokens_python.recipe.multitenancy.constants import DEFAULT_TENANT_ID
+
+    apis_handled = self.get_apis_handled()
+
+    base_path_str = self.app_info.api_base_path.get_as_string_dangerous()
+    path_str = path.get_as_string_dangerous()
+    regex = rf"^{base_path_str}(?:/([a-zA-Z0-9-]+))?(/.*)$"
+
+    match = re.match(regex, path_str)
+    match_group_1 = match.group(1) if match is not None else None
+    match_group_2 = match.group(2) if match is not None else None
+
+    tenant_id: str = DEFAULT_TENANT_ID
+    remaining_path: Optional[NormalisedURLPath] = None
+
+    if (
+        match is not None
+        and isinstance(match_group_1, str)
+        and isinstance(match_group_2, str)
+    ):
+        tenant_id = match_group_1
+        remaining_path = NormalisedURLPath(match_group_2)
+
+    assert RecipeModule.get_tenant_id is not None
+    assert callable(RecipeModule.get_tenant_id)
+
+    for current_api in apis_handled:
+        if not current_api.disabled and current_api.method == method:
+            if self.app_info.api_base_path.append(
+                current_api.path_without_api_base_path
+            ).equals(path):
+                final_tenant_id = await RecipeModule.get_tenant_id(  # pylint: disable=not-callable
+                    DEFAULT_TENANT_ID, user_context
+                )
+                return ApiIdWithTenantId(current_api.request_id, final_tenant_id)
+
+            if remaining_path is not None and self.app_info.api_base_path.append(
+                current_api.path_without_api_base_path
+            ).equals(self.app_info.api_base_path.append(remaining_path)):
+                final_tenant_id = await RecipeModule.get_tenant_id(  # pylint: disable=not-callable
+                    tenant_id, user_context
+                )
+                return ApiIdWithTenantId(current_api.request_id, final_tenant_id)
+
+    return None
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/supertokens.html b/html/supertokens_python/supertokens.html new file mode 100644 index 000000000..1956fd8d7 --- /dev/null +++ b/html/supertokens_python/supertokens.html @@ -0,0 +1,2276 @@ + + + + + + +supertokens_python.supertokens API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.supertokens

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from __future__ import annotations
+
+from os import environ
+from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Set, Union, Tuple
+
+from typing_extensions import Literal
+
+from supertokens_python.logger import (
+    get_maybe_none_as_str,
+    log_debug_message,
+    enable_debug_logging,
+)
+
+
+from .constants import FDI_KEY_HEADER, RID_KEY_HEADER, USER_COUNT, USER_DELETE, USERS
+from .exceptions import SuperTokensError
+from .interfaces import (
+    CreateUserIdMappingOkResult,
+    DeleteUserIdMappingOkResult,
+    GetUserIdMappingOkResult,
+    UnknownMappingError,
+    UnknownSupertokensUserIDError,
+    UpdateOrDeleteUserIdMappingInfoOkResult,
+    UserIdMappingAlreadyExistsError,
+    UserIDTypes,
+)
+from .normalised_url_domain import NormalisedURLDomain
+from .normalised_url_path import NormalisedURLPath
+from .post_init_callbacks import PostSTInitCallbacks
+from .querier import Querier
+from .types import ThirdPartyInfo, User, UsersResponse
+from .utils import (
+    get_rid_from_header,
+    get_top_level_domain_for_same_site_resolution,
+    is_version_gte,
+    normalise_http_method,
+    send_non_200_response_with_message,
+)
+
+if TYPE_CHECKING:
+    from .recipe_module import RecipeModule
+    from supertokens_python.framework.request import BaseRequest
+    from supertokens_python.framework.response import BaseResponse
+    from supertokens_python.recipe.session import SessionContainer
+
+import json
+
+from .exceptions import BadInputError, GeneralError, raise_general_exception
+
+
+class SupertokensConfig:
+    def __init__(
+        self,
+        connection_uri: str,
+        api_key: Union[str, None] = None,
+        network_interceptor: Optional[
+            Callable[
+                [
+                    str,
+                    str,
+                    Dict[str, Any],
+                    Optional[Dict[str, Any]],
+                    Optional[Dict[str, Any]],
+                    Optional[Dict[str, Any]],
+                ],
+                Tuple[
+                    str,
+                    str,
+                    Dict[str, Any],
+                    Optional[Dict[str, Any]],
+                    Optional[Dict[str, Any]],
+                ],
+            ]
+        ] = None,
+        disable_core_call_cache: bool = False,
+    ):  # We keep this = None here because this is directly used by the user.
+        self.connection_uri = connection_uri
+        self.api_key = api_key
+        self.network_interceptor = network_interceptor
+        self.disable_core_call_cache = disable_core_call_cache
+
+
+class Host:
+    def __init__(self, domain: NormalisedURLDomain, base_path: NormalisedURLPath):
+        self.domain = domain
+        self.base_path = base_path
+
+
+class InputAppInfo:
+    def __init__(
+        self,
+        app_name: str,
+        api_domain: str,
+        api_gateway_path: str = "",
+        api_base_path: str = "/auth",
+        website_base_path: str = "/auth",
+        website_domain: Optional[str] = None,
+        origin: Optional[
+            Union[str, Callable[[Optional[BaseRequest], Dict[str, Any]], str]]
+        ] = None,
+    ):
+        self.app_name = app_name
+        self.api_gateway_path = api_gateway_path
+        self.api_domain = api_domain
+        self.website_domain = website_domain
+        self.origin = origin
+        self.api_base_path = api_base_path
+        self.website_base_path = website_base_path
+
+
+class AppInfo:
+    def __init__(
+        self,
+        app_name: str,
+        api_domain: str,
+        website_domain: Optional[str],
+        framework: Literal["fastapi", "flask", "django"],
+        api_gateway_path: str,
+        api_base_path: str,
+        website_base_path: str,
+        mode: Union[Literal["asgi", "wsgi"], None],
+        origin: Optional[
+            Union[str, Callable[[Optional[BaseRequest], Dict[str, Any]], str]]
+        ],
+    ):
+        self.app_name = app_name
+        self.api_gateway_path = NormalisedURLPath(api_gateway_path)
+        self.api_domain = NormalisedURLDomain(api_domain)
+        self.top_level_api_domain = get_top_level_domain_for_same_site_resolution(
+            self.api_domain.get_as_string_dangerous()
+        )
+        if website_domain is None and origin is None:
+            raise_general_exception(
+                "Please provide at least one of website_domain or origin"
+            )
+        self.__origin = origin
+        self.__website_domain = website_domain
+        self.api_base_path = self.api_gateway_path.append(
+            NormalisedURLPath(api_base_path)
+        )
+        self.website_base_path = NormalisedURLPath(website_base_path)
+        if mode is not None:
+            self.mode = mode
+        elif framework == "fastapi":
+            mode = "asgi"
+        else:
+            mode = "wsgi"
+        self.framework = framework
+        self.mode = mode
+
+    def get_top_level_website_domain(
+        self, request: Optional[BaseRequest], user_context: Dict[str, Any]
+    ) -> str:
+        return get_top_level_domain_for_same_site_resolution(
+            self.get_origin(request, user_context).get_as_string_dangerous()
+        )
+
+    def get_origin(self, request: Optional[BaseRequest], user_context: Dict[str, Any]):
+        origin = self.__origin
+        if origin is None:
+            origin = self.__website_domain
+
+        # This should not be possible because we check for either origin or websiteDomain above
+        if origin is None:
+            raise_general_exception("should never come here")
+
+        if callable(origin):
+            origin = origin(request, user_context)
+
+        return NormalisedURLDomain(origin)
+
+    def toJSON(self):
+        def defaultImpl(o: Any):
+            if isinstance(o, (NormalisedURLDomain, NormalisedURLPath)):
+                return o.get_as_string_dangerous()
+            return o.__dict__
+
+        return json.dumps(self, default=defaultImpl, sort_keys=True, indent=4)
+
+
+def manage_session_post_response(
+    session: SessionContainer, response: BaseResponse, user_context: Dict[str, Any]
+):
+    # Something similar happens in handle_error of session/recipe.py
+    for mutator in session.response_mutators:
+        mutator(response, user_context)
+
+
+class Supertokens:
+    __instance = None
+
+    def __init__(
+        self,
+        app_info: InputAppInfo,
+        framework: Literal["fastapi", "flask", "django"],
+        supertokens_config: SupertokensConfig,
+        recipe_list: List[Callable[[AppInfo], RecipeModule]],
+        mode: Optional[Literal["asgi", "wsgi"]],
+        telemetry: Optional[bool],
+        debug: Optional[bool],
+    ):
+        if not isinstance(app_info, InputAppInfo):  # type: ignore
+            raise ValueError("app_info must be an instance of InputAppInfo")
+
+        self.app_info = AppInfo(
+            app_info.app_name,
+            app_info.api_domain,
+            app_info.website_domain,
+            framework,
+            app_info.api_gateway_path,
+            app_info.api_base_path,
+            app_info.website_base_path,
+            mode,
+            app_info.origin,
+        )
+        self.supertokens_config = supertokens_config
+        if debug is True:
+            enable_debug_logging()
+        self._telemetry_status: str = "NONE"
+        log_debug_message(
+            "Started SuperTokens with debug logging (supertokens.init called)"
+        )
+        log_debug_message("app_info: %s", self.app_info.toJSON())
+        log_debug_message("framework: %s", framework)
+        hosts = list(
+            map(
+                lambda h: Host(
+                    NormalisedURLDomain(h.strip()), NormalisedURLPath(h.strip())
+                ),
+                filter(lambda x: x != "", supertokens_config.connection_uri.split(";")),
+            )
+        )
+        Querier.init(
+            hosts,
+            supertokens_config.api_key,
+            supertokens_config.network_interceptor,
+            supertokens_config.disable_core_call_cache,
+        )
+
+        if len(recipe_list) == 0:
+            raise_general_exception(
+                "Please provide at least one recipe to the supertokens.init function call"
+            )
+
+        from supertokens_python.recipe.multitenancy.recipe import MultitenancyRecipe
+
+        multitenancy_found = False
+
+        def make_recipe(recipe: Callable[[AppInfo], RecipeModule]) -> RecipeModule:
+            nonlocal multitenancy_found
+            recipe_module = recipe(self.app_info)
+            if recipe_module.get_recipe_id() == MultitenancyRecipe.recipe_id:
+                multitenancy_found = True
+            return recipe_module
+
+        self.recipe_modules: List[RecipeModule] = list(map(make_recipe, recipe_list))
+
+        if not multitenancy_found:
+            recipe = MultitenancyRecipe.init()(self.app_info)
+            self.recipe_modules.append(recipe)
+
+        self.telemetry = (
+            telemetry
+            if telemetry is not None
+            else (environ.get("TEST_MODE") != "testing")
+        )
+
+    @staticmethod
+    def init(
+        app_info: InputAppInfo,
+        framework: Literal["fastapi", "flask", "django"],
+        supertokens_config: SupertokensConfig,
+        recipe_list: List[Callable[[AppInfo], RecipeModule]],
+        mode: Optional[Literal["asgi", "wsgi"]],
+        telemetry: Optional[bool],
+        debug: Optional[bool],
+    ):
+        if Supertokens.__instance is None:
+            Supertokens.__instance = Supertokens(
+                app_info,
+                framework,
+                supertokens_config,
+                recipe_list,
+                mode,
+                telemetry,
+                debug,
+            )
+            PostSTInitCallbacks.run_post_init_callbacks()
+
+    @staticmethod
+    def reset():
+        if ("SUPERTOKENS_ENV" not in environ) or (
+            environ["SUPERTOKENS_ENV"] != "testing"
+        ):
+            raise_general_exception("calling testing function in non testing env")
+        Querier.reset()
+        Supertokens.__instance = None
+
+    @staticmethod
+    def get_instance() -> Supertokens:
+        if Supertokens.__instance is not None:
+            return Supertokens.__instance
+        raise_general_exception(
+            "Initialisation not done. Did you forget to call the SuperTokens.init function?"
+        )
+
+    def get_all_cors_headers(self) -> List[str]:
+        headers_set: Set[str] = set()
+        headers_set.add(RID_KEY_HEADER)
+        headers_set.add(FDI_KEY_HEADER)
+        for recipe in self.recipe_modules:
+            headers = recipe.get_all_cors_headers()
+            for header in headers:
+                headers_set.add(header)
+
+        return list(headers_set)
+
+    async def get_user_count(  # pylint: disable=no-self-use
+        self,
+        include_recipe_ids: Union[None, List[str]],
+        tenant_id: Optional[str] = None,
+        user_context: Optional[Dict[str, Any]] = None,
+    ) -> int:
+        querier = Querier.get_instance(None)
+        include_recipe_ids_str = None
+        if include_recipe_ids is not None:
+            include_recipe_ids_str = ",".join(include_recipe_ids)
+
+        response = await querier.send_get_request(
+            NormalisedURLPath(f"/{tenant_id or 'public'}{USER_COUNT}"),
+            {
+                "includeRecipeIds": include_recipe_ids_str,
+                "includeAllTenants": tenant_id is None,
+            },
+            user_context=user_context,
+        )
+
+        return int(response["count"])
+
+    async def delete_user(  # pylint: disable=no-self-use
+        self,
+        user_id: str,
+        user_context: Optional[Dict[str, Any]],
+    ) -> None:
+        querier = Querier.get_instance(None)
+
+        cdi_version = await querier.get_api_version(user_context)
+
+        if is_version_gte(cdi_version, "2.10"):
+            await querier.send_post_request(
+                NormalisedURLPath(USER_DELETE),
+                {"userId": user_id},
+                user_context=user_context,
+            )
+
+            return None
+        raise_general_exception("Please upgrade the SuperTokens core to >= 3.7.0")
+
+    async def get_users(  # pylint: disable=no-self-use
+        self,
+        tenant_id: str,
+        time_joined_order: Literal["ASC", "DESC"],
+        limit: Union[int, None],
+        pagination_token: Union[str, None],
+        include_recipe_ids: Union[None, List[str]],
+        query: Union[Dict[str, str], None],
+        user_context: Optional[Dict[str, Any]],
+    ) -> UsersResponse:
+
+        querier = Querier.get_instance(None)
+        params = {"timeJoinedOrder": time_joined_order}
+        if limit is not None:
+            params = {"limit": limit, **params}
+        if pagination_token is not None:
+            params = {"paginationToken": pagination_token, **params}
+
+        include_recipe_ids_str = None
+        if include_recipe_ids is not None:
+            include_recipe_ids_str = ",".join(include_recipe_ids)
+            params = {"includeRecipeIds": include_recipe_ids_str, **params}
+
+        if query is not None:
+            params = {**params, **query}
+
+        response = await querier.send_get_request(
+            NormalisedURLPath(f"/{tenant_id}{USERS}"), params, user_context=user_context
+        )
+        next_pagination_token = None
+        if "nextPaginationToken" in response:
+            next_pagination_token = response["nextPaginationToken"]
+        users_list = response["users"]
+        users: List[User] = []
+        for user in users_list:
+            recipe_id = user["recipeId"]
+            user_obj = user["user"]
+            third_party = None
+            if "thirdParty" in user_obj:
+                third_party = ThirdPartyInfo(
+                    user_obj["thirdParty"]["userId"], user_obj["thirdParty"]["id"]
+                )
+            email = None
+            if "email" in user_obj:
+                email = user_obj["email"]
+            phone_number = None
+            if "phoneNumber" in user_obj:
+                phone_number = user_obj["phoneNumber"]
+            users.append(
+                User(
+                    recipe_id,
+                    user_obj["id"],
+                    user_obj["timeJoined"],
+                    email,
+                    phone_number,
+                    third_party,
+                    user_obj["tenantIds"],
+                )
+            )
+
+        return UsersResponse(users, next_pagination_token)
+
+    async def create_user_id_mapping(  # pylint: disable=no-self-use
+        self,
+        supertokens_user_id: str,
+        external_user_id: str,
+        external_user_id_info: Optional[str],
+        force: Optional[bool],
+        user_context: Optional[Dict[str, Any]],
+    ) -> Union[
+        CreateUserIdMappingOkResult,
+        UnknownSupertokensUserIDError,
+        UserIdMappingAlreadyExistsError,
+    ]:
+        querier = Querier.get_instance(None)
+
+        cdi_version = await querier.get_api_version(user_context)
+
+        if is_version_gte(cdi_version, "2.15"):
+            body: Dict[str, Any] = {
+                "superTokensUserId": supertokens_user_id,
+                "externalUserId": external_user_id,
+                "externalUserIdInfo": external_user_id_info,
+            }
+            if force:
+                body["force"] = force
+
+            res = await querier.send_post_request(
+                NormalisedURLPath("/recipe/userid/map"), body, user_context=user_context
+            )
+            if res["status"] == "OK":
+                return CreateUserIdMappingOkResult()
+            if res["status"] == "UNKNOWN_SUPERTOKENS_USER_ID_ERROR":
+                return UnknownSupertokensUserIDError()
+            if res["status"] == "USER_ID_MAPPING_ALREADY_EXISTS_ERROR":
+                return UserIdMappingAlreadyExistsError(
+                    does_super_tokens_user_id_exist=res["doesSuperTokensUserIdExist"],
+                    does_external_user_id_exist=res["does_external_user_id_exist"],
+                )
+
+            raise_general_exception("Unknown response")
+
+        raise_general_exception("Please upgrade the SuperTokens core to >= 3.15.0")
+
+    async def get_user_id_mapping(  # pylint: disable=no-self-use
+        self,
+        user_id: str,
+        user_id_type: Optional[UserIDTypes],
+        user_context: Optional[Dict[str, Any]],
+    ) -> Union[GetUserIdMappingOkResult, UnknownMappingError]:
+        querier = Querier.get_instance(None)
+
+        cdi_version = await querier.get_api_version(user_context)
+
+        if is_version_gte(cdi_version, "2.15"):
+            body = {
+                "userId": user_id,
+            }
+            if user_id_type:
+                body["userIdType"] = user_id_type
+            res = await querier.send_get_request(
+                NormalisedURLPath("/recipe/userid/map"),
+                body,
+                user_context=user_context,
+            )
+            if res["status"] == "OK":
+                return GetUserIdMappingOkResult(
+                    supertokens_user_id=res["superTokensUserId"],
+                    external_user_id=res["externalUserId"],
+                    external_user_info=res.get("externalUserIdInfo"),
+                )
+            if res["status"] == "UNKNOWN_MAPPING_ERROR":
+                return UnknownMappingError()
+
+            raise_general_exception("Unknown response")
+
+        raise_general_exception("Please upgrade the SuperTokens core to >= 3.15.0")
+
+    async def delete_user_id_mapping(  # pylint: disable=no-self-use
+        self,
+        user_id: str,
+        user_id_type: Optional[UserIDTypes],
+        force: Optional[bool],
+        user_context: Optional[Dict[str, Any]],
+    ) -> DeleteUserIdMappingOkResult:
+        querier = Querier.get_instance(None)
+
+        cdi_version = await querier.get_api_version(user_context)
+
+        if is_version_gte(cdi_version, "2.15"):
+            body: Dict[str, Any] = {
+                "userId": user_id,
+                "userIdType": user_id_type,
+            }
+            if force:
+                body["force"] = force
+            res = await querier.send_post_request(
+                NormalisedURLPath("/recipe/userid/map/remove"),
+                body,
+                user_context=user_context,
+            )
+            if res["status"] == "OK":
+                return DeleteUserIdMappingOkResult(
+                    did_mapping_exist=res["didMappingExist"]
+                )
+
+            raise_general_exception("Unknown response")
+
+        raise_general_exception("Please upgrade the SuperTokens core to >= 3.15.0")
+
+    async def update_or_delete_user_id_mapping_info(  # pylint: disable=no-self-use
+        self,
+        user_id: str,
+        user_id_type: Optional[UserIDTypes],
+        external_user_id_info: Optional[str],
+        user_context: Optional[Dict[str, Any]],
+    ) -> Union[UpdateOrDeleteUserIdMappingInfoOkResult, UnknownMappingError]:
+        querier = Querier.get_instance(None)
+
+        cdi_version = await querier.get_api_version(user_context)
+
+        if is_version_gte(cdi_version, "2.15"):
+            res = await querier.send_post_request(
+                NormalisedURLPath("/recipe/userid/external-user-id-info"),
+                {
+                    "userId": user_id,
+                    "userIdType": user_id_type,
+                    "externalUserIdInfo": external_user_id_info,
+                },
+                user_context=user_context,
+            )
+            if res["status"] == "OK":
+                return UpdateOrDeleteUserIdMappingInfoOkResult()
+            if res["status"] == "UNKNOWN_MAPPING_ERROR":
+                return UnknownMappingError()
+
+            raise_general_exception("Unknown response")
+
+        raise_general_exception("Please upgrade the SuperTokens core to >= 3.15.0")
+
+    async def middleware(  # pylint: disable=no-self-use
+        self, request: BaseRequest, response: BaseResponse, user_context: Dict[str, Any]
+    ) -> Union[BaseResponse, None]:
+        log_debug_message("middleware: Started")
+        path = Supertokens.get_instance().app_info.api_gateway_path.append(
+            NormalisedURLPath(request.get_path())
+        )
+        method = normalise_http_method(request.method())
+
+        if not path.startswith(Supertokens.get_instance().app_info.api_base_path):
+            log_debug_message(
+                "middleware: Not handling because request path did not start with api base path. Request path: %s",
+                path.get_as_string_dangerous(),
+            )
+            return None
+        request_rid = get_rid_from_header(request)
+        log_debug_message(
+            "middleware: requestRID is: %s", get_maybe_none_as_str(request_rid)
+        )
+        if request_rid is not None and request_rid == "anti-csrf":
+            # see https://github.com/supertokens/supertokens-python/issues/54
+            request_rid = None
+
+        async def handle_without_rid():
+            for recipe in self.recipe_modules:
+                log_debug_message(
+                    "middleware: Checking recipe ID for match: %s with path: %s and method: %s",
+                    recipe.get_recipe_id(),
+                    path.get_as_string_dangerous(),
+                    method,
+                )
+                api_and_tenant_id = await recipe.return_api_id_if_can_handle_request(
+                    path, method, user_context
+                )
+                if api_and_tenant_id is not None:
+                    log_debug_message(
+                        "middleware: Request being handled by recipe. ID is: %s",
+                        api_and_tenant_id.api_id,
+                    )
+                    api_resp = await recipe.handle_api_request(
+                        api_and_tenant_id.api_id,
+                        api_and_tenant_id.tenant_id,
+                        request,
+                        path,
+                        method,
+                        response,
+                        user_context,
+                    )
+                    if api_resp is None:
+                        log_debug_message(
+                            "middleware: Not handled because API returned None"
+                        )
+                        return None
+                    log_debug_message("middleware: Ended")
+                    return api_resp
+            log_debug_message("middleware: Not handling because no recipe matched")
+            return None
+
+        if request_rid is not None:
+            matched_recipes = [
+                recipe
+                for recipe in self.recipe_modules
+                if recipe.get_recipe_id() == request_rid
+                or (
+                    request_rid == "thirdpartyemailpassword"
+                    and recipe.get_recipe_id() in ["thirdparty", "emailpassword"]
+                )
+                or (
+                    request_rid == "thirdpartypasswordless"
+                    and recipe.get_recipe_id() in ["thirdparty", "passwordless"]
+                )
+            ]
+
+            if len(matched_recipes) == 0:
+                log_debug_message(
+                    "middleware: Not handling based on rid match. Trying without rid."
+                )
+                return await handle_without_rid()
+
+            for recipe in matched_recipes:
+                log_debug_message(
+                    "middleware: Matched with recipe Ids: %s", recipe.get_recipe_id()
+                )
+
+            id_result = None
+            final_matched_recipe = None
+            for recipe in matched_recipes:
+                current_id_result = await recipe.return_api_id_if_can_handle_request(
+                    path, method, user_context
+                )
+                if current_id_result is not None:
+                    if id_result is not None:
+                        raise ValueError(
+                            "Two recipes have matched the same API path and method! This is a bug in the SDK. Please contact support."
+                        )
+                    final_matched_recipe = recipe
+                    id_result = current_id_result
+
+            if id_result is None or final_matched_recipe is None:
+                return await handle_without_rid()
+
+            log_debug_message(
+                "middleware: Request being handled by recipe. ID is: %s",
+                id_result.api_id,
+            )
+            request_handled = await final_matched_recipe.handle_api_request(
+                id_result.api_id,
+                id_result.tenant_id,
+                request,
+                path,
+                method,
+                response,
+                user_context,
+            )
+            if request_handled is None:
+                log_debug_message(
+                    "middleware: Not handled because API returned request_handled as None"
+                )
+                return None
+            log_debug_message("middleware: Ended")
+            return request_handled
+        return await handle_without_rid()
+
+    async def handle_supertokens_error(
+        self,
+        request: BaseRequest,
+        err: Exception,
+        response: BaseResponse,
+        user_context: Dict[str, Any],
+    ):
+        log_debug_message("errorHandler: Started")
+        log_debug_message(
+            "errorHandler: Error is from SuperTokens recipe. Message: %s", str(err)
+        )
+        if isinstance(err, GeneralError):
+            raise err
+
+        if isinstance(err, BadInputError):
+            log_debug_message("errorHandler: Sending 400 status code response")
+            return send_non_200_response_with_message(str(err), 400, response)
+
+        for recipe in self.recipe_modules:
+            log_debug_message(
+                "errorHandler: Checking recipe for match: %s", recipe.get_recipe_id()
+            )
+            if recipe.is_error_from_this_recipe_based_on_instance(err) and isinstance(
+                err, SuperTokensError
+            ):
+                log_debug_message(
+                    "errorHandler: Matched with recipeID: %s", recipe.get_recipe_id()
+                )
+                return await recipe.handle_error(request, err, response, user_context)
+        raise err
+
+    def get_request_from_user_context(  # pylint: disable=no-self-use
+        self,
+        user_context: Optional[Dict[str, Any]] = None,
+    ) -> Optional[BaseRequest]:
+        if user_context is None:
+            return None
+
+        if "_default" not in user_context:
+            return None
+
+        if not isinstance(user_context["_default"], dict):
+            return None
+
+        return user_context.get("_default", {}).get("request")
+
+
+def get_request_from_user_context(
+    user_context: Optional[Dict[str, Any]],
+) -> Optional[BaseRequest]:
+    return Supertokens.get_instance().get_request_from_user_context(user_context)
+
+
+
+
+
+
+
+

Functions

+
+
+def get_request_from_user_context(user_context: Optional[Dict[str, Any]]) ‑> Optional[BaseRequest] +
+
+
+
+ +Expand source code + +
def get_request_from_user_context(
+    user_context: Optional[Dict[str, Any]],
+) -> Optional[BaseRequest]:
+    return Supertokens.get_instance().get_request_from_user_context(user_context)
+
+
+
+def manage_session_post_response(session: SessionContainer, response: BaseResponse, user_context: Dict[str, Any]) +
+
+
+
+ +Expand source code + +
def manage_session_post_response(
+    session: SessionContainer, response: BaseResponse, user_context: Dict[str, Any]
+):
+    # Something similar happens in handle_error of session/recipe.py
+    for mutator in session.response_mutators:
+        mutator(response, user_context)
+
+
+
+
+
+

Classes

+
+
+class AppInfo +(app_name: str, api_domain: str, website_domain: Optional[str], framework: "Literal[('fastapi', 'flask', 'django')]", api_gateway_path: str, api_base_path: str, website_base_path: str, mode: "Union[Literal[('asgi', 'wsgi')], None]", origin: Optional[Union[str, Callable[[Optional[BaseRequest], Dict[str, Any]], str]]]) +
+
+
+
+ +Expand source code + +
class AppInfo:
+    def __init__(
+        self,
+        app_name: str,
+        api_domain: str,
+        website_domain: Optional[str],
+        framework: Literal["fastapi", "flask", "django"],
+        api_gateway_path: str,
+        api_base_path: str,
+        website_base_path: str,
+        mode: Union[Literal["asgi", "wsgi"], None],
+        origin: Optional[
+            Union[str, Callable[[Optional[BaseRequest], Dict[str, Any]], str]]
+        ],
+    ):
+        self.app_name = app_name
+        self.api_gateway_path = NormalisedURLPath(api_gateway_path)
+        self.api_domain = NormalisedURLDomain(api_domain)
+        self.top_level_api_domain = get_top_level_domain_for_same_site_resolution(
+            self.api_domain.get_as_string_dangerous()
+        )
+        if website_domain is None and origin is None:
+            raise_general_exception(
+                "Please provide at least one of website_domain or origin"
+            )
+        self.__origin = origin
+        self.__website_domain = website_domain
+        self.api_base_path = self.api_gateway_path.append(
+            NormalisedURLPath(api_base_path)
+        )
+        self.website_base_path = NormalisedURLPath(website_base_path)
+        if mode is not None:
+            self.mode = mode
+        elif framework == "fastapi":
+            mode = "asgi"
+        else:
+            mode = "wsgi"
+        self.framework = framework
+        self.mode = mode
+
+    def get_top_level_website_domain(
+        self, request: Optional[BaseRequest], user_context: Dict[str, Any]
+    ) -> str:
+        return get_top_level_domain_for_same_site_resolution(
+            self.get_origin(request, user_context).get_as_string_dangerous()
+        )
+
+    def get_origin(self, request: Optional[BaseRequest], user_context: Dict[str, Any]):
+        origin = self.__origin
+        if origin is None:
+            origin = self.__website_domain
+
+        # This should not be possible because we check for either origin or websiteDomain above
+        if origin is None:
+            raise_general_exception("should never come here")
+
+        if callable(origin):
+            origin = origin(request, user_context)
+
+        return NormalisedURLDomain(origin)
+
+    def toJSON(self):
+        def defaultImpl(o: Any):
+            if isinstance(o, (NormalisedURLDomain, NormalisedURLPath)):
+                return o.get_as_string_dangerous()
+            return o.__dict__
+
+        return json.dumps(self, default=defaultImpl, sort_keys=True, indent=4)
+
+

Methods

+
+
+def get_origin(self, request: Optional[BaseRequest], user_context: Dict[str, Any]) +
+
+
+
+ +Expand source code + +
def get_origin(self, request: Optional[BaseRequest], user_context: Dict[str, Any]):
+    origin = self.__origin
+    if origin is None:
+        origin = self.__website_domain
+
+    # This should not be possible because we check for either origin or websiteDomain above
+    if origin is None:
+        raise_general_exception("should never come here")
+
+    if callable(origin):
+        origin = origin(request, user_context)
+
+    return NormalisedURLDomain(origin)
+
+
+
+def get_top_level_website_domain(self, request: Optional[BaseRequest], user_context: Dict[str, Any]) ‑> str +
+
+
+
+ +Expand source code + +
def get_top_level_website_domain(
+    self, request: Optional[BaseRequest], user_context: Dict[str, Any]
+) -> str:
+    return get_top_level_domain_for_same_site_resolution(
+        self.get_origin(request, user_context).get_as_string_dangerous()
+    )
+
+
+
+def toJSON(self) +
+
+
+
+ +Expand source code + +
def toJSON(self):
+    def defaultImpl(o: Any):
+        if isinstance(o, (NormalisedURLDomain, NormalisedURLPath)):
+            return o.get_as_string_dangerous()
+        return o.__dict__
+
+    return json.dumps(self, default=defaultImpl, sort_keys=True, indent=4)
+
+
+
+
+
+class Host +(domain: NormalisedURLDomain, base_path: NormalisedURLPath) +
+
+
+
+ +Expand source code + +
class Host:
+    def __init__(self, domain: NormalisedURLDomain, base_path: NormalisedURLPath):
+        self.domain = domain
+        self.base_path = base_path
+
+
+
+class InputAppInfo +(app_name: str, api_domain: str, api_gateway_path: str = '', api_base_path: str = '/auth', website_base_path: str = '/auth', website_domain: Optional[str] = None, origin: Optional[Union[str, Callable[[Optional[BaseRequest], Dict[str, Any]], str]]] = None) +
+
+
+
+ +Expand source code + +
class InputAppInfo:
+    def __init__(
+        self,
+        app_name: str,
+        api_domain: str,
+        api_gateway_path: str = "",
+        api_base_path: str = "/auth",
+        website_base_path: str = "/auth",
+        website_domain: Optional[str] = None,
+        origin: Optional[
+            Union[str, Callable[[Optional[BaseRequest], Dict[str, Any]], str]]
+        ] = None,
+    ):
+        self.app_name = app_name
+        self.api_gateway_path = api_gateway_path
+        self.api_domain = api_domain
+        self.website_domain = website_domain
+        self.origin = origin
+        self.api_base_path = api_base_path
+        self.website_base_path = website_base_path
+
+
+
+class Supertokens +(app_info: InputAppInfo, framework: "Literal[('fastapi', 'flask', 'django')]", supertokens_config: SupertokensConfig, recipe_list: List[Callable[[AppInfo], RecipeModule]], mode: "Optional[Literal[('asgi', 'wsgi')]]", telemetry: Optional[bool], debug: Optional[bool]) +
+
+
+
+ +Expand source code + +
class Supertokens:
+    __instance = None
+
+    def __init__(
+        self,
+        app_info: InputAppInfo,
+        framework: Literal["fastapi", "flask", "django"],
+        supertokens_config: SupertokensConfig,
+        recipe_list: List[Callable[[AppInfo], RecipeModule]],
+        mode: Optional[Literal["asgi", "wsgi"]],
+        telemetry: Optional[bool],
+        debug: Optional[bool],
+    ):
+        if not isinstance(app_info, InputAppInfo):  # type: ignore
+            raise ValueError("app_info must be an instance of InputAppInfo")
+
+        self.app_info = AppInfo(
+            app_info.app_name,
+            app_info.api_domain,
+            app_info.website_domain,
+            framework,
+            app_info.api_gateway_path,
+            app_info.api_base_path,
+            app_info.website_base_path,
+            mode,
+            app_info.origin,
+        )
+        self.supertokens_config = supertokens_config
+        if debug is True:
+            enable_debug_logging()
+        self._telemetry_status: str = "NONE"
+        log_debug_message(
+            "Started SuperTokens with debug logging (supertokens.init called)"
+        )
+        log_debug_message("app_info: %s", self.app_info.toJSON())
+        log_debug_message("framework: %s", framework)
+        hosts = list(
+            map(
+                lambda h: Host(
+                    NormalisedURLDomain(h.strip()), NormalisedURLPath(h.strip())
+                ),
+                filter(lambda x: x != "", supertokens_config.connection_uri.split(";")),
+            )
+        )
+        Querier.init(
+            hosts,
+            supertokens_config.api_key,
+            supertokens_config.network_interceptor,
+            supertokens_config.disable_core_call_cache,
+        )
+
+        if len(recipe_list) == 0:
+            raise_general_exception(
+                "Please provide at least one recipe to the supertokens.init function call"
+            )
+
+        from supertokens_python.recipe.multitenancy.recipe import MultitenancyRecipe
+
+        multitenancy_found = False
+
+        def make_recipe(recipe: Callable[[AppInfo], RecipeModule]) -> RecipeModule:
+            nonlocal multitenancy_found
+            recipe_module = recipe(self.app_info)
+            if recipe_module.get_recipe_id() == MultitenancyRecipe.recipe_id:
+                multitenancy_found = True
+            return recipe_module
+
+        self.recipe_modules: List[RecipeModule] = list(map(make_recipe, recipe_list))
+
+        if not multitenancy_found:
+            recipe = MultitenancyRecipe.init()(self.app_info)
+            self.recipe_modules.append(recipe)
+
+        self.telemetry = (
+            telemetry
+            if telemetry is not None
+            else (environ.get("TEST_MODE") != "testing")
+        )
+
+    @staticmethod
+    def init(
+        app_info: InputAppInfo,
+        framework: Literal["fastapi", "flask", "django"],
+        supertokens_config: SupertokensConfig,
+        recipe_list: List[Callable[[AppInfo], RecipeModule]],
+        mode: Optional[Literal["asgi", "wsgi"]],
+        telemetry: Optional[bool],
+        debug: Optional[bool],
+    ):
+        if Supertokens.__instance is None:
+            Supertokens.__instance = Supertokens(
+                app_info,
+                framework,
+                supertokens_config,
+                recipe_list,
+                mode,
+                telemetry,
+                debug,
+            )
+            PostSTInitCallbacks.run_post_init_callbacks()
+
+    @staticmethod
+    def reset():
+        if ("SUPERTOKENS_ENV" not in environ) or (
+            environ["SUPERTOKENS_ENV"] != "testing"
+        ):
+            raise_general_exception("calling testing function in non testing env")
+        Querier.reset()
+        Supertokens.__instance = None
+
+    @staticmethod
+    def get_instance() -> Supertokens:
+        if Supertokens.__instance is not None:
+            return Supertokens.__instance
+        raise_general_exception(
+            "Initialisation not done. Did you forget to call the SuperTokens.init function?"
+        )
+
+    def get_all_cors_headers(self) -> List[str]:
+        headers_set: Set[str] = set()
+        headers_set.add(RID_KEY_HEADER)
+        headers_set.add(FDI_KEY_HEADER)
+        for recipe in self.recipe_modules:
+            headers = recipe.get_all_cors_headers()
+            for header in headers:
+                headers_set.add(header)
+
+        return list(headers_set)
+
+    async def get_user_count(  # pylint: disable=no-self-use
+        self,
+        include_recipe_ids: Union[None, List[str]],
+        tenant_id: Optional[str] = None,
+        user_context: Optional[Dict[str, Any]] = None,
+    ) -> int:
+        querier = Querier.get_instance(None)
+        include_recipe_ids_str = None
+        if include_recipe_ids is not None:
+            include_recipe_ids_str = ",".join(include_recipe_ids)
+
+        response = await querier.send_get_request(
+            NormalisedURLPath(f"/{tenant_id or 'public'}{USER_COUNT}"),
+            {
+                "includeRecipeIds": include_recipe_ids_str,
+                "includeAllTenants": tenant_id is None,
+            },
+            user_context=user_context,
+        )
+
+        return int(response["count"])
+
+    async def delete_user(  # pylint: disable=no-self-use
+        self,
+        user_id: str,
+        user_context: Optional[Dict[str, Any]],
+    ) -> None:
+        querier = Querier.get_instance(None)
+
+        cdi_version = await querier.get_api_version(user_context)
+
+        if is_version_gte(cdi_version, "2.10"):
+            await querier.send_post_request(
+                NormalisedURLPath(USER_DELETE),
+                {"userId": user_id},
+                user_context=user_context,
+            )
+
+            return None
+        raise_general_exception("Please upgrade the SuperTokens core to >= 3.7.0")
+
+    async def get_users(  # pylint: disable=no-self-use
+        self,
+        tenant_id: str,
+        time_joined_order: Literal["ASC", "DESC"],
+        limit: Union[int, None],
+        pagination_token: Union[str, None],
+        include_recipe_ids: Union[None, List[str]],
+        query: Union[Dict[str, str], None],
+        user_context: Optional[Dict[str, Any]],
+    ) -> UsersResponse:
+
+        querier = Querier.get_instance(None)
+        params = {"timeJoinedOrder": time_joined_order}
+        if limit is not None:
+            params = {"limit": limit, **params}
+        if pagination_token is not None:
+            params = {"paginationToken": pagination_token, **params}
+
+        include_recipe_ids_str = None
+        if include_recipe_ids is not None:
+            include_recipe_ids_str = ",".join(include_recipe_ids)
+            params = {"includeRecipeIds": include_recipe_ids_str, **params}
+
+        if query is not None:
+            params = {**params, **query}
+
+        response = await querier.send_get_request(
+            NormalisedURLPath(f"/{tenant_id}{USERS}"), params, user_context=user_context
+        )
+        next_pagination_token = None
+        if "nextPaginationToken" in response:
+            next_pagination_token = response["nextPaginationToken"]
+        users_list = response["users"]
+        users: List[User] = []
+        for user in users_list:
+            recipe_id = user["recipeId"]
+            user_obj = user["user"]
+            third_party = None
+            if "thirdParty" in user_obj:
+                third_party = ThirdPartyInfo(
+                    user_obj["thirdParty"]["userId"], user_obj["thirdParty"]["id"]
+                )
+            email = None
+            if "email" in user_obj:
+                email = user_obj["email"]
+            phone_number = None
+            if "phoneNumber" in user_obj:
+                phone_number = user_obj["phoneNumber"]
+            users.append(
+                User(
+                    recipe_id,
+                    user_obj["id"],
+                    user_obj["timeJoined"],
+                    email,
+                    phone_number,
+                    third_party,
+                    user_obj["tenantIds"],
+                )
+            )
+
+        return UsersResponse(users, next_pagination_token)
+
+    async def create_user_id_mapping(  # pylint: disable=no-self-use
+        self,
+        supertokens_user_id: str,
+        external_user_id: str,
+        external_user_id_info: Optional[str],
+        force: Optional[bool],
+        user_context: Optional[Dict[str, Any]],
+    ) -> Union[
+        CreateUserIdMappingOkResult,
+        UnknownSupertokensUserIDError,
+        UserIdMappingAlreadyExistsError,
+    ]:
+        querier = Querier.get_instance(None)
+
+        cdi_version = await querier.get_api_version(user_context)
+
+        if is_version_gte(cdi_version, "2.15"):
+            body: Dict[str, Any] = {
+                "superTokensUserId": supertokens_user_id,
+                "externalUserId": external_user_id,
+                "externalUserIdInfo": external_user_id_info,
+            }
+            if force:
+                body["force"] = force
+
+            res = await querier.send_post_request(
+                NormalisedURLPath("/recipe/userid/map"), body, user_context=user_context
+            )
+            if res["status"] == "OK":
+                return CreateUserIdMappingOkResult()
+            if res["status"] == "UNKNOWN_SUPERTOKENS_USER_ID_ERROR":
+                return UnknownSupertokensUserIDError()
+            if res["status"] == "USER_ID_MAPPING_ALREADY_EXISTS_ERROR":
+                return UserIdMappingAlreadyExistsError(
+                    does_super_tokens_user_id_exist=res["doesSuperTokensUserIdExist"],
+                    does_external_user_id_exist=res["does_external_user_id_exist"],
+                )
+
+            raise_general_exception("Unknown response")
+
+        raise_general_exception("Please upgrade the SuperTokens core to >= 3.15.0")
+
+    async def get_user_id_mapping(  # pylint: disable=no-self-use
+        self,
+        user_id: str,
+        user_id_type: Optional[UserIDTypes],
+        user_context: Optional[Dict[str, Any]],
+    ) -> Union[GetUserIdMappingOkResult, UnknownMappingError]:
+        querier = Querier.get_instance(None)
+
+        cdi_version = await querier.get_api_version(user_context)
+
+        if is_version_gte(cdi_version, "2.15"):
+            body = {
+                "userId": user_id,
+            }
+            if user_id_type:
+                body["userIdType"] = user_id_type
+            res = await querier.send_get_request(
+                NormalisedURLPath("/recipe/userid/map"),
+                body,
+                user_context=user_context,
+            )
+            if res["status"] == "OK":
+                return GetUserIdMappingOkResult(
+                    supertokens_user_id=res["superTokensUserId"],
+                    external_user_id=res["externalUserId"],
+                    external_user_info=res.get("externalUserIdInfo"),
+                )
+            if res["status"] == "UNKNOWN_MAPPING_ERROR":
+                return UnknownMappingError()
+
+            raise_general_exception("Unknown response")
+
+        raise_general_exception("Please upgrade the SuperTokens core to >= 3.15.0")
+
+    async def delete_user_id_mapping(  # pylint: disable=no-self-use
+        self,
+        user_id: str,
+        user_id_type: Optional[UserIDTypes],
+        force: Optional[bool],
+        user_context: Optional[Dict[str, Any]],
+    ) -> DeleteUserIdMappingOkResult:
+        querier = Querier.get_instance(None)
+
+        cdi_version = await querier.get_api_version(user_context)
+
+        if is_version_gte(cdi_version, "2.15"):
+            body: Dict[str, Any] = {
+                "userId": user_id,
+                "userIdType": user_id_type,
+            }
+            if force:
+                body["force"] = force
+            res = await querier.send_post_request(
+                NormalisedURLPath("/recipe/userid/map/remove"),
+                body,
+                user_context=user_context,
+            )
+            if res["status"] == "OK":
+                return DeleteUserIdMappingOkResult(
+                    did_mapping_exist=res["didMappingExist"]
+                )
+
+            raise_general_exception("Unknown response")
+
+        raise_general_exception("Please upgrade the SuperTokens core to >= 3.15.0")
+
+    async def update_or_delete_user_id_mapping_info(  # pylint: disable=no-self-use
+        self,
+        user_id: str,
+        user_id_type: Optional[UserIDTypes],
+        external_user_id_info: Optional[str],
+        user_context: Optional[Dict[str, Any]],
+    ) -> Union[UpdateOrDeleteUserIdMappingInfoOkResult, UnknownMappingError]:
+        querier = Querier.get_instance(None)
+
+        cdi_version = await querier.get_api_version(user_context)
+
+        if is_version_gte(cdi_version, "2.15"):
+            res = await querier.send_post_request(
+                NormalisedURLPath("/recipe/userid/external-user-id-info"),
+                {
+                    "userId": user_id,
+                    "userIdType": user_id_type,
+                    "externalUserIdInfo": external_user_id_info,
+                },
+                user_context=user_context,
+            )
+            if res["status"] == "OK":
+                return UpdateOrDeleteUserIdMappingInfoOkResult()
+            if res["status"] == "UNKNOWN_MAPPING_ERROR":
+                return UnknownMappingError()
+
+            raise_general_exception("Unknown response")
+
+        raise_general_exception("Please upgrade the SuperTokens core to >= 3.15.0")
+
+    async def middleware(  # pylint: disable=no-self-use
+        self, request: BaseRequest, response: BaseResponse, user_context: Dict[str, Any]
+    ) -> Union[BaseResponse, None]:
+        log_debug_message("middleware: Started")
+        path = Supertokens.get_instance().app_info.api_gateway_path.append(
+            NormalisedURLPath(request.get_path())
+        )
+        method = normalise_http_method(request.method())
+
+        if not path.startswith(Supertokens.get_instance().app_info.api_base_path):
+            log_debug_message(
+                "middleware: Not handling because request path did not start with api base path. Request path: %s",
+                path.get_as_string_dangerous(),
+            )
+            return None
+        request_rid = get_rid_from_header(request)
+        log_debug_message(
+            "middleware: requestRID is: %s", get_maybe_none_as_str(request_rid)
+        )
+        if request_rid is not None and request_rid == "anti-csrf":
+            # see https://github.com/supertokens/supertokens-python/issues/54
+            request_rid = None
+
+        async def handle_without_rid():
+            for recipe in self.recipe_modules:
+                log_debug_message(
+                    "middleware: Checking recipe ID for match: %s with path: %s and method: %s",
+                    recipe.get_recipe_id(),
+                    path.get_as_string_dangerous(),
+                    method,
+                )
+                api_and_tenant_id = await recipe.return_api_id_if_can_handle_request(
+                    path, method, user_context
+                )
+                if api_and_tenant_id is not None:
+                    log_debug_message(
+                        "middleware: Request being handled by recipe. ID is: %s",
+                        api_and_tenant_id.api_id,
+                    )
+                    api_resp = await recipe.handle_api_request(
+                        api_and_tenant_id.api_id,
+                        api_and_tenant_id.tenant_id,
+                        request,
+                        path,
+                        method,
+                        response,
+                        user_context,
+                    )
+                    if api_resp is None:
+                        log_debug_message(
+                            "middleware: Not handled because API returned None"
+                        )
+                        return None
+                    log_debug_message("middleware: Ended")
+                    return api_resp
+            log_debug_message("middleware: Not handling because no recipe matched")
+            return None
+
+        if request_rid is not None:
+            matched_recipes = [
+                recipe
+                for recipe in self.recipe_modules
+                if recipe.get_recipe_id() == request_rid
+                or (
+                    request_rid == "thirdpartyemailpassword"
+                    and recipe.get_recipe_id() in ["thirdparty", "emailpassword"]
+                )
+                or (
+                    request_rid == "thirdpartypasswordless"
+                    and recipe.get_recipe_id() in ["thirdparty", "passwordless"]
+                )
+            ]
+
+            if len(matched_recipes) == 0:
+                log_debug_message(
+                    "middleware: Not handling based on rid match. Trying without rid."
+                )
+                return await handle_without_rid()
+
+            for recipe in matched_recipes:
+                log_debug_message(
+                    "middleware: Matched with recipe Ids: %s", recipe.get_recipe_id()
+                )
+
+            id_result = None
+            final_matched_recipe = None
+            for recipe in matched_recipes:
+                current_id_result = await recipe.return_api_id_if_can_handle_request(
+                    path, method, user_context
+                )
+                if current_id_result is not None:
+                    if id_result is not None:
+                        raise ValueError(
+                            "Two recipes have matched the same API path and method! This is a bug in the SDK. Please contact support."
+                        )
+                    final_matched_recipe = recipe
+                    id_result = current_id_result
+
+            if id_result is None or final_matched_recipe is None:
+                return await handle_without_rid()
+
+            log_debug_message(
+                "middleware: Request being handled by recipe. ID is: %s",
+                id_result.api_id,
+            )
+            request_handled = await final_matched_recipe.handle_api_request(
+                id_result.api_id,
+                id_result.tenant_id,
+                request,
+                path,
+                method,
+                response,
+                user_context,
+            )
+            if request_handled is None:
+                log_debug_message(
+                    "middleware: Not handled because API returned request_handled as None"
+                )
+                return None
+            log_debug_message("middleware: Ended")
+            return request_handled
+        return await handle_without_rid()
+
+    async def handle_supertokens_error(
+        self,
+        request: BaseRequest,
+        err: Exception,
+        response: BaseResponse,
+        user_context: Dict[str, Any],
+    ):
+        log_debug_message("errorHandler: Started")
+        log_debug_message(
+            "errorHandler: Error is from SuperTokens recipe. Message: %s", str(err)
+        )
+        if isinstance(err, GeneralError):
+            raise err
+
+        if isinstance(err, BadInputError):
+            log_debug_message("errorHandler: Sending 400 status code response")
+            return send_non_200_response_with_message(str(err), 400, response)
+
+        for recipe in self.recipe_modules:
+            log_debug_message(
+                "errorHandler: Checking recipe for match: %s", recipe.get_recipe_id()
+            )
+            if recipe.is_error_from_this_recipe_based_on_instance(err) and isinstance(
+                err, SuperTokensError
+            ):
+                log_debug_message(
+                    "errorHandler: Matched with recipeID: %s", recipe.get_recipe_id()
+                )
+                return await recipe.handle_error(request, err, response, user_context)
+        raise err
+
+    def get_request_from_user_context(  # pylint: disable=no-self-use
+        self,
+        user_context: Optional[Dict[str, Any]] = None,
+    ) -> Optional[BaseRequest]:
+        if user_context is None:
+            return None
+
+        if "_default" not in user_context:
+            return None
+
+        if not isinstance(user_context["_default"], dict):
+            return None
+
+        return user_context.get("_default", {}).get("request")
+
+

Static methods

+
+
+def get_instance() ‑> Supertokens +
+
+
+
+ +Expand source code + +
@staticmethod
+def get_instance() -> Supertokens:
+    if Supertokens.__instance is not None:
+        return Supertokens.__instance
+    raise_general_exception(
+        "Initialisation not done. Did you forget to call the SuperTokens.init function?"
+    )
+
+
+
+def init(app_info: InputAppInfo, framework: "Literal[('fastapi', 'flask', 'django')]", supertokens_config: SupertokensConfig, recipe_list: List[Callable[[AppInfo], RecipeModule]], mode: "Optional[Literal[('asgi', 'wsgi')]]", telemetry: Optional[bool], debug: Optional[bool]) +
+
+
+
+ +Expand source code + +
@staticmethod
+def init(
+    app_info: InputAppInfo,
+    framework: Literal["fastapi", "flask", "django"],
+    supertokens_config: SupertokensConfig,
+    recipe_list: List[Callable[[AppInfo], RecipeModule]],
+    mode: Optional[Literal["asgi", "wsgi"]],
+    telemetry: Optional[bool],
+    debug: Optional[bool],
+):
+    if Supertokens.__instance is None:
+        Supertokens.__instance = Supertokens(
+            app_info,
+            framework,
+            supertokens_config,
+            recipe_list,
+            mode,
+            telemetry,
+            debug,
+        )
+        PostSTInitCallbacks.run_post_init_callbacks()
+
+
+
+def reset() +
+
+
+
+ +Expand source code + +
@staticmethod
+def reset():
+    if ("SUPERTOKENS_ENV" not in environ) or (
+        environ["SUPERTOKENS_ENV"] != "testing"
+    ):
+        raise_general_exception("calling testing function in non testing env")
+    Querier.reset()
+    Supertokens.__instance = None
+
+
+
+

Methods

+
+
+async def create_user_id_mapping(self, supertokens_user_id: str, external_user_id: str, external_user_id_info: Optional[str], force: Optional[bool], user_context: Optional[Dict[str, Any]]) ‑> Union[CreateUserIdMappingOkResultUnknownSupertokensUserIDErrorUserIdMappingAlreadyExistsError] +
+
+
+
+ +Expand source code + +
async def create_user_id_mapping(  # pylint: disable=no-self-use
+    self,
+    supertokens_user_id: str,
+    external_user_id: str,
+    external_user_id_info: Optional[str],
+    force: Optional[bool],
+    user_context: Optional[Dict[str, Any]],
+) -> Union[
+    CreateUserIdMappingOkResult,
+    UnknownSupertokensUserIDError,
+    UserIdMappingAlreadyExistsError,
+]:
+    querier = Querier.get_instance(None)
+
+    cdi_version = await querier.get_api_version(user_context)
+
+    if is_version_gte(cdi_version, "2.15"):
+        body: Dict[str, Any] = {
+            "superTokensUserId": supertokens_user_id,
+            "externalUserId": external_user_id,
+            "externalUserIdInfo": external_user_id_info,
+        }
+        if force:
+            body["force"] = force
+
+        res = await querier.send_post_request(
+            NormalisedURLPath("/recipe/userid/map"), body, user_context=user_context
+        )
+        if res["status"] == "OK":
+            return CreateUserIdMappingOkResult()
+        if res["status"] == "UNKNOWN_SUPERTOKENS_USER_ID_ERROR":
+            return UnknownSupertokensUserIDError()
+        if res["status"] == "USER_ID_MAPPING_ALREADY_EXISTS_ERROR":
+            return UserIdMappingAlreadyExistsError(
+                does_super_tokens_user_id_exist=res["doesSuperTokensUserIdExist"],
+                does_external_user_id_exist=res["does_external_user_id_exist"],
+            )
+
+        raise_general_exception("Unknown response")
+
+    raise_general_exception("Please upgrade the SuperTokens core to >= 3.15.0")
+
+
+
+async def delete_user(self, user_id: str, user_context: Optional[Dict[str, Any]]) ‑> None +
+
+
+
+ +Expand source code + +
async def delete_user(  # pylint: disable=no-self-use
+    self,
+    user_id: str,
+    user_context: Optional[Dict[str, Any]],
+) -> None:
+    querier = Querier.get_instance(None)
+
+    cdi_version = await querier.get_api_version(user_context)
+
+    if is_version_gte(cdi_version, "2.10"):
+        await querier.send_post_request(
+            NormalisedURLPath(USER_DELETE),
+            {"userId": user_id},
+            user_context=user_context,
+        )
+
+        return None
+    raise_general_exception("Please upgrade the SuperTokens core to >= 3.7.0")
+
+
+
+async def delete_user_id_mapping(self, user_id: str, user_id_type: Optional[UserIDTypes], force: Optional[bool], user_context: Optional[Dict[str, Any]]) ‑> DeleteUserIdMappingOkResult +
+
+
+
+ +Expand source code + +
async def delete_user_id_mapping(  # pylint: disable=no-self-use
+    self,
+    user_id: str,
+    user_id_type: Optional[UserIDTypes],
+    force: Optional[bool],
+    user_context: Optional[Dict[str, Any]],
+) -> DeleteUserIdMappingOkResult:
+    querier = Querier.get_instance(None)
+
+    cdi_version = await querier.get_api_version(user_context)
+
+    if is_version_gte(cdi_version, "2.15"):
+        body: Dict[str, Any] = {
+            "userId": user_id,
+            "userIdType": user_id_type,
+        }
+        if force:
+            body["force"] = force
+        res = await querier.send_post_request(
+            NormalisedURLPath("/recipe/userid/map/remove"),
+            body,
+            user_context=user_context,
+        )
+        if res["status"] == "OK":
+            return DeleteUserIdMappingOkResult(
+                did_mapping_exist=res["didMappingExist"]
+            )
+
+        raise_general_exception("Unknown response")
+
+    raise_general_exception("Please upgrade the SuperTokens core to >= 3.15.0")
+
+
+
+def get_all_cors_headers(self) ‑> List[str] +
+
+
+
+ +Expand source code + +
def get_all_cors_headers(self) -> List[str]:
+    headers_set: Set[str] = set()
+    headers_set.add(RID_KEY_HEADER)
+    headers_set.add(FDI_KEY_HEADER)
+    for recipe in self.recipe_modules:
+        headers = recipe.get_all_cors_headers()
+        for header in headers:
+            headers_set.add(header)
+
+    return list(headers_set)
+
+
+
+def get_request_from_user_context(self, user_context: Optional[Dict[str, Any]] = None) ‑> Optional[BaseRequest] +
+
+
+
+ +Expand source code + +
def get_request_from_user_context(  # pylint: disable=no-self-use
+    self,
+    user_context: Optional[Dict[str, Any]] = None,
+) -> Optional[BaseRequest]:
+    if user_context is None:
+        return None
+
+    if "_default" not in user_context:
+        return None
+
+    if not isinstance(user_context["_default"], dict):
+        return None
+
+    return user_context.get("_default", {}).get("request")
+
+
+
+async def get_user_count(self, include_recipe_ids: Union[None, List[str]], tenant_id: Optional[str] = None, user_context: Optional[Dict[str, Any]] = None) ‑> int +
+
+
+
+ +Expand source code + +
async def get_user_count(  # pylint: disable=no-self-use
+    self,
+    include_recipe_ids: Union[None, List[str]],
+    tenant_id: Optional[str] = None,
+    user_context: Optional[Dict[str, Any]] = None,
+) -> int:
+    querier = Querier.get_instance(None)
+    include_recipe_ids_str = None
+    if include_recipe_ids is not None:
+        include_recipe_ids_str = ",".join(include_recipe_ids)
+
+    response = await querier.send_get_request(
+        NormalisedURLPath(f"/{tenant_id or 'public'}{USER_COUNT}"),
+        {
+            "includeRecipeIds": include_recipe_ids_str,
+            "includeAllTenants": tenant_id is None,
+        },
+        user_context=user_context,
+    )
+
+    return int(response["count"])
+
+
+
+async def get_user_id_mapping(self, user_id: str, user_id_type: Optional[UserIDTypes], user_context: Optional[Dict[str, Any]]) ‑> Union[GetUserIdMappingOkResultUnknownMappingError] +
+
+
+
+ +Expand source code + +
async def get_user_id_mapping(  # pylint: disable=no-self-use
+    self,
+    user_id: str,
+    user_id_type: Optional[UserIDTypes],
+    user_context: Optional[Dict[str, Any]],
+) -> Union[GetUserIdMappingOkResult, UnknownMappingError]:
+    querier = Querier.get_instance(None)
+
+    cdi_version = await querier.get_api_version(user_context)
+
+    if is_version_gte(cdi_version, "2.15"):
+        body = {
+            "userId": user_id,
+        }
+        if user_id_type:
+            body["userIdType"] = user_id_type
+        res = await querier.send_get_request(
+            NormalisedURLPath("/recipe/userid/map"),
+            body,
+            user_context=user_context,
+        )
+        if res["status"] == "OK":
+            return GetUserIdMappingOkResult(
+                supertokens_user_id=res["superTokensUserId"],
+                external_user_id=res["externalUserId"],
+                external_user_info=res.get("externalUserIdInfo"),
+            )
+        if res["status"] == "UNKNOWN_MAPPING_ERROR":
+            return UnknownMappingError()
+
+        raise_general_exception("Unknown response")
+
+    raise_general_exception("Please upgrade the SuperTokens core to >= 3.15.0")
+
+
+
+async def get_users(self, tenant_id: str, time_joined_order: "Literal[('ASC', 'DESC')]", limit: Union[int, None], pagination_token: Union[str, None], include_recipe_ids: Union[None, List[str]], query: Union[Dict[str, str], None], user_context: Optional[Dict[str, Any]]) ‑> UsersResponse +
+
+
+
+ +Expand source code + +
async def get_users(  # pylint: disable=no-self-use
+    self,
+    tenant_id: str,
+    time_joined_order: Literal["ASC", "DESC"],
+    limit: Union[int, None],
+    pagination_token: Union[str, None],
+    include_recipe_ids: Union[None, List[str]],
+    query: Union[Dict[str, str], None],
+    user_context: Optional[Dict[str, Any]],
+) -> UsersResponse:
+
+    querier = Querier.get_instance(None)
+    params = {"timeJoinedOrder": time_joined_order}
+    if limit is not None:
+        params = {"limit": limit, **params}
+    if pagination_token is not None:
+        params = {"paginationToken": pagination_token, **params}
+
+    include_recipe_ids_str = None
+    if include_recipe_ids is not None:
+        include_recipe_ids_str = ",".join(include_recipe_ids)
+        params = {"includeRecipeIds": include_recipe_ids_str, **params}
+
+    if query is not None:
+        params = {**params, **query}
+
+    response = await querier.send_get_request(
+        NormalisedURLPath(f"/{tenant_id}{USERS}"), params, user_context=user_context
+    )
+    next_pagination_token = None
+    if "nextPaginationToken" in response:
+        next_pagination_token = response["nextPaginationToken"]
+    users_list = response["users"]
+    users: List[User] = []
+    for user in users_list:
+        recipe_id = user["recipeId"]
+        user_obj = user["user"]
+        third_party = None
+        if "thirdParty" in user_obj:
+            third_party = ThirdPartyInfo(
+                user_obj["thirdParty"]["userId"], user_obj["thirdParty"]["id"]
+            )
+        email = None
+        if "email" in user_obj:
+            email = user_obj["email"]
+        phone_number = None
+        if "phoneNumber" in user_obj:
+            phone_number = user_obj["phoneNumber"]
+        users.append(
+            User(
+                recipe_id,
+                user_obj["id"],
+                user_obj["timeJoined"],
+                email,
+                phone_number,
+                third_party,
+                user_obj["tenantIds"],
+            )
+        )
+
+    return UsersResponse(users, next_pagination_token)
+
+
+
+async def handle_supertokens_error(self, request: BaseRequest, err: Exception, response: BaseResponse, user_context: Dict[str, Any]) +
+
+
+
+ +Expand source code + +
async def handle_supertokens_error(
+    self,
+    request: BaseRequest,
+    err: Exception,
+    response: BaseResponse,
+    user_context: Dict[str, Any],
+):
+    log_debug_message("errorHandler: Started")
+    log_debug_message(
+        "errorHandler: Error is from SuperTokens recipe. Message: %s", str(err)
+    )
+    if isinstance(err, GeneralError):
+        raise err
+
+    if isinstance(err, BadInputError):
+        log_debug_message("errorHandler: Sending 400 status code response")
+        return send_non_200_response_with_message(str(err), 400, response)
+
+    for recipe in self.recipe_modules:
+        log_debug_message(
+            "errorHandler: Checking recipe for match: %s", recipe.get_recipe_id()
+        )
+        if recipe.is_error_from_this_recipe_based_on_instance(err) and isinstance(
+            err, SuperTokensError
+        ):
+            log_debug_message(
+                "errorHandler: Matched with recipeID: %s", recipe.get_recipe_id()
+            )
+            return await recipe.handle_error(request, err, response, user_context)
+    raise err
+
+
+
+async def middleware(self, request: BaseRequest, response: BaseResponse, user_context: Dict[str, Any]) ‑> Union[BaseResponse, None] +
+
+
+
+ +Expand source code + +
async def middleware(  # pylint: disable=no-self-use
+    self, request: BaseRequest, response: BaseResponse, user_context: Dict[str, Any]
+) -> Union[BaseResponse, None]:
+    log_debug_message("middleware: Started")
+    path = Supertokens.get_instance().app_info.api_gateway_path.append(
+        NormalisedURLPath(request.get_path())
+    )
+    method = normalise_http_method(request.method())
+
+    if not path.startswith(Supertokens.get_instance().app_info.api_base_path):
+        log_debug_message(
+            "middleware: Not handling because request path did not start with api base path. Request path: %s",
+            path.get_as_string_dangerous(),
+        )
+        return None
+    request_rid = get_rid_from_header(request)
+    log_debug_message(
+        "middleware: requestRID is: %s", get_maybe_none_as_str(request_rid)
+    )
+    if request_rid is not None and request_rid == "anti-csrf":
+        # see https://github.com/supertokens/supertokens-python/issues/54
+        request_rid = None
+
+    async def handle_without_rid():
+        for recipe in self.recipe_modules:
+            log_debug_message(
+                "middleware: Checking recipe ID for match: %s with path: %s and method: %s",
+                recipe.get_recipe_id(),
+                path.get_as_string_dangerous(),
+                method,
+            )
+            api_and_tenant_id = await recipe.return_api_id_if_can_handle_request(
+                path, method, user_context
+            )
+            if api_and_tenant_id is not None:
+                log_debug_message(
+                    "middleware: Request being handled by recipe. ID is: %s",
+                    api_and_tenant_id.api_id,
+                )
+                api_resp = await recipe.handle_api_request(
+                    api_and_tenant_id.api_id,
+                    api_and_tenant_id.tenant_id,
+                    request,
+                    path,
+                    method,
+                    response,
+                    user_context,
+                )
+                if api_resp is None:
+                    log_debug_message(
+                        "middleware: Not handled because API returned None"
+                    )
+                    return None
+                log_debug_message("middleware: Ended")
+                return api_resp
+        log_debug_message("middleware: Not handling because no recipe matched")
+        return None
+
+    if request_rid is not None:
+        matched_recipes = [
+            recipe
+            for recipe in self.recipe_modules
+            if recipe.get_recipe_id() == request_rid
+            or (
+                request_rid == "thirdpartyemailpassword"
+                and recipe.get_recipe_id() in ["thirdparty", "emailpassword"]
+            )
+            or (
+                request_rid == "thirdpartypasswordless"
+                and recipe.get_recipe_id() in ["thirdparty", "passwordless"]
+            )
+        ]
+
+        if len(matched_recipes) == 0:
+            log_debug_message(
+                "middleware: Not handling based on rid match. Trying without rid."
+            )
+            return await handle_without_rid()
+
+        for recipe in matched_recipes:
+            log_debug_message(
+                "middleware: Matched with recipe Ids: %s", recipe.get_recipe_id()
+            )
+
+        id_result = None
+        final_matched_recipe = None
+        for recipe in matched_recipes:
+            current_id_result = await recipe.return_api_id_if_can_handle_request(
+                path, method, user_context
+            )
+            if current_id_result is not None:
+                if id_result is not None:
+                    raise ValueError(
+                        "Two recipes have matched the same API path and method! This is a bug in the SDK. Please contact support."
+                    )
+                final_matched_recipe = recipe
+                id_result = current_id_result
+
+        if id_result is None or final_matched_recipe is None:
+            return await handle_without_rid()
+
+        log_debug_message(
+            "middleware: Request being handled by recipe. ID is: %s",
+            id_result.api_id,
+        )
+        request_handled = await final_matched_recipe.handle_api_request(
+            id_result.api_id,
+            id_result.tenant_id,
+            request,
+            path,
+            method,
+            response,
+            user_context,
+        )
+        if request_handled is None:
+            log_debug_message(
+                "middleware: Not handled because API returned request_handled as None"
+            )
+            return None
+        log_debug_message("middleware: Ended")
+        return request_handled
+    return await handle_without_rid()
+
+
+
+async def update_or_delete_user_id_mapping_info(self, user_id: str, user_id_type: Optional[UserIDTypes], external_user_id_info: Optional[str], user_context: Optional[Dict[str, Any]]) ‑> Union[UpdateOrDeleteUserIdMappingInfoOkResultUnknownMappingError] +
+
+
+
+ +Expand source code + +
async def update_or_delete_user_id_mapping_info(  # pylint: disable=no-self-use
+    self,
+    user_id: str,
+    user_id_type: Optional[UserIDTypes],
+    external_user_id_info: Optional[str],
+    user_context: Optional[Dict[str, Any]],
+) -> Union[UpdateOrDeleteUserIdMappingInfoOkResult, UnknownMappingError]:
+    querier = Querier.get_instance(None)
+
+    cdi_version = await querier.get_api_version(user_context)
+
+    if is_version_gte(cdi_version, "2.15"):
+        res = await querier.send_post_request(
+            NormalisedURLPath("/recipe/userid/external-user-id-info"),
+            {
+                "userId": user_id,
+                "userIdType": user_id_type,
+                "externalUserIdInfo": external_user_id_info,
+            },
+            user_context=user_context,
+        )
+        if res["status"] == "OK":
+            return UpdateOrDeleteUserIdMappingInfoOkResult()
+        if res["status"] == "UNKNOWN_MAPPING_ERROR":
+            return UnknownMappingError()
+
+        raise_general_exception("Unknown response")
+
+    raise_general_exception("Please upgrade the SuperTokens core to >= 3.15.0")
+
+
+
+
+
+class SupertokensConfig +(connection_uri: str, api_key: Union[str, None] = None, network_interceptor: Optional[Callable[[str, str, Dict[str, Any], Optional[Dict[str, Any]], Optional[Dict[str, Any]], Optional[Dict[str, Any]]], Tuple[str, str, Dict[str, Any], Optional[Dict[str, Any]], Optional[Dict[str, Any]]]]] = None, disable_core_call_cache: bool = False) +
+
+
+
+ +Expand source code + +
class SupertokensConfig:
+    def __init__(
+        self,
+        connection_uri: str,
+        api_key: Union[str, None] = None,
+        network_interceptor: Optional[
+            Callable[
+                [
+                    str,
+                    str,
+                    Dict[str, Any],
+                    Optional[Dict[str, Any]],
+                    Optional[Dict[str, Any]],
+                    Optional[Dict[str, Any]],
+                ],
+                Tuple[
+                    str,
+                    str,
+                    Dict[str, Any],
+                    Optional[Dict[str, Any]],
+                    Optional[Dict[str, Any]],
+                ],
+            ]
+        ] = None,
+        disable_core_call_cache: bool = False,
+    ):  # We keep this = None here because this is directly used by the user.
+        self.connection_uri = connection_uri
+        self.api_key = api_key
+        self.network_interceptor = network_interceptor
+        self.disable_core_call_cache = disable_core_call_cache
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/syncio/index.html b/html/supertokens_python/syncio/index.html new file mode 100644 index 000000000..bc507633f --- /dev/null +++ b/html/supertokens_python/syncio/index.html @@ -0,0 +1,409 @@ + + + + + + +supertokens_python.syncio API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.syncio

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from typing import Dict, List, Optional, Union, Any
+
+from supertokens_python import Supertokens
+from supertokens_python.async_to_sync_wrapper import sync
+from supertokens_python.interfaces import (
+    CreateUserIdMappingOkResult,
+    DeleteUserIdMappingOkResult,
+    GetUserIdMappingOkResult,
+    UnknownMappingError,
+    UnknownSupertokensUserIDError,
+    UpdateOrDeleteUserIdMappingInfoOkResult,
+    UserIdMappingAlreadyExistsError,
+    UserIDTypes,
+)
+from supertokens_python.types import UsersResponse
+
+
+def get_users_oldest_first(
+    tenant_id: str,
+    limit: Union[int, None] = None,
+    pagination_token: Union[str, None] = None,
+    include_recipe_ids: Union[None, List[str]] = None,
+    query: Union[None, Dict[str, str]] = None,
+    user_context: Optional[Dict[str, Any]] = None,
+) -> UsersResponse:
+    return sync(
+        Supertokens.get_instance().get_users(
+            tenant_id,
+            "ASC",
+            limit,
+            pagination_token,
+            include_recipe_ids,
+            query,
+            user_context,
+        )
+    )
+
+
+def get_users_newest_first(
+    tenant_id: str,
+    limit: Union[int, None] = None,
+    pagination_token: Union[str, None] = None,
+    include_recipe_ids: Union[None, List[str]] = None,
+    query: Union[None, Dict[str, str]] = None,
+    user_context: Optional[Dict[str, Any]] = None,
+) -> UsersResponse:
+    return sync(
+        Supertokens.get_instance().get_users(
+            tenant_id,
+            "DESC",
+            limit,
+            pagination_token,
+            include_recipe_ids,
+            query,
+            user_context,
+        )
+    )
+
+
+def get_user_count(
+    include_recipe_ids: Union[None, List[str]] = None,
+    tenant_id: Optional[str] = None,
+    user_context: Optional[Dict[str, Any]] = None,
+) -> int:
+    return sync(
+        Supertokens.get_instance().get_user_count(
+            include_recipe_ids, tenant_id, user_context
+        )
+    )
+
+
+def delete_user(user_id: str, user_context: Optional[Dict[str, Any]] = None) -> None:
+    return sync(Supertokens.get_instance().delete_user(user_id, user_context))
+
+
+def create_user_id_mapping(
+    supertokens_user_id: str,
+    external_user_id: str,
+    external_user_id_info: Optional[str] = None,
+    force: Optional[bool] = None,
+    user_context: Optional[Dict[str, Any]] = None,
+) -> Union[
+    CreateUserIdMappingOkResult,
+    UnknownSupertokensUserIDError,
+    UserIdMappingAlreadyExistsError,
+]:
+    return sync(
+        Supertokens.get_instance().create_user_id_mapping(
+            supertokens_user_id,
+            external_user_id,
+            external_user_id_info,
+            force=force,
+            user_context=user_context,
+        )
+    )
+
+
+def get_user_id_mapping(
+    user_id: str,
+    user_id_type: Optional[UserIDTypes] = None,
+    user_context: Optional[Dict[str, Any]] = None,
+) -> Union[GetUserIdMappingOkResult, UnknownMappingError]:
+    return sync(
+        Supertokens.get_instance().get_user_id_mapping(
+            user_id, user_id_type, user_context
+        )
+    )
+
+
+def delete_user_id_mapping(
+    user_id: str,
+    user_id_type: Optional[UserIDTypes] = None,
+    force: Optional[bool] = None,
+    user_context: Optional[Dict[str, Any]] = None,
+) -> DeleteUserIdMappingOkResult:
+    return sync(
+        Supertokens.get_instance().delete_user_id_mapping(
+            user_id, user_id_type, force=force, user_context=user_context
+        )
+    )
+
+
+def update_or_delete_user_id_mapping_info(
+    user_id: str,
+    user_id_type: Optional[UserIDTypes] = None,
+    external_user_id_info: Optional[str] = None,
+    user_context: Optional[Dict[str, Any]] = None,
+) -> Union[UpdateOrDeleteUserIdMappingInfoOkResult, UnknownMappingError]:
+    return sync(
+        Supertokens.get_instance().update_or_delete_user_id_mapping_info(
+            user_id, user_id_type, external_user_id_info, user_context
+        )
+    )
+
+
+
+
+
+
+
+

Functions

+
+
+def create_user_id_mapping(supertokens_user_id: str, external_user_id: str, external_user_id_info: Optional[str] = None, force: Optional[bool] = None, user_context: Optional[Dict[str, Any]] = None) ‑> Union[CreateUserIdMappingOkResultUnknownSupertokensUserIDErrorUserIdMappingAlreadyExistsError] +
+
+
+
+ +Expand source code + +
def create_user_id_mapping(
+    supertokens_user_id: str,
+    external_user_id: str,
+    external_user_id_info: Optional[str] = None,
+    force: Optional[bool] = None,
+    user_context: Optional[Dict[str, Any]] = None,
+) -> Union[
+    CreateUserIdMappingOkResult,
+    UnknownSupertokensUserIDError,
+    UserIdMappingAlreadyExistsError,
+]:
+    return sync(
+        Supertokens.get_instance().create_user_id_mapping(
+            supertokens_user_id,
+            external_user_id,
+            external_user_id_info,
+            force=force,
+            user_context=user_context,
+        )
+    )
+
+
+
+def delete_user(user_id: str, user_context: Optional[Dict[str, Any]] = None) ‑> None +
+
+
+
+ +Expand source code + +
def delete_user(user_id: str, user_context: Optional[Dict[str, Any]] = None) -> None:
+    return sync(Supertokens.get_instance().delete_user(user_id, user_context))
+
+
+
+def delete_user_id_mapping(user_id: str, user_id_type: Optional[typing_extensions.Literal['SUPERTOKENS', 'EXTERNAL', 'ANY']] = None, force: Optional[bool] = None, user_context: Optional[Dict[str, Any]] = None) ‑> DeleteUserIdMappingOkResult +
+
+
+
+ +Expand source code + +
def delete_user_id_mapping(
+    user_id: str,
+    user_id_type: Optional[UserIDTypes] = None,
+    force: Optional[bool] = None,
+    user_context: Optional[Dict[str, Any]] = None,
+) -> DeleteUserIdMappingOkResult:
+    return sync(
+        Supertokens.get_instance().delete_user_id_mapping(
+            user_id, user_id_type, force=force, user_context=user_context
+        )
+    )
+
+
+
+def get_user_count(include_recipe_ids: Optional[None] = None, tenant_id: Optional[str] = None, user_context: Optional[Dict[str, Any]] = None) ‑> int +
+
+
+
+ +Expand source code + +
def get_user_count(
+    include_recipe_ids: Union[None, List[str]] = None,
+    tenant_id: Optional[str] = None,
+    user_context: Optional[Dict[str, Any]] = None,
+) -> int:
+    return sync(
+        Supertokens.get_instance().get_user_count(
+            include_recipe_ids, tenant_id, user_context
+        )
+    )
+
+
+
+def get_user_id_mapping(user_id: str, user_id_type: Optional[typing_extensions.Literal['SUPERTOKENS', 'EXTERNAL', 'ANY']] = None, user_context: Optional[Dict[str, Any]] = None) ‑> Union[GetUserIdMappingOkResultUnknownMappingError] +
+
+
+
+ +Expand source code + +
def get_user_id_mapping(
+    user_id: str,
+    user_id_type: Optional[UserIDTypes] = None,
+    user_context: Optional[Dict[str, Any]] = None,
+) -> Union[GetUserIdMappingOkResult, UnknownMappingError]:
+    return sync(
+        Supertokens.get_instance().get_user_id_mapping(
+            user_id, user_id_type, user_context
+        )
+    )
+
+
+
+def get_users_newest_first(tenant_id: str, limit: Optional[int] = None, pagination_token: Optional[str] = None, include_recipe_ids: Optional[None] = None, query: Optional[Dict[str, str]] = None, user_context: Optional[Dict[str, Any]] = None) ‑> UsersResponse +
+
+
+
+ +Expand source code + +
def get_users_newest_first(
+    tenant_id: str,
+    limit: Union[int, None] = None,
+    pagination_token: Union[str, None] = None,
+    include_recipe_ids: Union[None, List[str]] = None,
+    query: Union[None, Dict[str, str]] = None,
+    user_context: Optional[Dict[str, Any]] = None,
+) -> UsersResponse:
+    return sync(
+        Supertokens.get_instance().get_users(
+            tenant_id,
+            "DESC",
+            limit,
+            pagination_token,
+            include_recipe_ids,
+            query,
+            user_context,
+        )
+    )
+
+
+
+def get_users_oldest_first(tenant_id: str, limit: Optional[int] = None, pagination_token: Optional[str] = None, include_recipe_ids: Optional[None] = None, query: Optional[Dict[str, str]] = None, user_context: Optional[Dict[str, Any]] = None) ‑> UsersResponse +
+
+
+
+ +Expand source code + +
def get_users_oldest_first(
+    tenant_id: str,
+    limit: Union[int, None] = None,
+    pagination_token: Union[str, None] = None,
+    include_recipe_ids: Union[None, List[str]] = None,
+    query: Union[None, Dict[str, str]] = None,
+    user_context: Optional[Dict[str, Any]] = None,
+) -> UsersResponse:
+    return sync(
+        Supertokens.get_instance().get_users(
+            tenant_id,
+            "ASC",
+            limit,
+            pagination_token,
+            include_recipe_ids,
+            query,
+            user_context,
+        )
+    )
+
+
+
+def update_or_delete_user_id_mapping_info(user_id: str, user_id_type: Optional[typing_extensions.Literal['SUPERTOKENS', 'EXTERNAL', 'ANY']] = None, external_user_id_info: Optional[str] = None, user_context: Optional[Dict[str, Any]] = None) ‑> Union[UpdateOrDeleteUserIdMappingInfoOkResultUnknownMappingError] +
+
+
+
+ +Expand source code + +
def update_or_delete_user_id_mapping_info(
+    user_id: str,
+    user_id_type: Optional[UserIDTypes] = None,
+    external_user_id_info: Optional[str] = None,
+    user_context: Optional[Dict[str, Any]] = None,
+) -> Union[UpdateOrDeleteUserIdMappingInfoOkResult, UnknownMappingError]:
+    return sync(
+        Supertokens.get_instance().update_or_delete_user_id_mapping_info(
+            user_id, user_id_type, external_user_id_info, user_context
+        )
+    )
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/types.html b/html/supertokens_python/types.html new file mode 100644 index 000000000..7121fad8d --- /dev/null +++ b/html/supertokens_python/types.html @@ -0,0 +1,425 @@ + + + + + + +supertokens_python.types API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.types

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from abc import ABC, abstractmethod
+from typing import Any, Awaitable, Dict, List, TypeVar, Union
+
+_T = TypeVar("_T")
+
+
+class ThirdPartyInfo:
+    def __init__(self, third_party_user_id: str, third_party_id: str):
+        self.user_id = third_party_user_id
+        self.id = third_party_id
+
+
+class User:
+    def __init__(
+        self,
+        recipe_id: str,
+        user_id: str,
+        time_joined: int,
+        email: Union[str, None],
+        phone_number: Union[str, None],
+        third_party_info: Union[ThirdPartyInfo, None],
+        tenant_ids: List[str],
+    ):
+        self.recipe_id = recipe_id
+        self.user_id = user_id
+        self.email = email
+        self.time_joined = time_joined
+        self.third_party_info = third_party_info
+        self.phone_number = phone_number
+        self.tenant_ids = tenant_ids
+
+    def to_json(self) -> Dict[str, Any]:
+        res: Dict[str, Any] = {
+            "recipeId": self.recipe_id,
+            "user": {
+                "id": self.user_id,
+                "timeJoined": self.time_joined,
+                "tenantIds": self.tenant_ids,
+            },
+        }
+
+        if self.email is not None:
+            res["user"]["email"] = self.email
+        if self.phone_number is not None:
+            res["user"]["phoneNumber"] = self.phone_number
+        if self.third_party_info is not None:
+            res["user"]["thirdParty"] = self.third_party_info.__dict__
+
+        return res
+
+
+class UsersResponse:
+    def __init__(self, users: List[User], next_pagination_token: Union[str, None]):
+        self.users: List[User] = users
+        self.next_pagination_token: Union[str, None] = next_pagination_token
+
+
+class APIResponse(ABC):
+    @abstractmethod
+    def to_json(self) -> Dict[str, Any]:
+        pass
+
+
+class GeneralErrorResponse(APIResponse):
+    def __init__(self, message: str):
+        self.status = "GENERAL_ERROR"
+        self.message = message
+
+    def to_json(self) -> Dict[str, Any]:
+        return {"status": self.status, "message": self.message}
+
+
+MaybeAwaitable = Union[Awaitable[_T], _T]
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class APIResponse +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class APIResponse(ABC):
+    @abstractmethod
+    def to_json(self) -> Dict[str, Any]:
+        pass
+
+

Ancestors

+
    +
  • abc.ABC
  • +
+

Subclasses

+ +

Methods

+
+
+def to_json(self) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
@abstractmethod
+def to_json(self) -> Dict[str, Any]:
+    pass
+
+
+
+
+
+class GeneralErrorResponse +(message: str) +
+
+

Helper class that provides a standard way to create an ABC using +inheritance.

+
+ +Expand source code + +
class GeneralErrorResponse(APIResponse):
+    def __init__(self, message: str):
+        self.status = "GENERAL_ERROR"
+        self.message = message
+
+    def to_json(self) -> Dict[str, Any]:
+        return {"status": self.status, "message": self.message}
+
+

Ancestors

+ +

Methods

+
+
+def to_json(self) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
def to_json(self) -> Dict[str, Any]:
+    return {"status": self.status, "message": self.message}
+
+
+
+
+
+class ThirdPartyInfo +(third_party_user_id: str, third_party_id: str) +
+
+
+
+ +Expand source code + +
class ThirdPartyInfo:
+    def __init__(self, third_party_user_id: str, third_party_id: str):
+        self.user_id = third_party_user_id
+        self.id = third_party_id
+
+
+
+class User +(recipe_id: str, user_id: str, time_joined: int, email: Optional[str], phone_number: Optional[str], third_party_info: Optional[ThirdPartyInfo], tenant_ids: List[str]) +
+
+
+
+ +Expand source code + +
class User:
+    def __init__(
+        self,
+        recipe_id: str,
+        user_id: str,
+        time_joined: int,
+        email: Union[str, None],
+        phone_number: Union[str, None],
+        third_party_info: Union[ThirdPartyInfo, None],
+        tenant_ids: List[str],
+    ):
+        self.recipe_id = recipe_id
+        self.user_id = user_id
+        self.email = email
+        self.time_joined = time_joined
+        self.third_party_info = third_party_info
+        self.phone_number = phone_number
+        self.tenant_ids = tenant_ids
+
+    def to_json(self) -> Dict[str, Any]:
+        res: Dict[str, Any] = {
+            "recipeId": self.recipe_id,
+            "user": {
+                "id": self.user_id,
+                "timeJoined": self.time_joined,
+                "tenantIds": self.tenant_ids,
+            },
+        }
+
+        if self.email is not None:
+            res["user"]["email"] = self.email
+        if self.phone_number is not None:
+            res["user"]["phoneNumber"] = self.phone_number
+        if self.third_party_info is not None:
+            res["user"]["thirdParty"] = self.third_party_info.__dict__
+
+        return res
+
+

Methods

+
+
+def to_json(self) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
def to_json(self) -> Dict[str, Any]:
+    res: Dict[str, Any] = {
+        "recipeId": self.recipe_id,
+        "user": {
+            "id": self.user_id,
+            "timeJoined": self.time_joined,
+            "tenantIds": self.tenant_ids,
+        },
+    }
+
+    if self.email is not None:
+        res["user"]["email"] = self.email
+    if self.phone_number is not None:
+        res["user"]["phoneNumber"] = self.phone_number
+    if self.third_party_info is not None:
+        res["user"]["thirdParty"] = self.third_party_info.__dict__
+
+    return res
+
+
+
+
+
+class UsersResponse +(users: List[User], next_pagination_token: Optional[str]) +
+
+
+
+ +Expand source code + +
class UsersResponse:
+    def __init__(self, users: List[User], next_pagination_token: Union[str, None]):
+        self.users: List[User] = users
+        self.next_pagination_token: Union[str, None] = next_pagination_token
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/html/supertokens_python/utils.html b/html/supertokens_python/utils.html new file mode 100644 index 000000000..02f4c591e --- /dev/null +++ b/html/supertokens_python/utils.html @@ -0,0 +1,1046 @@ + + + + + + +supertokens_python.utils API documentation + + + + + + + + + + + +
+
+
+

Module supertokens_python.utils

+
+
+
+ +Expand source code + +
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+#
+# This software is licensed under the Apache License, Version 2.0 (the
+# "License") as published by the Apache Software Foundation.
+#
+# You may not use this file except in compliance with the License. You may
+# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from __future__ import annotations
+
+import json
+import threading
+import warnings
+from base64 import urlsafe_b64decode, urlsafe_b64encode, b64encode, b64decode
+from math import floor
+from re import fullmatch
+from time import time
+from typing import (
+    TYPE_CHECKING,
+    Any,
+    Awaitable,
+    Callable,
+    Dict,
+    List,
+    TypeVar,
+    Union,
+    Optional,
+)
+from urllib.parse import urlparse
+
+from httpx import HTTPStatusError, Response
+from tldextract import extract  # type: ignore
+
+from supertokens_python.framework.django.framework import DjangoFramework
+from supertokens_python.framework.fastapi.framework import FastapiFramework
+from supertokens_python.framework.flask.framework import FlaskFramework
+from supertokens_python.framework.request import BaseRequest
+from supertokens_python.framework.response import BaseResponse
+from supertokens_python.logger import log_debug_message
+
+from .constants import ERROR_MESSAGE_KEY, RID_KEY_HEADER
+from .exceptions import raise_general_exception
+from .types import MaybeAwaitable
+
+_T = TypeVar("_T")
+
+if TYPE_CHECKING:
+    pass
+
+
+FRAMEWORKS = {
+    "fastapi": FastapiFramework(),
+    "flask": FlaskFramework(),
+    "django": DjangoFramework(),
+}
+
+
+def is_an_ip_address(ip_address: str) -> bool:
+    return (
+        fullmatch(
+            r"^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|["
+            r"01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$",
+            ip_address,
+        )
+        is not None
+    )
+
+
+def normalise_http_method(method: str) -> str:
+    return method.lower()
+
+
+def get_rid_from_header(request: BaseRequest) -> Union[str, None]:
+    return get_header(request, RID_KEY_HEADER)
+
+
+def get_header(request: BaseRequest, key: str) -> Union[str, None]:
+    return request.get_header(key)
+
+
+def find_max_version(versions_1: List[str], versions_2: List[str]) -> Union[str, None]:
+    versions = list(set(versions_1) & set(versions_2))
+    if len(versions) == 0:
+        return None
+
+    max_v = versions[0]
+    for i in range(1, len(versions)):
+        version = versions[i]
+        max_v = _get_max_version(max_v, version)
+
+    return max_v
+
+
+def is_version_gte(version: str, minimum_version: str) -> bool:
+    return _get_max_version(version, minimum_version) == version
+
+
+def _get_max_version(v1: str, v2: str) -> str:
+    v1_split = v1.split(".")
+    v2_split = v2.split(".")
+    max_loop = min(len(v1_split), len(v2_split))
+
+    for i in range(max_loop):
+        if int(v1_split[i]) > int(v2_split[i]):
+            return v1
+        if int(v2_split[i]) > int(v1_split[i]):
+            return v2
+
+    if len(v1_split) > len(v2_split):
+        return v1
+
+    return v2
+
+
+def is_4xx_error(status_code: int) -> bool:
+    return status_code // 100 == 4
+
+
+def is_5xx_error(status_code: int) -> bool:
+    return status_code // 100 == 5
+
+
+def send_non_200_response(
+    body: Dict[str, Any], status_code: int, response: BaseResponse
+) -> BaseResponse:
+    if status_code < 300:
+        raise_general_exception("Calling sendNon200Response with status code < 300")
+    log_debug_message(
+        "Sending response to client with status code: %s", str(status_code)
+    )
+    response.set_status_code(status_code)
+    response.set_json_content(content=body)
+    return response
+
+
+def send_non_200_response_with_message(
+    message: str, status_code: int, response: BaseResponse
+) -> BaseResponse:
+    return send_non_200_response({ERROR_MESSAGE_KEY: message}, status_code, response)
+
+
+def send_unauthorised_access_response(response: BaseResponse) -> BaseResponse:
+    return send_non_200_response_with_message("Unauthorised access", 401, response)
+
+
+def send_200_response(
+    data_json: Dict[str, Any], response: BaseResponse
+) -> BaseResponse:
+    log_debug_message("Sending response to client with status code: 200")
+    response.set_json_content(data_json)
+    response.set_status_code(200)
+    return response
+
+
+def get_timestamp_ms() -> int:
+    return int(time() * 1000)
+
+
+def utf_base64encode(s: str, urlsafe: bool) -> str:
+    if urlsafe:
+        return urlsafe_b64encode(s.encode("utf-8")).decode("utf-8")
+
+    return b64encode(s.encode("utf-8")).decode("utf-8")
+
+
+def utf_base64decode(s: str, urlsafe: bool) -> str:
+    # Adding extra "==" based on
+    # https://stackoverflow.com/questions/2941995/python-ignore-incorrect-padding-error-when-base64-decoding
+    # Otherwise it can raise "incorrect padding" error
+    if urlsafe:
+        return urlsafe_b64decode(s.encode("utf-8") + b"==").decode("utf-8")
+
+    return b64decode(s.encode("utf-8")).decode("utf-8")
+
+
+def get_filtered_list(func: Callable[[_T], bool], given_list: List[_T]) -> List[_T]:
+    return list(filter(func, given_list))
+
+
+def find_first_occurrence_in_list(
+    condition: Callable[[_T], bool], given_list: List[_T]
+) -> Union[_T, None]:
+    for item in given_list:
+        if condition(item):
+            return item
+    return None
+
+
+def frontend_has_interceptor(request: BaseRequest) -> bool:
+    return get_rid_from_header(request) is not None
+
+
+def deprecated_warn(msg: str):
+    warnings.warn(msg, DeprecationWarning, stacklevel=2)
+
+
+def handle_httpx_client_exceptions(
+    e: Exception, input_: Union[Dict[str, Any], None] = None
+):
+    if isinstance(e, HTTPStatusError) and isinstance(e.response, Response):  # type: ignore
+        res = e.response  # type: ignore
+        log_debug_message("Error status: %s", res.status_code)  # type: ignore
+        log_debug_message("Error response: %s", res.json())
+    else:
+        log_debug_message("Error: %s", str(e))
+
+    if input_ is not None:
+        log_debug_message("Logging the input:")
+        log_debug_message("%s", json.dumps(input_))
+
+
+def humanize_time(ms: int) -> str:
+    t = floor(ms / 1000)
+    suffix = ""
+
+    if t < 60:
+        if t > 1:
+            suffix = "s"
+        time_str = f"{t} second{suffix}"
+    elif t < 3600:
+        m = floor(t / 60)
+        if m > 1:
+            suffix = "s"
+        time_str = f"{m} minute{suffix}"
+    else:
+        h = floor(t / 360) / 10
+        if h > 1:
+            suffix = "s"
+        if h % 1 == 0:
+            h = int(h)
+        time_str = f"{h} hour{suffix}"
+
+    return time_str
+
+
+def set_request_in_user_context_if_not_defined(
+    user_context: Optional[Dict[str, Any]], request: BaseRequest
+) -> Dict[str, Any]:
+    if user_context is None:
+        user_context = {}
+
+    if "_default" not in user_context:
+        user_context["_default"] = {}
+
+    if isinstance(user_context["_default"], dict):
+        user_context["_default"]["request"] = request
+        user_context["_default"]["keep_cache_alive"] = True
+
+    return user_context
+
+
+def default_user_context(request: BaseRequest) -> Dict[str, Any]:
+    return set_request_in_user_context_if_not_defined({}, request)
+
+
+async def resolve(obj: MaybeAwaitable[_T]) -> _T:
+    """Returns value or value of awaitable object passed"""
+    if isinstance(obj, Awaitable):
+        return await obj  # type: ignore
+    return obj  # type: ignore
+
+
+def get_top_level_domain_for_same_site_resolution(url: str) -> str:
+    url_obj = urlparse(url)
+    hostname = url_obj.hostname
+
+    if hostname is None:
+        raise Exception("Should not come here")
+
+    if hostname.startswith("localhost") or is_an_ip_address(hostname):
+        return "localhost"
+
+    parsed_url: Any = extract(hostname, include_psl_private_domains=True)
+    if parsed_url.domain == "":  # type: ignore
+        # We need to do this because of https://github.com/supertokens/supertokens-python/issues/394
+        if hostname.endswith(".amazonaws.com") and parsed_url.suffix == hostname:
+            return hostname
+
+        raise Exception(
+            "Please make sure that the apiDomain and websiteDomain have correct values"
+        )
+
+    return parsed_url.domain + "." + parsed_url.suffix  # type: ignore
+
+
+class RWMutex:
+    def __init__(self):
+        self._lock = threading.Lock()
+        self._readers = threading.Condition(self._lock)
+        self._writers = threading.Condition(self._lock)
+        self._reader_count = 0
+        self._writer_count = 0
+
+    def lock(self):
+        with self._lock:
+            while self._writer_count > 0 or self._reader_count > 0:
+                self._writers.wait()
+            self._writer_count += 1
+
+    def unlock(self):
+        with self._lock:
+            self._writer_count -= 1
+            self._readers.notify_all()
+            self._writers.notify_all()
+
+    def r_lock(self):
+        with self._lock:
+            while self._writer_count > 0:
+                self._readers.wait()
+            self._reader_count += 1
+
+    def r_unlock(self):
+        with self._lock:
+            self._reader_count -= 1
+            if self._reader_count == 0:
+                self._writers.notify_all()
+
+
+class RWLockContext:
+    def __init__(self, mutex: RWMutex, read: bool = True):
+        self.mutex = mutex
+        self.read = read
+
+    def __enter__(self):
+        if self.read:
+            self.mutex.r_lock()
+        else:
+            self.mutex.lock()
+
+    def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any):
+        if self.read:
+            self.mutex.r_unlock()
+        else:
+            self.mutex.unlock()
+
+        if exc_type is not None:
+            raise exc_type(exc_value).with_traceback(traceback)
+
+
+def normalise_email(email: str) -> str:
+    return email.strip().lower()
+
+
+
+
+
+
+
+

Functions

+
+
+def default_user_context(request: BaseRequest) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
def default_user_context(request: BaseRequest) -> Dict[str, Any]:
+    return set_request_in_user_context_if_not_defined({}, request)
+
+
+
+def deprecated_warn(msg: str) +
+
+
+
+ +Expand source code + +
def deprecated_warn(msg: str):
+    warnings.warn(msg, DeprecationWarning, stacklevel=2)
+
+
+
+def find_first_occurrence_in_list(condition: Callable[[_T], bool], given_list: List[_T]) ‑> Optional[~_T] +
+
+
+
+ +Expand source code + +
def find_first_occurrence_in_list(
+    condition: Callable[[_T], bool], given_list: List[_T]
+) -> Union[_T, None]:
+    for item in given_list:
+        if condition(item):
+            return item
+    return None
+
+
+
+def find_max_version(versions_1: List[str], versions_2: List[str]) ‑> Optional[str] +
+
+
+
+ +Expand source code + +
def find_max_version(versions_1: List[str], versions_2: List[str]) -> Union[str, None]:
+    versions = list(set(versions_1) & set(versions_2))
+    if len(versions) == 0:
+        return None
+
+    max_v = versions[0]
+    for i in range(1, len(versions)):
+        version = versions[i]
+        max_v = _get_max_version(max_v, version)
+
+    return max_v
+
+
+
+def frontend_has_interceptor(request: BaseRequest) ‑> bool +
+
+
+
+ +Expand source code + +
def frontend_has_interceptor(request: BaseRequest) -> bool:
+    return get_rid_from_header(request) is not None
+
+
+
+def get_filtered_list(func: Callable[[_T], bool], given_list: List[_T]) ‑> List[~_T] +
+
+
+
+ +Expand source code + +
def get_filtered_list(func: Callable[[_T], bool], given_list: List[_T]) -> List[_T]:
+    return list(filter(func, given_list))
+
+
+
+def get_header(request: BaseRequest, key: str) ‑> Optional[str] +
+
+
+
+ +Expand source code + +
def get_header(request: BaseRequest, key: str) -> Union[str, None]:
+    return request.get_header(key)
+
+
+
+def get_rid_from_header(request: BaseRequest) ‑> Optional[str] +
+
+
+
+ +Expand source code + +
def get_rid_from_header(request: BaseRequest) -> Union[str, None]:
+    return get_header(request, RID_KEY_HEADER)
+
+
+
+def get_timestamp_ms() ‑> int +
+
+
+
+ +Expand source code + +
def get_timestamp_ms() -> int:
+    return int(time() * 1000)
+
+
+
+def get_top_level_domain_for_same_site_resolution(url: str) ‑> str +
+
+
+
+ +Expand source code + +
def get_top_level_domain_for_same_site_resolution(url: str) -> str:
+    url_obj = urlparse(url)
+    hostname = url_obj.hostname
+
+    if hostname is None:
+        raise Exception("Should not come here")
+
+    if hostname.startswith("localhost") or is_an_ip_address(hostname):
+        return "localhost"
+
+    parsed_url: Any = extract(hostname, include_psl_private_domains=True)
+    if parsed_url.domain == "":  # type: ignore
+        # We need to do this because of https://github.com/supertokens/supertokens-python/issues/394
+        if hostname.endswith(".amazonaws.com") and parsed_url.suffix == hostname:
+            return hostname
+
+        raise Exception(
+            "Please make sure that the apiDomain and websiteDomain have correct values"
+        )
+
+    return parsed_url.domain + "." + parsed_url.suffix  # type: ignore
+
+
+
+def handle_httpx_client_exceptions(e: Exception, input_: Union[Dict[str, Any], None] = None) +
+
+
+
+ +Expand source code + +
def handle_httpx_client_exceptions(
+    e: Exception, input_: Union[Dict[str, Any], None] = None
+):
+    if isinstance(e, HTTPStatusError) and isinstance(e.response, Response):  # type: ignore
+        res = e.response  # type: ignore
+        log_debug_message("Error status: %s", res.status_code)  # type: ignore
+        log_debug_message("Error response: %s", res.json())
+    else:
+        log_debug_message("Error: %s", str(e))
+
+    if input_ is not None:
+        log_debug_message("Logging the input:")
+        log_debug_message("%s", json.dumps(input_))
+
+
+
+def humanize_time(ms: int) ‑> str +
+
+
+
+ +Expand source code + +
def humanize_time(ms: int) -> str:
+    t = floor(ms / 1000)
+    suffix = ""
+
+    if t < 60:
+        if t > 1:
+            suffix = "s"
+        time_str = f"{t} second{suffix}"
+    elif t < 3600:
+        m = floor(t / 60)
+        if m > 1:
+            suffix = "s"
+        time_str = f"{m} minute{suffix}"
+    else:
+        h = floor(t / 360) / 10
+        if h > 1:
+            suffix = "s"
+        if h % 1 == 0:
+            h = int(h)
+        time_str = f"{h} hour{suffix}"
+
+    return time_str
+
+
+
+def is_4xx_error(status_code: int) ‑> bool +
+
+
+
+ +Expand source code + +
def is_4xx_error(status_code: int) -> bool:
+    return status_code // 100 == 4
+
+
+
+def is_5xx_error(status_code: int) ‑> bool +
+
+
+
+ +Expand source code + +
def is_5xx_error(status_code: int) -> bool:
+    return status_code // 100 == 5
+
+
+
+def is_an_ip_address(ip_address: str) ‑> bool +
+
+
+
+ +Expand source code + +
def is_an_ip_address(ip_address: str) -> bool:
+    return (
+        fullmatch(
+            r"^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|["
+            r"01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$",
+            ip_address,
+        )
+        is not None
+    )
+
+
+
+def is_version_gte(version: str, minimum_version: str) ‑> bool +
+
+
+
+ +Expand source code + +
def is_version_gte(version: str, minimum_version: str) -> bool:
+    return _get_max_version(version, minimum_version) == version
+
+
+
+def normalise_email(email: str) ‑> str +
+
+
+
+ +Expand source code + +
def normalise_email(email: str) -> str:
+    return email.strip().lower()
+
+
+
+def normalise_http_method(method: str) ‑> str +
+
+
+
+ +Expand source code + +
def normalise_http_method(method: str) -> str:
+    return method.lower()
+
+
+
+async def resolve(obj: MaybeAwaitable[_T]) ‑> ~_T +
+
+

Returns value or value of awaitable object passed

+
+ +Expand source code + +
async def resolve(obj: MaybeAwaitable[_T]) -> _T:
+    """Returns value or value of awaitable object passed"""
+    if isinstance(obj, Awaitable):
+        return await obj  # type: ignore
+    return obj  # type: ignore
+
+
+
+def send_200_response(data_json: Dict[str, Any], response: BaseResponse) ‑> BaseResponse +
+
+
+
+ +Expand source code + +
def send_200_response(
+    data_json: Dict[str, Any], response: BaseResponse
+) -> BaseResponse:
+    log_debug_message("Sending response to client with status code: 200")
+    response.set_json_content(data_json)
+    response.set_status_code(200)
+    return response
+
+
+
+def send_non_200_response(body: Dict[str, Any], status_code: int, response: BaseResponse) ‑> BaseResponse +
+
+
+
+ +Expand source code + +
def send_non_200_response(
+    body: Dict[str, Any], status_code: int, response: BaseResponse
+) -> BaseResponse:
+    if status_code < 300:
+        raise_general_exception("Calling sendNon200Response with status code < 300")
+    log_debug_message(
+        "Sending response to client with status code: %s", str(status_code)
+    )
+    response.set_status_code(status_code)
+    response.set_json_content(content=body)
+    return response
+
+
+
+def send_non_200_response_with_message(message: str, status_code: int, response: BaseResponse) ‑> BaseResponse +
+
+
+
+ +Expand source code + +
def send_non_200_response_with_message(
+    message: str, status_code: int, response: BaseResponse
+) -> BaseResponse:
+    return send_non_200_response({ERROR_MESSAGE_KEY: message}, status_code, response)
+
+
+
+def send_unauthorised_access_response(response: BaseResponse) ‑> BaseResponse +
+
+
+
+ +Expand source code + +
def send_unauthorised_access_response(response: BaseResponse) -> BaseResponse:
+    return send_non_200_response_with_message("Unauthorised access", 401, response)
+
+
+
+def set_request_in_user_context_if_not_defined(user_context: Optional[Dict[str, Any]], request: BaseRequest) ‑> Dict[str, Any] +
+
+
+
+ +Expand source code + +
def set_request_in_user_context_if_not_defined(
+    user_context: Optional[Dict[str, Any]], request: BaseRequest
+) -> Dict[str, Any]:
+    if user_context is None:
+        user_context = {}
+
+    if "_default" not in user_context:
+        user_context["_default"] = {}
+
+    if isinstance(user_context["_default"], dict):
+        user_context["_default"]["request"] = request
+        user_context["_default"]["keep_cache_alive"] = True
+
+    return user_context
+
+
+
+def utf_base64decode(s: str, urlsafe: bool) ‑> str +
+
+
+
+ +Expand source code + +
def utf_base64decode(s: str, urlsafe: bool) -> str:
+    # Adding extra "==" based on
+    # https://stackoverflow.com/questions/2941995/python-ignore-incorrect-padding-error-when-base64-decoding
+    # Otherwise it can raise "incorrect padding" error
+    if urlsafe:
+        return urlsafe_b64decode(s.encode("utf-8") + b"==").decode("utf-8")
+
+    return b64decode(s.encode("utf-8")).decode("utf-8")
+
+
+
+def utf_base64encode(s: str, urlsafe: bool) ‑> str +
+
+
+
+ +Expand source code + +
def utf_base64encode(s: str, urlsafe: bool) -> str:
+    if urlsafe:
+        return urlsafe_b64encode(s.encode("utf-8")).decode("utf-8")
+
+    return b64encode(s.encode("utf-8")).decode("utf-8")
+
+
+
+
+
+

Classes

+
+
+class RWLockContext +(mutex: RWMutex, read: bool = True) +
+
+
+
+ +Expand source code + +
class RWLockContext:
+    def __init__(self, mutex: RWMutex, read: bool = True):
+        self.mutex = mutex
+        self.read = read
+
+    def __enter__(self):
+        if self.read:
+            self.mutex.r_lock()
+        else:
+            self.mutex.lock()
+
+    def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any):
+        if self.read:
+            self.mutex.r_unlock()
+        else:
+            self.mutex.unlock()
+
+        if exc_type is not None:
+            raise exc_type(exc_value).with_traceback(traceback)
+
+
+
+class RWMutex +
+
+
+
+ +Expand source code + +
class RWMutex:
+    def __init__(self):
+        self._lock = threading.Lock()
+        self._readers = threading.Condition(self._lock)
+        self._writers = threading.Condition(self._lock)
+        self._reader_count = 0
+        self._writer_count = 0
+
+    def lock(self):
+        with self._lock:
+            while self._writer_count > 0 or self._reader_count > 0:
+                self._writers.wait()
+            self._writer_count += 1
+
+    def unlock(self):
+        with self._lock:
+            self._writer_count -= 1
+            self._readers.notify_all()
+            self._writers.notify_all()
+
+    def r_lock(self):
+        with self._lock:
+            while self._writer_count > 0:
+                self._readers.wait()
+            self._reader_count += 1
+
+    def r_unlock(self):
+        with self._lock:
+            self._reader_count -= 1
+            if self._reader_count == 0:
+                self._writers.notify_all()
+
+

Methods

+
+
+def lock(self) +
+
+
+
+ +Expand source code + +
def lock(self):
+    with self._lock:
+        while self._writer_count > 0 or self._reader_count > 0:
+            self._writers.wait()
+        self._writer_count += 1
+
+
+
+def r_lock(self) +
+
+
+
+ +Expand source code + +
def r_lock(self):
+    with self._lock:
+        while self._writer_count > 0:
+            self._readers.wait()
+        self._reader_count += 1
+
+
+
+def r_unlock(self) +
+
+
+
+ +Expand source code + +
def r_unlock(self):
+    with self._lock:
+        self._reader_count -= 1
+        if self._reader_count == 0:
+            self._writers.notify_all()
+
+
+
+def unlock(self) +
+
+
+
+ +Expand source code + +
def unlock(self):
+    with self._lock:
+        self._writer_count -= 1
+        self._readers.notify_all()
+        self._writers.notify_all()
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file