Skip to content

Commit

Permalink
Retired Emails SDK Changes (#187)
Browse files Browse the repository at this point in the history
* Retired Emails SDK Changes

* Minor version bump.

* Remove OAuth tenant features.

* Include docs fix

* Include `retired_email_addresses` field.
  • Loading branch information
jcook-stytch authored Jul 26, 2024
1 parent ff5aa7c commit f59943f
Show file tree
Hide file tree
Showing 25 changed files with 356 additions and 119 deletions.
2 changes: 0 additions & 2 deletions stytch/b2b/magiclinks_email.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,6 @@ func (c *MagicLinksEmailClient) LoginOrSignup(
// their status to `invited`. Sending invites to already `active` Members will return an error.
//
// The magic link invite will be valid for 1 week.
//
// /%}
func (c *MagicLinksEmailClient) Invite(
ctx context.Context,
body *email.InviteParams,
Expand Down
21 changes: 1 addition & 20 deletions stytch/b2b/organizations.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,25 +92,6 @@ func (c *OrganizationsClient) Get(
// *See the [Organization authentication settings](https://stytch.com/docs/b2b/api/org-auth-settings)
// resource to learn more about fields like `email_jit_provisioning`, `email_invites`,
// `sso_jit_provisioning`, etc., and their behaviors.
//
// Our RBAC implementation offers out-of-the-box handling of authorization checks for this endpoint. If you
// pass in
// a header containing a `session_token` or a `session_jwt` for an unexpired Member Session, we will check
// that the
// Member Session has the necessary permissions. The specific permissions needed depend on which of the
// optional fields
// are passed in the request. For example, if the `organization_name` argument is provided, the Member
// Session must have
// permission to perform the `update.info.name` action on the `stytch.organization` Resource.
//
// If the Member Session does not contain a Role that satisfies the requested permissions, or if the
// Member's Organization
// does not match the `organization_id` passed in the request, a 403 error will be thrown. Otherwise, the
// request will
// proceed as normal.
//
// To learn more about our RBAC implementation, see our
// [RBAC guide](https://stytch.com/docs/b2b/guides/rbac/overview).
func (c *OrganizationsClient) Update(
ctx context.Context,
body *organizations.UpdateParams,
Expand Down Expand Up @@ -144,7 +125,7 @@ func (c *OrganizationsClient) Update(
}

// Delete: Deletes an Organization specified by `organization_id`. All Members of the Organization will
// also be deleted. /%}
// also be deleted.
func (c *OrganizationsClient) Delete(
ctx context.Context,
body *organizations.DeleteParams,
Expand Down
48 changes: 48 additions & 0 deletions stytch/b2b/organizations/members/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,20 @@ type SearchParams struct {
Query *organizations.SearchQuery `json:"query,omitempty"`
}

// UnlinkRetiredEmailParams: Request type for `Members.UnlinkRetiredEmail`.
type UnlinkRetiredEmailParams struct {
// OrganizationID: 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.
OrganizationID string `json:"organization_id,omitempty"`
// MemberID: 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.
MemberID string `json:"member_id,omitempty"`
// EmailID: The globally unique UUID of a Member's email.
EmailID string `json:"email_id,omitempty"`
// EmailAddress: The email address of the Member.
EmailAddress string `json:"email_address,omitempty"`
}

// UpdateParams: Request type for `Members.Update`.
type UpdateParams struct {
// OrganizationID: Globally unique UUID that identifies a specific Organization. The `organization_id` is
Expand Down Expand Up @@ -339,6 +353,19 @@ func (o *SearchRequestOptions) AddHeaders(headers map[string][]string) map[strin
return headers
}

// UnlinkRetiredEmailRequestOptions:
type UnlinkRetiredEmailRequestOptions struct {
// Authorization: Optional authorization object.
// Pass in an active Stytch Member session token or session JWT and the request
// will be run using that member's permissions.
Authorization methodoptions.Authorization `json:"authorization,omitempty"`
}

func (o *UnlinkRetiredEmailRequestOptions) AddHeaders(headers map[string][]string) map[string][]string {
headers = o.Authorization.AddHeaders(headers)
return headers
}

// UpdateRequestOptions:
type UpdateRequestOptions struct {
// Authorization: Optional authorization object.
Expand Down Expand Up @@ -495,6 +522,27 @@ type SearchResponse struct {
StatusCode int32 `json:"status_code,omitempty"`
}

// UnlinkRetiredEmailResponse: Response type for `Members.UnlinkRetiredEmail`.
type UnlinkRetiredEmailResponse struct {
// RequestID: Globally unique UUID that is returned with every API call. This value is important to log for
// debugging purposes; we may ask for this value to help identify a specific API call when helping you
// debug an issue.
RequestID string `json:"request_id,omitempty"`
// MemberID: Globally unique UUID that identifies a specific Member.
MemberID string `json:"member_id,omitempty"`
// OrganizationID: 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.
OrganizationID string `json:"organization_id,omitempty"`
// Member: The [Member object](https://stytch.com/docs/b2b/api/member-object)
Member organizations.Member `json:"member,omitempty"`
// Organization: The [Organization object](https://stytch.com/docs/b2b/api/organization-object).
Organization organizations.Organization `json:"organization,omitempty"`
// StatusCode: The HTTP status code of the response. Stytch follows standard HTTP response status code
// patterns, e.g. 2XX values equate to success, 3XX values are redirects, 4XX are client errors, and 5XX
// are server errors.
StatusCode int32 `json:"status_code,omitempty"`
}

// UpdateResponse: Response type for `Members.Update`.
type UpdateResponse struct {
// RequestID: Globally unique UUID that is returned with every API call. This value is important to log for
Expand Down
13 changes: 10 additions & 3 deletions stytch/b2b/organizations/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ package organizations
import (
"time"

"github.com/stytchauth/stytch-go/v15/stytch/b2b/scim"
"github.com/stytchauth/stytch-go/v15/stytch/methodoptions"
)

Expand Down Expand Up @@ -211,8 +212,9 @@ type Member struct {
// who create an Organization through the
// [discovery flow](https://stytch.com/docs/b2b/api/create-organization-via-discovery). See the
// [RBAC guide](https://stytch.com/docs/b2b/guides/rbac/stytch-default) for more details on this Role.
IsAdmin bool `json:"is_admin,omitempty"`
TOTPRegistrationID string `json:"totp_registration_id,omitempty"`
IsAdmin bool `json:"is_admin,omitempty"`
TOTPRegistrationID string `json:"totp_registration_id,omitempty"`
RetiredEmailAddresses []RetiredEmail `json:"retired_email_addresses,omitempty"`
// MFAEnrolled: Sets whether the Member is enrolled in MFA. If true, the Member must complete an MFA step
// whenever they wish to log in to their Organization. If false, the Member only needs to complete an MFA
// step if the Organization's MFA policy is set to `REQUIRED_FOR_ALL`.
Expand Down Expand Up @@ -475,6 +477,11 @@ type ResultsMetadata struct {
NextCursor string `json:"next_cursor,omitempty"`
}

type RetiredEmail struct {
EmailID string `json:"email_id,omitempty"`
EmailAddress string `json:"email_address,omitempty"`
}

// SCIMRegistration:
type SCIMRegistration struct {
// ConnectionID: The ID of the SCIM connection.
Expand All @@ -484,7 +491,7 @@ type SCIMRegistration struct {
// ExternalID: The ID of the member given by the identity provider.
ExternalID string `json:"external_id,omitempty"`
// SCIMAttributes: An object for storing SCIM attributes brought over from the identity provider.
SCIMAttributes map[string]any `json:"scim_attributes,omitempty"`
SCIMAttributes *scim.SCIMAttributes `json:"scim_attributes,omitempty"`
}

// SSORegistration:
Expand Down
104 changes: 57 additions & 47 deletions stytch/b2b/organizations_members.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,25 +30,6 @@ func NewOrganizationsMembersClient(c stytch.Client) *OrganizationsMembersClient
}

// Update: Updates a Member specified by `organization_id` and `member_id`.
//
// Our RBAC implementation offers out-of-the-box handling of authorization checks for this endpoint. If you
// pass in
// a header containing a `session_token` or a `session_jwt` for an unexpired Member Session, we will check
// that the
// Member Session has the necessary permissions. The specific permissions needed depend on which of the
// optional fields
// are passed in the request. For example, if the `organization_name` argument is provided, the Member
// Session must have
// permission to perform the `update.info.name` action on the `stytch.organization` Resource.
//
// If the Member Session does not contain a Role that satisfies the requested permissions, or if the
// Member's Organization
// does not match the `organization_id` passed in the request, a 403 error will be thrown. Otherwise, the
// request will
// proceed as normal.
//
// To learn more about our RBAC implementation, see our
// [RBAC guide](https://stytch.com/docs/b2b/guides/rbac/overview).
func (c *OrganizationsMembersClient) Update(
ctx context.Context,
body *members.UpdateParams,
Expand Down Expand Up @@ -81,7 +62,7 @@ func (c *OrganizationsMembersClient) Update(
return &retVal, err
}

// Delete: Deletes a Member specified by `organization_id` and `member_id`. /%}
// Delete: Deletes a Member specified by `organization_id` and `member_id`.
func (c *OrganizationsMembersClient) Delete(
ctx context.Context,
body *members.DeleteParams,
Expand All @@ -106,7 +87,7 @@ func (c *OrganizationsMembersClient) Delete(
}

// Reactivate: Reactivates a deleted Member's status and its associated email status (if applicable) to
// active, specified by `organization_id` and `member_id`. /%}
// active, specified by `organization_id` and `member_id`.
func (c *OrganizationsMembersClient) Reactivate(
ctx context.Context,
body *members.ReactivateParams,
Expand Down Expand Up @@ -149,8 +130,6 @@ func (c *OrganizationsMembersClient) Reactivate(
// Member to enter a new phone number
// and calling the [OTP SMS send](https://stytch.com/docs/b2b/api/otp-sms-send) endpoint, then calling the
// [OTP SMS Authenticate](https://stytch.com/docs/b2b/api/authenticate-otp-sms) endpoint.
//
// /%}
func (c *OrganizationsMembersClient) DeleteMFAPhoneNumber(
ctx context.Context,
body *members.DeleteMFAPhoneNumberParams,
Expand Down Expand Up @@ -181,8 +160,6 @@ func (c *OrganizationsMembersClient) DeleteMFAPhoneNumber(
//
// Existing Member Sessions that include the TOTP authentication factor will not be revoked if the
// registration is deleted, and MFA will not be enforced until the Member logs in again.
//
// /%}
func (c *OrganizationsMembersClient) DeleteTOTP(
ctx context.Context,
body *members.DeleteTOTPParams,
Expand Down Expand Up @@ -210,26 +187,6 @@ func (c *OrganizationsMembersClient) DeleteTOTP(
// required. Submitting an empty `query` returns all non-deleted Members within the specified Organizations.
//
// *All fuzzy search filters require a minimum of three characters.
//
// Our RBAC implementation offers out-of-the-box handling of authorization checks for this endpoint. If you
// pass in
// a header containing a `session_token` or a `session_jwt` for an unexpired Member Session, we will check
// that the
// Member Session has permission to perform the `search` action on the `stytch.member` Resource. In
// addition, enforcing
// RBAC on this endpoint means that you may only search for Members within the calling Member's
// Organization, so the
// `organization_ids` argument may only contain the `organization_id` of the Member Session passed in the
// header.
//
// If the Member Session does not contain a Role that satisfies the requested permission, or if the
// `organization_ids`
// argument contains an `organization_id` that the Member Session does not belong to, a 403 error will be
// thrown.
// Otherwise, the request will proceed as normal.
//
// To learn more about our RBAC implementation, see our
// [RBAC guide](https://stytch.com/docs/b2b/guides/rbac/overview).
func (c *OrganizationsMembersClient) Search(
ctx context.Context,
body *members.SearchParams,
Expand Down Expand Up @@ -262,7 +219,7 @@ func (c *OrganizationsMembersClient) Search(
return &retVal, err
}

// DeletePassword: Delete a Member's password. /%}
// DeletePassword: Delete a Member's password.
func (c *OrganizationsMembersClient) DeletePassword(
ctx context.Context,
body *members.DeletePasswordParams,
Expand Down Expand Up @@ -309,7 +266,60 @@ func (c *OrganizationsMembersClient) DangerouslyGet(
return &retVal, err
}

// Create: Creates a Member. An `organization_id` and `email_address` are required. /%}
// UnlinkRetiredEmail: Unlinks a retired email address from a Member specified by their `organization_id`
// and `member_id`. The email address
// to be retired can be identified in the request body by either its `email_id`, its `email_address`, or
// both. If using
// both identifiers they must refer to the same email.
//
// A previously active email address can be marked as retired in one of two ways:
//
// - It's replaced with a new primary email address during an explicit Member update.
// - A new email address is surfaced by an OAuth, SAML or OIDC provider. In this case the new email address
// becomes the
//
// Member's primary email address and the old primary email address is retired.
//
// A retired email address cannot be used by other Members in the same Organization. However, unlinking
// retired email
// addresses allows then to be subsequently re-used by other Organization Members. Retired email addresses
// can be viewed
// on the [Member object](https://stytch.com/docs/b2b/api/member-object).
//
// %}
func (c *OrganizationsMembersClient) UnlinkRetiredEmail(
ctx context.Context,
body *members.UnlinkRetiredEmailParams,
methodOptions ...*members.UnlinkRetiredEmailRequestOptions,
) (*members.UnlinkRetiredEmailResponse, error) {
var jsonBody []byte
var err error
if body != nil {
jsonBody, err = json.Marshal(body)
if err != nil {
return nil, stytcherror.NewClientLibraryError("error marshaling request body")
}
}

headers := make(map[string][]string)
for _, methodOption := range methodOptions {
headers = methodOption.AddHeaders(headers)
}

var retVal members.UnlinkRetiredEmailResponse
err = c.C.NewRequest(
ctx,
"POST",
fmt.Sprintf("/v1/b2b/organizations/%s/members/%s/unlink_retired_email", body.OrganizationID, body.MemberID),
nil,
jsonBody,
&retVal,
headers,
)
return &retVal, err
}

// Create: Creates a Member. An `organization_id` and `email_address` are required.
func (c *OrganizationsMembersClient) Create(
ctx context.Context,
body *members.CreateParams,
Expand Down
2 changes: 2 additions & 0 deletions stytch/b2b/passwords.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ func (c *PasswordsClient) StrengthCheck(
// Migrate: Adds an existing password to a member's email that doesn't have a password yet. We support
// migrating members from passwords stored with bcrypt, scrypt, argon2, MD-5, SHA-1, and PBKDF2. This
// endpoint has a rate limit of 100 requests per second.
//
// The member's email will be marked as verified when you use this endpoint.
func (c *PasswordsClient) Migrate(
ctx context.Context,
body *passwords.MigrateParams,
Expand Down
3 changes: 2 additions & 1 deletion stytch/b2b/passwords/email/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ import (
type ResetParams struct {
// PasswordResetToken: The password reset token to authenticate.
PasswordResetToken string `json:"password_reset_token,omitempty"`
// Password: The password to reset.
// Password: The password to authenticate, reset, or set for the first time. Any UTF8 character is allowed,
// e.g. spaces, emojis, non-English characers, etc.
Password string `json:"password,omitempty"`
// SessionToken: Reuse an existing session instead of creating a new one. If you provide a `session_token`,
// Stytch will update the session.
Expand Down
4 changes: 2 additions & 2 deletions stytch/b2b/passwords/existingpassword/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ import (
type ResetParams struct {
// EmailAddress: The email address of the Member.
EmailAddress string `json:"email_address,omitempty"`
// ExistingPassword: The member's current password that they supplied.
// ExistingPassword: The Member's current password that they supplied.
ExistingPassword string `json:"existing_password,omitempty"`
// NewPassword: The member's elected new password.
// NewPassword: The Member's elected new password.
NewPassword string `json:"new_password,omitempty"`
// OrganizationID: 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.
Expand Down
3 changes: 2 additions & 1 deletion stytch/b2b/passwords/session/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ type ResetParams struct {
// OrganizationID: 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.
OrganizationID string `json:"organization_id,omitempty"`
// Password: The password to authenticate.
// Password: The password to authenticate, reset, or set for the first time. Any UTF8 character is allowed,
// e.g. spaces, emojis, non-English characers, etc.
Password string `json:"password,omitempty"`
// SessionToken: A secret token for a given Stytch Session.
SessionToken string `json:"session_token,omitempty"`
Expand Down
6 changes: 4 additions & 2 deletions stytch/b2b/passwords/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ type AuthenticateParams struct {
OrganizationID string `json:"organization_id,omitempty"`
// EmailAddress: The email address of the Member.
EmailAddress string `json:"email_address,omitempty"`
// Password: The password to authenticate.
// Password: The password to authenticate, reset, or set for the first time. Any UTF8 character is allowed,
// e.g. spaces, emojis, non-English characers, etc.
Password string `json:"password,omitempty"`
// SessionToken: A secret token for a given Stytch Session.
SessionToken string `json:"session_token,omitempty"`
Expand Down Expand Up @@ -148,7 +149,8 @@ type MigrateParams struct {

// StrengthCheckParams: Request type for `Passwords.StrengthCheck`.
type StrengthCheckParams struct {
// Password: The password to authenticate.
// Password: The password to authenticate, reset, or set for the first time. Any UTF8 character is allowed,
// e.g. spaces, emojis, non-English characers, etc.
Password string `json:"password,omitempty"`
// EmailAddress: The email address of the Member.
EmailAddress string `json:"email_address,omitempty"`
Expand Down
2 changes: 2 additions & 0 deletions stytch/b2b/passwords_email.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ func (c *PasswordsEmailClient) ResetStart(
//
// If a valid `session_token` or `session_jwt` is passed in, the Member will not be required to complete an
// MFA step.
//
// Note that a successful password reset by email will revoke all active sessions for the `member_id`.
func (c *PasswordsEmailClient) Reset(
ctx context.Context,
body *email.ResetParams,
Expand Down
3 changes: 3 additions & 0 deletions stytch/b2b/passwords_existingpassword.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ func NewPasswordsExistingPasswordClient(c stytch.Client) *PasswordsExistingPassw
//
// If a valid `session_token` or `session_jwt` is passed in, the Member will not be required to complete an
// MFA step.
//
// Note that a successful password reset via an existing password will revoke all active sessions for the
// `member_id`.
func (c *PasswordsExistingPasswordClient) Reset(
ctx context.Context,
body *existingpassword.ResetParams,
Expand Down
Loading

0 comments on commit f59943f

Please sign in to comment.