Passwordless.dev consists of three key parts:
- An open-source client side library, used by your frontend to make requests to the end-user's browser WebAuthn API and requests to the passwordless.dev APIs.
- JavaScript Client: JavaScript
- Android Client: Java, Kotlin
- A public RESTful API used to complete FIDO2 WebAuthn cryptographic exchanges with the browser.
- a private RESTful API used to initiate key registrations, verify signins, and retrieve keys for end-users. This is typically your backend.
- Android 9+ (API 28)
- Generate SHA256 for your app.
keytool -list -v -keystore "%USERPROFILE%\.android\debug.keystore" -alias androiddebugkey -storepass android -keypass android
keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android
Go to the root directory of the project from the terminal and run the below command
./gradlew signingReport
-
Put this SHA256 along with your root android package name in your backend to generate assetlink.json for your app at "https://yourexample.com/.well-known/assetlinks.json" If you are using example-nodejs-backend. then just put these 2 variables in .env file.
-
Generate APK key hash, this hash is required in the origin field to authenticate the app passwordless.
keytool -list -v -keystore ~/.android/debug.keystore | grep "SHA256: " | cut -d " " -f 3 | xxd -r -p | openssl base64 | sed 's/=//g'
Run this on PowerShell
# Run keytool command and extract SHA256 hash
$keytoolOutput = keytool -list -v -keystore $HOME\.android\debug.keystore
$sha256Hash = ($keytoolOutput | Select-String "SHA256: ").ToString().Split(" ")[2]
# Remove any non-hex characters from the hash
$hexHash = $sha256Hash -replace "[^0-9A-Fa-f]"
# Convert the hexadecimal string to a byte array
$byteArray = [byte[]]@()
for ($i = 0; $i -lt $hexHash.Length; $i += 2) {
$byteArray += [byte]([Convert]::ToUInt32($hexHash.Substring($i, 2), 16))
}
# Convert the byte array to a base64 string
$base64String = [Convert]::ToBase64String($byteArray)
Write-Output $base64String
- Get your public key by registering at https://admin.passwordless.dev/Account/Login
- Now, update yourbackend/config/DemoPasswordlessOptions.kt and xml/assetlinks.xml
- Run the app!
- Setup
PasswordlessOptions
Class: Begin by configuring the PasswordlessOptions class, as demonstrated in the example. Fill in the required parameters:
/**
* @property API_URL The Passwordless.dev server url.
* @property API_KEY Your public API key.
* @property RP_ID This stands for “relying party”; it can be considered as describing the organization responsible for registering and authenticating the user.
* Set this as base url for your backend. So, https://<Relying Party ID>/.well-known/assetlinks.json is accessible
* @property ORIGIN This is your generated key for your app, refer readme on how to generate this.
* String Format: "android:apk-key-hash:<Hash value>" , Example "android:apk-key-hash:NX7853gQH6KKGF4iT7WmpEtBDw7njd75WuaAFKzyW44"
* @property YOUR_BACKEND_URL This is where your backend is hosted.
*/
class DemoPasswordlessOptions {
companion object {
const val API_KEY = "personal:public:fef3b07e9022454cb72377b96d1ac329"
const val RP_ID = "990b-163-53-252-8.ngrok-free.app"
const val YOUR_BACKEND_URL = "https://990b-163-53-252-8.ngrok-free.app"
const val ORIGIN = "android:apk-key-hash:NX7853gQH6KKGF4iT7WmpEtBDw7njd75WuaAFKzyW44"
const val API_URL = "https://v4.passwordless.dev"
}
}
- Update Assetlinks File: Modify the assetlinks file here
<resources>
<string name="assetlinks">https://yourexample.com/.well-known/assetlinks.json</string>
</resources>
- Declare
PasswordlessClient
: Establish a singletonPasswordlessClient
object using thePasswordlessOptions
class. Utilize the values fromDemoPasswordlessOptions
:
@Provides
@Singleton
fun providePasswordlessClient(): PasswordlessClient {
val options = PasswordlessOptions(
DemoPasswordlessOptions.API_KEY,
DemoPasswordlessOptions.RP_ID,
DemoPasswordlessOptions.ORIGIN,
DemoPasswordlessOptions.API_URL
)
return PasswordlessClient(options)
}
- Set the Context of PasswordlessClient:Ensure the context is set to the current activity. Note that this must be an Activity context of the current activity.
/** Context needs to be set according to current activity
* If there are different activity handling register and signin,
* then call this on every activity
*/
_passwordless.setContext(this)
- Set Coroutine Scope: Set the coroutine scope, passing lifecycleScope of the current fragment.
RegisterFragment.kt , LoginFragment.kt
//Scope needs to be updated according to current class
_passwordless
.setCoroutineScope(lifecycleScope)
- Call Your Backend with User Details:Make a call to your backend with user details (e.g., username, alias) and retrieve the registration token.
- Call Passwordless Register Function
_passwordless.register(
token =responseToken,
nickname = nickname
) { success, exception, result ->
if (success) {
Toast.makeText(context, result.toString(), Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(
context,
"Exception: " + getPasskeyFailureMessage(exception as Exception),
Toast.LENGTH_SHORT
).show()
}
}
- Take Alias as Input: Gather the alias as input from the user.
- Call Passwordless Login: Initiate the login process with the alias and response callback.
_passwordless.login(alias) { success, exception, result ->
if (success) {
lifecycleScope.launch {
val clientDataResponse =
httpClient.login(UserLoginRequest(result?.token!!))
if (clientDataResponse.isSuccessful) {
val data = clientDataResponse.body()
showText(data.toString())
}
}
} else {
showException(exception)
}
}
To talk to the passwordless team, send us an email at support@passwordless.dev
You can find Bitwarden's other code repositories at https://github.com/bitwarden and more information on https://bitwarden.com/.