Skip to content

Commit

Permalink
Adds support for GitHub B2B OAuth, Cross-Org Passwords (#223)
Browse files Browse the repository at this point in the history
* Adds support for GitHub B2B OAuth

* Docs updates.

* Fix: name mangling

* Bump version, include cross-org password updates
  • Loading branch information
jcook-stytch authored Oct 29, 2024
1 parent 3e7155d commit 99e2c57
Show file tree
Hide file tree
Showing 23 changed files with 818 additions and 45 deletions.
4 changes: 2 additions & 2 deletions stytch/b2b/api/discovery_organizations.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ def create(
`NOT_ALLOWED` – disable JIT provisioning by OAuth Tenant.
- allowed_oauth_tenants: A map of allowed OAuth tenants. If this field is not passed in, the Organization will not allow JIT provisioning by OAuth Tenant. Allowed keys are "slack" and "hubspot".
- allowed_oauth_tenants: A map of allowed OAuth tenants. If this field is not passed in, the Organization will not allow JIT provisioning by OAuth Tenant. Allowed keys are "slack", "hubspot", and "github".
""" # noqa
headers: Dict[str, str] = {}
data: Dict[str, Any] = {
Expand Down Expand Up @@ -310,7 +310,7 @@ async def create_async(
`NOT_ALLOWED` – disable JIT provisioning by OAuth Tenant.
- allowed_oauth_tenants: A map of allowed OAuth tenants. If this field is not passed in, the Organization will not allow JIT provisioning by OAuth Tenant. Allowed keys are "slack" and "hubspot".
- allowed_oauth_tenants: A map of allowed OAuth tenants. If this field is not passed in, the Organization will not allow JIT provisioning by OAuth Tenant. Allowed keys are "slack", "hubspot", and "github".
""" # noqa
headers: Dict[str, str] = {}
data: Dict[str, Any] = {
Expand Down
6 changes: 4 additions & 2 deletions stytch/b2b/api/oauth_discovery.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ def authenticate(
session_custom_claims: Optional[Dict[str, Any]] = None,
pkce_code_verifier: Optional[str] = None,
) -> AuthenticateResponse:
"""Authenticates the Discovery token and exchanges it for an Intermediate Session Token. Intermediate Session Tokens can be used for various Discovery login flows and are valid for 10 minutes.
"""Authenticates the Discovery token and exchanges it for an Intermediate
Session Token. Intermediate Session Tokens can be used for various Discovery login flows and are valid for 10 minutes.
Fields:
- discovery_oauth_token: The Discovery OAuth token to authenticate.
Expand Down Expand Up @@ -68,7 +69,8 @@ async def authenticate_async(
session_custom_claims: Optional[Dict[str, Any]] = None,
pkce_code_verifier: Optional[str] = None,
) -> AuthenticateResponse:
"""Authenticates the Discovery token and exchanges it for an Intermediate Session Token. Intermediate Session Tokens can be used for various Discovery login flows and are valid for 10 minutes.
"""Authenticates the Discovery token and exchanges it for an Intermediate
Session Token. Intermediate Session Tokens can be used for various Discovery login flows and are valid for 10 minutes.
Fields:
- discovery_oauth_token: The Discovery OAuth token to authenticate.
Expand Down
8 changes: 4 additions & 4 deletions stytch/b2b/api/organizations.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ def create(
`NOT_ALLOWED` – disable JIT provisioning by OAuth Tenant.
- allowed_oauth_tenants: A map of allowed OAuth tenants. If this field is not passed in, the Organization will not allow JIT provisioning by OAuth Tenant. Allowed keys are "slack" and "hubspot".
- allowed_oauth_tenants: A map of allowed OAuth tenants. If this field is not passed in, the Organization will not allow JIT provisioning by OAuth Tenant. Allowed keys are "slack", "hubspot", and "github".
""" # noqa
headers: Dict[str, str] = {}
data: Dict[str, Any] = {
Expand Down Expand Up @@ -266,7 +266,7 @@ async def create_async(
`NOT_ALLOWED` – disable JIT provisioning by OAuth Tenant.
- allowed_oauth_tenants: A map of allowed OAuth tenants. If this field is not passed in, the Organization will not allow JIT provisioning by OAuth Tenant. Allowed keys are "slack" and "hubspot".
- allowed_oauth_tenants: A map of allowed OAuth tenants. If this field is not passed in, the Organization will not allow JIT provisioning by OAuth Tenant. Allowed keys are "slack", "hubspot", and "github".
""" # noqa
headers: Dict[str, str] = {}
data: Dict[str, Any] = {
Expand Down Expand Up @@ -478,7 +478,7 @@ def update(
If this field is provided and a session header is passed into the request, the Member Session must have permission to perform the `update.settings.oauth-tenant-jit-provisioning` action on the `stytch.organization` Resource.
- allowed_oauth_tenants: A map of allowed OAuth tenants. If this field is not passed in, the Organization will not allow JIT provisioning by OAuth Tenant. Allowed keys are "slack" and "hubspot".
- allowed_oauth_tenants: A map of allowed OAuth tenants. If this field is not passed in, the Organization will not allow JIT provisioning by OAuth Tenant. Allowed keys are "slack", "hubspot", and "github".
If this field is provided and a session header is passed into the request, the Member Session must have permission to perform the `update.settings.allowed-oauth-tenants` action on the `stytch.organization` Resource.
""" # noqa
Expand Down Expand Up @@ -666,7 +666,7 @@ async def update_async(
If this field is provided and a session header is passed into the request, the Member Session must have permission to perform the `update.settings.oauth-tenant-jit-provisioning` action on the `stytch.organization` Resource.
- allowed_oauth_tenants: A map of allowed OAuth tenants. If this field is not passed in, the Organization will not allow JIT provisioning by OAuth Tenant. Allowed keys are "slack" and "hubspot".
- allowed_oauth_tenants: A map of allowed OAuth tenants. If this field is not passed in, the Organization will not allow JIT provisioning by OAuth Tenant. Allowed keys are "slack", "hubspot", and "github".
If this field is provided and a session header is passed into the request, the Member Session must have permission to perform the `update.settings.allowed-oauth-tenants` action on the `stytch.organization` Resource.
""" # noqa
Expand Down
18 changes: 18 additions & 0 deletions stytch/b2b/api/organizations_members.py
Original file line number Diff line number Diff line change
Expand Up @@ -655,6 +655,15 @@ def oidc_providers(
member_id: str,
include_refresh_token: Optional[bool] = None,
) -> OIDCProvidersResponse:
"""Retrieve the saved OIDC access tokens and ID tokens for a member. After a successful OIDC login, Stytch will save the
issued access token and ID token from the identity provider. If a refresh token has been issued, Stytch will refresh the
access token automatically.
Fields:
- 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_id: Globally unique UUID that identifies a specific Member. The `member_id` is critical to perform operations on a Member, so be sure to preserve this value.
- include_refresh_token: Whether to return the refresh token Stytch has stored for the OAuth Provider. Defaults to false. **Important:** If your application exchanges the refresh token, Stytch may not be able to automatically refresh access tokens in the future.
""" # noqa
headers: Dict[str, str] = {}
data: Dict[str, Any] = {
"organization_id": organization_id,
Expand All @@ -676,6 +685,15 @@ async def oidc_providers_async(
member_id: str,
include_refresh_token: Optional[bool] = None,
) -> OIDCProvidersResponse:
"""Retrieve the saved OIDC access tokens and ID tokens for a member. After a successful OIDC login, Stytch will save the
issued access token and ID token from the identity provider. If a refresh token has been issued, Stytch will refresh the
access token automatically.
Fields:
- 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_id: Globally unique UUID that identifies a specific Member. The `member_id` is critical to perform operations on a Member, so be sure to preserve this value.
- include_refresh_token: Whether to return the refresh token Stytch has stored for the OAuth Provider. Defaults to false. **Important:** If your application exchanges the refresh token, Stytch may not be able to automatically refresh access tokens in the future.
""" # noqa
headers: Dict[str, str] = {}
data: Dict[str, Any] = {
"organization_id": organization_id,
Expand Down
173 changes: 173 additions & 0 deletions stytch/b2b/api/organizations_members_oauth_providers.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@
from typing import Any, Dict, Optional

from stytch.b2b.models.organizations_members_oauth_providers import (
GithubResponse,
GoogleResponse,
HubspotResponse,
MicrosoftResponse,
SlackResponse,
)
from stytch.core.api_base import ApiBase
from stytch.core.http.client import AsyncClient, SyncClient
Expand Down Expand Up @@ -155,3 +158,173 @@ async def microsoft_async(
)
res = await self.async_client.get(url, data, headers)
return MicrosoftResponse.from_json(res.response.status, res.json)

def slack(
self,
organization_id: str,
member_id: str,
) -> SlackResponse:
"""Retrieve the saved Slack access token and ID token for a member. After a successful OAuth login, Stytch will save the
issued access token and ID token from the identity provider.
Fields:
- 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_id: Globally unique UUID that identifies a specific Member. The `member_id` is critical to perform operations on a Member, so be sure to preserve this value.
""" # noqa
headers: Dict[str, str] = {}
data: Dict[str, Any] = {
"organization_id": organization_id,
"member_id": member_id,
}

url = self.api_base.url_for(
"/v1/b2b/organizations/{organization_id}/members/{member_id}/oauth_providers/slack",
data,
)
res = self.sync_client.get(url, data, headers)
return SlackResponse.from_json(res.response.status_code, res.json)

async def slack_async(
self,
organization_id: str,
member_id: str,
) -> SlackResponse:
"""Retrieve the saved Slack access token and ID token for a member. After a successful OAuth login, Stytch will save the
issued access token and ID token from the identity provider.
Fields:
- 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_id: Globally unique UUID that identifies a specific Member. The `member_id` is critical to perform operations on a Member, so be sure to preserve this value.
""" # noqa
headers: Dict[str, str] = {}
data: Dict[str, Any] = {
"organization_id": organization_id,
"member_id": member_id,
}

url = self.api_base.url_for(
"/v1/b2b/organizations/{organization_id}/members/{member_id}/oauth_providers/slack",
data,
)
res = await self.async_client.get(url, data, headers)
return SlackResponse.from_json(res.response.status, res.json)

def hubspot(
self,
organization_id: str,
member_id: str,
include_refresh_token: Optional[bool] = None,
) -> HubspotResponse:
"""Retrieve the saved Hubspot access token and ID token for a member. After a successful OAuth login, Stytch will save the
issued access token and ID token from the identity provider. If a refresh token has been issued, Stytch will refresh the
access token automatically.
Fields:
- 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_id: Globally unique UUID that identifies a specific Member. The `member_id` is critical to perform operations on a Member, so be sure to preserve this value.
- include_refresh_token: Whether to return the refresh token Stytch has stored for the OAuth Provider. Defaults to false. **Important:** If your application exchanges the refresh token, Stytch may not be able to automatically refresh access tokens in the future.
""" # noqa
headers: Dict[str, str] = {}
data: Dict[str, Any] = {
"organization_id": organization_id,
"member_id": member_id,
}
if include_refresh_token is not None:
data["include_refresh_token"] = include_refresh_token

url = self.api_base.url_for(
"/v1/b2b/organizations/{organization_id}/members/{member_id}/oauth_providers/hubspot",
data,
)
res = self.sync_client.get(url, data, headers)
return HubspotResponse.from_json(res.response.status_code, res.json)

async def hubspot_async(
self,
organization_id: str,
member_id: str,
include_refresh_token: Optional[bool] = None,
) -> HubspotResponse:
"""Retrieve the saved Hubspot access token and ID token for a member. After a successful OAuth login, Stytch will save the
issued access token and ID token from the identity provider. If a refresh token has been issued, Stytch will refresh the
access token automatically.
Fields:
- 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_id: Globally unique UUID that identifies a specific Member. The `member_id` is critical to perform operations on a Member, so be sure to preserve this value.
- include_refresh_token: Whether to return the refresh token Stytch has stored for the OAuth Provider. Defaults to false. **Important:** If your application exchanges the refresh token, Stytch may not be able to automatically refresh access tokens in the future.
""" # noqa
headers: Dict[str, str] = {}
data: Dict[str, Any] = {
"organization_id": organization_id,
"member_id": member_id,
}
if include_refresh_token is not None:
data["include_refresh_token"] = include_refresh_token

url = self.api_base.url_for(
"/v1/b2b/organizations/{organization_id}/members/{member_id}/oauth_providers/hubspot",
data,
)
res = await self.async_client.get(url, data, headers)
return HubspotResponse.from_json(res.response.status, res.json)

def github(
self,
organization_id: str,
member_id: str,
include_refresh_token: Optional[bool] = None,
) -> GithubResponse:
"""Retrieve the saved GitHub access token for a Member. After a successful OAuth login, Stytch will save the
issued access token from the identity provider. GitHub does not issue refresh tokens, but will invalidate access
tokens after very long periods of inactivity.
Fields:
- 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_id: Globally unique UUID that identifies a specific Member. The `member_id` is critical to perform operations on a Member, so be sure to preserve this value.
- include_refresh_token: Whether to return the refresh token Stytch has stored for the OAuth Provider. Defaults to false. **Important:** If your application exchanges the refresh token, Stytch may not be able to automatically refresh access tokens in the future.
""" # noqa
headers: Dict[str, str] = {}
data: Dict[str, Any] = {
"organization_id": organization_id,
"member_id": member_id,
}
if include_refresh_token is not None:
data["include_refresh_token"] = include_refresh_token

url = self.api_base.url_for(
"/v1/b2b/organizations/{organization_id}/members/{member_id}/oauth_providers/github",
data,
)
res = self.sync_client.get(url, data, headers)
return GithubResponse.from_json(res.response.status_code, res.json)

async def github_async(
self,
organization_id: str,
member_id: str,
include_refresh_token: Optional[bool] = None,
) -> GithubResponse:
"""Retrieve the saved GitHub access token for a Member. After a successful OAuth login, Stytch will save the
issued access token from the identity provider. GitHub does not issue refresh tokens, but will invalidate access
tokens after very long periods of inactivity.
Fields:
- 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_id: Globally unique UUID that identifies a specific Member. The `member_id` is critical to perform operations on a Member, so be sure to preserve this value.
- include_refresh_token: Whether to return the refresh token Stytch has stored for the OAuth Provider. Defaults to false. **Important:** If your application exchanges the refresh token, Stytch may not be able to automatically refresh access tokens in the future.
""" # noqa
headers: Dict[str, str] = {}
data: Dict[str, Any] = {
"organization_id": organization_id,
"member_id": member_id,
}
if include_refresh_token is not None:
data["include_refresh_token"] = include_refresh_token

url = self.api_base.url_for(
"/v1/b2b/organizations/{organization_id}/members/{member_id}/oauth_providers/github",
data,
)
res = await self.async_client.get(url, data, headers)
return GithubResponse.from_json(res.response.status, res.json)
Loading

0 comments on commit 99e2c57

Please sign in to comment.