Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions android/kotlin-authenticatorapp/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
*.iml
.gradle
/local.properties
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
local.properties
275 changes: 275 additions & 0 deletions android/kotlin-authenticatorapp/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,275 @@
[![Ping Identity](https://www.pingidentity.com/content/dam/picr/nav/Ping-Logo-2.svg)](https://github.com/ForgeRock/ping-android-sdk)

# Ping Authenticator Sample App

This sample application demonstrates how to implement multi-factor authentication using the Ping Identity SDK. The app allows users to register and manage both OATH credentials (TOTP/HOTP) and Push authentication credentials.

## Disclaimer

This application is a sample and not intended for production use. It is provided for educational purposes to demonstrate the use of the Ping Identity SDK.

The application uses a public reverse geocoding service for location mapping. This service is not guaranteed to be accurate or available. For a production application, it is recommended to use a more robust and reliable geocoding service.

## Features

### OATH Authentication
- **QR Code Scanning**: Register accounts by scanning QR codes containing OATH credentials
- **Manual Entry**: Manually enter account details
- **Journey Authentication**: Register accounts through authenticated Journey login flows
- **TOTP Support**: Automatic generation of time-based one-time passwords with countdown timer
- **HOTP Support**: Counter-based one-time passwords with refresh capability
- **Copy OTP**: Copy to clipboard functionality

### Push Authentication
- **QR Code Registration**: Register for push authentication by scanning QR codes
- **Journey Authentication**: Register for push authentication through authenticated Journey login flows
- **Push Notifications**: Receive and respond to authentication requests
- **System Notifications**: Display system notifications when push requests are received
- **Direct Actions**: Approve or deny authentication requests directly from system notification tray (DEFAULT type)
- **Push Biometric Authentication**: Authenticate using fingerprint or face recognition (BIOMETRIC type)
- **Push Challenge Verification**: Verify challenge numbers for enhanced security (CHALLENGE type)
- **Location Display**: View location information when provided in push notifications
- **Notification Management**: Clean up old notifications via Settings screen
- **Device Token Management**: View device token information

### Journey-based Credential Enrollment
- **User Authentication**: Allow the app to authenticate users through Journey flows
- **Seamless Integration**: MFA registration integrated directly into authentication flows
- **User Association**: Journey-registered credentials are automatically associated with the authenticated user

### Common
- **Account Management**: View, organize, and delete accounts
- **Account Grouping**: Group MFA accounts with the same issuer/account name

## Architecture overview

The Ping Authenticator App sample is a modular Android application built on Model-View-ViewModel architecture with Kotlin, Jetpack Compose, and the Ping SDK for secure multi-factor authentication (MFA).

```
┌─────────────────────────────┐
│ Presentation Layer │ ← UI: Jetpack Compose screens, navigation
├─────────────────────────────┤
│ Domain Layer │ ← ViewModels, business logic, state
├─────────────────────────────┤
│ Data/Service Layer │ ← Managers, services, secure storage
├─────────────────────────────┤
│ SDK Layer │ ← Ping SDK: push, oath, and journey modules
└─────────────────────────────┘
```

- **Presentation Layer**: Android Activities/Fragments for user interaction.
- **Domain Layer**: Handles business logic, orchestrates feature flows, and manages state.
- **Data/Service Layer**: Integrates with Ping SDK modules (`push`, `oath`, `journey`) and other services.
- **SDK Layer**: Abstracts the complexity to deal with MFA capabilities and comunication with Ping backend.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fix spelling error.

"comunication" should be "communication".

📝 Proposed fix
-- **SDK Layer**: Abstracts the complexity to deal with MFA capabilities and comunication with Ping backend.
+- **SDK Layer**: Abstracts the complexity to deal with MFA capabilities and communication with Ping backend.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- **SDK Layer**: Abstracts the complexity to deal with MFA capabilities and comunication with Ping backend.
- **SDK Layer**: Abstracts the complexity to deal with MFA capabilities and communication with Ping backend.
🧰 Tools
🪛 LanguageTool

[grammar] ~63-~63: Ensure spelling is correct
Context: ...exity to deal with MFA capabilities and comunication with Ping backend. The application follows ...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@android/kotlin-authenticatorapp/README.md` at line 63, Replace the misspelled
word "comunication" with "communication" in the README sentence that starts with
"SDK Layer" (the phrase currently reads "Abstracts the complexity to deal with
MFA capabilities and comunication with Ping backend"); update that line so it
reads "Abstracts the complexity to deal with MFA capabilities and communication
with Ping backend".


The application follows modern Android development practices:

- **Kotlin**: 100% Kotlin codebase
- **Jetpack Compose**: Declarative UI toolkit for building native UI
- **ViewModel**: Architecture component for managing UI-related data in a lifecycle conscious way
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fix grammar: hyphenate compound adjective.

"lifecycle conscious" should be "lifecycle-conscious" when used as a compound adjective.

📝 Proposed fix
-- **ViewModel**: Architecture component for managing UI-related data in a lifecycle conscious way
+- **ViewModel**: Architecture component for managing UI-related data in a lifecycle-conscious way
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- **ViewModel**: Architecture component for managing UI-related data in a lifecycle conscious way
- **ViewModel**: Architecture component for managing UI-related data in a lifecycle-conscious way
🧰 Tools
🪛 LanguageTool

[grammar] ~69-~69: Use a hyphen to join words.
Context: ... managing UI-related data in a lifecycle conscious way - Coroutines: For asyn...

(QB_NEW_EN_HYPHEN)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@android/kotlin-authenticatorapp/README.md` at line 69, In the README.md
ViewModel bullet, fix the grammar by hyphenating the compound adjective: change
the phrase "lifecycle conscious" to "lifecycle-conscious" in the line that reads
"**ViewModel**: Architecture component for managing UI-related data in a
lifecycle conscious way" so it becomes "**ViewModel**: Architecture component
for managing UI-related data in a lifecycle-conscious way".

- **Coroutines**: For asynchronous operations
- **Navigation**: For handling navigation between screens
- **Material 3**: For modern, adaptive UI components
- **Firebase Cloud Messaging**: For receiving push notifications
- **OpenStreetMap**: For displaying location information in push notifications

## Implementation Details

### Code Structure Overview

```
src/main/kotlin/com/pingidentity/authenticatorapp/
├── AuthenticatorApp.kt # App initialization, SDK clients (Push, OATH, Journey)
├── managers/
│ ├── JourneyManager.kt # Journey logic, state, integration
│ ├── PushManager.kt # Push logic, state, integration
│ └── OathManager.kt # OATH logic, state, integration
├── ui/
│ ├── AccountsScreen.kt # Account management UI
│ ├── PushNotificationsScreen.kt # Push notification UI
│ └── ... # Other Compose screens
├── data/
│ ├── AuthenticatorViewModel.kt # Coordinates between Push and OATH managers and handles UI-specific logic
│ ├── LoginViewModel.kt # Coordinates between Journey and other managers handling UI-specific logic
│ ├── DiagnosticLogger.kt # Logging
│ └── UserPreferences.kt # Preferences
└── ...
```

**Key Classes & Structure:**

- `AuthenticatorApp.kt`: Initializes Push, OATH, and Journey clients, manages global app state.
- `managers/`: Integrates Ping SDK modules.
- `managers/JourneyManager.kt`: Handles Journey lifecycle with MFA registration.
- `managers/PushManager.kt`: Encapsulates push notification logic and state.
- `managers/OathManager.kt`: Handles OATH token lifecycle and OTP generation.
- `ui/`: Compose screens and components for account and notification management.
- `data/`: Models, preferences, logging.

### Push Module
- **Device Registration**: Registers device with Ping backend for push authentication.
- **Notification Handling**: Listens for push requests, displays actionable notifications.
- **User Actions**: Approve/deny requests from notification or app UI.
- **Result Reporting**: Communicates user decisions to Ping backend securely.

**Class:** `PushManager.kt`

**Flow Diagram (textual):**
```
Push Request → PushManager → Notification UI → User Action → Ping Backend
```

#### Push Authentication Types

The app handles three different types of push authentication:

1. **DEFAULT**: Simple approval/denial directly from the notification
```kotlin
// Approve a standard notification
pushClient.approveNotification(notificationId)
```

2. **BIOMETRIC**: Authentication using biometric verification
```kotlin
// Approve with biometric authentication
pushClient.approveBiometricNotification(notificationId)
```

3. **CHALLENGE**: Verification using challenge numbers
```kotlin
// Get challenge numbers
val numbers = pushNotification.getNumbersChallenge()

// Approve with challenge response
pushClient.approveChallengeNotification(notificationId, challengeResponse)
```


### OATH Module
- **Token Provisioning**: Enrolls OATH tokens via QR/manual entry or Journey authentication flows.
- **Code Generation**: Generates OTP codes (TOTP/HOTP) for authentication.
- **Token Management**: UI for listing, renaming, deleting tokens.
- **Security**: OTP codes can be hidden (optional).

**Class:** `OathManager.kt`

**Flow Diagram (textual):**
```
Enroll Token → OathManager → Generate OTP → Display in UI → User enters code
Journey Flow → Auto-Register → Associate with User → Mark as Journey-enabled
```

### Journey Module
- **Authentication Flows**: Handles PingOne Advanced Identity Cloud (AIC) authentication journeys.
- **MFA Registration**: Automatically registers MFA credentials during authentication flows.
- **User Association**: Associates registered credentials with authenticated users.

**Class:** `LoginViewModel.kt`

**Flow Diagram (textual):**
```
Start Journey → Authentication Steps → MFA Registration → Success → Associate Credentials
```

### QR Code Scanning

The app uses CameraX and ML Kit to scan and decode QR codes:

#### OATH QR Codes (otpauth:// URIs):
```
otpauth://totp/Example:alice@google.com?secret=JBSWY3DPEHPK3PXP&issuer=Example&algorithm=SHA1&digits=6&period=30
```

#### Push QR Codes (pushauth:// URIs):
```
pushauth://push/Example:bob@example.com?pushauth_uri=https://example.com/push&client_id=clientId123
```

### Journey-Based Registration

The app also supports registering MFA credentials through authenticated Journey flows:

#### Journey Authentication Flow:
1. **Start Journey**: Initiate authentication with PingOne Advanced Identity Cloud
2. **Authentication Steps**: Complete required authentication steps (username/password, etc.)
3. **MFA Registration**: Journey automatically provides MFA registration URIs during the flow
4. **Auto-Registration**: App automatically registers OATH/Push credentials from Journey callbacks
5. **User Association**: Successfully authenticated credentials are associated with the user session

This provides a seamless user experience where MFA credentials are automatically registered during the authentication process without requiring separate QR code scanning.

## Getting Started

### Prerequisites

- Android Studio Koala | 2024.1.1 or newer
- Android SDK 29 or higher
- Gradle 8.7 or newer

### Building the App

1. Clone the repository
2. Open the project in Android Studio
3. Build and run on your device or emulator

## Testing

### Testing OATH Functionality

To test the app's OATH functionality, you can:

1. **QR Code Method**:
- Use any TOTP/HOTP QR code generator
- Create test credentials using command line tools like `oathtool`
- Use online TOTP testing services

2. **Journey Method**:
- Configure a PingOne Advanced Identity Cloud environment with OATH MFA
- Set up Journey flows that include MFA registration steps
- Test the full authentication flow including automatic credential registration

### Testing Push Functionality

To test the app's Push functionality, you need:

1. **QR Code Method**:
- A PingAM account with push authentication configured
- FCM configured for your Android application
- The app properly registered with FCM to receive push notifications

2. **Journey Method**:
- A PingOne Advanced Identity Cloud environment with Push MFA configured
- Journey flows that include Push registration steps
- FCM properly configured to receive push notifications
- Test environment to generate push authentication requests

### Testing Journey Integration

To test the Journey-based MFA registration:

1. Set up a PingOne Advanced Identity Cloud environment
2. Configure Journey flows with MFA registration callbacks
3. Test the complete flow: authentication → MFA registration → credential association
4. Verify that registered credentials show user session indicators in the UI

## Contributing

Contributions are welcome! Please read the [contributing guidelines](../../CONTRIBUTING.md) for more information.

## Troubleshooting

- **Push notifications are not being received**:
- Ensure that your device has a valid internet connection.
- Verify that the device token is correctly registered with the push notification service.
- Check the server logs to see if the push notification is being sent successfully.
- **QR code is not scanning**:
- Make sure that the QR code is well-lit and in focus.
- Try scanning the QR code from a different distance or angle.
- Ensure that the QR code is in the correct format.

## License

Copyright (c) 2025 Ping Identity Corporation. All rights reserved.
This software may be modified and distributed under the terms of the MIT license. See the [LICENSE](../LICENSE) file for details.

© Copyright 2025-2026 Ping Identity Corporation. All Rights Reserved
1 change: 1 addition & 0 deletions android/kotlin-authenticatorapp/app/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
91 changes: 91 additions & 0 deletions android/kotlin-authenticatorapp/app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.compose.compiler)
alias(libs.plugins.googleServices)
alias(libs.plugins.kotlinSerialization)
}

android {
namespace = "com.pingidentity.authenticatorapp"
compileSdk {
version = release(36) {
minorApiLevel = 1
}
}

defaultConfig {
applicationId = "com.pingidentity.authenticatorapp"
minSdk = 29
targetSdk = 36
versionCode = 1
versionName = "1.0"

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}

buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
}

dependencies {
// Core Android dependencies
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.appcompat)
implementation(libs.material)

// Kotlinx Serialization
implementation(libs.kotlinx.serialization.json)

// Ping Identity SDK dependencies
implementation(libs.ping.oath)
implementation(libs.ping.push)
implementation(libs.ping.journey)

// Compose
implementation(platform(libs.androidx.compose.bom))
implementation(libs.compose.ui)
implementation(libs.androidx.ui.graphics)
implementation(libs.androidx.ui.tooling.preview)
implementation(libs.compose.material3)
implementation(libs.androidx.navigation.compose)
implementation(libs.androidx.material.icons.extended)

// CameraX dependencies for QR scanning
implementation(libs.androidx.camera.camera2)
implementation(libs.androidx.camera.lifecycle)
implementation(libs.androidx.camera.view)
implementation(libs.barcode.scanning)

// ViewModel
implementation(libs.androidx.lifecycle.viewmodel.compose)

// HTTP client for reverse geocoding API calls
implementation(libs.ktor.client.cio)
implementation(libs.ktor.client.core)
implementation(libs.ktor.client.content.negotiation)
implementation(libs.ktor.serialization.kotlinx.json)

// Image loading
implementation(libs.coil.compose)

// Firebase Cloud Messaging for push notifications
implementation(platform(libs.firebase.bom))
implementation(libs.firebase.messaging)

// Maps for location display - using free OpenStreetMap
implementation(libs.osmdroid.android)

// Biometric
implementation(libs.androidx.biometric)
}
Loading