Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add email otp to b2b #225

Merged
merged 2 commits into from
Nov 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions stytch/b2b/api/organizations.py
Original file line number Diff line number Diff line change
Expand Up @@ -501,9 +501,9 @@ def update(
if sso_jit_provisioning is not None:
data["sso_jit_provisioning"] = sso_jit_provisioning
if sso_jit_provisioning_allowed_connections is not None:
data[
"sso_jit_provisioning_allowed_connections"
] = sso_jit_provisioning_allowed_connections
data["sso_jit_provisioning_allowed_connections"] = (
sso_jit_provisioning_allowed_connections
)
if email_allowed_domains is not None:
data["email_allowed_domains"] = email_allowed_domains
if email_jit_provisioning is not None:
Expand Down Expand Up @@ -689,9 +689,9 @@ async def update_async(
if sso_jit_provisioning is not None:
data["sso_jit_provisioning"] = sso_jit_provisioning
if sso_jit_provisioning_allowed_connections is not None:
data[
"sso_jit_provisioning_allowed_connections"
] = sso_jit_provisioning_allowed_connections
data["sso_jit_provisioning_allowed_connections"] = (
sso_jit_provisioning_allowed_connections
)
if email_allowed_domains is not None:
data["email_allowed_domains"] = email_allowed_domains
if email_jit_provisioning is not None:
Expand Down
6 changes: 6 additions & 0 deletions stytch/b2b/api/otp.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

from __future__ import annotations

from stytch.b2b.api.otp_email import Email
from stytch.b2b.api.otp_sms import Sms
from stytch.core.api_base import ApiBase
from stytch.core.http.client import AsyncClient, SyncClient
Expand All @@ -23,3 +24,8 @@ def __init__(
sync_client=self.sync_client,
async_client=self.async_client,
)
self.email = Email(
api_base=self.api_base,
sync_client=self.sync_client,
async_client=self.async_client,
)
263 changes: 263 additions & 0 deletions stytch/b2b/api/otp_email.py

Large diffs are not rendered by default.

130 changes: 130 additions & 0 deletions stytch/b2b/api/otp_email_discovery.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
# !!!
# WARNING: This file is autogenerated
# Only modify code within MANUAL() sections
# or your changes may be overwritten later!
# !!!

from __future__ import annotations

from typing import Any, Dict, Optional, Union

from stytch.b2b.models.otp_email_discovery import (
AuthenticateResponse,
SendRequestLocale,
SendResponse,
)
from stytch.core.api_base import ApiBase
from stytch.core.http.client import AsyncClient, SyncClient


class Discovery:
def __init__(
self, api_base: ApiBase, sync_client: SyncClient, async_client: AsyncClient
) -> None:
self.api_base = api_base
self.sync_client = sync_client
self.async_client = async_client

def send(
self,
email_address: str,
login_template_id: Optional[str] = None,
locale: Optional[Union[SendRequestLocale, str]] = None,
) -> SendResponse:
"""Send a discovery OTP to an email address. The OTP is valid for 10 minutes. Only the most recently sent OTP is valid: when an OTP is sent, all OTPs previously sent to the same email address are invalidated, even if unused or unexpired.

Fields:
- email_address: The email address to start the discovery flow for.
- login_template_id: Use a custom template for login emails. By default, it will use your default email template. The template must be a template using our built-in customizations or a custom HTML email for OTP - Login.
- locale: Used to determine which language to use when sending the user this delivery method. Parameter is a [IETF BCP 47 language tag](https://www.w3.org/International/articles/language-tags/), e.g. `"en"`.

Currently supported languages are English (`"en"`), Spanish (`"es"`), and Brazilian Portuguese (`"pt-br"`); if no value is provided, the copy defaults to English.

Request support for additional languages [here](https://docs.google.com/forms/d/e/1FAIpQLScZSpAu_m2AmLXRT3F3kap-s_mcV6UTBitYn6CdyWP0-o7YjQ/viewform?usp=sf_link")!

""" # noqa
headers: Dict[str, str] = {}
data: Dict[str, Any] = {
"email_address": email_address,
}
if login_template_id is not None:
data["login_template_id"] = login_template_id
if locale is not None:
data["locale"] = locale

url = self.api_base.url_for("/v1/b2b/otps/email/discovery/send", data)
res = self.sync_client.post(url, data, headers)
return SendResponse.from_json(res.response.status_code, res.json)

async def send_async(
self,
email_address: str,
login_template_id: Optional[str] = None,
locale: Optional[SendRequestLocale] = None,
) -> SendResponse:
"""Send a discovery OTP to an email address. The OTP is valid for 10 minutes. Only the most recently sent OTP is valid: when an OTP is sent, all OTPs previously sent to the same email address are invalidated, even if unused or unexpired.

Fields:
- email_address: The email address to start the discovery flow for.
- login_template_id: Use a custom template for login emails. By default, it will use your default email template. The template must be a template using our built-in customizations or a custom HTML email for OTP - Login.
- locale: Used to determine which language to use when sending the user this delivery method. Parameter is a [IETF BCP 47 language tag](https://www.w3.org/International/articles/language-tags/), e.g. `"en"`.

Currently supported languages are English (`"en"`), Spanish (`"es"`), and Brazilian Portuguese (`"pt-br"`); if no value is provided, the copy defaults to English.

Request support for additional languages [here](https://docs.google.com/forms/d/e/1FAIpQLScZSpAu_m2AmLXRT3F3kap-s_mcV6UTBitYn6CdyWP0-o7YjQ/viewform?usp=sf_link")!

""" # noqa
headers: Dict[str, str] = {}
data: Dict[str, Any] = {
"email_address": email_address,
}
if login_template_id is not None:
data["login_template_id"] = login_template_id
if locale is not None:
data["locale"] = locale

url = self.api_base.url_for("/v1/b2b/otps/email/discovery/send", data)
res = await self.async_client.post(url, data, headers)
return SendResponse.from_json(res.response.status, res.json)

def authenticate(
self,
email_address: str,
code: str,
) -> AuthenticateResponse:
"""Authenticates the OTP and returns an intermediate session token. Intermediate session tokens can be used for various Discovery login flows and are valid for 10 minutes.

Fields:
- email_address: The email address of the Member.
- code: The code to authenticate.
""" # noqa
headers: Dict[str, str] = {}
data: Dict[str, Any] = {
"email_address": email_address,
"code": code,
}

url = self.api_base.url_for("/v1/b2b/otps/email/discovery/authenticate", data)
res = self.sync_client.post(url, data, headers)
return AuthenticateResponse.from_json(res.response.status_code, res.json)

async def authenticate_async(
self,
email_address: str,
code: str,
) -> AuthenticateResponse:
"""Authenticates the OTP and returns an intermediate session token. Intermediate session tokens can be used for various Discovery login flows and are valid for 10 minutes.

Fields:
- email_address: The email address of the Member.
- code: The code to authenticate.
""" # noqa
headers: Dict[str, str] = {}
data: Dict[str, Any] = {
"email_address": email_address,
"code": code,
}

url = self.api_base.url_for("/v1/b2b/otps/email/discovery/authenticate", data)
res = await self.async_client.post(url, data, headers)
return AuthenticateResponse.from_json(res.response.status, res.json)
12 changes: 6 additions & 6 deletions stytch/b2b/api/passwords_discovery_email.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,9 @@ def reset_start(
if reset_password_template_id is not None:
data["reset_password_template_id"] = reset_password_template_id
if reset_password_expiration_minutes is not None:
data[
"reset_password_expiration_minutes"
] = reset_password_expiration_minutes
data["reset_password_expiration_minutes"] = (
reset_password_expiration_minutes
)
if pkce_code_challenge is not None:
data["pkce_code_challenge"] = pkce_code_challenge
if locale is not None:
Expand Down Expand Up @@ -132,9 +132,9 @@ async def reset_start_async(
if reset_password_template_id is not None:
data["reset_password_template_id"] = reset_password_template_id
if reset_password_expiration_minutes is not None:
data[
"reset_password_expiration_minutes"
] = reset_password_expiration_minutes
data["reset_password_expiration_minutes"] = (
reset_password_expiration_minutes
)
if pkce_code_challenge is not None:
data["pkce_code_challenge"] = pkce_code_challenge
if locale is not None:
Expand Down
12 changes: 6 additions & 6 deletions stytch/b2b/api/passwords_email.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,9 @@ def reset_start(
if reset_password_redirect_url is not None:
data["reset_password_redirect_url"] = reset_password_redirect_url
if reset_password_expiration_minutes is not None:
data[
"reset_password_expiration_minutes"
] = reset_password_expiration_minutes
data["reset_password_expiration_minutes"] = (
reset_password_expiration_minutes
)
if code_challenge is not None:
data["code_challenge"] = code_challenge
if login_redirect_url is not None:
Expand Down Expand Up @@ -138,9 +138,9 @@ async def reset_start_async(
if reset_password_redirect_url is not None:
data["reset_password_redirect_url"] = reset_password_redirect_url
if reset_password_expiration_minutes is not None:
data[
"reset_password_expiration_minutes"
] = reset_password_expiration_minutes
data["reset_password_expiration_minutes"] = (
reset_password_expiration_minutes
)
if code_challenge is not None:
data["code_challenge"] = code_challenge
if login_redirect_url is not None:
Expand Down
71 changes: 71 additions & 0 deletions stytch/b2b/models/otp_email.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# !!!
# WARNING: This file is autogenerated
# Only modify code within MANUAL() sections
# or your changes may be overwritten later!
# !!!

from __future__ import annotations

import enum
from typing import Optional

from stytch.b2b.models.mfa import MfaRequired
from stytch.b2b.models.organizations import Member, Organization
from stytch.b2b.models.sessions import MemberSession
from stytch.core.response_base import ResponseBase


class AuthenticateRequestLocale(str, enum.Enum):
EN = "en"
ES = "es"
PTBR = "pt-br"


class LoginOrSignupRequestLocale(str, enum.Enum):
EN = "en"
ES = "es"
PTBR = "pt-br"


class AuthenticateResponse(ResponseBase):
"""Response type for `Email.authenticate`.
Fields:
- member_id: Globally unique UUID that identifies a specific Member.
- method_id: The email or device involved in the authentication.
- organization_id: Globally unique UUID that identifies a specific Organization. The `organization_id` is critical to perform operations on an Organization, so be sure to preserve this value.
- member: The [Member object](https://stytch.com/docs/b2b/api/member-object)
- session_token: A secret token for a given Stytch Session.
- session_jwt: The JSON Web Token (JWT) for a given Stytch Session.
- member_session: The [Session object](https://stytch.com/docs/b2b/api/session-object).
- organization: The [Organization object](https://stytch.com/docs/b2b/api/organization-object).
- intermediate_session_token: The Intermediate Session Token. This token does not necessarily belong to a specific instance of a Member, but represents a bag of factors that may be converted to a member session. The token can be used with the [OTP SMS Authenticate endpoint](https://stytch.com/docs/b2b/api/authenticate-otp-sms), [TOTP Authenticate endpoint](https://stytch.com/docs/b2b/api/authenticate-totp), or [Recovery Codes Recover endpoint](https://stytch.com/docs/b2b/api/recovery-codes-recover) to complete an MFA flow and log in to the Organization. It can also be used with the [Exchange Intermediate Session endpoint](https://stytch.com/docs/b2b/api/exchange-intermediate-session) to join a specific Organization that allows the factors represented by the intermediate session token; or the [Create Organization via Discovery endpoint](https://stytch.com/docs/b2b/api/create-organization-via-discovery) to create a new Organization and Member.
- member_authenticated: Indicates whether the Member is fully authenticated. If false, the Member needs to complete an MFA step to log in to the Organization.
- mfa_required: Information about the MFA requirements of the Organization and the Member's options for fulfilling MFA.
""" # noqa

member_id: str
method_id: str
organization_id: str
member: Member
session_token: str
session_jwt: str
member_session: MemberSession
organization: Organization
intermediate_session_token: str
member_authenticated: bool
mfa_required: Optional[MfaRequired] = None


class LoginOrSignupResponse(ResponseBase):
"""Response type for `Email.login_or_signup`.
Fields:
- member_id: Globally unique UUID that identifies a specific Member.
- member_created: A flag indicating `true` if a new Member object was created and `false` if the Member object already existed.
- member: The [Member object](https://stytch.com/docs/b2b/api/member-object)
- organization: The [Organization object](https://stytch.com/docs/b2b/api/organization-object).
""" # noqa

member_id: str
member_created: bool
member: Member
organization: Organization
49 changes: 49 additions & 0 deletions stytch/b2b/models/otp_email_discovery.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# !!!
# WARNING: This file is autogenerated
# Only modify code within MANUAL() sections
# or your changes may be overwritten later!
# !!!

from __future__ import annotations

import enum
from typing import List

from stytch.b2b.models.discovery import DiscoveredOrganization
from stytch.core.response_base import ResponseBase


class SendRequestLocale(str, enum.Enum):
EN = "en"
ES = "es"
PTBR = "pt-br"


class AuthenticateResponse(ResponseBase):
"""Response type for `Discovery.authenticate`.
Fields:
- intermediate_session_token: The Intermediate Session Token. This token does not necessarily belong to a specific instance of a Member, but represents a bag of factors that may be converted to a member session. The token can be used with the [OTP SMS Authenticate endpoint](https://stytch.com/docs/b2b/api/authenticate-otp-sms), [TOTP Authenticate endpoint](https://stytch.com/docs/b2b/api/authenticate-totp), or [Recovery Codes Recover endpoint](https://stytch.com/docs/b2b/api/recovery-codes-recover) to complete an MFA flow and log in to the Organization. It can also be used with the [Exchange Intermediate Session endpoint](https://stytch.com/docs/b2b/api/exchange-intermediate-session) to join a specific Organization that allows the factors represented by the intermediate session token; or the [Create Organization via Discovery endpoint](https://stytch.com/docs/b2b/api/create-organization-via-discovery) to create a new Organization and Member.
- email_address: The email address.
- discovered_organizations: An array of `discovered_organization` objects tied to the `intermediate_session_token`, `session_token`, or `session_jwt`. See the [Discovered Organization Object](https://stytch.com/docs/b2b/api/discovered-organization-object) for complete details.

Note that Organizations will only appear here under any of the following conditions:
1. The end user is already a Member of the Organization.
2. The end user is invited to the Organization.
3. The end user can join the Organization because:

a) The Organization allows JIT provisioning.

b) The Organizations' allowed domains list contains the Member's email domain.

c) The Organization has at least one other Member with a verified email address with the same domain as the end user (to prevent phishing attacks).
""" # noqa

intermediate_session_token: str
email_address: str
discovered_organizations: List[DiscoveredOrganization]


class SendResponse(ResponseBase):
"""Response type for `Discovery.send`.
Fields:
""" # noqa
12 changes: 6 additions & 6 deletions stytch/consumer/api/passwords_email.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,9 @@ def reset_start(
if reset_password_redirect_url is not None:
data["reset_password_redirect_url"] = reset_password_redirect_url
if reset_password_expiration_minutes is not None:
data[
"reset_password_expiration_minutes"
] = reset_password_expiration_minutes
data["reset_password_expiration_minutes"] = (
reset_password_expiration_minutes
)
if code_challenge is not None:
data["code_challenge"] = code_challenge
if attributes is not None:
Expand Down Expand Up @@ -133,9 +133,9 @@ async def reset_start_async(
if reset_password_redirect_url is not None:
data["reset_password_redirect_url"] = reset_password_redirect_url
if reset_password_expiration_minutes is not None:
data[
"reset_password_expiration_minutes"
] = reset_password_expiration_minutes
data["reset_password_expiration_minutes"] = (
reset_password_expiration_minutes
)
if code_challenge is not None:
data["code_challenge"] = code_challenge
if attributes is not None:
Expand Down
Loading
Loading