solibo-sdk

Authentication & MFA

The Solibo SDK provides a robust authentication system built on top of AWS Cognito, supporting various login methods and Multi-Factor Authentication (MFA).

Authentication Flows

The primary entry point for authentication is the Auth class, accessible via sdk.auth.

1. Password-based Authentication (SRP)

We use the Secure Remote Password (SRP) protocol for secure password-based authentication. This ensures that passwords are never sent over the wire.

val result = sdk.auth.initiateAuth(
    fcmDevice = FcmDeviceType.ANDROID, // or IOS, WEB
    creds = Credentials(login = "user@example.com", pwd = "your-password")
)

handleAuthResult(result)

Users can log in using a one-time code sent via email or SMS.

// 1. Initiate the magic login
val result = sdk.auth.initiateAuth(
    fcmDevice = FcmDeviceType.ANDROID,
    creds = Credentials(login = "user@example.com"),
    usePassword = false
)

// 2. After receiving the code via email/SMS
val auth = sdk.auth.loginByCode(
    login = "user@example.com",
    code = "123456",
    fcmDevice = FcmDeviceType.ANDROID
)

3. OAuth Authentication

Used for third-party providers (e.g., Google, Apple).

val result = sdk.auth.loginByOAuth(
    code = "oauth-code",
    state = "oauth-state",
    fcmDevice = FcmDeviceType.ANDROID
)

Handling Authentication Results

The initiateAuth, loginByCode, and loginByOAuth methods return a SoliboAuthentication object. You must check this object to determine if the authentication is complete or if a challenge is required.

fun handleAuthResult(result: SoliboAuthentication) {
    if (result.tokens != null) {
        // Authentication successful!
        println("Logged in as ${result.userId}")
    } else if (result.challenge != null) {
        // A challenge is required (e.g., MFA)
        when (result.challenge?.type) {
            AuthChallengeType.SMS_MFA -> promptForSmsCode()
            AuthChallengeType.TOTF_MFA -> promptForTotpCode()
            AuthChallengeType.EMAIL_TOTF_MFA -> promptForEmailCode()
            AuthChallengeType.SELECT_MFA -> showMfaSelection(result.challenge.mfaTypes)
            AuthChallengeType.SETUP_MFA -> startMfaSetup()
            else -> println("Unsupported challenge: ${result.challenge?.type}")
        }
    }
}

3. Handling Multiple MFA Types (SELECT_MFA)

If a user has multiple MFA methods enabled, they might receive a SELECT_MFA challenge.

// 1. Select the desired MFA type
sdk.auth.createMfaSelection(MfaType.TOTP)

// 2. The server will then send a challenge (e.g. SMS code) or require another step
// Follow the challenge-response flow again.

Multi-Factor Authentication (MFA)

The SDK supports managing and answering MFA challenges.

Answering an MFA Challenge

When an MFA challenge is returned, you can use answerMfaChallenge to provide the verification code. The SDK automatically tracks the current challenge type, so you usually don’t need to pass it explicitly.

// Simplest way: the SDK remembers the challenge type from the previous step
val auth = sdk.auth.answerMfaChallenge(code = "123456")

// Optionally, you can specify the type and other details
val auth = sdk.auth.answerMfaChallenge(
    code = "123456",
    challengeType = AuthChallengeType.SMS_MFA
)

MFA Management (Enrolling & Disabling)

To enable MFA, a user typically needs to associate a TOTP device or set an MFA preference.

1. Enrolling in TOTP MFA

// Step A: Request TOTP association (returns QR code data/secret)
val association = sdk.auth.createTOTPMfa()
println("Secret: ${association.secretCode}")

// Step B: Verify the association with a code from the authenticator app
sdk.auth.verifyCreateTOTPMfa(code = "123456", deviceName = "My Phone")

2. Setting MFA Preference

// Set preferred MFA type (SMS, TOTP, or null to disable)
val success = sdk.auth.createMfaPreference(MfaType.TOTP)

3. Removing/Disabling MFA

val success = sdk.auth.removeMfa()

React Integration (solibo-query)

The @solibo/solibo-query package provides hooks that simplify these flows in React applications.

Login with MFA Example

import { useInitiateAuth, useAnswerMfaChallenge } from '@solibo/solibo-query';
import { AuthChallengeType } from '@solibo/solibo-sdk';

function Login() {
  const loginMutation = useInitiateAuth();
  const verifyMutation = useAnswerMfaChallenge();
  const [challenge, setChallenge] = useState<AuthChallengeType | null>(null);

  const handleLogin = async (e) => {
    const result = await loginMutation.mutateAsync({
      creds: { login: 'user@example.com', pwd: 'password' },
      fcmDevice: 'WEB'
    });

    if (result.challenge) {
      setChallenge(result.challenge.type);
    }
  };

  if (challenge) {
    return (
      <MfaVerification 
        onVerify={(code) => verifyMutation.mutate({ code })} 
      />
    );
  }

  return <button onClick={handleLogin}>Login</button>;
}

MFA Management Hooks