Skip to content
Merged
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
11 changes: 1 addition & 10 deletions .github/workflows/apply_spotless.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,7 @@ jobs:
java-version: '17'

- name: Run spotlessApply
run: ./gradlew :compose:spotlessApply --init-script gradle/init.gradle.kts --no-configuration-cache --stacktrace

- name: Run spotlessApply for Wear
run: ./gradlew :wear:spotlessApply --init-script gradle/init.gradle.kts --no-configuration-cache --stacktrace

- name: Run spotlessApply for Misc
run: ./gradlew :misc:spotlessApply --init-script gradle/init.gradle.kts --no-configuration-cache --stacktrace

- name: Run spotlessApply for XR
run: ./gradlew :xr:spotlessApply --init-script gradle/init.gradle.kts --no-configuration-cache --stacktrace
run: ./gradlew spotlessApply --init-script gradle/init.gradle.kts --no-configuration-cache --stacktrace

- name: Auto-commit if spotlessApply has changes
uses: stefanzweifel/git-auto-commit-action@v5
Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
/.idea/modules.xml
/.idea/workspace.xml
.DS_Store
/build
build
/captures
.externalNativeBuild
.idea/*
Expand Down
7 changes: 7 additions & 0 deletions identity/credentialmanager/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,13 @@ android {
buildFeatures {
compose = true
}
sourceSets {
named("main") {
java {
srcDir("src/main/java")
}
}
}
}

dependencies {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,19 @@
/*
* Copyright 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.example.identity.credentialmanager

import android.app.Activity
Expand All @@ -13,39 +29,39 @@ import androidx.credentials.exceptions.GetCredentialException

// This class is mostly copied from https://github.com/android/identity-samples/blob/main/WebView/CredentialManagerWebView/CredentialManagerHandler.kt.
class CredentialManagerHandler(private val activity: Activity) {
private val mCredMan = CredentialManager.create(activity.applicationContext)
private val TAG = "CredentialManagerHandler"
/**
* Encapsulates the create passkey API for credential manager in a less error-prone manner.
*
* @param request a create public key credential request JSON required by [CreatePublicKeyCredentialRequest].
* @return [CreatePublicKeyCredentialResponse] containing the result of the credential creation.
*/
suspend fun createPasskey(request: String): CreatePublicKeyCredentialResponse {
val createRequest = CreatePublicKeyCredentialRequest(request)
try {
return mCredMan.createCredential(activity, createRequest) as CreatePublicKeyCredentialResponse
} catch (e: CreateCredentialException) {
// For error handling use guidance from https://developer.android.com/training/sign-in/passkeys
Log.i(TAG, "Error creating credential: ErrMessage: ${e.errorMessage}, ErrType: ${e.type}")
throw e
private val mCredMan = CredentialManager.create(activity.applicationContext)
private val TAG = "CredentialManagerHandler"
/**
* Encapsulates the create passkey API for credential manager in a less error-prone manner.
*
* @param request a create public key credential request JSON required by [CreatePublicKeyCredentialRequest].
* @return [CreatePublicKeyCredentialResponse] containing the result of the credential creation.
*/
suspend fun createPasskey(request: String): CreatePublicKeyCredentialResponse {
val createRequest = CreatePublicKeyCredentialRequest(request)
try {
return mCredMan.createCredential(activity, createRequest) as CreatePublicKeyCredentialResponse
} catch (e: CreateCredentialException) {
// For error handling use guidance from https://developer.android.com/training/sign-in/passkeys
Log.i(TAG, "Error creating credential: ErrMessage: ${e.errorMessage}, ErrType: ${e.type}")
throw e
}
}
}

/**
* Encapsulates the get passkey API for credential manager in a less error-prone manner.
*
* @param request a get public key credential request JSON required by [GetCredentialRequest].
* @return [GetCredentialResponse] containing the result of the credential retrieval.
*/
suspend fun getPasskey(request: String): GetCredentialResponse {
val getRequest = GetCredentialRequest(listOf(GetPublicKeyCredentialOption(request, null)))
try {
return mCredMan.getCredential(activity, getRequest)
} catch (e: GetCredentialException) {
// For error handling use guidance from https://developer.android.com/training/sign-in/passkeys
Log.i(TAG, "Error retrieving credential: ${e.message}")
throw e
/**
* Encapsulates the get passkey API for credential manager in a less error-prone manner.
*
* @param request a get public key credential request JSON required by [GetCredentialRequest].
* @return [GetCredentialResponse] containing the result of the credential retrieval.
*/
suspend fun getPasskey(request: String): GetCredentialResponse {
val getRequest = GetCredentialRequest(listOf(GetPublicKeyCredentialOption(request, null)))
try {
return mCredMan.getCredential(activity, getRequest)
} catch (e: GetCredentialException) {
// For error handling use guidance from https://developer.android.com/training/sign-in/passkeys
Log.i(TAG, "Error retrieving credential: ${e.message}")
throw e
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,19 @@
/*
* Copyright 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.example.identity.credentialmanager

import android.annotation.SuppressLint
Expand Down Expand Up @@ -46,7 +62,7 @@ import java.security.spec.ECParameterSpec
import java.security.spec.ECPoint
import java.security.spec.EllipticCurve

class CredentialProviderDummyActivity: FragmentActivity() {
class CredentialProviderDummyActivity : FragmentActivity() {

private val PERSONAL_ACCOUNT_ID: String = ""
private val FAMILY_ACCOUNT_ID: String = ""
Expand Down Expand Up @@ -85,7 +101,7 @@ class CredentialProviderDummyActivity: FragmentActivity() {

val biometricPrompt = BiometricPrompt(
this,
{ }, // Pass in your own executor
{ }, // Pass in your own executor
object : AuthenticationCallback() {
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
super.onAuthenticationError(errorCode, errString)
Expand All @@ -109,7 +125,7 @@ class CredentialProviderDummyActivity: FragmentActivity() {

// Generate a credential key pair
val spec = ECGenParameterSpec("secp256r1")
val keyPairGen = KeyPairGenerator.getInstance("EC");
val keyPairGen = KeyPairGenerator.getInstance("EC")
keyPairGen.initialize(spec)
val keyPair = keyPairGen.genKeyPair()

Expand Down Expand Up @@ -165,7 +181,7 @@ class CredentialProviderDummyActivity: FragmentActivity() {
@RequiresApi(VERSION_CODES.P)
fun appInfoToOrigin(info: CallingAppInfo): String {
val cert = info.signingInfo.apkContentsSigners[0].toByteArray()
val md = MessageDigest.getInstance("SHA-256");
val md = MessageDigest.getInstance("SHA-256")
val certHash = md.digest(cert)
// This is the format for origin
return "android:apk-key-hash:${b64Encode(certHash)}"
Expand Down Expand Up @@ -240,7 +256,7 @@ class CredentialProviderDummyActivity: FragmentActivity() {
)
)

//Set the final response back
// Set the final response back
val result = Intent()
val response = CreatePasswordResponse()
PendingIntentHandler.setCreateCredentialResponse(result, response)
Expand Down Expand Up @@ -300,10 +316,11 @@ class CredentialProviderDummyActivity: FragmentActivity() {

val biometricPrompt = BiometricPrompt(
this,
{ }, // Pass in your own executor
{ }, // Pass in your own executor
object : BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationError(
errorCode: Int, errString: CharSequence
errorCode: Int,
errString: CharSequence
) {
super.onAuthenticationError(errorCode, errString)
finish()
Expand All @@ -330,7 +347,7 @@ class CredentialProviderDummyActivity: FragmentActivity() {
packageName = packageName
)

val sig = Signature.getInstance("SHA256withECDSA");
val sig = Signature.getInstance("SHA256withECDSA")
sig.initSign(privateKey)
sig.update(response.dataToSign())
response.signature = sig.sign()
Expand Down Expand Up @@ -401,9 +418,7 @@ class CredentialProviderDummyActivity: FragmentActivity() {
}

// [START android_identity_credential_pending_intent]
fun createSettingsPendingIntent(): PendingIntent
// [END android_identity_credential_pending_intent]
{
fun createSettingsPendingIntent(): PendingIntent { // [END android_identity_credential_pending_intent]
return PendingIntent.getBroadcast(this, 0, Intent(), PendingIntent.FLAG_IMMUTABLE)
}

Expand Down Expand Up @@ -468,7 +483,7 @@ data class CredentialsInfo(
val passwords: List<PasswordInfo> = listOf()
)

class ECPrivateKeyImpl: ECPrivateKey {
class ECPrivateKeyImpl : ECPrivateKey {
override fun getAlgorithm(): String = ""
override fun getFormat(): String = ""
override fun getEncoded(): ByteArray = byteArrayOf()
Expand Down
Loading