Credential management
Apart from registering and authenticating with a credential, your users require the ability to self-manage their own credentials. To exemplify how the Hanko API enables credential management, assume a simple user profile section in your relying party frontend as shown in the following mockup:

In this profile sections users can:
- view a list of their registered credentials along with metadata for individual credentials (e.g. a custom name, authenticator type, date of registration etc.) - see Retrieve credentials
- deregister a credential (for example in case of device loss) - see Deregister credentials
- rename registered credentials - see Rename credentials
Retrieve credentials
The API allows you to retrieve both a list of credentials (optionally paginated and filtered by a user ID) and single credentials by credential ID.
Get a list of registered credentials
To retrieve a list of registered credentials, issue a GET
request to the /v1/webauthn/credentials
endpoint
(see also the
API reference). Credentials can be filtered using a user_id
as a query parameter.
- cURL
- Go
- Java
curl -X GET 'API_BASE_URL/v1/webauthn/credentials?user_id=USER_ID' \
-H 'Authorization: secret API_SECRET'
This is example uses the Hanko Go SDK.
import (
"github.com/teamhanko/hanko-sdk-golang/webauthn"
"encoding/json"
"net/http"
)
// For demonstration purposes only. Do not hardcode sensitive information!
var apiUrl = ... // Your relying party's API base URL
var apiSecret = ... // Your relying party's API secret
var apiKeyId = ... // Your relying party's API key ID
// Instantiate an API client
var apiClient = webauthn.NewClient(apiUrl, apiSecret).WithHmac(apiKeyId)
func GetCredentialListHandler(w http.ResponseWriter, r *http.Request) {
// Mock function to look up currently logged in user in your existing user store.
// The user model must include an ID.
user := userStore.getCurrentUser()
// Construct a query for filtering credentials
query := webauthn.NewCredentialQuery().WithUserId(user.ID)
// Retrieve credentials
response, apiErr := apiClient.ListCredentials(&query)
if apiErr != nil {
//handle error
}
// Return API credential list response
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(response)
}
This is example uses the Hanko Java SDK.
@RestController
public class HankoController {
private final HankoWebAuthnClient apiClient;
private final UserStore userStore;
public HankoController() {
// For demonstration purposes only. Do not hardcode sensitive information!
String apiUrl = "..."; // Your relying party's API base URL
String apiSecret = "..."; // Your relying party's API secret
String apiKeyId = "..."; // Your relying party's API key ID
HankoClientConfig config = new HankoClientConfig(apiUrl, apiSecret, apiKeyId);
// Instantiate an API client
this.apiClient = new HankoWebAuthnClient(config);
this.userStore = new UserStore();
}
@GetMapping("/webauthn/credentials")
public List<WebAuthnCredential> listCredentials() {
// Mock User type and function to look up user in your existing user store. The user model
// must include a user ID in order to filter credentials by user ID (see below)
User user = userStore.getCurrentUser();
// Construct a query for filtering credentials
CredentialQuery query = new CredentialQuery()
query.setUserid(user.getId());
List<WebAuthnCredential> credentials = new ArrayList<>();
// Handle error explicitly or use, for example, a global exception
// handler using Spring's ControllerAdvice
try {
credentials = apiClient.listCredentials(query);
} catch (final HankoException ex) {
// Handle error
}
return credentials;
}
}
The API returns a list of credential objects that contain metadata about the registered credential (see the API reference for a detailed description of its properties).
Example JSON response: credential list
[
{
"id": "dYbKuEGZpnamd5DU8m3b8l4oh6OBMqnesdvHS9mlsNA",
"user": {
"id": "e3be22a7-13cf-4235-a09c-380dfd44ac04"
},
"createdAt": "2021-04-29T08:45:11.858984Z",
"lastUsed": "2021-04-29T08:45:11.85898Z",
"name": "My Yubikey",
"userVerification": true,
"isResidentKey": false,
"authenticator": {
"aaguid": "f244b67e-5364-4fd5-9f90-c396227317db"
}
},
{
"id": "U0-8xnjB7qKNU47llamQqyE6N-OheMID-XeyLT2LaVU",
"user": {
"id": "e3be22a7-13cf-4235-a09c-380dfd44ac04"
},
"createdAt": "2021-04-29T08:45:12.033146Z",
"lastUsed": "2021-04-29T08:45:12.033144Z",
"name": "My MacBook",
"userVerification": true,
"isResidentKey": false,
"authenticator": {
"aaguid": "a7d6d93a-8a0d-11e8-9a94-a6cf71072f73"
}
}
]
Get a single credential
To retrieve a single credential, issue a GET
request to the /v1/webauthn/credentials/:credential_id
endpoint
(see also the
API reference).
- cURL
- Go
- Java
curl -X GET 'API_BASE_URL/v1/webauthn/credentials/CREDENTIAL_ID' \
-H 'Authorization: secret API_SECRET'
This is example uses the Hanko Go SDK.
import (
"github.com/teamhanko/hanko-sdk-golang/webauthn"
"encoding/json"
"net/http"
)
// For demonstration purposes only. Do not hardcode sensitive information!
var apiUrl = ... // Your relying party's API base URL
var apiSecret = ... // Your relying party's API secret
var apiKeyId = ... // Your relying party's API key ID
// Instantiate an API client
var apiClient = webauthn.NewClient(apiUrl, apiSecret).WithHmac(apiKeyId)
func GetCredentialHandler(w http.ResponseWriter, r *http.Request) {
// Extract `credential_id` from URL query
credentialId := r.URL.Query().Get("credential_id")
// Retrieve credential
response, apiErr := apiClient.GetCredential(credential_id)
if apiErr != nil {
//handle error
}
// Return API credential response
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(response)
}
This is example uses the Hanko Java SDK.
@RestController
public class HankoController {
private final HankoWebAuthnClient apiClient;
public HankoController() {
// For demonstration purposes only. Do not hardcode sensitive information!
String apiUrl = "..."; // Your relying party's API base URL
String apiSecret = "..."; // Your relying party's API secret
String apiKeyId = "..."; // Your relying party's API key ID
HankoClientConfig config = new HankoClientConfig(apiUrl, apiSecret, apiKeyId);
// Instantiate an API client
this.apiClient = new HankoWebAuthnClient(config);
}
@GetMapping("/webauthn/credentials/{credential_id}")
public WebAuthnCredential getCredential(
// Extract ID for the credential to get (in this example provided as a path parameter)
@PathVariable("credential_id") final String credentialId
) {
WebAuthnCredential credential;
// Handle error explicitly or use, for example, a global exception
// handler using Spring's ControllerAdvice
try {
credentials = apiClient.getCredential(credentialId);
} catch (final HankoException ex) {
// Handle error
}
return credential;
}
}
The API returns a single credential object that contains metadata about the registered credential (see the API reference for a detailed description of its properties).
Example JSON response: credential
{
"id": "dYbKuEGZpnamd5DU8m3b8l4oh6OBMqnesdvHS9mlsNA",
"user": {
"id": "e3be22a7-13cf-4235-a09c-380dfd44ac04"
},
"createdAt": "2021-04-29T08:45:11.858984Z",
"lastUsed": "2021-04-29T08:45:11.85898Z",
"name": "My Yubikey",
"userVerification": true,
"isResidentKey": false,
"authenticator": {
"aaguid": "f244b67e-5364-4fd5-9f90-c396227317db"
}
}
Deregister credentials
There are some scenarios in which the deregistration of a credential with a service might be desired, for example in the case of account removal or authenticator device loss.
To deregister an existing credentials, issue a DELETE
request to the /v1/webauthn/credentials/:credential_id
endpoint (see also the
API reference).
- cURL
- Go
- Java
curl -X DELETE 'API_BASE_URL/v1/webauthn/credentials/CREDENTIAL_ID \
-H 'Authorization: secret API_SECRET'
This is example uses the Hanko Go SDK.
import (
"github.com/teamhanko/hanko-sdk-golang/webauthn"
"encoding/json"
"net/http"
)
// For demonstration purposes only. Do not hardcode sensitive information!
var apiUrl = ... // Your relying party's API base URL
var apiSecret = ... // Your relying party's API secret
var apiKeyId = ... // Your relying party's API key ID
// Instantiate an API client
var apiClient = webauthn.NewClient(apiUrl, apiSecret).WithHmac(apiKeyId)
// Mock user store to lookup user data
var userStore = ...
func DeleteCredentialHandler(w http.ResponseWriter, r *http.Request) {
// Extract `credential_id` from URL query
credentialId := r.URL.Query().Get("credential_id")
// Mock function to look up currently logged in user in your existing user store.
// The user model must include an ID.
user := userStore.getCurrentUser()
// Check if credential ID to delete is actually a credential associated with the current user.
// I.e.: Retrieve the credential for the given `credential_id`, then check if
// the returned credential has an associated user ID equal to the current user
credential, apiErr := apiClient.GetCredential(credential_id)
if apiErr != nil {
//handle error
}
if (credential.User.ID != user.ID) {
// handle unauthorized action
}
// Deregister/delete credential
response, apiErr := apiClient.DeleteCredential(credential_id)
if apiErr != nil {
//handle error
}
// Return API credential deregistration/deletion response
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusNoContent)
json.NewEncoder(w).Encode(response)
}
This is example uses the Hanko Java SDK.
@RestController
public class HankoController {
private final HankoWebAuthnClient apiClient;
private final UserStore userStore;
public HankoController() {
// For demonstration purposes only. Do not hardcode sensitive information!
String apiUrl = "..."; // Your relying party's API base URL
String apiSecret = "..."; // Your relying party's API secret
String apiKeyId = "..."; // Your relying party's API key ID
HankoClientConfig config = new HankoClientConfig(apiUrl, apiSecret, apiKeyId);
// Instantiate an API client
this.apiClient = new HankoWebAuthnClient(config);
this.userStore = new UserStore();
}
@DeleteMapping("/webauthn/credentials/{credential_id}")
public Boolean deleteCredential(
// Extract ID for the credential to delete (in this example provided as a path parameter)
@PathVariable("credential_id") final String credentialId
) {
// Mock User type and function to look up user in your existing user store. The user model
// must include a user ID in order to filter credentials by user ID (see below)
User user = userStore.getCurrentUser();
// Check if credential ID to delete is actually a credential associated with the current user.
// I.e.: Retrieve the credential for the given `credential_id`, then check if
// the returned credential has an associated user ID equal to the current user
WebAuthnCredential credential = null;
try {
WebAuthnCredential = apiClient.getCredential(credentialId);
} catch (HankoException ex) {
// Handle error
}
if (credential != null && !credential.getUser().getId().equals(user.getId())) {
// handle unauthorized action
}
boolean deleted = false;
try {
// Delete credential
deleted = apiClient.deleteCredential(credentialId);
} catch (HankoException ex) {
// Handle error
}
return deleted;
}
}
On successful deregistration, the API returns a response with a simple HTTP 204 - No Content
status.
note
Credential deregistration removes only public credential material on the server side. This means
that private credential material will remain on an authenticator device unless a user explicitly deletes it from the
authenticator. How credential material can be removed depends on the model of the authenticator: some manufacturers
like Yubico offer dedicated software that provides a device
settings UI, allowing for resetting the authenticator device. Google Chrome's security settings also allow you interact
with security keys (e.g. via entering chrome://settings/securityKeys
in the URL bar) and resetting the data on it.
Rename credentials
Credentials have a name
property that allows for easily distinguishing registered credentials (e.g. "My MacBook" or
"My Yubikey"). On registration this name
has a default initial value but credentials can be renamed by issuing
a PUT
request to the /v1/webauthn/credentials/:credential_id
endpoint (see also the
API reference). Provide the desired new credential name
in the request body.
- cURL
- Go
- Java
curl -X PUT 'API_BASE_URL/v1/webauthn/credentials/CREDENTIAL_ID' \
-H 'Content-Type: application/json'
-H 'Authorization: secret API_SECRET'
-d '{ "name": "My Yubikey" }'
This is example uses the Hanko Go SDK.
import (
"github.com/teamhanko/hanko-sdk-golang/webauthn"
"encoding/json"
"net/http"
)
// For demonstration purposes only. Do not hardcode sensitive information!
var apiUrl = ... // Your relying party's API base URL
var apiSecret = ... // Your relying party's API secret
var apiKeyId = ... // Your relying party's API key ID
// Instantiate an API client
var apiClient = webauthn.NewClient(apiUrl, apiSecret).WithHmac(apiKeyId)
func UpdateCredentialHandler(w http.ResponseWriter, r *http.Request) {
// Extract `credential_id` from URL query
credentialId := r.URL.Query().Get("credential_id")
// Mock function to look up currently logged in user in your existing user store.
// The user model must include an ID.
user := userStore.getCurrentUser()
// Check if credential ID to update is actually a credential associated with the current user.
// I.e.: Retrieve the credential for the given `credential_id`, then check if
// the returned credential has an associated user ID equal to the current user.
credential := apiClient.GetCredential(credential_id)
if apiErr != nil {
//handle error
}
if (credential.User.ID != user.ID) {
// handle unauthorized action
}
// Construct credential update request
updateRequest := webauthn.NewCredentialUpdateRequest().WithName("My Yubikey") // example new name
// Retrieve credentials
response, apiErr := apiClient.UpdateCredential(credential_id, &updateRequest)
if apiErr != nil {
//handle error
}
// Return API credential update response
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(response)
}
This is example uses the Hanko Java SDK.
@RestController
public class HankoController {
private final HankoWebAuthnClient apiClient;
private final UserStore userStore;
public HankoController() {
// For demonstration purposes only. Do not hardcode sensitive information!
String apiUrl = "..."; // Your relying party's API base URL
String apiSecret = "..."; // Your relying party's API secret
String apiKeyId = "..."; // Your relying party's API key ID
HankoClientConfig config = new HankoClientConfig(apiUrl, apiSecret, apiKeyId);
// Instantiate an API client
this.apiClient = new HankoWebAuthnClient(config);
this.userStore = new UserStore();
}
@PutMapping("/webauthn/credentials/{credential_id}")
public WebAuthnCredential updateCredential(
// Extract ID for the credential to update (in this example provided as a path parameter)
@PathVariable("credential_id") final String credentialId,
@RequestBody final Map<String, String> updatePayload
) {
// Mock User type and function to look up user in your existing user store. The user model
// must include a user ID in order to filter credentials by user ID (see below)
User user = userStore.getCurrentUser();
// Check if credential ID to delete is actually a credential associated with the current user.
// I.e.: Retrieve the credential for the given `credential_id`, then check if
// the returned credential has an associated user ID equal to the current user
WebAuthnCredential credential = null;
try {
credential = apiClient.getCredential(credentialId);
} catch (HankoException ex) {
// Handle error
}
if (credential != null && credential.getUser().getId() != user.getId()) {
// handle unauthorized action
}
WebAuthnCredential updatedCredential = null;
try {
// Extract new credential name from request
String newCredentialName = updatePayload.get("name");
// Construct credential update request
CredentialUpdateRequest updateRequest = new CredentialUpdateRequest(name);
// Update credential
updatedCredential = apiClient.updateCredential(credentialId, updateRequest);
} catch (HankoException ex) {
// Handle error
}
return updatedCredential;
}
}
On successful update the API returns the updated credential.
Try the example application
Are you looking for a more immersive learning experience? See how it all works by trying out our example application.