Skip to content

Commit

Permalink
Add B2B OAuth & OTP (#145)
Browse files Browse the repository at this point in the history
* Add B2B OAuth & OTP

* version bump

* clean
  • Loading branch information
alex-stytch authored Jul 24, 2023
1 parent 6d8372a commit 8c481c2
Show file tree
Hide file tree
Showing 32 changed files with 811 additions and 15 deletions.
13 changes: 12 additions & 1 deletion stytch/b2b/api/discovery_intermediate_sessions.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@

from typing import Any, Dict, Optional

from stytch.b2b.models.discovery_intermediate_sessions import ExchangeResponse
from stytch.b2b.models.discovery_intermediate_sessions import (
ExchangeRequestLocale,
ExchangeResponse,
)
from stytch.core.api_base import ApiBase
from stytch.core.http.client import AsyncClient, SyncClient

Expand All @@ -30,6 +33,7 @@ def exchange(
organization_id: str,
session_duration_minutes: Optional[int] = None,
session_custom_claims: Optional[Dict[str, Any]] = None,
locale: Optional[ExchangeRequestLocale] = None,
) -> ExchangeResponse:
"""Exchange an Intermediate Session for a fully realized [Member Session](https://stytch.com/docs/b2b/api/session-object) in a desired [Organization](https://stytch.com/docs/b2b/api/organization-object).
This operation consumes the Intermediate Session.
Expand All @@ -53,6 +57,7 @@ def exchange(
`session_duration_minutes`. Claims will be included on the Session object and in the JWT. To update a key in an existing Session, supply a new value. To
delete a key, supply a null value. Custom claims made with reserved claims (`iss`, `sub`, `aud`, `exp`, `nbf`, `iat`, `jti`) will be ignored.
Total custom claims size cannot exceed four kilobytes.
- locale: (no documentation yet)
""" # noqa
data: Dict[str, Any] = {
"intermediate_session_token": intermediate_session_token,
Expand All @@ -62,6 +67,8 @@ def exchange(
data["session_duration_minutes"] = session_duration_minutes
if session_custom_claims is not None:
data["session_custom_claims"] = session_custom_claims
if locale is not None:
data["locale"] = locale.value

url = self.api_base.url_for(
"/v1/b2b/discovery/intermediate_sessions/exchange", data
Expand All @@ -75,6 +82,7 @@ async def exchange_async(
organization_id: str,
session_duration_minutes: Optional[int] = None,
session_custom_claims: Optional[Dict[str, Any]] = None,
locale: Optional[ExchangeRequestLocale] = None,
) -> ExchangeResponse:
"""Exchange an Intermediate Session for a fully realized [Member Session](https://stytch.com/docs/b2b/api/session-object) in a desired [Organization](https://stytch.com/docs/b2b/api/organization-object).
This operation consumes the Intermediate Session.
Expand All @@ -98,6 +106,7 @@ async def exchange_async(
`session_duration_minutes`. Claims will be included on the Session object and in the JWT. To update a key in an existing Session, supply a new value. To
delete a key, supply a null value. Custom claims made with reserved claims (`iss`, `sub`, `aud`, `exp`, `nbf`, `iat`, `jti`) will be ignored.
Total custom claims size cannot exceed four kilobytes.
- locale: (no documentation yet)
""" # noqa
data: Dict[str, Any] = {
"intermediate_session_token": intermediate_session_token,
Expand All @@ -107,6 +116,8 @@ async def exchange_async(
data["session_duration_minutes"] = session_duration_minutes
if session_custom_claims is not None:
data["session_custom_claims"] = session_custom_claims
if locale is not None:
data["locale"] = locale.value

url = self.api_base.url_for(
"/v1/b2b/discovery/intermediate_sessions/exchange", data
Expand Down
8 changes: 8 additions & 0 deletions stytch/b2b/api/discovery_organizations.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ def create(
email_invites: Optional[str] = None,
auth_methods: Optional[str] = None,
allowed_auth_methods: Optional[List[str]] = None,
mfa_policy: Optional[str] = None,
) -> CreateResponse:
"""If an end user does not want to join any already-existing organization, or has no possible organizations to join, this endpoint can be used to create a new
[Organization](https://stytch.com/docs/b2b/api/organization-object) and [Member](https://stytch.com/docs/b2b/api/member-object).
Expand Down Expand Up @@ -103,6 +104,7 @@ def create(
An array of allowed authentication methods. This list is enforced when `auth_methods` is set to `RESTRICTED`.
The list's accepted values are: `sso`, `magic_link`, `password`, `google_oauth`, and `microsoft_oauth`.
- mfa_policy: (no documentation yet)
""" # noqa
data: Dict[str, Any] = {
"intermediate_session_token": intermediate_session_token,
Expand All @@ -129,6 +131,8 @@ def create(
data["auth_methods"] = auth_methods
if allowed_auth_methods is not None:
data["allowed_auth_methods"] = allowed_auth_methods
if mfa_policy is not None:
data["mfa_policy"] = mfa_policy

url = self.api_base.url_for("/v1/b2b/discovery/organizations/create", data)
res = self.sync_client.post(url, data)
Expand All @@ -149,6 +153,7 @@ async def create_async(
email_invites: Optional[str] = None,
auth_methods: Optional[str] = None,
allowed_auth_methods: Optional[List[str]] = None,
mfa_policy: Optional[str] = None,
) -> CreateResponse:
"""If an end user does not want to join any already-existing organization, or has no possible organizations to join, this endpoint can be used to create a new
[Organization](https://stytch.com/docs/b2b/api/organization-object) and [Member](https://stytch.com/docs/b2b/api/member-object).
Expand Down Expand Up @@ -213,6 +218,7 @@ async def create_async(
An array of allowed authentication methods. This list is enforced when `auth_methods` is set to `RESTRICTED`.
The list's accepted values are: `sso`, `magic_link`, `password`, `google_oauth`, and `microsoft_oauth`.
- mfa_policy: (no documentation yet)
""" # noqa
data: Dict[str, Any] = {
"intermediate_session_token": intermediate_session_token,
Expand All @@ -239,6 +245,8 @@ async def create_async(
data["auth_methods"] = auth_methods
if allowed_auth_methods is not None:
data["allowed_auth_methods"] = allowed_auth_methods
if mfa_policy is not None:
data["mfa_policy"] = mfa_policy

url = self.api_base.url_for("/v1/b2b/discovery/organizations/create", data)
res = await self.async_client.post(url, data)
Expand Down
13 changes: 12 additions & 1 deletion stytch/b2b/api/magic_links.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@

from stytch.b2b.api.magic_links_discovery import Discovery
from stytch.b2b.api.magic_links_email import Email
from stytch.b2b.models.magic_links import AuthenticateResponse
from stytch.b2b.models.magic_links import (
AuthenticateRequestLocale,
AuthenticateResponse,
)
from stytch.core.api_base import ApiBase
from stytch.core.http.client import AsyncClient, SyncClient

Expand All @@ -36,6 +39,7 @@ def authenticate(
session_jwt: Optional[str] = None,
session_duration_minutes: Optional[int] = None,
session_custom_claims: Optional[Dict[str, Any]] = None,
locale: Optional[AuthenticateRequestLocale] = None,
) -> AuthenticateResponse:
"""Authenticate a Member with a Magic Link. This endpoint requires a Magic Link token that is not expired or previously used. If the Member’s status is `pending` or `invited`, they will be updated to `active`. Provide the `session_duration_minutes` parameter to set the lifetime of the session. If the `session_duration_minutes` parameter is not specified, a Stytch session will be created with a 60 minute duration.
Expand All @@ -62,6 +66,7 @@ def authenticate(
`session_duration_minutes`. Claims will be included on the Session object and in the JWT. To update a key in an existing Session, supply a new value. To
delete a key, supply a null value. Custom claims made with reserved claims (`iss`, `sub`, `aud`, `exp`, `nbf`, `iat`, `jti`) will be ignored.
Total custom claims size cannot exceed four kilobytes.
- locale: (no documentation yet)
""" # noqa
data: Dict[str, Any] = {
"magic_links_token": magic_links_token,
Expand All @@ -76,6 +81,8 @@ def authenticate(
data["session_duration_minutes"] = session_duration_minutes
if session_custom_claims is not None:
data["session_custom_claims"] = session_custom_claims
if locale is not None:
data["locale"] = locale.value

url = self.api_base.url_for("/v1/b2b/magic_links/authenticate", data)
res = self.sync_client.post(url, data)
Expand All @@ -89,6 +96,7 @@ async def authenticate_async(
session_jwt: Optional[str] = None,
session_duration_minutes: Optional[int] = None,
session_custom_claims: Optional[Dict[str, Any]] = None,
locale: Optional[AuthenticateRequestLocale] = None,
) -> AuthenticateResponse:
"""Authenticate a Member with a Magic Link. This endpoint requires a Magic Link token that is not expired or previously used. If the Member’s status is `pending` or `invited`, they will be updated to `active`. Provide the `session_duration_minutes` parameter to set the lifetime of the session. If the `session_duration_minutes` parameter is not specified, a Stytch session will be created with a 60 minute duration.
Expand All @@ -115,6 +123,7 @@ async def authenticate_async(
`session_duration_minutes`. Claims will be included on the Session object and in the JWT. To update a key in an existing Session, supply a new value. To
delete a key, supply a null value. Custom claims made with reserved claims (`iss`, `sub`, `aud`, `exp`, `nbf`, `iat`, `jti`) will be ignored.
Total custom claims size cannot exceed four kilobytes.
- locale: (no documentation yet)
""" # noqa
data: Dict[str, Any] = {
"magic_links_token": magic_links_token,
Expand All @@ -129,6 +138,8 @@ async def authenticate_async(
data["session_duration_minutes"] = session_duration_minutes
if session_custom_claims is not None:
data["session_custom_claims"] = session_custom_claims
if locale is not None:
data["locale"] = locale.value

url = self.api_base.url_for("/v1/b2b/magic_links/authenticate", data)
res = await self.async_client.post(url, data)
Expand Down
133 changes: 133 additions & 0 deletions stytch/b2b/api/oauth.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
# !!!
# 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

from stytch.b2b.api.oauth_discovery import Discovery
from stytch.b2b.models.oauth import AuthenticateRequestLocale, AuthenticateResponse
from stytch.core.api_base import ApiBase
from stytch.core.http.client import AsyncClient, SyncClient


class OAuth:
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
self.discovery = Discovery(api_base, sync_client, async_client)

def authenticate(
self,
oauth_token: str,
session_token: Optional[str] = None,
session_duration_minutes: Optional[int] = None,
session_jwt: Optional[str] = None,
session_custom_claims: Optional[Dict[str, Any]] = None,
pkce_code_verifier: Optional[str] = None,
locale: Optional[AuthenticateRequestLocale] = None,
) -> AuthenticateResponse:
"""Authenticate a Member given a `token`. This endpoint verifies that the member completed the OAuth flow by verifying that the token is valid and hasn't expired. Provide the `session_duration_minutes` parameter to set the lifetime of the session. If the `session_duration_minutes` parameter is not specified, a Stytch session will be created with a 60 minute duration.
Fields:
- oauth_token: The token to authenticate.
- session_token: A secret token for a given Stytch Session.
- session_duration_minutes: Set the session lifetime to be this many minutes from now. This will start a new session if one doesn't already exist,
returning both an opaque `session_token` and `session_jwt` for this session. Remember that the `session_jwt` will have a fixed lifetime of
five minutes regardless of the underlying session duration, and will need to be refreshed over time.
This value must be a minimum of 5 and a maximum of 527040 minutes (366 days).
If a `session_token` or `session_jwt` is provided then a successful authentication will continue to extend the session this many minutes.
If the `session_duration_minutes` parameter is not specified, a Stytch session will be created with a 60 minute duration. If you don't want
to use the Stytch session product, you can ignore the session fields in the response.
- session_jwt: The JSON Web Token (JWT) for a given Stytch Session.
- session_custom_claims: Add a custom claims map to the Session being authenticated. Claims are only created if a Session is initialized by providing a value in
`session_duration_minutes`. Claims will be included on the Session object and in the JWT. To update a key in an existing Session, supply a new value. To
delete a key, supply a null value. Custom claims made with reserved claims (`iss`, `sub`, `aud`, `exp`, `nbf`, `iat`, `jti`) will be ignored.
Total custom claims size cannot exceed four kilobytes.
- pkce_code_verifier: A base64url encoded one time secret used to validate that the request starts and ends on the same device.
- locale: (no documentation yet)
""" # noqa
data: Dict[str, Any] = {
"oauth_token": oauth_token,
}
if session_token is not None:
data["session_token"] = session_token
if session_duration_minutes is not None:
data["session_duration_minutes"] = session_duration_minutes
if session_jwt is not None:
data["session_jwt"] = session_jwt
if session_custom_claims is not None:
data["session_custom_claims"] = session_custom_claims
if pkce_code_verifier is not None:
data["pkce_code_verifier"] = pkce_code_verifier
if locale is not None:
data["locale"] = locale.value

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

async def authenticate_async(
self,
oauth_token: str,
session_token: Optional[str] = None,
session_duration_minutes: Optional[int] = None,
session_jwt: Optional[str] = None,
session_custom_claims: Optional[Dict[str, Any]] = None,
pkce_code_verifier: Optional[str] = None,
locale: Optional[AuthenticateRequestLocale] = None,
) -> AuthenticateResponse:
"""Authenticate a Member given a `token`. This endpoint verifies that the member completed the OAuth flow by verifying that the token is valid and hasn't expired. Provide the `session_duration_minutes` parameter to set the lifetime of the session. If the `session_duration_minutes` parameter is not specified, a Stytch session will be created with a 60 minute duration.
Fields:
- oauth_token: The token to authenticate.
- session_token: A secret token for a given Stytch Session.
- session_duration_minutes: Set the session lifetime to be this many minutes from now. This will start a new session if one doesn't already exist,
returning both an opaque `session_token` and `session_jwt` for this session. Remember that the `session_jwt` will have a fixed lifetime of
five minutes regardless of the underlying session duration, and will need to be refreshed over time.
This value must be a minimum of 5 and a maximum of 527040 minutes (366 days).
If a `session_token` or `session_jwt` is provided then a successful authentication will continue to extend the session this many minutes.
If the `session_duration_minutes` parameter is not specified, a Stytch session will be created with a 60 minute duration. If you don't want
to use the Stytch session product, you can ignore the session fields in the response.
- session_jwt: The JSON Web Token (JWT) for a given Stytch Session.
- session_custom_claims: Add a custom claims map to the Session being authenticated. Claims are only created if a Session is initialized by providing a value in
`session_duration_minutes`. Claims will be included on the Session object and in the JWT. To update a key in an existing Session, supply a new value. To
delete a key, supply a null value. Custom claims made with reserved claims (`iss`, `sub`, `aud`, `exp`, `nbf`, `iat`, `jti`) will be ignored.
Total custom claims size cannot exceed four kilobytes.
- pkce_code_verifier: A base64url encoded one time secret used to validate that the request starts and ends on the same device.
- locale: (no documentation yet)
""" # noqa
data: Dict[str, Any] = {
"oauth_token": oauth_token,
}
if session_token is not None:
data["session_token"] = session_token
if session_duration_minutes is not None:
data["session_duration_minutes"] = session_duration_minutes
if session_jwt is not None:
data["session_jwt"] = session_jwt
if session_custom_claims is not None:
data["session_custom_claims"] = session_custom_claims
if pkce_code_verifier is not None:
data["pkce_code_verifier"] = pkce_code_verifier
if locale is not None:
data["locale"] = locale.value

url = self.api_base.url_for("/v1/b2b/oauth/authenticate", data)
res = await self.async_client.post(url, data)
return AuthenticateResponse.from_json(res.response.status, res.json)
Loading

0 comments on commit 8c481c2

Please sign in to comment.