Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
1f4a730
Create Android base
mariobodemann Aug 23, 2024
e4ddf66
Preliminary fix links
mariobodemann Aug 27, 2024
c4bbb68
Fix sidebar titles of sections
mariobodemann Aug 27, 2024
a180e8d
android: rename simple example code
mariobodemann Aug 27, 2024
6135a53
android: first draft tutorial
mariobodemann Aug 27, 2024
4f75fc9
ignore yarn.lock
mariobodemann Aug 27, 2024
e0342c4
android: getting-started: adding headlines
mariobodemann Aug 27, 2024
86e69c4
android: getting-started: remove border around screenshot
mariobodemann Aug 27, 2024
5a1aeb9
android: statics: remove more screenshot border
mariobodemann Aug 27, 2024
0a0e3d4
android:fix authn spelling
mariobodemann Aug 27, 2024
f6fa9f3
android: relying party deploying
mariobodemann Aug 28, 2024
332b547
android: deploy: do not fail if no team
mariobodemann Aug 28, 2024
98b8e0e
android: fix: wrong devtunnel url
mariobodemann Aug 28, 2024
ebec15f
android:fix relying party architecture
mariobodemann Aug 28, 2024
834fe44
Android: Build: Deploy.sh adds devtunnel endpoints
mariobodemann Aug 29, 2024
c1369d9
deploy: Add error when docker is not running
mariobodemann Aug 30, 2024
f7a108b
android:deploy add building of android examples
mariobodemann Aug 30, 2024
8cf69dc
android:quick-start: document best case scenario
mariobodemann Aug 30, 2024
b18d1f9
android:getting-started:polish + clarify android studio version of wr…
mariobodemann Aug 30, 2024
e3ccb53
android:pawskey:add app infrastructure
mariobodemann Aug 30, 2024
49b6469
android:pawskey:add test
mariobodemann Aug 30, 2024
8e54de3
android:deploy: launch app on successful build, if connected
mariobodemann Sep 2, 2024
530c0fd
android:pawskeys: add more libraries and sort imports
mariobodemann Sep 3, 2024
c66a416
android:pawskeys:polish: remove empty constructor brackets
mariobodemann Sep 3, 2024
3be904a
android:pawskeys:polish: add initial relying party implementation
mariobodemann Sep 3, 2024
1118f29
android:pawskeys:connect relying party with ui
mariobodemann Sep 3, 2024
7269af8
android:pawskeys:test fix missing Response type.
mariobodemann Sep 3, 2024
903725a
java: send 200 for /v1/status
mariobodemann Sep 6, 2024
151191e
docs:android: polish and update structure
mariobodemann Sep 6, 2024
c990bf5
docs:ios: add structure parraleling android
mariobodemann Sep 6, 2024
31ec671
docs:mobile: Merge RP sections
mariobodemann Sep 6, 2024
5eed099
docs:android:advanced:credman add notes for later
mariobodemann Sep 10, 2024
0fe45f2
android:pawskeys:polishing
mariobodemann Sep 10, 2024
e335150
android:pawskeys:integrate AttestationOptionsRequest
mariobodemann Sep 13, 2024
7c160a7
android: deploy: prefer usb device for installing
mariobodemann Jan 7, 2025
52fa8b3
android add more draft documentation
mariobodemann Jan 7, 2025
8f3cf9b
android: update dependencies and use attestation request
mariobodemann Jan 7, 2025
e98546c
android: add delete button to user messages
mariobodemann Jan 7, 2025
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
57 changes: 49 additions & 8 deletions deploy/deploy.sh
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,7 @@ if [ "$DEPLOYMENT_ENVIRONMENT" == "devtunnel" ]; then
N=$(echo $DEVELOPMENT_TEAM | wc -w)
if [[ $N -lt 1 ]] ; then
echo Cannot find a DEVELOPMENT_TEAM
echo "Please edit your .env file and fill in your DEVELOPMENT_TEAM"
exit
echo "Please edit your .env file and fill in your DEVELOPMENT_TEAM, if you want to build the iOS examples."
fi
if [[ $N -gt 1 ]] ; then
echo You have more than one Team ID
Expand All @@ -49,7 +48,7 @@ fi

# stop and remove any running containers as they may need to be restarted
echo "### removing any running containers"
docker compose stop
docker compose stop || { echo "$(tput bold)Please start a docker machine.$(tput sgr0)"; exit; }
docker compose rm

# copy sources so they can be copied into docker images
Expand Down Expand Up @@ -125,14 +124,17 @@ if [ "$DEPLOYMENT_ENVIRONMENT" == "devtunnel" ]; then
-e "s#^API_BASE_URI[= ].*#API_BASE_URI = $HOST-8080.$REGION#" \
-e "s#^RP_ID[= ].*#RP_ID = $hostname#" \
../examples/clients/mobile/iOS/PawsKey/Constants.xcconfig
sed -i '' \
-e "s#^API_BASE_URI[=].*#API_BASE_URI=$HOST-8080.$REGION#" \
-e "s#^RELYING_PARTY_ID[=].*#RELYING_PARTY_ID=$hostname#" \
../examples/clients/mobile/android/PawsKey/gradle.properties

echo "### editing iOS BankApp sources"
sed -i '' \
-e "s#^BANK_AUTH_DOMAIN[= ].*#BANK_AUTH_DOMAIN = $HOST-8081.$REGION#" \
-e "s#^BANK_API_DOMAIN[= ].*#BANK_API_DOMAIN = $HOST-8082.$REGION#" \
../examples/clients/mobile/iOS/PKBank/Constants.xcconfig


# TODO: instead of editing source files, make endpoints configurable
sed -i '' "s#http://host.docker.internal#https://$hostname#;s#http://localhost#https://$hostname#" keycloak/source/src/main/java/com/yubicolabs/PasskeyAuthenticator/PasskeyAuthenticator.java
sed -i '' "s#http://host.docker.internal#https://$hostname#;s#http://localhost#https://$hostname#" keycloak/source/src/main/java/com/yubicolabs/PasskeyAuthenticator/PasskeyRegistrationAuthenticator.java
Expand All @@ -154,11 +156,50 @@ if [ "$DEPLOYMENT_ENVIRONMENT" == "devtunnel" ]; then
echo
fi

echo "### starting devtunnel. Type ^C to stop the tunnel and take down all containers"
devtunnel host $TUNNELID > /dev/null
if (echo $DEPLOYMENT_CLIENTS | tr ',' '\n' | grep -Fqx android); then
echo "### building android examples"

if pushd ../examples/clients/mobile/android/PawsKey/ > /dev/null; then
if [[ $(command -v adb) ]]; then
ADB="adb"
elif [[ -e ${ANDROID_HOME}/platform-tools/adb ]]; then
ADB="${ANDROID_HOME}/platform-tools/adb"
else
ADB=""
fi

if [[ -n ${ADB} ]] && [[ $(${ADB} devices | wc -l) -gt 2 ]]; then
echo found a connected phone, installing app
./gradlew installDebug

echo your android application is deployed to
echo
echo "$(tput bold) a connected phone $(tput sgr0)"
echo

${ADB} -d shell am start -n io.yubicolabs.pawskey/io.yubicolabs.pawskey.MainActivity || { echo "Android app could not be launched. See above for details."; }

else
echo no phone found, building app without installing
./gradlew assembleDebug

echo your android application ia deployed here:
echo
echo "$(tput bold) ../examples/clients/mobile/android/PawsKey/app/build/outputs/apk/debug/app-debug.apk $(tput sgr0)"
echo
fi

popd > /dev/null
else
echo android example folder not found.
fi
fi

echo "### starting devtunnel. Type ^C to stop the tunnel and take down all containers"
devtunnel host $TUNNELID > /dev/null

docker compose down
exit
docker compose down
exit
fi

# default is deploy on localhost
Expand Down
1 change: 1 addition & 0 deletions docs/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@
npm-debug.log*
yarn-debug.log*
yarn-error.log*
yarn.lock
8 changes: 8 additions & 0 deletions docs/docs/mobile-clients/_category_.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"label": "Mobile clients",
"position": 6,
"link": {
"type": "generated-index",
"description": "This section contains mobile client examples."
}
}
8 changes: 8 additions & 0 deletions docs/docs/mobile-clients/android/_category_.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"label": "Android",
"position": 1,
"link": {
"type": "generated-index",
"description": "Find a detailed description on how to build several android apps to use WebAuthn."
}
}
8 changes: 8 additions & 0 deletions docs/docs/mobile-clients/android/advanced/_category_.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"label": "Advanced",
"position": 100,
"link": {
"type": "generated-index",
"description": "Advanced topics not covered elsewhere."
}
}
8 changes: 8 additions & 0 deletions docs/docs/mobile-clients/android/advanced/getting-started.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
sidebar_position: 1
---

# Complex Use Cases

:::tip
Add content here
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
sidebar_position: 2
---

# Credential Manager on Android

## Signing / Uploading Key Fingerprinting

:::tip
Automate deploy script to include apk fingerprinting

:::tip
Add why credential manager on google play services
8 changes: 8 additions & 0 deletions docs/docs/mobile-clients/android/app-authenticate-user.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
sidebar_position: 4
---

# Authentication

:::tip
How to authenticate, ui, api, sdk
8 changes: 8 additions & 0 deletions docs/docs/mobile-clients/android/app-polishing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
sidebar_position: 5
---

# Polishing

:::tip
Think about simple gotchas to be added before starting production of this sample.
25 changes: 25 additions & 0 deletions docs/docs/mobile-clients/android/app-register-user.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
sidebar_position: 3
---

# Registration

In this section we will explain how our app can use register users using a yubikey and the relying party. In the
following
graphic we see the 'authenticator' (i.e. the Yubikey creating credentials), the client (i.e. out android app mediating)
and the relying party called 'application'.

<img
src={require('/img/reg-flow.jpg').default}
alt="Relying Party Architectural Diagram."
style={{ width: "100%" }}
/>

So in order to register the user, we need the Android application to implement the communication to the relying party
next.

## Register with Relying Party

## Create Credentials on Authenticator

## Provide Public Key to Relying Party
180 changes: 180 additions & 0 deletions docs/docs/mobile-clients/android/app-to-rp.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
---
sidebar_position: 2
---

# Connecting to the Relying Party

We will cover the backend part, called relying party, only briefly and for a more indepth discussion, please follow
the [Relying Party](/passkey-workshop/docs/category/relying-party) section.

We'll use the Relying Party as an API to register a new user using their Yubikey. The idea is that we build an Android
App that communicates via REST to the Relying Party and the Yubikey to register a new user.

In the later section [banking app example](advanced/getting-started.md) we will use that basis for a more complex
banking example, complete with several activities, user flows and more.

In order to build the connection, we need to understand how to build the Relying Party backend service, make it run, and
connect the Android App to it.

<img
src={require('/img/mobile/android/relying-party-architecture.jpg').default}
alt="Relying Party Architectural Diagram."
style={{ width: "100%" }}
/>

## Deploying the Relying Party

Summarizing from [Relying Party](/passkey-workshop/docs/category/relying-party) what we need to start the backend, is to
execute the `./deploy.sh` (or `./deploy.ps1` on windows) script from the `deploy` folder.

To call the deploy script, you need `docker` and a `virtual machine`. Either can be fulfilled by either
installing [docker desktop](https://docs.docker.com/desktop/)
or [docker with compose](https://docs.docker.com/compose/install/) and [podman](https://podman.io/docs/installation).
Please see the appropriate documentation, since this will not be covered here.

:::tip Running Virtual Machines
Please remember to run a virtual machine for docker, i.e. `podman machine start` if you receive errors like
```Cannot connect to the Docker daemon at unix:///var/run/docker.sock.```
:::

Running the deploy script will start the server on the localhost using http. Feel free to explore the web frontend
running on your machine here [http://localhost:3000](http://localhost:3000). This will be the part we are reimplementing
in Android, so feel free to click around, register and get familiar.

## Android and Secure HTTP Traffic

Since [Android 9 (API Level 27)](https://developer.android.com/privacy-and-security/security-config#CleartextTrafficPermitted)
all internet traffic needs to be secured from eavesdropping using secure transport. This means we cannot use our local
server running on `localhost` to connect to our Android App. So we need to secure our traffic.

The solution is to use a service like [devtunnel](https://learn.microsoft.com/en-gb/azure/developer/dev-tunnels/) to
expose a local web service to the internet.

### Installation of devtunnel

Please install devtunnel like so:

* `brew install devtunnel` (mac) or
* `curl -sL https://aka.ms/DevTunnelCliInstall | bash` (linux) or
* `winget install Microsoft.devtunnel` (windows)

Once installed, we need to tell the deploy script to use devtunnel. Please copy the [default.enf](/deploy/default.env)
default configuration file to [.env](/deploy/.env) file in the deploy folder. Now that we have copied the default
configuration file, please update this line

```
DEPLOYMENT_ENVIRONMENT=localhost
```

to set the deployment environment to be the devtunnel:

```
DEPLOYMENT_ENVIRONMENT=devtunnel
```

:::tip
Please remember to login to devtunnel: `devtunnel user login`. (Using 'other' for the microsoft login, you can use
GitHub and will not need to create a new microsoft account.)
:::

To start the deployment to the devtunnel, please execute the `./deploy.sh` (mac or linux) or `./deploy.ps1`(windows).

## Validating deployment

If everything went smoothly, you should see an output similar to

```shell
[...]
your demo application will be deployed here:

https://XXXXXXXXXXXXXX-3000.euw.devtunnels.ms/test_panel

your bank application will be deployed here:

https://XXXXXXXXXXXXXX-3002.euw.devtunnels.ms/

### starting devtunnel. Type ^C to stop the tunnel and take down all containers
```

To verify, let us browse to the status page of the just deployed webservice. Therefore, you need to take the url the
devtunnel is running on and update it. From the example above, please take the
``` https://XXXXXXXXXXXXXX-3000.euw.devtunnels.ms/test_panel``` url and replace the `3000` with the port the service is
running on, `8080` in our case.

You should be able to visit the relying party running on ```https://XXXXXXXXXXXXXX-8080.euw.devtunnels.ms/``` (please
replace the X with your endpoint) and be greeted with a similar image then this screenshot:

<img
src={require('/img/mobile/android/relying-party-status-hint.png').default}
alt="Android Studio: Menu new project."
style={{ width: "50%" }}
/>

The security check is to make sure that we know what we are doing, and since the url is under our control it is fine to
accept the connection and hit `continue`.

If everything is running as planned, you will see a message like this

```json
{
"status": "ok"
}
```

This means our local server is available through the devtunnel, and works as expected. You can now also browse through
the other components, securely transmitting data from your local machine through the tunnel to the local browser.
Now that we know how to [deploy and run our backend](relying-party.md), let us connect our
[Android app](https://github.com/YubicoLabs/passkey-workshop/tree/main/examples/clients/mobile/android) to it.

## Deploy script

The deploy script sets up the backend relying party to be deployed on docker and then exposed through `devtunnel` from
your local machine to the broad internet. Using TLS it is able to allow a connection from your phone securely to your
laptop.

Once this connection is established, the script finds the Android examples and modifies the configuration
in [gradle.properties](../../../../examples/clients/mobile/android/PawsKey/gradle.properties) to reflect the newly
created tunnel. This way if the tunnel changes, a call to the deploy script will also update that configuration. You
might want to change the configuration manually to contain the endpoint your relying party is running on.

Additionally the deploy script also updates the relying party id, as in the id used to identify which credentials to
select. You will also need to update that configuration, if you update the endpoint.

Once the configuration is updated, the script builds the android examples using `gradle`. With the source, which we will
go through in detail soon, we try to reflect current best practices and will not emphasize common ways of working.
Please follow linked literature if you want to get up to speed in everything from MVVM[1], Jetpack Compose[2] and DI
using Hilt[3].

## Dependencies

Additionally, the mentioned libraries and architectures the android examples depend on retrofit and kotlinx
serialization: Those are used in order to communicate with the relying
parties: [RelyingPartyService.kt](../../../../examples/clients/mobile/android/PawsKey/app/src/main/java/io/yubicolabs/pawskey/RelyingPartyService.kt)
implements the Retrofit Service to communicate with the relying
party. [PassKeyService.kt](../../../../examples/clients/mobile/android/PawsKey/app/src/main/java/io/yubicolabs/pawskey/PassKeyService.kt)
is used for communicating with the passkeys, may it be from the platform perspective or the Yubico SDK point of view.

All of this is tied together by the [MainViewModel.kt][5], that abstracts from the service implementations to expose the
view relevant details. Finally, the [MainActivity.kt][4] displays relevant details to the user. And gets called when she
wants to sign in / log in or interact otherwise with the app.

## Hello Relying Party Server

Let us follow the flow of the example app to communicate with the relying party with the example of requesting a status
from it's `/v1/status` endpoint. Once the user starts the app, the [MainActivity][4] initializes and creates the
[MainViewModel][5]. Once the [MainViewModel][5] is created, it asks its [Repository for RelyingParty][6] to fetch it's
status. Fetching the status has two effects: A) we are making sure the network is working. And B) that we are connecting
to a well known backend, before we are starting more complex interactions.


[1]: https://developer.android.com/topic/architecture

[2]: https://developer.android.com/compose

[3]: https://developer.android.com/training/dependency-injection/hilt-android

[4]: ../../../../examples/clients/mobile/android/PawsKey/app/src/main/java/io/yubicolabs/pawskey/MainActivity.kt

[5]: ../../../../examples/clients/mobile/android/PawsKey/app/src/main/java/io/yubicolabs/pawskey/MainViewModel.kt

[6]: ../../../../examples/clients/mobile/android/PawsKey/app/src/main/java/io/yubicolabs/pawskey/RelyingPartyService.kt
Loading