Summary of change
Modifications to Support Android Native WebAuthn Origins
Context
Currently, when attempting to define an Android Native origin (in the formatandroid:apk-key-hash:<base64Url-string-without-padding-of-fingerprint>) throughsupertokens_python.recipe.webauthn.register_options andsupertokens_python.recipe.webauthn.sign_in_options, the Core returns anINVALID_OPTIONS_ERROR status without specifying a reason. The "origin" field of these functions appears to only work with HTTP(S) URLs.
There is no indication of this limitation in any of the documentation I've reviewed, which leads me to believe this has never been tested under these conditions (i.e., from an AndroidNative application, without an Pre-Built UI (frontend SDK), as is possible withreact-native-passkey).
I initially attempted to useFunction overrides, a solution I'm not particularly fond of. While this can work during the "Register Options" phase (where it's possible to switch the Android Native origin to the HTTPS URL provided as the origin when calling the Python function to bypass the Core), it becomes impossible during the Sign-In phase, as this invalidates the signature sent by the device, causing the Core to legitimately reject the request.
Therefore, I propose this PR with several modifications to allow the Core to support this origin format in the same way it handles HTTP(S) URLs, avoiding these workarounds (which don't work anyway).
This is my first pull request to an open source project of this scale, and I'm open to any feedback if I'm on the wrong track or doing things incorrectly.
Modified Files
1. OptionsValidator.java
File:src/main/java/io/supertokens/webauthn/validator/OptionsValidator.java
Changelog:
- Added detection for Android origins starting with
android:apk-key-hash: - Strict validation of the URL-safe base64 hash:
- Verification that the hash is not empty
- Validation of URL-safe base64 format (allowed characters: A-Z, a-z, 0-9, -, _)
- Verification of exact length: 43 characters (base64 of SHA-256 fingerprint, 32 bytes)
- HTTP(S) origins continue to work normally
2. Utils.java (Tests)
File:src/test/java/io/supertokens/test/webauthn/Utils.java
Modifications:
- Added overloads for
registerOptions() andsignInOptions() accepting anorigin parameter - Allows easy testing with different types of origins
3. TestAndroidOriginValidation.java (New File)
File:src/test/java/io/supertokens/test/webauthn/api/TestAndroidOriginValidation.java
Tests Added:
testValidAndroidOrigin() : Verifies that a valid Android origin is acceptedtestValidAndroidOriginWithAlternativeHash() : Tests with an alternative valid hashtestAndroidOriginWithEmptyHash() : Verifies rejection of an empty hashtestAndroidOriginWithInvalidCharacters() : Verifies rejection of invalid characterstestAndroidOriginWithInvalidLength() : Verifies rejection of incorrect length (must be exactly 43 characters)testAndroidOriginForSignInOptions() : Tests Android origins for sign-intestMixedOriginsSupport() : Verifies that HTTP, HTTPS, and Android coexist
Test Execution
I executed this test to validate proper functionality:
From thesupertokens-root:
./gradlew :supertokens-core:test --tests"io.supertokens.test.webauthn.api.TestAndroidOriginValidation"

Origin Examples
Valid Android Origins
android:apk-key-hash:47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFUandroid:apk-key-hash:sYUC8p5I9SxqFernBPHmDxz_YVZXmVJdW8s-m3RTTqEandroid:apk-key-hash:AbCdEfGhIjKlMnOpQrStUvWxYz0123456789-_AbCd
Invalid Android Origins
android:apk-key-hash: # Empty hashandroid:apk-key-hash:invalid@hash#with$special! # Invalid characters (not URL-safe base64)android:apk-key-hash:abc # Incorrect length (must be 43 characters)android:apk-key-hash:Lir5oIjf552K/XN4bTul0VS2aiE= # Incorrect length and contains invalid characters (+/ and =)
HTTP(S) Origins (Still Supported)
http://example.comhttps://example.comhttps://subdomain.example.com
WebAuthn Specification Compliance
These modifications comply with the WebAuthn specification, which defines different origin formats depending on the platform:
Security Notes
- The URL-safe base64 hash (43 characters) represents the SHA-256 fingerprint of the Android application's signing certificate
- This validation ensures that only legitimate Android applications with the correct certificate can use WebAuthn
- Android origins are not validated against the
relyingPartyId in the same way as HTTP(S) origins (compliant with the spec) - The hash format follows the WebAuthn specification for Android native applications
Test Plan
Command used to test the implementation:
gradletest --tests"io.supertokens.test.webauthn.api.TestAndroidOriginValidation"
Documentation changes
I don't feel it's necessary to make changes to the documentation, as there was nothing to suggest the current limitation. This should be transparent to the few people in this situation.
Checklist for important updates
Uh oh!
There was an error while loading.Please reload this page.
Summary of change
Modifications to Support Android Native WebAuthn Origins
Context
Currently, when attempting to define an Android Native origin (in the format
android:apk-key-hash:<base64Url-string-without-padding-of-fingerprint>) throughsupertokens_python.recipe.webauthn.register_options andsupertokens_python.recipe.webauthn.sign_in_options, the Core returns anINVALID_OPTIONS_ERRORstatus without specifying a reason. The "origin" field of these functions appears to only work with HTTP(S) URLs.There is no indication of this limitation in any of the documentation I've reviewed, which leads me to believe this has never been tested under these conditions (i.e., from an AndroidNative application, without an Pre-Built UI (frontend SDK), as is possible withreact-native-passkey).
I initially attempted to useFunction overrides, a solution I'm not particularly fond of. While this can work during the "Register Options" phase (where it's possible to switch the Android Native origin to the HTTPS URL provided as the origin when calling the Python function to bypass the Core), it becomes impossible during the Sign-In phase, as this invalidates the signature sent by the device, causing the Core to legitimately reject the request.
Therefore, I propose this PR with several modifications to allow the Core to support this origin format in the same way it handles HTTP(S) URLs, avoiding these workarounds (which don't work anyway).
This is my first pull request to an open source project of this scale, and I'm open to any feedback if I'm on the wrong track or doing things incorrectly.
Modified Files
1. OptionsValidator.java
File:
src/main/java/io/supertokens/webauthn/validator/OptionsValidator.javaChangelog:
android:apk-key-hash:2. Utils.java (Tests)
File:
src/test/java/io/supertokens/test/webauthn/Utils.javaModifications:
registerOptions()andsignInOptions()accepting anoriginparameter3. TestAndroidOriginValidation.java (New File)
File:
src/test/java/io/supertokens/test/webauthn/api/TestAndroidOriginValidation.javaTests Added:
testValidAndroidOrigin(): Verifies that a valid Android origin is acceptedtestValidAndroidOriginWithAlternativeHash(): Tests with an alternative valid hashtestAndroidOriginWithEmptyHash(): Verifies rejection of an empty hashtestAndroidOriginWithInvalidCharacters(): Verifies rejection of invalid characterstestAndroidOriginWithInvalidLength(): Verifies rejection of incorrect length (must be exactly 43 characters)testAndroidOriginForSignInOptions(): Tests Android origins for sign-intestMixedOriginsSupport(): Verifies that HTTP, HTTPS, and Android coexistTest Execution
I executed this test to validate proper functionality:
From thesupertokens-root:
./gradlew :supertokens-core:test --tests"io.supertokens.test.webauthn.api.TestAndroidOriginValidation"Origin Examples
Valid Android Origins
Invalid Android Origins
HTTP(S) Origins (Still Supported)
WebAuthn Specification Compliance
These modifications comply with the WebAuthn specification, which defines different origin formats depending on the platform:
https://example.com,http://localhost, andhttps://<RPID>(in accordance withApple App Site Association (AASA))android:apk-key-hash:<base64Url-string-without-padding-of-fingerprint>(seehttps://developer.android.com/identity/passkeys/create-passkeys#verify)Security Notes
relyingPartyIdin the same way as HTTP(S) origins (compliant with the spec)Test Plan
Command used to test the implementation:
Documentation changes
I don't feel it's necessary to make changes to the documentation, as there was nothing to suggest the current limitation. This should be transparent to the few people in this situation.
Checklist for important updates
coreDriverInterfaceSupported.jsonfile has been updated (if needed)pluginInterfaceSupported.jsonfile has been updated (if needed)build.gradlegetPaidFeatureStatsfunction in FeatureFlag.java filebuild.gradle, please make sure to add themin
implementationDependencies.json.getValidFieldsinio/supertokens/config/CoreConfig.javaif new aliases were added for any coreconfig (similar to the
access_token_signing_key_update_intervalconfig alias).git tag) in the formatvX.Y.Z, and then find thelatest branch (
git branch --all) whoseX.Yis greater than the latest released tag.app_id_to_user_idtable, make sure to delete from this table when deletingthe user as well if
deleteUserIdMappingToois false.