Skip to content

Commit e26d34c

Browse files
authored
Merge branch 'version-10.0.0-dev' into feat/S3
2 parents d2becd6 + 9387fd5 commit e26d34c

File tree

3 files changed

+436
-0
lines changed

3 files changed

+436
-0
lines changed
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* Copyright 2025 Google Inc. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5+
* in compliance with the License. You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software distributed under the
10+
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
11+
* express or implied. See the License for the specific language governing permissions and
12+
* limitations under the License.
13+
*/
14+
15+
package com.firebase.ui.auth.compose.credentialmanager
16+
17+
/**
18+
* Represents a password credential retrieved from the system credential manager.
19+
*
20+
* @property username The username/identifier associated with the credential
21+
* @property password The password associated with the credential
22+
*/
23+
data class PasswordCredential(
24+
val username: String,
25+
val password: String
26+
)
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
/*
2+
* Copyright 2025 Google Inc. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5+
* in compliance with the License. You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software distributed under the
10+
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
11+
* express or implied. See the License for the specific language governing permissions and
12+
* limitations under the License.
13+
*/
14+
15+
package com.firebase.ui.auth.compose.credentialmanager
16+
17+
import android.content.Context
18+
import androidx.credentials.CreatePasswordRequest
19+
import androidx.credentials.CredentialManager
20+
import androidx.credentials.GetCredentialRequest
21+
import androidx.credentials.GetPasswordOption
22+
import androidx.credentials.PasswordCredential as AndroidPasswordCredential
23+
import androidx.credentials.exceptions.CreateCredentialCancellationException
24+
import androidx.credentials.exceptions.CreateCredentialException
25+
import androidx.credentials.exceptions.GetCredentialCancellationException
26+
import androidx.credentials.exceptions.GetCredentialException
27+
import androidx.credentials.exceptions.NoCredentialException
28+
29+
/**
30+
* Handler for password credential operations using Android's Credential Manager.
31+
*
32+
* This class provides methods to save and retrieve password credentials through
33+
* the system credential manager, which displays native UI prompts to the user.
34+
*
35+
* @property context The Android context used for credential operations
36+
*/
37+
class PasswordCredentialHandler(
38+
private val context: Context
39+
) {
40+
private val credentialManager: CredentialManager = CredentialManager.create(context)
41+
42+
/**
43+
* Saves a password credential to the system credential manager.
44+
*
45+
* This method displays a system prompt to the user asking if they want to save
46+
* the credential. The operation is performed asynchronously using Kotlin coroutines.
47+
*
48+
* @param username The username/identifier for the credential
49+
* @param password The password to save
50+
* @throws CreateCredentialException if the credential cannot be saved
51+
* @throws CreateCredentialCancellationException if the user cancels the save operation
52+
* @throws IllegalArgumentException if username or password is blank
53+
*/
54+
suspend fun savePassword(username: String, password: String) {
55+
require(username.isNotBlank()) { "Username cannot be blank" }
56+
require(password.isNotBlank()) { "Password cannot be blank" }
57+
58+
val request = CreatePasswordRequest(
59+
id = username,
60+
password = password
61+
)
62+
63+
try {
64+
credentialManager.createCredential(context, request)
65+
} catch (e: CreateCredentialCancellationException) {
66+
// User cancelled the save operation
67+
throw PasswordCredentialCancelledException("User cancelled password save operation", e)
68+
} catch (e: CreateCredentialException) {
69+
// Other credential creation errors
70+
throw PasswordCredentialException("Failed to save password credential", e)
71+
}
72+
}
73+
74+
/**
75+
* Retrieves a password credential from the system credential manager.
76+
*
77+
* This method displays a system prompt showing available credentials for the user
78+
* to select from. The operation is performed asynchronously using Kotlin coroutines.
79+
*
80+
* @return PasswordCredential containing the username and password
81+
* @throws NoCredentialException if no credentials are available
82+
* @throws GetCredentialCancellationException if the user cancels the retrieval operation
83+
* @throws GetCredentialException if the credential cannot be retrieved
84+
*/
85+
suspend fun getPassword(): PasswordCredential {
86+
val getPasswordOption = GetPasswordOption()
87+
val request = GetCredentialRequest.Builder()
88+
.addCredentialOption(getPasswordOption)
89+
.build()
90+
91+
try {
92+
val result = credentialManager.getCredential(context, request)
93+
val credential = result.credential
94+
95+
if (credential is AndroidPasswordCredential) {
96+
return PasswordCredential(
97+
username = credential.id,
98+
password = credential.password
99+
)
100+
} else {
101+
throw PasswordCredentialException("Retrieved credential is not a password credential")
102+
}
103+
} catch (e: GetCredentialCancellationException) {
104+
// User cancelled the retrieval operation
105+
throw PasswordCredentialCancelledException("User cancelled password retrieval operation", e)
106+
} catch (e: NoCredentialException) {
107+
// No credentials available
108+
throw PasswordCredentialNotFoundException("No password credentials found", e)
109+
} catch (e: GetCredentialException) {
110+
// Other credential retrieval errors
111+
throw PasswordCredentialException("Failed to retrieve password credential", e)
112+
}
113+
}
114+
}
115+
116+
/**
117+
* Base exception for password credential operations.
118+
*/
119+
open class PasswordCredentialException(
120+
message: String,
121+
cause: Throwable? = null
122+
) : Exception(message, cause)
123+
124+
/**
125+
* Exception thrown when a password credential operation is cancelled by the user.
126+
*/
127+
class PasswordCredentialCancelledException(
128+
message: String,
129+
cause: Throwable? = null
130+
) : PasswordCredentialException(message, cause)
131+
132+
/**
133+
* Exception thrown when no password credentials are found.
134+
*/
135+
class PasswordCredentialNotFoundException(
136+
message: String,
137+
cause: Throwable? = null
138+
) : PasswordCredentialException(message, cause)

0 commit comments

Comments
 (0)