Skip to content

Commit 29ac50c

Browse files
Getting library ready to publish. (#57)
* Add Android lint baseline for `cmp-sample-android` * Created `lint-baseline.xml` in `cmp-sample-android` to suppress existing lint issues across multiple categories, including Scoped Storage, Obsolete SDK versions, unused resources, and typos. * Recorded current lint violations for security (custom trust managers), performance (long vector paths), and API deprecations to serve as a baseline for future checks. * Update dependencies, Gradle, and build configurations * Upgraded Gradle wrapper from 8.13 to 9.1.0. * Updated various dependencies in `libs.versions.toml`, including Kotlin, `composeBom`, and `spotless`. * Moved `.editorconfig` to the root directory and configured it for ktlint function naming. * Added the `vanniktech.mavenPublish` plugin to the `mifos-authenticator-passcode` module. * Simplified the `cmp-sample-web` build by removing the custom `commonWebpackConfig` and adding explicit Compose dependencies for `jsMain` and `wasmJsMain`. * Updated `yarn.lock` with changes to multiple JS dependencies. * Minor clean-up in build scripts and baseline files. * Update integration guide in README.md * Rewrote the "How to implement Passcode" section into a comprehensive "Mifos Passcode Authenticator Integration Guide". * Added detailed steps for implementing `PasscodeStorageAdapter` and initializing `PasscodeManager` using `rememberPasscodeManager`. * Included code examples for setting up navigation logic and implementing the `PasscodeScreen` composable. * Added documentation for advanced usage, including changing and deleting passcodes using `PasscodeAction`. * Provided a summary of key components (`PasscodeStorageAdapter`, `PasscodeManager`, `PasscodeScreen`, and `PasscodeAction`). * Update project documentation and structure overview * Refactored the "Project Structure" section in `README.md` to provide a clearer overview of the project's modules, including `build-logic`, `cmp-sample` modules, and shared components. * Updated descriptions for `mifos-authenticator-biometrics` and `mifos-authenticator-passcode` to accurately reflect platform-specific implementations and logic distribution. * Added descriptions for the `cmp-sample-shared` module and its role in demonstrating authentication flows. * Improved formatting and readability of the documentation. * Add library documentation and improve code comments * Added a comprehensive `README.md` for the `mifos-authenticator-biometrics` module, providing a library overview, feature list, and usage examples for Android, iOS, and Windows. * Added detailed KDoc comments to common interfaces and enums, including `AuthenticationResult`, `PlatformAuthOptions`, `PlatformAuthenticatorStatus`, and `RegistrationResult`. * Enhanced documentation in `PlatformAuthenticationProvider` and `PlatformAuthenticator` to better describe platform-specific requirements (e.g., `FragmentActivity` on Android and passkey registration on Windows). * Improved parameter descriptions and return type documentation across the public API. * Added KDoc to `PlatformAvailableAuthenticationOption` to clarify its role in querying available device authentication methods. * Add comprehensive documentation and library publishing configurations * Added KDoc documentation to `PasscodeScreen`, `PasscodeManager`, `PasscodeStorageAdapter`, and related configuration classes to improve API clarity. * Configured `mavenPublishing` in `mifos-authenticator-passcode` and `mifos-authenticator-biometrics` with POM metadata, license information (MPL 2.0), and developer details. * Updated GitHub Actions `publish.yml` to correctly order GPG signing environment variables. * Moved the passcode integration guide from the root `README.md` to a module-specific `README.md` in `mifos-authenticator-passcode` and added a customization section. * Removed the copyright header from `PasscodeScreenConfig.kt`. * Update documentation for mifos-authenticator-biometrics * Extensively updated `mifos-authenticator-biometrics/README.md` with detailed installation instructions, a quick-start guide, and comprehensive API references for `PlatformAuthenticationProvider` and `PlatformAuthenticator`. * Added platform-specific implementation details for Android, iOS, Windows, and Web (WebAuthn). * Included best practices for Android thread management, registration data storage, and lifecycle handling. * Added detailed ViewModel integration examples for registration and authentication flows. * Removed redundant platform authenticator usage details from the root `README.md`, centralizing documentation within the module directory. * Update documentation for mifos-authenticator-biometrics * Extensively updated `mifos-authenticator-biometrics/README.md` with detailed installation instructions, a quick-start guide, and comprehensive API references for `PlatformAuthenticationProvider` and `PlatformAuthenticator`. * Added platform-specific implementation details for Android, iOS, Windows, and Web (WebAuthn). * Included best practices for Android thread management, registration data storage, and lifecycle handling. * Added detailed ViewModel integration examples for registration and authentication flows. * Removed redundant platform authenticator usage details from the root `README.md`, centralizing documentation within the module directory. * Configure maven-publish plugin and add module-specific properties * Downgraded `vanniktech-mavenPublish` plugin from version `0.35.0` to `0.31.0` in `libs.versions.toml`. * Added `gradle.properties` files to `mifos-authenticator-biometrics` and `mifos-authenticator-passcode` modules to define maven publishing coordinates and project metadata. * Updated the `build.gradle.kts` files in both modules to utilize the new properties for configuring the `maven-publish` plugin, enabling publishing to Maven Central. * Adjusted the `inceptionYear` in the POM configuration for both modules from `2026` to `2025`. * Update developer information and clean up build configuration * Updated developer ID, name, and URL to "openMF" and "Mifos Initiative" in the Maven publication settings for `mifos-authenticator-biometrics` and `mifos-authenticator-passcode`. * Removed a commented-out `coordinates` line in `mifos-authenticator-biometrics/build.gradle.kts`.
1 parent b2e9e2d commit 29ac50c

29 files changed

Lines changed: 2738 additions & 439 deletions

File tree

.editorconfig

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
[*.{kt,kts}]
2+
ij_kotlin_allow_trailing_comma=true
3+
ij_kotlin_allow_trailing_comma_on_call_site=true
4+
ktlint_function_naming_ignore_when_annotated_with=Composable, Test, Preview

.github/workflows/publish.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,6 @@ jobs:
1919
env:
2020
ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.MAVEN_CENTRAL_USERNAME }}
2121
ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.MAVEN_CENTRAL_PASSWORD }}
22+
ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.GPG_KEY_CONTENTS }}
2223
ORG_GRADLE_PROJECT_signingInMemoryKeyId: ${{ secrets.SIGNING_KEY_ID }}
2324
ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.SIGNING_PASSWORD }}
24-
ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.GPG_KEY_CONTENTS }}

README.md

Lines changed: 20 additions & 315 deletions
Original file line numberDiff line numberDiff line change
@@ -35,328 +35,33 @@ Designed with modularity and security in mind, this library is a foundational pa
3535
| Linux | ✅ Supported ||
3636
| Web | ✅ Supported ||
3737

38-
---
39-
40-
## Project Structure
41-
```
42-
mifos-authenticator
43-
-core
44-
-designSystem
45-
-mifos-authenticator-passcode
46-
-commonMain
47-
-mifos-authenticator-biometrics
48-
-androidMain
49-
-commonMain
50-
-desktopMain(jvm)
51-
-windows
52-
-linux
53-
-mac
54-
-jsMain
55-
-wasmJsMain
56-
```
57-
### `core`
58-
- **`designsystem`**
59-
- It provides the foundation for theming.
60-
### `mifos-authenticator-biometrics`
61-
- **`commonMain/`**
62-
- Platform-agnostic Platform Authenticator logic.
63-
- `androidMain/`
64-
- Biometric Prompt implementation for Platform Authenticator.
65-
- `iosMain/`
66-
- LocalAuthenticator implementation for Platform Authenticator.
67-
- `desktopMain(jvm)/`
68-
- `windows/`
69-
- Windows Hello implementation for Platform Authenticator.
70-
- `linux/`
71-
72-
- `macOS/`
73-
- LocalAuthenticator implementation for Platform Authenticator.
74-
- `jsMain/`
75-
- WebAuthN implementation for using the available FIDO2 or Platform Authenticator.
76-
- `wasmMain/`
77-
- WebAuthN implementation for using the available FIDO2 or Platform Authenticator.
78-
### `mifos-authenticator-passcode`
79-
This module provides logic for pass-code authentication and the pass-code screen.
80-
81-
- **`commonMain/`**
82-
- Contains working logic for passcode authentication.
83-
- Defines the passcode screen ui and its components.
84-
85-
86-
### `sample`
87-
88-
Cross-platform sample implementation of the passcode screen UI and Platform Authenticator:
89-
90-
- **`commonMain/`**
91-
– Shared Platform Authenticator using Compose Multiplatform.
92-
- Logic for using the Passcode implementation.
93-
- **`<platform>Main/`**
94-
– Platform-specific UI wiring for the Platform Authenticator.
95-
96-
---
97-
98-
## How to implement Passcode
99-
The `PasscodeScreen` is a composable function designed to handle passcode authentication or setup workflows in your app. It is powered by a state-preserving utility `rememberPasscodeSaver`, which manages the current passcode state and provides utility functions for saving and clearing the passcode.
100-
101-
### How to Use
102-
103-
To use the `PasscodeScreen`, you must first set up a `PasscodeSaver` instance using `rememberPasscodeSaver`.
104-
105-
```kotlin
106-
val passcodeSaver = rememberPasscodeSaver(
107-
currentPasscode = currentPasscode,
108-
isPasscodeSet = isPasscodeAlreadySet,
109-
savePasscode = { passcode -> /* handle saving */ },
110-
clearPasscode = { /* handle clearing */ }
111-
)
112-
```
113-
Then pass this `passcodeSaver` to the `PasscodeScreen`:
114-
115-
```kotlin
116-
PasscodeScreen(
117-
passcodeSaver = passcodeSaver,
118-
onForgotButton = { /* handle forgot passcode */ },
119-
onSkipButton = { /* handle skip action */ },
120-
onPasscodeRejected = { /* handle wrong passcode entry */ },
121-
onPasscodeConfirm = { passcode -> /* handle successful confirmation */ }
122-
)
123-
```
124-
## Parameters
125-
126-
### `PasscodeScreen`
127-
128-
- **`passcodeSaver`** – Required. Handles the passcode input and stores the current state.
129-
- **`onForgotButton`** – Called when the user taps the **"Forgot"** button.
130-
- **`onSkipButton`** – Called when the user taps the **"Skip"** button.
131-
- **`onPasscodeRejected`** – Optional. Called when the entered passcode is wrong.
132-
- **`onPasscodeConfirm`** – Called when the user enters the correct passcode or finishes setting a new one.
133-
134-
### `rememberPasscodeSaver`
135-
136-
- **`currentPasscode`** – The current passcode (if already set).
137-
- **`isPasscodeSet`** – Tells the screen whether the user is verifying an existing passcode or creating a new one.
138-
- **`savePasscode`** – A function that saves the passcode.
139-
- **`clearPasscode`** – A function that clears the saved passcode.
140-
141-
## How it works
142-
143-
- If there's already a passcode, the screen asks the user to enter it and checks if it matches.
144-
- If no passcode is set, the screen helps the user create and confirm a new one.
145-
- The `rememberPasscodeSaver` keeps everything in sync and remembers the state even if the screen recomposes.
146-
147-
## Screenshots
148-
149-
## Mobile
150-
| | |
151-
|:-------------------------------------:|:-------------------------------------:|
152-
|<img src= https://github.com/user-attachments/assets/b0ea6771-8bd2-403b-9d32-ae0f12eed847 />| <img src= https://github.com/user-attachments/assets/06415a14-3271-42b7-9ddb-93e0bad529fe />|
153-
154-
## Desktop and web
155-
156-
<img src = https://github.com/user-attachments/assets/fe699e95-d911-46c2-8d96-78e80a90dac6 />
157-
<img src="https://github.com/user-attachments/assets/9c3a3d20-b801-42f0-a345-4bb491f95ce4" />
158-
<img src="https://github.com/user-attachments/assets/bac1d30a-013d-42b1-8b21-bb3dd8a3ea84" />
15938

16039
---
16140

162-
## Platform Authenticator Usage
163-
164-
This module provides a unified and multiplatform way to handle device-based authentication. It uses a `PlatformAuthenticator` to interact with platform-specific mechanisms (like Windows Hello or Android BiometricPrompt) and wraps it in a thread-safe `PlatformAuthenticationProvider` for easy and safe use in your application.
165-
166-
---
167-
168-
## Getting Started
169-
170-
To use the platform authenticator, first create an instance of `PlatformAuthenticator`, and then pass it to PlatformAuthenticationProvider.
171-
172-
On `Android`, you must pass a `FragmentActivity` or an `AppCompatActivity`. On other platforms, this is not required. <br>
173-
174-
```kotlin
175-
// 1. Create the platform-specific authenticator instance
176-
// On Android
177-
val authenticator = PlatformAuthenticator(this)
178-
// On other platforms
179-
val authenticator = PlatformAuthenticator()
180-
181-
// 2. Create the provider instance to interact with
182-
val authProvider = PlatformAuthenticationProvider(authenticator)
183-
```
184-
---
185-
186-
## API Overview
187-
188-
### `PlatformAuthenticationProvider` (Recommended)
189-
This is the main class you should interact with. It acts as a thread-safe facade that simplifies using the `PlatformAuthenticator`.
190-
191-
```kotlin
192-
final class PlatformAuthenticationProvider(private val authenticator: PlatformAuthenticator) {
193-
194-
// Checks the current status of the device authenticator.
195-
fun deviceAuthenticatorStatus(): Set<PlatformAuthenticatorStatus>
196-
197-
// Prompts the user to set up a platform authenticator.
198-
fun setupPlatformAuthenticator()
199-
200-
// Registers a user and creates a platform-specific passkey.
201-
suspend fun registerUser(
202-
userName: String = "",
203-
emailId: String = "",
204-
displayName: String = ""
205-
): RegistrationResult
206-
207-
// Verifies the user against their registered credential.
208-
suspend fun onAuthenticatorClick(
209-
appName: String = "",
210-
savedRegistrationData: String? = null
211-
): AuthenticationResult
212-
}
213-
```
214-
`PlatformAuthenticator` (Underlying Engine)
215-
This `expect class` contains the core platform-specific logic. It's managed by the `PlatformAuthenticationProvider`.
216-
217-
```kotlin
218-
expect class PlatformAuthenticator private constructor() {
219-
constructor(activity: Any? = null)
220-
fun getDeviceAuthenticatorStatus(): Set<PlatformAuthenticatorStatus>
221-
fun setDeviceAuthOption()
222-
suspend fun registerUser(...): RegistrationResult
223-
suspend fun authenticate(...): AuthenticationResult
224-
}
225-
```
226-
227-
### Important Note for Android Developers
228-
**Thread Requirement**: The Android `BiometricPrompt` API, which this module uses under the hood, requires that it be invoked from the main thread.
229-
230-
Therefore, you must call `platformAuthenticationProvider.registerUser(...)` and `platformAuthenticationProvider.onAuthenticatorClick(...)` from the **Main dispatcher**. Using `viewModelScope` in Android ViewModels handles this correctly, but it's good practice to be explicit.
231-
232-
```kotlin
233-
// Always launch from the Main thread for these calls
234-
viewModelScope.launch(Dispatchers.Main) {
235-
// ... call registerUser or onAuthenticatorClick
236-
}
237-
```
238-
239-
### PlatformAuthenticatorStatus (enum)
240-
The `getDeviceAuthenticatorStatus()` function returns a set of the following values:
241-
242-
- `NOT_AVAILABLE` – Platform authenticator is not supported on the device.
243-
- `NOT_SETUP` – Authenticator is available but not set up yet.
244-
- `DEVICE_CREDENTIAL_SET` – Device credential (PIN, password, etc.) is available.
245-
- `BIOMETRICS_NOT_SET` – Biometrics are supported but not configured.
246-
- `BIOMETRICS_NOT_AVAILABLE` – Biometrics are not available on the device.
247-
- `BIOMETRICS_UNAVAILABLE` – Biometrics are temporarily unavailable.
248-
- `BIOMETRICS_SET` – Biometrics are available and configured.
249-
250-
### ViewModel Integration Examples
251-
Here’s how you can integrate `PlatformAuthenticationProvider` into your ViewModels, ensuring calls are made on the correct thread.
252-
253-
**Registration ViewModel**
254-
This ViewModel handles the logic for the user registration screen.
255-
256-
```kotlin
257-
class ChooseAuthOptionScreenViewmodel(
258-
private val platformAuthenticationProvider: PlatformAuthenticationProvider,
259-
// other dependencies...
260-
) : ViewModel() {
261-
262-
private val _registrationResult = MutableStateFlow<RegistrationResult?>(null)
263-
val registrationResult = _registrationResult.asStateFlow()
264-
265-
private val _authenticatorStatus = MutableStateFlow(platformAuthenticationProvider.deviceAuthenticatorStatus())
266-
val authenticatorStatus = _authenticatorStatus.asStateFlow()
267-
268-
fun updatePlatformAuthenticatorStatus() {
269-
_authenticatorStatus.value = platformAuthenticationProvider.deviceAuthenticatorStatus()
270-
}
271-
272-
fun registerUser(userID: String = "", userEmail: String = "", displayName: String = "") {
273-
// Explicitly launch on the Main thread
274-
viewModelScope.launch(Dispatchers.Main) {
275-
_registrationResult.value = platformAuthenticationProvider.registerUser(
276-
userID,
277-
userEmail,
278-
displayName
279-
)
280-
}
281-
}
282-
}
283-
```
284-
285-
**Possible return values:**
286-
287-
- `RegistrationResult.Success` - Its parameter contains the registration data that has to saved.
288-
The same data is passed as an argument to the `authenticate` function
289-
- `RegistrationResult.Error` - Its parameter contains a message telling what type of error was received.
290-
- `RegistrationResult.PlatformAuthenticatorNotAvailable`
291-
- `RegistrationResult.PlatformAuthenticatorNotSet`
292-
293-
294-
### Authentication ViewModel
295-
This ViewModel manages the authentication flow, such as on a login screen.
296-
297-
```kotlin
298-
class PlatformAuthenticationScreenViewModel(
299-
private val platformAuthenticationProvider: PlatformAuthenticationProvider,
300-
private val preferenceDataStore: PreferenceDataStore
301-
) : ViewModel() {
302-
303-
private val _authenticationResult = MutableStateFlow<AuthenticationResult?>(null)
304-
val authenticationResult = _authenticationResult.asStateFlow()
305-
306-
private val _isLoading = MutableStateFlow(false)
307-
val isLoading = _isLoading.asStateFlow()
308-
309-
fun authenticateUser(appName: String) {
310-
// Explicitly launch on the Main thread
311-
viewModelScope.launch(Dispatchers.Main) {
312-
_isLoading.value = true
313-
val savedData = preferenceDataStore.getRegistrationData()
314-
_authenticationResult.value = platformAuthenticationProvider.onAuthenticatorClick(appName, savedData)
315-
_isLoading.value = false
316-
}
317-
}
318-
319-
fun clearUserRegistrationFromApp() {
320-
preferenceDataStore.clearData(REGISTRATION_DATA)
321-
}
322-
}
323-
```
324-
325-
**Possible return values:**
326-
- `AuthenticationResult.Success`
327-
- `AuthenticationResult.Error`
328-
- `AuthenticationResult.UserNotRegistered` - If the user disables the platform authenticator, or in case of Windows Hello, the passkey is deleted or the Authenticator is disabled. The user should be logged out in this case and registered again.
41+
## Project Structure
32942

330-
### Setting Up the Authenticator
331-
Prompt the user to set up a device credential or biometric authentication:
43+
The project is organized into several modules to ensure a clear separation of concerns and to facilitate multiplatform development:
33244

333-
```kotlin
334-
authProvider.setupPlatformAuthenticator()
335-
```
336-
On `Android`, it actually redirects users to a screen for setting up a platform authentication method.
337-
On `Windows`, it will only show a message saying `Set up Windows Hello from settings`. Windows Hello
338-
itself shows a similar message in some cases and doesn't redirect users to the setup screen.
45+
- **`build-logic`**: Contains Gradle convention plugins used to standardize build configurations across all modules (linting, static analysis, multiplatform setup).
46+
- **`mifos-authenticator-passcode`**: A Compose Multiplatform library providing the logic and UI components for passcode authentication.
47+
- **`mifos-authenticator-biometrics`**: A library providing platform-specific implementations for device authentication (Biometrics, Windows Hello, WebAuthn).
48+
- **`cmp-sample-shared`**: Contains the shared business logic, navigation, and UI for the sample applications.
49+
- **`cmp-sample-android`**: The Android entry point for the sample application.
50+
- **`cmp-sample-ios`**: The iOS entry point for the sample application.
51+
- **`cmp-sample-desktop`**: The Desktop (JVM) entry point for the sample application.
52+
- **`cmp-sample-web`**: The Web (Wasm & JS) entry point for the sample application.
33953

340-
### RegistrationResult (sealed interface)
341-
Returned by `authProvider.registerUser()`.
54+
### `mifos-authenticator-biometrics`
55+
- **`commonMain/`**: Platform-agnostic interface and provider for device authentication.
56+
- **`androidMain/`**: Implementation using Android `BiometricPrompt`.
57+
- **`iosMain/`**: Implementation using iOS `LocalAuthentication`.
58+
- **`desktopMain/`**: Implementation using JNA for Windows Hello (on Windows) and placeholders for other desktop platforms.
59+
- **`jsMain/` / `wasmJsMain/`**: Implementation using the Web Authentication API (WebAuthn).
34260

343-
```kotlin
344-
sealed interface RegistrationResult {
345-
data class Success(val registrationData: String) : RegistrationResult
346-
data class Error(val message: String) : RegistrationResult
347-
data object PlatformAuthenticatorNotSet : RegistrationResult
348-
data object PlatformAuthenticatorNotAvailable : RegistrationResult
349-
}
350-
```
61+
### `mifos-authenticator-passcode`
62+
- **`commonMain/`**: Contains the `PasscodeManager`, `PasscodeStorageAdapter`, and the `PasscodeScreen` UI built with Compose Multiplatform.
35163

352-
### AuthenticationResult (sealed interface)
353-
Returned by `authProvider.onAuthenticatorClick()`.
64+
### `cmp-sample-shared`
65+
- **`commonMain/`**: Defines the shared UI, themes, and navigation logic. It integrates both the passcode and biometric modules to demonstrate a complete authentication flow.
35466

355-
```kotlin
356-
sealed interface AuthenticationResult {
357-
data object Success : AuthenticationResult
358-
data class Error(val message: String) : AuthenticationResult
359-
data object UserNotRegistered : AuthenticationResult
360-
}
361-
```
36267

cmp-sample-android/build.gradle.kts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
*
88
* See https://github.com/openMF/mifos-passcode-cmp/blob/development/LICENSE
99
*/
10-
import org.gradle.kotlin.dsl.support.kotlinCompilerOptions
1110
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
1211

1312
plugins {
@@ -29,7 +28,11 @@ android {
2928

3029
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
3130
}
32-
31+
buildTypes {
32+
getByName("release") {
33+
signingConfig = signingConfigs.getByName("debug")
34+
}
35+
}
3336
}
3437

3538
kotlin {
@@ -54,5 +57,4 @@ dependencies {
5457
androidTestImplementation(libs.androidx.espresso.core)
5558

5659
implementation(libs.androidx.activity.compose)
57-
58-
}
60+
}

0 commit comments

Comments
 (0)