diff --git a/docs/astro.config.mjs b/docs/astro.config.mjs
index 6cfa6ac..c3bd4d6 100644
--- a/docs/astro.config.mjs
+++ b/docs/astro.config.mjs
@@ -1,16 +1,26 @@
-import { defineConfig } from 'astro/config';
-import starlight from '@astrojs/starlight';
-import rehypeMermaid from 'rehype-mermaid';
-import tailwind from '@astrojs/tailwind';
-import starlightOpenAPI, { openAPISidebarGroups } from 'starlight-openapi';
+import { defineConfig } from "astro/config";
+import starlight from "@astrojs/starlight";
+import rehypeMermaid from "rehype-mermaid";
+import tailwind from "@astrojs/tailwind";
+import starlightOpenAPI, { openAPISidebarGroups } from "starlight-openapi";
+import path from 'node:path';
+import { fileURLToPath } from 'node:url';
// import starlightTypeDoc, { typeDocSidebarGroup } from "starlight-typedoc";
-import react from '@astrojs/react';
-
-import mdx from '@astrojs/mdx';
-import basicSsl from '@vitejs/plugin-basic-ssl';
+import react from "@astrojs/react";
+import mdx from "@astrojs/mdx";
+import basicSsl from '@vitejs/plugin-basic-ssl'
+const __filename = fileURLToPath(import.meta.url);
+const __dirname = path.dirname(__filename);
// https://astro.build/config
export default defineConfig({
vite: {
+ resolve: {
+ alias: {
+ '@/lib': path.resolve(__dirname, './src/lib'),
+ '@/assets': path.resolve(__dirname, './src/assets'),
+ '@/components': path.resolve(__dirname, './src/components'),
+ },
+ },
plugins: [basicSsl()],
server: {
https: true,
@@ -34,9 +44,58 @@ export default defineConfig({
head: [
// GTM Script
{
- tag: 'script',
- attrs: {
- 'is:inline': true,
+ label: "Integrations",
+ link: "/server/integrations",
+ },
+ ...openAPISidebarGroups,
+ ]
+ }, {
+ label: "Clients",
+ collapsed: true,
+ items: [{
+ label: "Android",
+ collapsed: true,
+ items: [
+ {
+ label: "Introduction",
+ link: "/clients/android/introduction",
+ },
+ {
+ label: "Registration",
+ link: "/clients/android/registration",
+ },
+ {
+ label: "Authentication",
+ link: "/clients/android/authentication",
+ },
+ {
+ label: "Peer Offer",
+ link: "/clients/android/offer",
+ },
+ {
+ label: "Peer Answer",
+ link: "/clients/android/answer",
+ },
+ {
+ label: "Provider Service",
+ badge: {
+ text: "^14",
+ variant: "danger"
+ },
+ items: [
+ {
+ label: "Introduction",
+ link: "/clients/android/provider-service/introduction",
+ },
+ {
+ label: "Registration",
+ link: "/clients/android/provider-service/registration",
+ },
+ {
+ label: "Authentication",
+ link: "/clients/android/provider-service/authentication",
+ }
+ ]
},
content: `
(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
diff --git a/docs/src/components/CodeUri.astro b/docs/src/components/CodeUri.astro
new file mode 100644
index 0000000..81fc2bd
--- /dev/null
+++ b/docs/src/components/CodeUri.astro
@@ -0,0 +1,15 @@
+---
+import { Code } from '@astrojs/starlight/components';
+import { fetchAndExtract } from '@/lib/fetchAndExtract.js';
+
+const { uri, lines, lang, title, mark, frame } = Astro.props;
+
+// 'lines' can optionally be specified in the following formats:
+// - '1-3' (inclusive range)
+// - '5' (single line)
+// - '1-10,15-20' (multiple ranges)
+// - '1-3,5,7-9' (mixed)
+const selectedCode = await fetchAndExtract(uri, lines);
+---
+
+
diff --git a/docs/src/content/docs/clients/android/provider-service/authenticate.md b/docs/src/content/docs/clients/android/provider-service/authenticate.md
deleted file mode 100644
index a9f70d7..0000000
--- a/docs/src/content/docs/clients/android/provider-service/authenticate.md
+++ /dev/null
@@ -1,13 +0,0 @@
----
-title: "Android 14: Authenticate"
----
-
-```kotlin
-// MainActivity.kt
-val request = PendingIntentHandler.retrieveProviderCreateCredentialRequest(intent)
-if (request.callingRequest is CreatePublicKeyCredentialRequest) {
- val result = viewModel.processCreatePasskey(this@CreatePasskeyActivity, request)
-} else {
- val text = resources.getString(R.string.get_passkey_error)
-}
-```
diff --git a/docs/src/content/docs/clients/android/provider-service/authentication.mdx b/docs/src/content/docs/clients/android/provider-service/authentication.mdx
new file mode 100644
index 0000000..a5a5a02
--- /dev/null
+++ b/docs/src/content/docs/clients/android/provider-service/authentication.mdx
@@ -0,0 +1,117 @@
+---
+title: "Android 14: Authentication Service"
+---
+
+import {Steps, TabItem, Tabs} from '@astrojs/starlight/components';
+import CodeUri from "@/components/CodeUri.astro";
+
+To handle sign in requests in your credential provider service, complete the steps shown in the following sections.
+
+## Query
+
+User sign-in is handled with the following steps:
+
+
+1. When a device tries to sign in a user, it prepares a [GetCredentialRequest](https://developer.android.com/reference/androidx/credentials/GetCredentialRequest) instance.
+2. The Android framework propagates this request to all applicable credential providers by binding to these services.
+3. The provider service then receives a `BeginGetCredentialRequest` that contains a list of `BeginGetCredentialOption`,
+ each of which contains parameters that can be used to retrieve matching credentials.
+
+
+To handle this request in your credential provider service,
+you can override the `onBeginGetCredentialRequest()` method as shown in the following example:
+
+```kotlin
+package foundation.algorand.demo
+
+class CustomProviderService: CredentialProviderService() {
+ companion object {
+ const val GET_PASSKEY_INTENT = 1
+ const val GET_PASSKEY_ACTION = "foundation.algorand.demo.GET_PASSKEY"
+ }
+
+ /**
+ * Handle Get Credential Requests
+ */
+ @RequiresApi(Build.VERSION_CODES.S)
+ override fun onBeginGetCredentialRequest(
+ request: BeginGetCredentialRequest,
+ cancellationSignal: CancellationSignal,
+ callback: OutcomeReceiver,
+ ) {
+ try {
+ callback.onResult(processGetCredentialRequest(request))
+ } catch (e: GetCredentialException) {
+ callback.onError(GetCredentialUnknownException())
+ }
+ }
+
+ /**
+ * Get a list of available PublicKeyCredential Entries
+ */
+ private fun processGetCredentialRequest(request: BeginGetCredentialRequest): BeginGetCredentialResponse{
+ Log.v(TAG, "processing GetCredentialRequest")
+ val deferredCredentials: Deferred> = scope.async {
+ credentialRepository.getDatabase(this@LiquidCredentialProviderService).credentialDao().getAllRegular()
+ }
+ val credentials = runBlocking {
+ deferredCredentials.await()
+ }
+ return BeginGetCredentialResponse(credentials.map {
+ val data = Bundle()
+ data.putString("credentialId", it.credentialId)
+ data.putString("userHandle", it.userHandle)
+ PublicKeyCredentialEntry.Builder(
+ this@LiquidCredentialProviderService,
+ it.userHandle,
+ createNewPendingIntent(GET_PASSKEY_ACTION, GET_PASSKEY_INTENT, data),
+ request.beginGetCredentialOptions[0] as BeginGetPublicKeyCredentialOption
+ )
+ .setIcon(Icon.createWithResource(this@LiquidCredentialProviderService, R.mipmap.ic_launcher))
+ .build()
+ })
+ }
+
+ /**
+ * This method creates a new PendingIntent for the given action and request code.
+ */
+ private fun createNewPendingIntent(action: String, requestCode: Int, extra: Bundle?): PendingIntent {
+ val intent = Intent(action).setPackage(PACKAGE_NAME)
+
+ if (extra != null) {
+ intent.putExtra("CREDENTIAL_DATA", extra)
+ }
+ return PendingIntent.getActivity(
+ applicationContext, requestCode,
+ intent, (PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT)
+ )
+ }
+}
+```
+
+## Selection
+
+Once you query and populate the credentials,
+now you need to handle the selection phase for the credentials being selected by the user.
+
+
+1. In the `onCreate` method of the corresponding Activity, retrieve the associated intent, and pass to [PendingIntentHandler.retrieveProviderGetCredentialRequest()](https://developer.android.com/reference/androidx/credentials/provider/ProviderGetCredentialRequest).
+2. Extract the [GetPublicKeyCredentialOption](https://developer.android.com/reference/androidx/credentials/GetPublicKeyCredentialOption) from the request retrieved above. Subsequently, extract the `requestJson` and `clientDataHash` from this option.
+3. Extract the `credentialId` from the intent extra, which was populated by the credential provider when the corresponding `PendingIntent` was set up.
+4. Extract the passkey from your local database using the request parameters accessed above.
+5. Assert that the passkey is valid with extracted metadata, and user verification.
+6. Construct a JSON response based on the W3 Web Authentication Assertion spec.
+7. Construct a PublicKeyCredential using the JSON generated above and set it on a final GetCredentialResponse.
+
+
+The following example illustrates how these steps can be implemented:
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/src/content/docs/clients/android/provider-service/introduction.mdx b/docs/src/content/docs/clients/android/provider-service/introduction.mdx
index a4751dc..9d96105 100644
--- a/docs/src/content/docs/clients/android/provider-service/introduction.mdx
+++ b/docs/src/content/docs/clients/android/provider-service/introduction.mdx
@@ -1,8 +1,8 @@
---
-title: "Android: Provider Service"
+title: "Android 14: Provider Service"
---
-import {Aside} from '@astrojs/starlight/components';
+import { Aside, Code, Steps } from "@astrojs/starlight/components";