List, Rename, Delete Passkeys
Users will need a way to create, view, and manage their passkeys after successfully registering a passkey with LoginID. Passkey management is important for security and user control. By listing, renaming, or deleting passkeys, users can:
- Keep their accounts secure by removing unused or compromised passkeys.
- Maintain clarity by renaming passkeys for easy identification.
There are two ways to access these passkey management methods:
- Using LoginID token from authenticated session with LoginID SDK
- Using an authorization token with backend integration
Prerequisites
- Create an application to obtain a base URL. The SDK uses this base URL to interact with the LoginID authentication service.
- Create an API key with at least passkey:read, passkey:write, reg:write scopes. (Only for backend integration method.)
Setup SDK
- Javascript
- Kotlin
- Swift
npm i @loginid/websdk3
Import and initialize an instance:
import { LoginIDWebSDK } from "@loginid/websdk3";
const lid = new LoginIDWebSDK({
baseUrl: process.env.LOGINID_BASE_URL,
});
To obtain the SDK:
- Go to the LoginID Dashboard and select one of your apps.
- Open the Get Started section and choose the Android icon.
- Follow the instructions to download the binary
.aarfile and include it in your project.
Initialize a singleton configuration for the LoginID service in the application's onCreate method.
class MyApp : Application() {
override fun onCreate() {
super.onCreate()
// Initialize the singleton instance
val config = LoginIDServiceConfig("<BASE_URL>")
LoginID.configure(config).client()
}
}
To obtain the SDK:
- Go to the LoginID Dashboard and select one of your apps.
- Open the Get Started section and choose the iOS icon.
- Follow the instructions to download the binary
xcframeworkfile and include it in your project.
Configure the singleton instance of LoginIDSDK with a base URL during the app's launch in the App struct initializer.
import SwiftUI
import LoginID
@main
struct MyApp: App {
init() {
let baseUrl = "<BASE_URL>"
// Initialize the singleton instance
LoginIDSDK.shared.configure(baseUrl: baseUrl)
}
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
Examples
The code samples provided are for backend integrations using authorization tokens. Using the LoginID token's authenticated session does not require passing any tokens and can call the methods directly after a successful passkey authentication.
- List Passkeys
- Rename Passkey
- Delete Passkey
- Javascript
- Kotlin
- Swift
This method automatically uses the WebAuthn Signal API to keep a user’s passkeys synchronized between the authenticator and the LoginID server. This helps ensure that passkey information shown in the browser remains up-to-date, including helping remove revoked or stale passkeys from the user's authenticator.
import { useAuth } from "../../contexts/AuthContext";
import { LoginIDWebSDK } from "@loginid/websdk3";
const config = {
baseUrl: BASE_URL,
};
const lid = new LoginIDWebSDK(config);
const PasskeyManagement: React.FC = () => {
const { user } = useAuth();
const [passkeys, setPasskeys] = useState([]);
useEffect(() => {
const run = async () => {
try {
// Get authorization token from your backend
// only if your backend is responsible for verifying user access.
// Passkey list
const _passkeys = await lid.listPasskeys({
authzToken: token,
});
setPasskeys(_passkeys);
} catch (e) {
if (e instanceof Error) {
setError(e.message);
}
}
};
run();
}, []);
};
import loginid.LoginID
import services.modals.LoginIDOptions
import services.modals.PasskeyInfo
class MainActivity : AppCompatActivity() {
private lateinit var errorTextView: TextView
private lateinit var passkeys: List<PasskeyInfo>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
errorTextView = findViewById(R.id.errorTextView)
handleListPasskeys()
}
private fun handleListPasskeys() {
val username = AuthContext.getUser().username
errorTextView.text = ""
GlobalScope.launch(Dispatchers.Main) {
try {
// Get authorization token from your backend
// only if your backend is responsible for verifying user access.
// Passkeys
val options = LoginIDOptions(token = token)
val _passkeys = LoginID.client().listPasskeys(options)
// Update UI with passkeys
passkeys = _passkeys
} catch (e: Exception) {
when (e) {
is LoginIDError -> {
errorTextView.text = e.message ?: "A LoginID error occurred"
}
else -> {
errorTextView.text = e.message ?: "An error occurred"
}
}
Log.e("Error", "An error has occurred", e)
}
}
}
}
import SwiftUI
import LoginID
@main
struct ExampleApp: App {
@State private var errorMessage: String? = nil
@State private var passkeys: [LoginIDPasskey] = []
@State private var user: User
init() {
// Configure the LoginID SDK
LoginIDSDK.shared.configure(
baseUrl: "<BASE_URL>",
)
user = AuthContext.shared.user
}
private func listPasskeys() async {
do {
// Get authorization token from your backend
// only if your backend is responsible for verifying user access.
// User's Passkey list
let options = LoginIDOptions(authzToken: token)
let _passkeys = try await LoginIDSDK.shared.listPasskeys(options: options)
passkeys = _passkeys
} catch let error as LoginIDError {
errorMessage = error.message
} catch {
errorMessage = error.localizedDescription
}
}
}
- Javascript
- Kotlin
- Swift
import { useAuth } from "../../contexts/AuthContext";
import { LoginIDWebSDK } from "@loginid/websdk3";
const config = {
baseUrl: BASE_URL,
};
const lid = new LoginIDWebSDK(config);
const RenamePasskey: React.FC = () => {
const [error, setError] = useState<string>("");
const [passkeyName, setPasskeyName] = useState<string>("");
const [passkeyId, setPasskeyId] = useState<string>("");
const { user } = useAuth();
// Example of renaming a passkey
const handleRename = async () => {
try {
// Get authorization token from your backend
// only if your backend is responsible for verifying user access.
await lid.renamePasskey(passkeyId, passkeyName, { authzToken: token });
// Optionally refetch or update state data
// fetchPasskeys(token);
} catch (e) {
if (e instanceof Error) {
setError(e.message);
}
} finally {
setRenameId("");
setPasskeyName("");
}
};
};
import loginid.LoginID
import services.modals.LoginIDOptions
import services.BackendService
import services.AuthContext
class RenamePasskeyActivity : AppCompatActivity() {
private lateinit var errorTextView: TextView
private lateinit var passkeyId: String
private lateinit var passkeyName: String
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_rename_passkey)
errorTextView = findViewById(R.id.errorTextView)
// You would set passkeyId and passkeyName from user input or state
passkeyId = "passkeyIdToRename"
passkeyName = "NewPasskeyName"
handleRename(passkeyId)
}
private fun handleRename(passkeyId: String) {
GlobalScope.launch(Dispatchers.Main) {
try {
// Get authorization token from your backend
// only if your backend is responsible for verifying user access.
val options = LoginIDOptions(token = token)
LoginID.client().renamePasskey(passkeyId, passkeyName, options)
} catch (e: Exception) {
handleException(e)
} finally {
passkeyId = ""
passkeyName = ""
}
}
}
private fun handleException(e: Exception) {
when (e) {
is LoginIDError -> errorTextView.text = e.message ?: "A LoginID error occurred"
else -> errorTextView.text = e.message ?: "An error occurred"
}
Log.e("Error", "An error has occurred", e)
}
}
import SwiftUI
import LoginID
@main
struct ExampleApp: App {
@State private var errorMessage: String? = nil
@State private var passkeyName: String = ""
@State private var passkeyId: String = ""
@State private var user: User
init() {
// Configure the LoginID SDK
LoginIDSDK.shared.configure(
baseUrl: "<BASE_URL>",
)
user = AuthContext.shared.user
}
private func renamePasskey() async {
do {
// Get authorization token from your backend
// only if your backend is responsible for verifying user access.
let options = LoginIDOptions(authzToken: token)
_ = try await LoginIDSDK.shared.renamePasskey(
id: passkeyId,
name: passkeyName,
options: options
)
// Optionally refetch or update state data
} catch let error as LoginIDError {
errorMessage = error.message
} catch {
errorMessage = error.localizedDescription
}
}
}
- Javascript
- Kotlin
- Swift
This method automatically uses the WebAuthn Signal API to keep a user’s passkeys synchronized between the authenticator and the LoginID server. This helps ensure that passkey information shown in the browser remains up-to-date, including helping remove revoked or stale passkeys from the user's authenticator.
import { LoginIDWebSDK } from "@loginid/websdk3";
const config = {
baseUrl: BASE_URL,
};
const lid = new LoginIDWebSDK(config);
const DeletePasskey: React.FC = () => {
const [error, setError] = useState<string>("");
const [passkeyId, setPasskeyId] = useState<string>("");
// Example of deleting a passkey
const handleDelete = async () => {
try {
// Get authorization token from your backend
//only if your backend is responsible for verifying user access.
await lid.deletePasskey(passkeyId, { authzToken: token });
// Optionally refetch or update state data
// fetchPasskeys(token);
} catch (e) {
if (e instanceof Error) {
setError(e.message);
}
} finally {
setPasskeyId("");
}
};
};
import loginid.LoginID
import services.modals.LoginIDOptions
import services.BackendService
class DeletePasskeyActivity : AppCompatActivity() {
private lateinit var errorTextView: TextView
private lateinit var passkeyId: String
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_delete_passkey)
errorTextView = findViewById(R.id.errorTextView)
// You would set passkeyId from user input or state
passkeyId = "passkeyIdToDelete"
handleDelete(passkeyId)
}
private fun handleDelete(passkeyId: String) {
GlobalScope.launch(Dispatchers.Main) {
try {
// Get authorization token from your backend
// only if your backend is responsible for verifying user access.
val options = LoginIDOptions(token = token)
LoginID.client().deletePasskey(passkeyId, options)
// Optionally update UI
} catch (e: Exception) {
handleException(e)
}
}
}
private fun handleException(e: Exception) {
when (e) {
is LoginIDError -> errorTextView.text = e.message ?: "A LoginID error occurred"
else -> errorTextView.text = e.message ?: "An error occurred"
}
Log.e("Error", "An error has occurred", e)
}
}
import SwiftUI
import LoginID
@main
struct ExampleApp: App {
@State private var errorMessage: String? = nil
@State private var passkeyId: String = ""
@State private var user: User
init() {
// Configure the LoginID SDK
LoginIDSDK.shared.configure(
baseUrl: "<BASE_URL>",
)
user = AuthContext.shared.user
}
private func deletePasskey() async {
do {
// Get authorization token from your backend
// only if your backend is responsible for verifying user access.
let options = LoginIDOptions(authzToken: token)
_ = try await LoginIDSDK.shared.deletePasskey(
id: passkeyId,
options: options
)
// Optionally refetch or update state data
} catch let error as LoginIDError {
errorMessage = error.message
} catch {
errorMessage = error.localizedDescription
}
}
}