Skip to content

Commit c0cd7b9

Browse files
authored
Merge branch 'main' into device_compatibility_mode
2 parents 304417d + 94c2b8f commit c0cd7b9

File tree

4 files changed

+259
-0
lines changed

4 files changed

+259
-0
lines changed

gradle/libs.versions.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ androidx-window-java = "1.3.0"
2727
androidxHiltNavigationCompose = "1.2.0"
2828
appcompat = "1.7.0"
2929
coil = "2.7.0"
30+
android-googleid = "1.1.1"
3031
# @keep
3132
compileSdk = "35"
3233
compose-latest = "1.7.8"
@@ -137,6 +138,7 @@ androidx-window = { module = "androidx.window:window", version.ref = "androidx-w
137138
androidx-window-core = { module = "androidx.window:window-core", version.ref = "androidx-window-core" }
138139
androidx-window-java = {module = "androidx.window:window-java", version.ref = "androidx-window-java" }
139140
androidx-work-runtime-ktx = "androidx.work:work-runtime-ktx:2.10.0"
141+
android-identity-googleid = {module = "com.google.android.libraries.identity.googleid:googleid", version.ref = "android-googleid"}
140142
appcompat = { module = "androidx.appcompat:appcompat", version.ref = "appcompat" }
141143
coil-kt-compose = { module = "io.coil-kt:coil-compose", version.ref = "coil" }
142144
compose-foundation = { module = "androidx.wear.compose:compose-foundation", version.ref = "wearComposeFoundation" }

identity/credentialmanager/build.gradle.kts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,11 @@ dependencies {
5555
// Android 13 and below.
5656
implementation(libs.androidx.credentials.play.services.auth)
5757
// [END android_identity_gradle_dependencies]
58+
// [START android_identity_siwg_gradle_dependencies]
59+
implementation(libs.androidx.credentials)
60+
implementation(libs.androidx.credentials.play.services.auth)
61+
implementation(libs.android.identity.googleid)
62+
// [END android_identity_siwg_gradle_dependencies]
5863
debugImplementation(libs.androidx.compose.ui.tooling)
5964
debugImplementation(libs.androidx.compose.ui.test.manifest)
6065
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
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+
package com.example.identity.credentialmanager
18+
19+
import android.content.Context
20+
import androidx.credentials.ClearCredentialStateRequest
21+
import androidx.credentials.ClearCredentialStateRequest.Companion.TYPE_CLEAR_RESTORE_CREDENTIAL
22+
import androidx.credentials.CreateRestoreCredentialRequest
23+
import androidx.credentials.CredentialManager
24+
import androidx.credentials.GetCredentialRequest
25+
import androidx.credentials.GetRestoreCredentialOption
26+
27+
class RestoreCredentialsFunctions(
28+
private val context: Context,
29+
private val credentialManager: CredentialManager,
30+
) {
31+
suspend fun createRestoreKey(
32+
createRestoreRequest: CreateRestoreCredentialRequest
33+
) {
34+
// [START android_identity_restore_cred_create]
35+
val credentialManager = CredentialManager.create(context)
36+
37+
// On a successful authentication create a Restore Key
38+
// Pass in the context and CreateRestoreCredentialRequest object
39+
val response = credentialManager.createCredential(context, createRestoreRequest)
40+
// [END android_identity_restore_cred_create]
41+
}
42+
43+
suspend fun getRestoreKey(
44+
fetchAuthenticationJson: () -> String,
45+
) {
46+
// [START android_identity_restore_cred_get]
47+
// Fetch the Authentication JSON from server
48+
val authenticationJson = fetchAuthenticationJson()
49+
50+
// Create the GetRestoreCredentialRequest object
51+
val options = GetRestoreCredentialOption(authenticationJson)
52+
val getRequest = GetCredentialRequest(listOf(options))
53+
54+
// The restore key can be fetched in two scenarios to
55+
// 1. On the first launch of app on the device, fetch the Restore Key
56+
// 2. In the onRestore callback (if the app implements the Backup Agent)
57+
val response = credentialManager.getCredential(context, getRequest)
58+
// [END android_identity_restore_cred_get]
59+
}
60+
61+
suspend fun deleteRestoreKey() {
62+
// [START android_identity_restore_cred_delete]
63+
// Create a ClearCredentialStateRequest object
64+
val clearRequest = ClearCredentialStateRequest(TYPE_CLEAR_RESTORE_CREDENTIAL)
65+
66+
// On user log-out, clear the restore key
67+
val response = credentialManager.clearCredentialState(clearRequest)
68+
// [END android_identity_restore_cred_delete]
69+
}
70+
}
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
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+
package com.example.identity.credentialmanager
18+
19+
import android.content.Context
20+
import android.util.Log
21+
import androidx.credentials.CredentialManager
22+
import androidx.credentials.CustomCredential
23+
import androidx.credentials.GetCredentialRequest
24+
import androidx.credentials.GetCredentialResponse
25+
import androidx.credentials.PasswordCredential
26+
import androidx.credentials.PublicKeyCredential
27+
import androidx.credentials.exceptions.GetCredentialException
28+
import com.google.android.libraries.identity.googleid.GetGoogleIdOption
29+
import com.google.android.libraries.identity.googleid.GetSignInWithGoogleOption
30+
import com.google.android.libraries.identity.googleid.GoogleIdTokenCredential
31+
import com.google.android.libraries.identity.googleid.GoogleIdTokenParsingException
32+
import kotlinx.coroutines.coroutineScope
33+
import kotlin.math.sign
34+
35+
const val WEB_CLIENT_ID = ""
36+
class SignInWithGoogleFunctions (
37+
context: Context,
38+
) {
39+
private val credentialManager = CredentialManager.create(context)
40+
private val activityContext = context
41+
// Placeholder for TAG log value.
42+
val TAG = ""
43+
44+
fun createGoogleIdOption(nonce: String): GetGoogleIdOption {
45+
// [START android_identity_siwg_instantiate_request]
46+
val googleIdOption: GetGoogleIdOption = GetGoogleIdOption.Builder()
47+
.setFilterByAuthorizedAccounts(true)
48+
.setServerClientId(WEB_CLIENT_ID)
49+
.setAutoSelectEnabled(true)
50+
// nonce string to use when generating a Google ID token
51+
.setNonce(nonce)
52+
.build()
53+
// [END android_identity_siwg_instantiate_request]
54+
55+
return googleIdOption
56+
}
57+
58+
private val googleIdOption = createGoogleIdOption("")
59+
60+
suspend fun signInUser() {
61+
// [START android_identity_siwg_signin_flow_create_request]
62+
val request: GetCredentialRequest = GetCredentialRequest.Builder()
63+
.addCredentialOption(googleIdOption)
64+
.build()
65+
66+
coroutineScope {
67+
try {
68+
val result = credentialManager.getCredential(
69+
request = request,
70+
context = activityContext,
71+
)
72+
handleSignIn(result)
73+
} catch (e: GetCredentialException) {
74+
// Handle failure
75+
}
76+
}
77+
// [END android_identity_siwg_signin_flow_create_request]
78+
}
79+
80+
// [START android_identity_siwg_signin_flow_handle_signin]
81+
fun handleSignIn(result: GetCredentialResponse) {
82+
// Handle the successfully returned credential.
83+
val credential = result.credential
84+
val responseJson: String
85+
86+
when (credential) {
87+
88+
// Passkey credential
89+
is PublicKeyCredential -> {
90+
// Share responseJson such as a GetCredentialResponse to your server to validate and
91+
// authenticate
92+
responseJson = credential.authenticationResponseJson
93+
}
94+
95+
// Password credential
96+
is PasswordCredential -> {
97+
// Send ID and password to your server to validate and authenticate.
98+
val username = credential.id
99+
val password = credential.password
100+
}
101+
102+
// GoogleIdToken credential
103+
is CustomCredential -> {
104+
if (credential.type == GoogleIdTokenCredential.TYPE_GOOGLE_ID_TOKEN_CREDENTIAL) {
105+
try {
106+
// Use googleIdTokenCredential and extract the ID to validate and
107+
// authenticate on your server.
108+
val googleIdTokenCredential = GoogleIdTokenCredential
109+
.createFrom(credential.data)
110+
// You can use the members of googleIdTokenCredential directly for UX
111+
// purposes, but don't use them to store or control access to user
112+
// data. For that you first need to validate the token:
113+
// pass googleIdTokenCredential.getIdToken() to the backend server.
114+
// see [validation instructions](https://developers.google.com/identity/gsi/web/guides/verify-google-id-token)
115+
} catch (e: GoogleIdTokenParsingException) {
116+
Log.e(TAG, "Received an invalid google id token response", e)
117+
}
118+
} else {
119+
// Catch any unrecognized custom credential type here.
120+
Log.e(TAG, "Unexpected type of credential")
121+
}
122+
}
123+
124+
else -> {
125+
// Catch any unrecognized credential type here.
126+
Log.e(TAG, "Unexpected type of credential")
127+
}
128+
}
129+
}
130+
// [END android_identity_siwg_signin_flow_handle_signin]
131+
132+
fun createGoogleSignInWithGoogleOption(nonce: String): GetSignInWithGoogleOption {
133+
// [START android_identity_siwg_get_siwg_option]
134+
val signInWithGoogleOption: GetSignInWithGoogleOption = GetSignInWithGoogleOption.Builder(
135+
serverClientId = WEB_CLIENT_ID
136+
).setNonce(nonce)
137+
.build()
138+
// [END android_identity_siwg_get_siwg_option]
139+
140+
return signInWithGoogleOption
141+
}
142+
143+
// [START android_identity_handle_siwg_option]
144+
fun handleSignInWithGoogleOption(result: GetCredentialResponse) {
145+
// Handle the successfully returned credential.
146+
val credential = result.credential
147+
148+
when (credential) {
149+
is CustomCredential -> {
150+
if (credential.type == GoogleIdTokenCredential.TYPE_GOOGLE_ID_TOKEN_CREDENTIAL) {
151+
try {
152+
// Use googleIdTokenCredential and extract id to validate and
153+
// authenticate on your server.
154+
val googleIdTokenCredential = GoogleIdTokenCredential
155+
.createFrom(credential.data)
156+
} catch (e: GoogleIdTokenParsingException) {
157+
Log.e(TAG, "Received an invalid google id token response", e)
158+
}
159+
}
160+
else {
161+
// Catch any unrecognized credential type here.
162+
Log.e(TAG, "Unexpected type of credential")
163+
}
164+
}
165+
166+
else -> {
167+
// Catch any unrecognized credential type here.
168+
Log.e(TAG, "Unexpected type of credential")
169+
}
170+
}
171+
}
172+
// [END android_identity_handle_siwg_option]
173+
174+
fun googleIdOptionFalseFilter() {
175+
// [START android_identity_siwg_instantiate_request_2]
176+
val googleIdOption: GetGoogleIdOption = GetGoogleIdOption.Builder()
177+
.setFilterByAuthorizedAccounts(false)
178+
.setServerClientId(WEB_CLIENT_ID)
179+
.build()
180+
// [END android_identity_siwg_instantiate_request_2]
181+
}
182+
}

0 commit comments

Comments
 (0)