Skip to content

Commit

Permalink
Add cross-org password support
Browse files Browse the repository at this point in the history
  • Loading branch information
bgier-stytch committed Oct 21, 2024
1 parent b605f3a commit 81f5569
Show file tree
Hide file tree
Showing 14 changed files with 435 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -313,13 +313,31 @@ public interface Members {
*/
public fun dangerouslyGetCompletable(data: DangerouslyGetRequest): CompletableFuture<StytchResult<GetResponse>>

/**
* 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.
*/
public suspend fun oidcProviders(data: OIDCProviderInformationRequest): StytchResult<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.
*/
public fun oidcProviders(
data: OIDCProviderInformationRequest,
callback: (StytchResult<OIDCProvidersResponse>) -> Unit,
)

/**
* 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.
*/
public fun oidcProvidersCompletable(data: OIDCProviderInformationRequest): CompletableFuture<StytchResult<OIDCProvidersResponse>>

/**
Expand Down
33 changes: 21 additions & 12 deletions stytch/src/main/kotlin/com/stytch/java/b2b/api/otpsms/OTPSms.kt
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ public interface Sms {
* An error will be thrown if the Member already has a phone number and the provided `mfa_phone_number` does not match the
* existing one.
*
* Note that sending another OTP code before the first has expired will invalidate the first code.
* OTP codes expire after two minutes. Note that sending another OTP code before the first has expired will invalidate the
* first code.
*
* If a Member has a phone number and is enrolled in MFA, then after a successful primary authentication event (e.g.
* [email magic link](https://stytch.com/docs/b2b/api/authenticate-magic-link) or
Expand Down Expand Up @@ -70,7 +71,8 @@ public interface Sms {
* An error will be thrown if the Member already has a phone number and the provided `mfa_phone_number` does not match the
* existing one.
*
* Note that sending another OTP code before the first has expired will invalidate the first code.
* OTP codes expire after two minutes. Note that sending another OTP code before the first has expired will invalidate the
* first code.
*
* If a Member has a phone number and is enrolled in MFA, then after a successful primary authentication event (e.g.
* [email magic link](https://stytch.com/docs/b2b/api/authenticate-magic-link) or
Expand Down Expand Up @@ -109,7 +111,8 @@ public interface Sms {
* An error will be thrown if the Member already has a phone number and the provided `mfa_phone_number` does not match the
* existing one.
*
* Note that sending another OTP code before the first has expired will invalidate the first code.
* OTP codes expire after two minutes. Note that sending another OTP code before the first has expired will invalidate the
* first code.
*
* If a Member has a phone number and is enrolled in MFA, then after a successful primary authentication event (e.g.
* [email magic link](https://stytch.com/docs/b2b/api/authenticate-magic-link) or
Expand Down Expand Up @@ -138,9 +141,11 @@ public interface Sms {
* SMS OTPs may not be used as a primary authentication mechanism. They can be used to complete an MFA requirement, or
* they can be used as a step-up factor to be added to an existing session.
*
* This endpoint verifies that the one-time passcode (OTP) is valid and hasn't expired or been previously used. A given
* Member may only have a single active OTP code at any given time. If a Member requests another OTP code before the first
* one has expired, the first one will be invalidated.
* This endpoint verifies that the one-time passcode (OTP) is valid and hasn't expired or been previously used. OTP codes
* expire after two minutes.
*
* A given Member may only have a single active OTP code at any given time. If a Member requests another OTP code before
* the first one has expired, the first one will be invalidated.
*
* Exactly one of `intermediate_session_token`, `session_token`, or `session_jwt` must be provided in the request.
* If an intermediate session token is provided, this operation will consume it.
Expand All @@ -167,9 +172,11 @@ public interface Sms {
* SMS OTPs may not be used as a primary authentication mechanism. They can be used to complete an MFA requirement, or
* they can be used as a step-up factor to be added to an existing session.
*
* This endpoint verifies that the one-time passcode (OTP) is valid and hasn't expired or been previously used. A given
* Member may only have a single active OTP code at any given time. If a Member requests another OTP code before the first
* one has expired, the first one will be invalidated.
* This endpoint verifies that the one-time passcode (OTP) is valid and hasn't expired or been previously used. OTP codes
* expire after two minutes.
*
* A given Member may only have a single active OTP code at any given time. If a Member requests another OTP code before
* the first one has expired, the first one will be invalidated.
*
* Exactly one of `intermediate_session_token`, `session_token`, or `session_jwt` must be provided in the request.
* If an intermediate session token is provided, this operation will consume it.
Expand Down Expand Up @@ -199,9 +206,11 @@ public interface Sms {
* SMS OTPs may not be used as a primary authentication mechanism. They can be used to complete an MFA requirement, or
* they can be used as a step-up factor to be added to an existing session.
*
* This endpoint verifies that the one-time passcode (OTP) is valid and hasn't expired or been previously used. A given
* Member may only have a single active OTP code at any given time. If a Member requests another OTP code before the first
* one has expired, the first one will be invalidated.
* This endpoint verifies that the one-time passcode (OTP) is valid and hasn't expired or been previously used. OTP codes
* expire after two minutes.
*
* A given Member may only have a single active OTP code at any given time. If a Member requests another OTP code before
* the first one has expired, the first one will be invalidated.
*
* Exactly one of `intermediate_session_token`, `session_token`, or `session_jwt` must be provided in the request.
* If an intermediate session token is provided, this operation will consume it.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ package com.stytch.java.b2b.api.passwords
// !!!

import com.squareup.moshi.Moshi
import com.stytch.java.b2b.api.passwordsdiscovery.Discovery
import com.stytch.java.b2b.api.passwordsdiscovery.DiscoveryImpl
import com.stytch.java.b2b.api.passwordsemail.Email
import com.stytch.java.b2b.api.passwordsemail.EmailImpl
import com.stytch.java.b2b.api.passwordsexistingpassword.ExistingPassword
Expand Down Expand Up @@ -37,6 +39,8 @@ public interface Passwords {

public val existingPassword: ExistingPassword

public val discovery: Discovery

/**
* This API allows you to check whether the user’s provided password is valid, and to provide feedback to the user on how
* to increase the strength of their password.
Expand Down Expand Up @@ -221,6 +225,7 @@ internal class PasswordsImpl(
override val email: Email = EmailImpl(httpClient, coroutineScope)
override val sessions: Sessions = SessionsImpl(httpClient, coroutineScope)
override val existingPassword: ExistingPassword = ExistingPasswordImpl(httpClient, coroutineScope)
override val discovery: Discovery = DiscoveryImpl(httpClient, coroutineScope)

override suspend fun strengthCheck(data: StrengthCheckRequest): StytchResult<StrengthCheckResponse> =
withContext(Dispatchers.IO) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package com.stytch.java.b2b.api.passwordsdiscovery

// !!!
// WARNING: This file is autogenerated
// Only modify code within MANUAL() sections
// or your changes may be overwritten later!
// !!!

import com.squareup.moshi.Moshi
import com.stytch.java.b2b.api.passwordsdiscoveryemail.Email
import com.stytch.java.b2b.api.passwordsdiscoveryemail.EmailImpl
import com.stytch.java.b2b.models.passwordsdiscovery.AuthenticateRequest
import com.stytch.java.b2b.models.passwordsdiscovery.AuthenticateResponse
import com.stytch.java.common.InstantAdapter
import com.stytch.java.common.StytchResult
import com.stytch.java.http.HttpClient
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.future.asCompletableFuture
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.util.concurrent.CompletableFuture

public interface Discovery {
public val email: Email

public suspend fun authenticate(data: AuthenticateRequest): StytchResult<AuthenticateResponse>

public fun authenticate(
data: AuthenticateRequest,
callback: (StytchResult<AuthenticateResponse>) -> Unit,
)

public fun authenticateCompletable(data: AuthenticateRequest): CompletableFuture<StytchResult<AuthenticateResponse>>
}

internal class DiscoveryImpl(
private val httpClient: HttpClient,
private val coroutineScope: CoroutineScope,
) : Discovery {
private val moshi = Moshi.Builder().add(InstantAdapter()).build()

override val email: Email = EmailImpl(httpClient, coroutineScope)

override suspend fun authenticate(data: AuthenticateRequest): StytchResult<AuthenticateResponse> =
withContext(Dispatchers.IO) {
var headers = emptyMap<String, String>()

val asJson = moshi.adapter(AuthenticateRequest::class.java).toJson(data)
httpClient.post("/v1/b2b/passwords/discovery/authenticate", asJson, headers)
}

override fun authenticate(
data: AuthenticateRequest,
callback: (StytchResult<AuthenticateResponse>) -> Unit,
) {
coroutineScope.launch {
callback(authenticate(data))
}
}

override fun authenticateCompletable(data: AuthenticateRequest): CompletableFuture<StytchResult<AuthenticateResponse>> =
coroutineScope.async {
authenticate(data)
}.asCompletableFuture()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package com.stytch.java.b2b.api.passwordsdiscoveryemail

// !!!
// WARNING: This file is autogenerated
// Only modify code within MANUAL() sections
// or your changes may be overwritten later!
// !!!

import com.squareup.moshi.Moshi
import com.stytch.java.b2b.models.passwordsdiscoveryemail.ResetRequest
import com.stytch.java.b2b.models.passwordsdiscoveryemail.ResetResponse
import com.stytch.java.b2b.models.passwordsdiscoveryemail.ResetStartRequest
import com.stytch.java.b2b.models.passwordsdiscoveryemail.ResetStartResponse
import com.stytch.java.common.InstantAdapter
import com.stytch.java.common.StytchResult
import com.stytch.java.http.HttpClient
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.future.asCompletableFuture
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.util.concurrent.CompletableFuture

public interface Email {
public suspend fun resetStart(data: ResetStartRequest): StytchResult<ResetStartResponse>

public fun resetStart(
data: ResetStartRequest,
callback: (StytchResult<ResetStartResponse>) -> Unit,
)

public fun resetStartCompletable(data: ResetStartRequest): CompletableFuture<StytchResult<ResetStartResponse>>

public suspend fun reset(data: ResetRequest): StytchResult<ResetResponse>

public fun reset(
data: ResetRequest,
callback: (StytchResult<ResetResponse>) -> Unit,
)

public fun resetCompletable(data: ResetRequest): CompletableFuture<StytchResult<ResetResponse>>
}

internal class EmailImpl(
private val httpClient: HttpClient,
private val coroutineScope: CoroutineScope,
) : Email {
private val moshi = Moshi.Builder().add(InstantAdapter()).build()

override suspend fun resetStart(data: ResetStartRequest): StytchResult<ResetStartResponse> =
withContext(Dispatchers.IO) {
var headers = emptyMap<String, String>()

val asJson = moshi.adapter(ResetStartRequest::class.java).toJson(data)
httpClient.post("/v1/b2b/passwords/discovery/email/reset/start", asJson, headers)
}

override fun resetStart(
data: ResetStartRequest,
callback: (StytchResult<ResetStartResponse>) -> Unit,
) {
coroutineScope.launch {
callback(resetStart(data))
}
}

override fun resetStartCompletable(data: ResetStartRequest): CompletableFuture<StytchResult<ResetStartResponse>> =
coroutineScope.async {
resetStart(data)
}.asCompletableFuture()

override suspend fun reset(data: ResetRequest): StytchResult<ResetResponse> =
withContext(Dispatchers.IO) {
var headers = emptyMap<String, String>()

val asJson = moshi.adapter(ResetRequest::class.java).toJson(data)
httpClient.post("/v1/b2b/passwords/discovery/email/reset", asJson, headers)
}

override fun reset(
data: ResetRequest,
callback: (StytchResult<ResetResponse>) -> Unit,
) {
coroutineScope.launch {
callback(reset(data))
}
}

override fun resetCompletable(data: ResetRequest): CompletableFuture<StytchResult<ResetResponse>> =
coroutineScope.async {
reset(data)
}.asCompletableFuture()
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ package com.stytch.java.b2b.api.passwordsemail
// !!!

import com.squareup.moshi.Moshi
import com.stytch.java.b2b.models.passwordsemail.DeleteRequest
import com.stytch.java.b2b.models.passwordsemail.DeleteResponse
import com.stytch.java.b2b.models.passwordsemail.RequireResetRequest
import com.stytch.java.b2b.models.passwordsemail.RequireResetRequestOptions
import com.stytch.java.b2b.models.passwordsemail.RequireResetResponse
import com.stytch.java.b2b.models.passwordsemail.ResetRequest
import com.stytch.java.b2b.models.passwordsemail.ResetResponse
import com.stytch.java.b2b.models.passwordsemail.ResetStartRequest
Expand Down Expand Up @@ -139,14 +140,21 @@ public interface Email {
*/
public fun resetCompletable(data: ResetRequest): CompletableFuture<StytchResult<ResetResponse>>

public suspend fun delete(data: DeleteRequest): StytchResult<DeleteResponse>
public suspend fun requireReset(
data: RequireResetRequest,
methodOptions: RequireResetRequestOptions? = null,
): StytchResult<RequireResetResponse>

public fun delete(
data: DeleteRequest,
callback: (StytchResult<DeleteResponse>) -> Unit,
public fun requireReset(
data: RequireResetRequest,
methodOptions: RequireResetRequestOptions? = null,
callback: (StytchResult<RequireResetResponse>) -> Unit,
)

public fun deleteCompletable(data: DeleteRequest): CompletableFuture<StytchResult<DeleteResponse>>
public fun requireResetCompletable(
data: RequireResetRequest,
methodOptions: RequireResetRequestOptions? = null,
): CompletableFuture<StytchResult<RequireResetResponse>>
}

internal class EmailImpl(
Expand Down Expand Up @@ -199,25 +207,35 @@ internal class EmailImpl(
reset(data)
}.asCompletableFuture()

override suspend fun delete(data: DeleteRequest): StytchResult<DeleteResponse> =
override suspend fun requireReset(
data: RequireResetRequest,
methodOptions: RequireResetRequestOptions?,
): StytchResult<RequireResetResponse> =
withContext(Dispatchers.IO) {
var headers = emptyMap<String, String>()
methodOptions?.let {
headers = methodOptions.addHeaders(headers)
}

val asJson = moshi.adapter(DeleteRequest::class.java).toJson(data)
httpClient.post("/v1/b2b/passwords/email/delete", asJson, headers)
val asJson = moshi.adapter(RequireResetRequest::class.java).toJson(data)
httpClient.post("/v1/b2b/passwords/email/require_reset", asJson, headers)
}

override fun delete(
data: DeleteRequest,
callback: (StytchResult<DeleteResponse>) -> Unit,
override fun requireReset(
data: RequireResetRequest,
methodOptions: RequireResetRequestOptions?,
callback: (StytchResult<RequireResetResponse>) -> Unit,
) {
coroutineScope.launch {
callback(delete(data))
callback(requireReset(data, methodOptions))
}
}

override fun deleteCompletable(data: DeleteRequest): CompletableFuture<StytchResult<DeleteResponse>> =
override fun requireResetCompletable(
data: RequireResetRequest,
methodOptions: RequireResetRequestOptions?,
): CompletableFuture<StytchResult<RequireResetResponse>> =
coroutineScope.async {
delete(data)
requireReset(data, methodOptions)
}.asCompletableFuture()
}
Loading

0 comments on commit 81f5569

Please sign in to comment.