Skip to content

Commit 35effd9

Browse files
authored
Add snippets for configure and sign in sections. Also move some snippets to files where they can be validated (#480)
1 parent a637a37 commit 35effd9

File tree

9 files changed

+301
-0
lines changed

9 files changed

+301
-0
lines changed

identity/credentialmanager/src/main/AndroidManifest.xml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,20 @@
11
<?xml version="1.0" encoding="utf-8"?>
2+
<!--
3+
~ Copyright 2025 Google LLC
4+
~
5+
~ Licensed under the Apache License, Version 2.0 (the "License");
6+
~ you may not use this file except in compliance with the License.
7+
~ You may obtain a copy of the License at
8+
~
9+
~ https://www.apache.org/licenses/LICENSE-2.0
10+
~
11+
~ Unless required by applicable law or agreed to in writing, software
12+
~ distributed under the License is distributed on an "AS IS" BASIS,
13+
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
~ See the License for the specific language governing permissions and
15+
~ limitations under the License.
16+
-->
17+
218
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
319

420
<application
@@ -19,6 +35,9 @@
1935
<category android:name="android.intent.category.LAUNCHER" />
2036
</intent-filter>
2137
</activity>
38+
<!-- // [START android_identity_assetlinks_manifest] -->
39+
<meta-data android:name="asset_statements" android:resource="@string/asset_statements" />
40+
<!-- // [END android_identity_assetlinks_manifest] -->
2241
</application>
2342

2443
</manifest>
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
/*
2+
* Copyright 2025 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
18+
package com.example.identity.credentialmanager
19+
20+
import android.content.Context
21+
import android.os.Build
22+
import android.os.Bundle
23+
import android.util.Log
24+
import androidx.annotation.RequiresApi
25+
import androidx.credentials.CredentialManager
26+
import androidx.credentials.CustomCredential
27+
import androidx.credentials.GetCredentialRequest
28+
import androidx.credentials.GetCredentialResponse
29+
import androidx.credentials.GetPasswordOption
30+
import androidx.credentials.GetPublicKeyCredentialOption
31+
import androidx.credentials.PasswordCredential
32+
import androidx.credentials.PublicKeyCredential
33+
import androidx.credentials.exceptions.GetCredentialException
34+
import kotlinx.coroutines.coroutineScope
35+
import kotlinx.coroutines.runBlocking
36+
import org.json.JSONObject
37+
38+
class CredentialManagerFunctions (
39+
context: Context,
40+
) {
41+
// [START android_identity_initialize_credman]
42+
// Use your app or activity context to instantiate a client instance of
43+
// CredentialManager.
44+
private val credentialManager = CredentialManager.create(context)
45+
// [END android_identity_initialize_credman]
46+
47+
// Placeholder for TAG log value.
48+
val TAG = ""
49+
/**
50+
* Retrieves a passkey from the credential manager.
51+
*
52+
* @param creationResult The result of the passkey creation operation.
53+
* @param context The activity context from the Composable, to be used in Credential Manager APIs
54+
* @return The [GetCredentialResponse] object containing the passkey, or null if an error occurred.
55+
*/
56+
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
57+
fun signInFlow(
58+
creationResult: JSONObject,
59+
activityContext: Context,
60+
) {
61+
val requestJson = creationResult.toString()
62+
// [START android_identity_get_password_passkey_options]
63+
// Retrieves the user's saved password for your app from their
64+
// password provider.
65+
val getPasswordOption = GetPasswordOption()
66+
67+
// Get passkey from the user's public key credential provider.
68+
val getPublicKeyCredentialOption = GetPublicKeyCredentialOption(
69+
requestJson = requestJson
70+
)
71+
// [END android_identity_get_password_passkey_options]
72+
var result: GetCredentialResponse
73+
// [START android_identity_get_credential_request]
74+
val credentialRequest = GetCredentialRequest(
75+
listOf(getPasswordOption, getPublicKeyCredentialOption),
76+
)
77+
// [END android_identity_get_credential_request]
78+
runBlocking {
79+
// getPrepareCredential request
80+
// [START android_identity_prepare_get_credential]
81+
coroutineScope {
82+
val response = credentialManager.prepareGetCredential(
83+
GetCredentialRequest(
84+
listOf(
85+
getPublicKeyCredentialOption,
86+
getPasswordOption
87+
)
88+
)
89+
)
90+
}
91+
// [END android_identity_prepare_get_credential]
92+
// getCredential request without handling exception.
93+
// [START android_identity_launch_sign_in_flow_1]
94+
coroutineScope {
95+
try {
96+
result = credentialManager.getCredential(
97+
// Use an activity-based context to avoid undefined system UI
98+
// launching behavior.
99+
context = activityContext,
100+
request = credentialRequest
101+
)
102+
handleSignIn(result)
103+
} catch (e: GetCredentialException) {
104+
// Handle failure
105+
}
106+
}
107+
// [END android_identity_launch_sign_in_flow_1]
108+
// getCredential request adding some exception handling.
109+
// [START android_identity_handle_exceptions_no_credential]
110+
coroutineScope {
111+
try {
112+
result = credentialManager.getCredential(
113+
context = activityContext,
114+
request = credentialRequest
115+
)
116+
} catch (e: GetCredentialException) {
117+
Log.e("CredentialManager", "No credential available", e)
118+
}
119+
}
120+
// [END android_identity_handle_exceptions_no_credential]
121+
}
122+
}
123+
124+
// [START android_identity_launch_sign_in_flow_2]
125+
fun handleSignIn(result: GetCredentialResponse) {
126+
// Handle the successfully returned credential.
127+
val credential = result.credential
128+
129+
when (credential) {
130+
is PublicKeyCredential -> {
131+
val responseJson = credential.authenticationResponseJson
132+
// Share responseJson i.e. a GetCredentialResponse on your server to
133+
// validate and authenticate
134+
}
135+
136+
is PasswordCredential -> {
137+
val username = credential.id
138+
val password = credential.password
139+
// Use id and password to send to your server to validate
140+
// and authenticate
141+
}
142+
143+
is CustomCredential -> {
144+
// If you are also using any external sign-in libraries, parse them
145+
// here with the utility functions provided.
146+
if (credential.type == ExampleCustomCredential.TYPE) {
147+
try {
148+
val ExampleCustomCredential =
149+
ExampleCustomCredential.createFrom(credential.data)
150+
// Extract the required credentials and complete the authentication as per
151+
// the federated sign in or any external sign in library flow
152+
} catch (e: ExampleCustomCredential.ExampleCustomCredentialParsingException) {
153+
// Unlikely to happen. If it does, you likely need to update the dependency
154+
// version of your external sign-in library.
155+
Log.e(TAG, "Failed to parse an ExampleCustomCredential", e)
156+
}
157+
} else {
158+
// Catch any unrecognized custom credential type here.
159+
Log.e(TAG, "Unexpected type of credential")
160+
}
161+
}
162+
else -> {
163+
// Catch any unrecognized credential type here.
164+
Log.e(TAG, "Unexpected type of credential")
165+
}
166+
}
167+
}
168+
// [END android_identity_launch_sign_in_flow_2]
169+
}
170+
171+
sealed class ExampleCustomCredential {
172+
class ExampleCustomCredentialParsingException : Throwable() {}
173+
174+
companion object {
175+
fun createFrom(data: Bundle): PublicKeyCredential {
176+
return PublicKeyCredential("")
177+
}
178+
179+
const val TYPE: String = ""
180+
}
181+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* Copyright 2025 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
{
18+
"snippets": [
19+
{
20+
"DigitalAssetLinking":
21+
// Digital asset linking
22+
// [START android_identity_assetlinks_json]
23+
[
24+
{
25+
"relation": [
26+
"delegate_permission/common.handle_all_urls",
27+
"delegate_permission/common.get_login_creds"
28+
],
29+
"target": {
30+
"namespace": "android_app",
31+
"package_name": "com.example.android",
32+
"sha256_cert_fingerprints": [
33+
SHA_HEX_VALUE
34+
]
35+
}
36+
}
37+
]
38+
// [END android_identity_assetlinks_json]
39+
},
40+
41+
// JSON request and response formats
42+
// [START android_identity_format_json_request_passkey]
43+
{
44+
"challenge": "T1xCsnxM2DNL2KdK5CLa6fMhD7OBqho6syzInk_n-Uo",
45+
"allowCredentials": [],
46+
"timeout": 1800000,
47+
"userVerification": "required",
48+
"rpId": "credential-manager-app-test.glitch.me"
49+
},
50+
// [END android_identity_format_json_request_passkey]
51+
52+
// [START android_identity_format_json_response_passkey]
53+
{
54+
"id": "KEDetxZcUfinhVi6Za5nZQ",
55+
"type": "public-key",
56+
"rawId": "KEDetxZcUfinhVi6Za5nZQ",
57+
"response": {
58+
"clientDataJSON": "eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiVDF4Q3NueE0yRE5MMktkSzVDTGE2Zk1oRDdPQnFobzZzeXpJbmtfbi1VbyIsIm9yaWdpbiI6ImFuZHJvaWQ6YXBrLWtleS1oYXNoOk1MTHpEdll4UTRFS1R3QzZVNlpWVnJGUXRIOEdjVi0xZDQ0NEZLOUh2YUkiLCJhbmRyb2lkUGFja2FnZU5hbWUiOiJjb20uZ29vZ2xlLmNyZWRlbnRpYWxtYW5hZ2VyLnNhbXBsZSJ9",
59+
"authenticatorData": "j5r_fLFhV-qdmGEwiukwD5E_5ama9g0hzXgN8thcFGQdAAAAAA",
60+
"signature": "MEUCIQCO1Cm4SA2xiG5FdKDHCJorueiS04wCsqHhiRDbbgITYAIgMKMFirgC2SSFmxrh7z9PzUqr0bK1HZ6Zn8vZVhETnyQ",
61+
"userHandle": "2HzoHm_hY0CjuEESY9tY6-3SdjmNHOoNqaPDcZGzsr0"
62+
}
63+
}
64+
// [END android_identity_format_json_response_passkey]
65+
]
66+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<!--
3+
~ Copyright 2025 Google LLC
4+
~
5+
~ Licensed under the Apache License, Version 2.0 (the "License");
6+
~ you may not use this file except in compliance with the License.
7+
~ You may obtain a copy of the License at
8+
~
9+
~ https://www.apache.org/licenses/LICENSE-2.0
10+
~
11+
~ Unless required by applicable law or agreed to in writing, software
12+
~ distributed under the License is distributed on an "AS IS" BASIS,
13+
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
~ See the License for the specific language governing permissions and
15+
~ limitations under the License.
16+
-->
17+
18+
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
19+
android:layout_width="match_parent"
20+
android:layout_height="match_parent">
21+
<!-- // [START android_identity_textview_isCredential] -->
22+
<TextView
23+
android:layout_width="match_parent"
24+
android:layout_height="wrap_content"
25+
android:isCredential="true" />
26+
<!-- // [END android_identity_textview_isCredential] -->
27+
28+
</LinearLayout>
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
11
<resources>
22
<string name="app_name">credentialmanager</string>
3+
// [START android_identity_assetlinks_app_association]
4+
<string name="asset_statements" translatable="false">
5+
[{
6+
\"include\": \"https://signin.example.com/.well-known/assetlinks.json\"
7+
}]
8+
</string>
9+
// [END android_identity_assetlinks_app_association]
310
</resources>

0 commit comments

Comments
 (0)