Skip to content

Commit cd1bb71

Browse files
committed
Added readme
1 parent 7bdb952 commit cd1bb71

File tree

2 files changed

+258
-0
lines changed

2 files changed

+258
-0
lines changed

README.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# 🔐 Biometric SDK for Android
2+
3+
A lightweight and test-covered biometric authentication SDK built using modern Kotlin practices.
4+
Seamlessly plug into your Android apps to handle device-level biometric and credential verification.
5+
6+
---
7+
8+
## 🚀 Features
9+
10+
- ✅ Supports Fingerprint, Face, and Device Credentials
11+
- ✅ Simple interface to plug into any Android Activity or Fragment
12+
- ✅ Flexible authentication requirements (`MANDATORY`, `OPTIONAL`, `BYPASSABLE`)
13+
- ✅ Robust error handling and fallback support
14+
- ✅ 100% test-covered logic using MockK
15+
- ✅ Built with modular, SDK-ready structure
16+
17+
---
18+
19+
## 📦 Installation (via JitPack)
20+
21+
1. Add JitPack to your `settings.gradle.kts` or `build.gradle.kts` (root):
22+
23+
```kotlin
24+
dependencyResolutionManagement {
25+
repositories {
26+
google()
27+
mavenCentral()
28+
maven { url = uri("https://jitpack.io") }
29+
}
30+
}
Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
package com.sparkstudios.biometric
2+
3+
import io.mockk.*
4+
import org.junit.Assert.*
5+
import org.junit.Before
6+
import org.junit.Test
7+
8+
class BiometricAuthHandlerTest {
9+
10+
private lateinit var biometricAuthenticator: BiometricAuthenticator
11+
12+
@Before
13+
fun setUp() {
14+
biometricAuthenticator = mockk()
15+
mockkObject(BiometricPromptController)
16+
17+
every { biometricAuthenticator.isSecurityFeaturePresent() } returns true
18+
every { biometricAuthenticator.isBiometricAvailableButNotSet() } returns false
19+
every { biometricAuthenticator.isBiometricAvailableAndEnrolled() } returns true
20+
every { BiometricPromptController.shouldShowBiometric() } returns true
21+
22+
}
23+
24+
@Test
25+
fun `returns BYPASS when bypass is true`() {
26+
27+
every { biometricAuthenticator.isSecurityFeaturePresent() } returns true
28+
every { biometricAuthenticator.isBiometricAvailableButNotSet() } returns false
29+
every { biometricAuthenticator.isBiometricAvailableAndEnrolled() } returns true
30+
every { BiometricPromptController.shouldShowBiometric() } returns true
31+
32+
val handler = BiometricAuthHandler(
33+
biometricAuthenticator,
34+
requirement = { AuthenticationRequirement.ENABLED_AND_MANDATORY },
35+
bypass = { true }
36+
)
37+
38+
val result = handler.evaluate()
39+
assertEquals(BiometricDecision.BYPASS, result)
40+
41+
every { biometricAuthenticator.isSecurityFeaturePresent() } returns true
42+
every { biometricAuthenticator.isBiometricAvailableButNotSet() } returns false
43+
every { biometricAuthenticator.isBiometricAvailableAndEnrolled() } returns true
44+
every { BiometricPromptController.shouldShowBiometric() } returns true
45+
}
46+
47+
@Test
48+
fun `returns BYPASS when requirement is NO_ENABLED`() {
49+
50+
every { biometricAuthenticator.isSecurityFeaturePresent() } returns true
51+
every { biometricAuthenticator.isBiometricAvailableButNotSet() } returns false
52+
every { biometricAuthenticator.isBiometricAvailableAndEnrolled() } returns true
53+
54+
every { BiometricPromptController.shouldShowBiometric() } returns true
55+
56+
val handler = BiometricAuthHandler(
57+
biometricAuthenticator,
58+
requirement = { AuthenticationRequirement.NO_ENABLED },
59+
bypass = { true }
60+
)
61+
62+
val result = handler.evaluate()
63+
assertEquals(BiometricDecision.BYPASS, result)
64+
}
65+
66+
@Test
67+
fun `returns SECURITY_NOT_PRESENT when feature is missing in ENABLED_AND_MANDATORY`() {
68+
69+
every { biometricAuthenticator.isSecurityFeaturePresent() } returns false
70+
71+
val handler = BiometricAuthHandler(
72+
biometricAuthenticator,
73+
requirement = { AuthenticationRequirement.ENABLED_AND_MANDATORY },
74+
bypass = { false }
75+
)
76+
77+
val result = handler.evaluate()
78+
assertEquals(BiometricDecision.SECURITY_NOT_PRESENT, result)
79+
}
80+
81+
@Test
82+
fun `returns SETUP_REQUIRED when biometric not set in ENABLED_AND_MANDATORY`() {
83+
84+
every { biometricAuthenticator.isSecurityFeaturePresent() } returns true
85+
every { biometricAuthenticator.isBiometricAvailableButNotSet() } returns true
86+
every { biometricAuthenticator.isBiometricAvailableAndEnrolled() } returns false
87+
every { BiometricPromptController.shouldShowBiometric() } returns true
88+
89+
val handler = BiometricAuthHandler(
90+
biometricAuthenticator,
91+
requirement = { AuthenticationRequirement.ENABLED_AND_MANDATORY },
92+
bypass = { false }
93+
)
94+
95+
val result = handler.evaluate()
96+
assertEquals(BiometricDecision.SETUP_REQUIRED, result)
97+
}
98+
99+
@Test
100+
fun `returns AUTHENTICATE when biometrics set and prompt should show in ENABLED_AND_MANDATORY`() {
101+
every { biometricAuthenticator.isSecurityFeaturePresent() } returns true
102+
every { biometricAuthenticator.isBiometricAvailableButNotSet() } returns false
103+
every { biometricAuthenticator.isBiometricAvailableAndEnrolled() } returns true
104+
every { BiometricPromptController.shouldShowBiometric() } returns true
105+
106+
val handler = BiometricAuthHandler(
107+
biometricAuthenticator,
108+
requirement = { AuthenticationRequirement.ENABLED_AND_MANDATORY },
109+
bypass = { false }
110+
)
111+
112+
val result = handler.evaluate()
113+
assertEquals(BiometricDecision.AUTHENTICATE, result)
114+
}
115+
116+
@Test
117+
fun `returns PROMPT_SKIPPED when prompt is not shown in ENABLED_AND_MANDATORY`() {
118+
every { biometricAuthenticator.isSecurityFeaturePresent() } returns true
119+
every { biometricAuthenticator.isBiometricAvailableButNotSet() } returns false
120+
every { BiometricPromptController.shouldShowBiometric() } returns false
121+
122+
val handler = BiometricAuthHandler(
123+
biometricAuthenticator,
124+
requirement = { AuthenticationRequirement.ENABLED_AND_MANDATORY },
125+
bypass = { false }
126+
)
127+
128+
val result = handler.evaluate()
129+
assertEquals(BiometricDecision.PROMPT_SKIPPED, result)
130+
}
131+
132+
@Test
133+
fun `returns AUTHENTICATE when biometric enrolled in ENABLED_BUT_NOT_MANDATORY`() {
134+
every { biometricAuthenticator.isSecurityFeaturePresent() } returns true
135+
every { biometricAuthenticator.isBiometricAvailableAndEnrolled() } returns true
136+
every { BiometricPromptController.shouldShowBiometric() } returns true
137+
138+
val handler = BiometricAuthHandler(
139+
biometricAuthenticator,
140+
requirement = { AuthenticationRequirement.ENABLED_BUT_NOT_MANDATORY },
141+
bypass = { false }
142+
)
143+
144+
val result = handler.evaluate()
145+
assertEquals(BiometricDecision.AUTHENTICATE, result)
146+
}
147+
148+
@Test
149+
fun `returns PROMPT_SKIPPED when biometric enrolled but prompt not shown in ENABLED_BUT_NOT_MANDATORY`() {
150+
every { biometricAuthenticator.isSecurityFeaturePresent() } returns true
151+
every { biometricAuthenticator.isBiometricAvailableAndEnrolled() } returns true
152+
every { BiometricPromptController.shouldShowBiometric() } returns false
153+
154+
val handler = BiometricAuthHandler(
155+
biometricAuthenticator,
156+
requirement = { AuthenticationRequirement.ENABLED_BUT_NOT_MANDATORY },
157+
bypass = { false }
158+
)
159+
160+
val result = handler.evaluate()
161+
assertEquals(BiometricDecision.PROMPT_SKIPPED, result)
162+
}
163+
164+
@Test
165+
fun `returns PROMPT_SKIPPED when biometrics not enrolled in ENABLED_BUT_NOT_MANDATORY`() {
166+
every { biometricAuthenticator.isSecurityFeaturePresent() } returns true
167+
every { biometricAuthenticator.isBiometricAvailableAndEnrolled() } returns false
168+
169+
val handler = BiometricAuthHandler(
170+
biometricAuthenticator,
171+
requirement = { AuthenticationRequirement.ENABLED_BUT_NOT_MANDATORY },
172+
bypass = { false }
173+
)
174+
175+
val result = handler.evaluate()
176+
assertEquals(BiometricDecision.PROMPT_SKIPPED, result)
177+
}
178+
179+
@Test
180+
fun `returns SECURITY_NOT_PRESENT when feature is missing in ENABLED_BUT_NOT_MANDATORY`() {
181+
every { biometricAuthenticator.isSecurityFeaturePresent() } returns false
182+
183+
val handler = BiometricAuthHandler(
184+
biometricAuthenticator,
185+
requirement = { AuthenticationRequirement.ENABLED_BUT_NOT_MANDATORY },
186+
bypass = { false }
187+
)
188+
189+
val result = handler.evaluate()
190+
assertEquals(BiometricDecision.SECURITY_NOT_PRESENT, result)
191+
}
192+
193+
@Test
194+
fun `conflicting biometric state - both enrolled and not set`() {
195+
every { biometricAuthenticator.isSecurityFeaturePresent() } returns true
196+
every { biometricAuthenticator.isBiometricAvailableButNotSet() } returns true
197+
every { biometricAuthenticator.isBiometricAvailableAndEnrolled() } returns true
198+
every { BiometricPromptController.shouldShowBiometric() } returns true
199+
200+
val handler = BiometricAuthHandler(
201+
biometricAuthenticator,
202+
requirement = { AuthenticationRequirement.ENABLED_AND_MANDATORY },
203+
bypass = { false }
204+
)
205+
206+
val result = handler.evaluate()
207+
assertEquals(BiometricDecision.BYPASS, result)
208+
}
209+
210+
@Test
211+
fun `biometric enrolled but prompt not shown`() {
212+
every { biometricAuthenticator.isSecurityFeaturePresent() } returns true
213+
every { biometricAuthenticator.isBiometricAvailableAndEnrolled() } returns true
214+
every { BiometricPromptController.shouldShowBiometric() } returns false
215+
216+
val handler = BiometricAuthHandler(
217+
biometricAuthenticator,
218+
requirement = { AuthenticationRequirement.ENABLED_BUT_NOT_MANDATORY },
219+
bypass = { false }
220+
)
221+
222+
val result = handler.evaluate()
223+
assertEquals(BiometricDecision.PROMPT_SKIPPED, result)
224+
}
225+
226+
227+
}
228+

0 commit comments

Comments
 (0)