Login (Android SDK)
Our SDK provides a set of components for secure-input of authentication credentials for various scenarios. This section covers:
- Password-based authentication
- Passcode-based authentication
- OTP (One-Time Password) verification
Each component is designed with security features, including built-in text masking, secure handling, and proper validation.
The components allow a user to input their credentials in a secure fashion, which is converted into a user session token. This can then be used to execute requests with our API, allowing the user to interact with their financial instrumentsInstrument A financial product owned by an Identity. There are two types: Managed Accounts (stored-value accounts that hold balances and can receive wire transfers) and Managed Cards (prepaid cards - virtual or physical - used for purchases). and our services.
Choosing a login approach
There are two ways to submit a password to POST /login_with_password, and the approach you choose determines whether you use our secure components.
Tokenised login (preferred)
Capture the password with our secure login component and convert it into a one-time-use token. The password is encrypted inside a view your app cannot read, so your code and servers never see the plaintext. You exchange the token - not the password - for a session.
Use this approach for the standard Weavr security model. Because the plaintext never touches your app, you stay out of PCI DSS scope and qualify for the lightest Self-Assessment Questionnaire. This is the approach the rest of this page documents.
Non-tokenised login
If you don't use our tokenisationTokenize Replace a card's primary account number (PAN) with a unique digital token that stands in for the real card during a transaction. When a cardholder adds a card to Apple Pay or Google Pay via push provisioning, the wallet provider stores a device-specific token rather than the underlying PAN, so the real card number isn't exposed on the device or shared with merchants. for the password - for example, you already operate a PCI DSS-compliant environment that handles the plaintext securely - capture the password in your own native field, validate it there, then submit the plaintext value to POST /login_with_password from your server.
Accepting a plaintext password at POST /login_with_password requires configuration by our team and isn't something you can turn on yourself in the Weavr portal. Reach out to our support team to enable it before you build this flow.
In this case, don't use our secure login component. The component exists to tokeniseTokenize Replace a card's primary account number (PAN) with a unique digital token that stands in for the real card during a transaction. When a cardholder adds a card to Apple Pay or Google Pay via push provisioning, the wallet provider stores a device-specific token rather than the underlying PAN, so the real card number isn't exposed on the device or shared with merchants. the value inside a sandboxed view; it deliberately gives your code no way to read or validate the plaintext a user types, so it can't back a flow that needs the raw password.
TokenisedTokenize Replace a card's primary account number (PAN) with a unique digital token that stands in for the real card during a transaction. When a cardholder adds a card to Apple Pay or Google Pay via push provisioning, the wallet provider stores a device-specific token rather than the underlying PAN, so the real card number isn't exposed on the device or shared with merchants. login is preferred for every integration that doesn't already carry its own PCI DSS obligations, because it keeps the plaintext password out of your systems entirely.
Where each component fits in your login screen
Our login components each render one credential field inside a tokenisedTokenize Replace a card's primary account number (PAN) with a unique digital token that stands in for the real card during a transaction. When a cardholder adds a card to Apple Pay or Google Pay via push provisioning, the wallet provider stores a device-specific token rather than the underlying PAN, so the real card number isn't exposed on the device or shared with merchants. view. The mockup below shows where they typically appear in your app - hover the numbered hotspots for the underlying class names.
Sign in
Welcome back
Hover or focus a numbered hotspot to see which secure component renders each field - click to jump to its docs.
Component types
Password - SecurePasswordEditText
Use it for: login screens, sign-up password creation, and change-password flows.
Use this component for password-based authentication (something you know). It ensures secure text input with masking and validation.
Password complexity
Customer passwords must satisfy all of the following:
- 8 to 30 characters
- at least one uppercase letter
- at least one lowercase letter
- at least one number
- at least one special character
- different from the user's last 5 passwords
Validate password
Surface these rules in your sign-up and change-password screens before the user submits.
To check a candidate password against the same rules without committing it, call POST /passwords/validate from your server.
The endpoint returns success if the password meets the policy and a conflict response if it doesn't.
Submit password
The API rejects passwords that don't meet these requirements with AUTH_009 PASSWORD_POLICY_VIOLATION.
Integration
In XML:
<io.weavr.components.SecurePasswordEditText
android:id="@+id/password"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:ems="10"
android:hint="Password"
android:minHeight="48dp"
android:inputType="textPassword"
android:maxLength="100"
android:fontFamily="@font/your_custom_font"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
In Kotlin:
val passwordEditText = SecurePasswordEditText(this).apply {
layoutParams = LayoutParams(
LayoutParams.MATCH_PARENT,
LayoutParams.WRAP_CONTENT
)
}
binding.root.addView(passwordEditText)
Security features
- Secure text masking
- Input type enforcement
- Maximum length validation (100 characters)
- Secure text handling
Passcode - SecurePasscodeEditText
Use it for: login screens that authenticate the user with a 4-digit passcode, and as the fallback input when biometric authentication fails.
For passcode-based authentication (e.g. 4-digit PINPIN Personal Identification Number - the numeric code a cardholder enters to authorize chip-and-PIN purchases and ATM withdrawals. PIN is only present on physical managed cards. Weavr returns it tokenized on `GET /managed_cards/{id}` (with a stepped-up token), and the SDK detokenizes it inside a secure PIN display component.). Supports both standard and segmented input.
Standard integration
In XML:
<io.weavr.components.SecurePasscodeEditText
android:id="@+id/passcode"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:ems="10"
android:hint="Passcode"
android:minHeight="48dp"
android:inputType="numberPassword"
android:maxLength="4"
android:fontFamily="@font/your_custom_font"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
In Kotlin:
val securePasscodeEditText = SecurePasscodeEditText(this)
securePasscodeEditText.setLayoutParams(LayoutParams(...,...))
binding.root.addView(securePasscodeEditText)
Segmented integration
For a better user experience, use the segmented version:
In XML:
<io.weavr.components.SecurePasscodeEditTextSegmented
android:id="@+id/passcode_segmented"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
app:segment_gap_percent="10"
app:segment_height="48dp"
app:segment_border_color_active="#000000"
app:segment_border_color_inactive="#CCCCCC"
app:segment_border_width="2dp"
app:segment_corner_radius="8dp"
app:customFont="@font/your_custom_font"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
In Kotlin:
val segmentedPasscode = SecurePasscodeEditTextSegmented(this).apply {
segmentGapPercent = 10f
segmentBorderColorActive = Color.BLACK
segmentBorderColorInactive = Color.GRAY
segmentHeight = 48
segmentBorderWidth = 2
segmentCornerRadius = 8f
layoutParams = LayoutParams(
LayoutParams.MATCH_PARENT,
LayoutParams.WRAP_CONTENT
)
}
binding.root.addView(segmentedPasscode)
Customization options for the segmented version
segmentGapPercent: Gap between segments (default: 10%)segmentHeight: Height of each segment (default:48dp)segmentBorderColorActive: Border color when segment is activesegmentBorderColorInactive: Border color when segment is inactivesegmentBorderWidth: Width of segment border (default:2dp)segmentCornerRadius: Corner radius of segments (default:8dp)customFont: Custom font for segments
Listeners
Both styles support a TextEntryCompleteListener:
securePasscodeEditText.setTextEntryCompleteListener(
object : SecurePasscodeEditText.TextEntryCompleteListener {
override fun onTextEntryComplete() {
// Called when input reaches max length
}
}
)
segmentedPasscode.setTextEntryCompleteListener(
object : SecurePasscodeEditTextSegmented.TextEntryCompleteListener {
override fun onTextEntryComplete() {
// Called when all segments are filled
}
}
)
Additional listener for the segmented version
A focus-change listener
segmentedPasscode.onSegmentFocusChangeListener =
object : OnSegmentFocusChangeListener {
override fun onFocusChange(fieldIndex: Int, hasFocus: Boolean) {
// fieldIndex: 0-based segment index
// hasFocus: whether segment gained/lost focus
}
}
This is useful for:
- Tracking user input progress
- Implementing custom focus-based animations
- Adding visual feedback when segments gain/lose focus
- Handling focus-based validation
OTP - SecureOTPField
Use it for: SMS one-time-password screens, typically for step-up authentication or phone-number verification during sign-up.
For one-time passwords (6 digits, segmented).
<io.weavr.components.SecureOTPField
android:id="@+id/otp"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
app:segment_gap_percent="10"
app:segment_height="48dp"
app:segment_border_color_active="#000000"
app:segment_border_color_inactive="#CCCCCC"
app:segment_border_width="2dp"
app:segment_corner_radius="8dp"
app:customFont="@font/your_custom_font"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
Common features
All components share these security features:
- Secure text masking
- Input type enforcement
- Length validation
- Secure text handling
- TokenizationTokenize Replace a card's primary account number (PAN) with a unique digital token that stands in for the real card during a transaction. When a cardholder adds a card to Apple Pay or Google Pay via push provisioning, the wallet provider stores a device-specific token rather than the underlying PAN, so the real card number isn't exposed on the device or shared with merchants. support
- Error handling
- UI customization options
Token management
A login component is used to create a user session token; which is required when executing requests to our APIs.
Creating tokens
Single component:
binding.password.createToken(
object : OnTokenizeListener {
override fun onTokenizeComplete(result: Map<String, String?>?) {
val token = result?.values?.first().orEmpty()
// Success
}
override fun onTokenizeFailed(error: ErrorResponse) {
// Failure
}
}
)
Group tokenizationTokenize Replace a card's primary account number (PAN) with a unique digital token that stands in for the real card during a transaction. When a cardholder adds a card to Apple Pay or Google Pay via push provisioning, the wallet provider stores a device-specific token rather than the underlying PAN, so the real card number isn't exposed on the device or shared with merchants.:
group1.createToken(
fieldKeys = arrayListOf("passKey0", "passKey1", "passKey2"),
callBack = object : WeavrResult<Map<String, String?>, ErrorResponse> {
override fun onSuccess(result: Map<String, String?>?) {
val firstValue = result?.get("passKey0")
// Success
}
override fun onFailure(error: ErrorResponse) {
// Failure
}
}
)
Setting user token
After successful login, set the user token:
UXComponents.setUserToken(
this,
authToken, // Token from login
onAssociateListener = object : OnAssociateListener {
override fun onAssociateComplete(message: String) { /* … */ }
override fun onAssociateFailed(error: ErrorResponse) { /* … */ }
}
)
Verifying token association
val isAssociated = UXComponents.isAssociated(this)
Security considerations
- Always use secure input types (
textPassword,numberPassword) - Implement proper error handling
- Use appropriate token management
- Follow biometric authentication guidelines when using passcodes
- Validate input length and format
Frequently asked questions
What happens if a user has already enrolled their device for Biometric Authentication, but then re-installs the app?
In this case, the user needs to re-enroll the device for biometrics. This also applies if the user clears cache or data of the app.
If the configuration of the passcode length changes, what is the impact on existing users?
If the program was configured with a passcode length of 4 digits, and this is changed to 6, the user continues to be able to log in with their existing passcode of 4 digits. However, when they change their passcode (via the "forgot passcode" flow) they are required to choose a passcode with the new length (6 in this example).
How does the forgot passcode flow work?
The forgotten passcode flow is initiated from the Weavr API. If an email has been set for the active user, an email is sent containing a URL that redirects the user to change their passcode.
Does the user have multiple attempts at login and challenges via Biometrics Authentication?
For situations when biometrics fail (wrong fingerprint, not enough light, etc.) the user can retry multiple times. If biometrics continues to fail after too many attempts, it automatically triggers the passcode log-in, which also allows multiple attempts.
How many retries are available until the user is blocked?
The default value is 3. After 3 failed biometric attempts, the user is presented with the passcode fallback screen, where 3 attempts are also allowed. The passcode is our Authentication passcode that the user chose when they were created as a user on Weavr, not the one that unlocks their device.
When is the user blocked if biometrics and passcode fail and for how long?
After 3 biometric attempts and 3 wrong passcode attempts, the user gets blocked for 30 minutes.