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)
2. Magic Link / Code-based Authentication
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
useCreateTOTPMfa(): Start TOTP association.useVerifyCreateTOTPMfa(): Confirm TOTP association.useCreateMfaPreference(): Set preferred MFA type.useRemoveMfa(): Disable MFA.useAnswerMfaChallenge(): Answer an MFA challenge during login.