How to implement mDocs revocation status checks in your holding application
Overview
This guide demonstrates how to implement revocation status checks for mDocs in your wallet applications. By implementing status checks, your wallet can verify whether credentials have been revoked, suspended, or remain valid before displaying or presenting them.
MATTR's implementation of mDocs revocation is based on the IETF Token Status List draft. A revocable mDoc includes a reference to a status list which is managed by the issuer. The list contains the revocation status of multiple credentials, and each credential references the index of its status within a specific status list.
Status lists are automatically created and managed by the issuer's MATTR VII tenant when issuing revocable mDocs. They are publicly available and can be consumed by holder applications to check the status of claimed mDocs.
For detailed information about how mDocs revocation works, including status list structure, tokens, and signing, see the Revocation documentation.
Prerequisites
This guide builds on knowledge from the Credential Claiming tutorial. It is recommended to complete that tutorial first, then return here to learn how to implement status checks.
Understanding revocation status
Status values
Revocable mDocs can have one of the following status values:
- Valid: The mDoc is valid and can be presented.
- Invalid: The mDoc is permanently revoked.
- Suspended: The mDoc is temporarily revoked.
- Unknown: The status cannot be determined, typically because the status list has expired or is unavailable (e.g. holder is offline).
How status information is stored
When a revocable mDoc is issued, it includes a status object in its MSO payload that references a status list:
// Rest of mDoc payload
"status": {
"status_list": {
"uri": "https://learn.vii.au01.mattr.global/v2/credentials/mobile/status-lists/f331c9be-f526-4577-bbac-ae93d6228f7a/token",
"idx": 0
}
}status_list: References the status list that holds the status information for this mDoc.uri: The publicly available endpoint where the status list token can be retrieved.idx: The index of this mDoc's status within the referenced status list.
When a verifier or holder retrieves a status list, the issuer cannot tell what specific mDoc they are checking the status for. This preserves holder privacy - the issuer does not know how often or to whom an mDoc is being presented.
Status list caching and updates
When retrieving a status list, the response is a signed status list token (a CBOR Web Token) that includes:
iat: Timestamp when the status list token was signed.exp: Expiry timestamp.ttl: Recommended duration in seconds before fetching a new token.status_list: The compressed status list containing the status of all mDocs included in this list.
Retrieving a status list for every credential operation would create performance issues and make offline presentation impossible. To address this, MATTR uses a caching mechanism based on the ttl and exp values:
- After retrieving a status list, the SDK will not fetch it again until the
ttlhas passed, as there are unlikely to be any changes. This optimizes performance and reduces unnecessary network requests. - If the SDK fails to retrieve an updated status list after the TTL (for example, because the device is offline), it can continue using the cached status until the status list token expiry date (
exp). - If the expiry date passes without a successful update, the credential status can no longer be trusted and it is changed to
Unknown. It is then up to the application to decide how to handle credentials withUnknownstatus.
When TTL and EXP changes take effect
If the issuer updates the status list TTL and/or EXP settings, the new values apply to the entire status list the next time it is generated and signed.
When the holder SDK next retrieves that status list, it will receive the updated token with the new TTL and EXP values. Until then, the SDK continues to use the previously cached status list with its original TTL and EXP values.
Example scenario:
- Credential is issued with a 24-hour TTL (default).
- The TTL configuration is changed to 2 hours by the issuer.
- The Holder's SDK will continue to use the cached status list with the original 24-hour TTL.
- The SDK will only receive the updated 2-hour TTL after it fetches a newly signed status list.
Implementing status checks
The Holder SDK always checks the revocation status for credentials that support revocation. This cannot be disabled.
When calling the getCredential method, you can control whether the SDK should attempt to retrieve an updated status list online using the skipStatusCheck parameter:
// Retrieve updated status list online (default behavior)
let credential = try await mobileCredentialHolder.getCredential(
credentialId: credentialId,
skipStatusCheck: false
)
// Use valid cached status list only (skip online update)
let credential = try await mobileCredentialHolder.getCredential(
credentialId: credentialId,
skipStatusCheck: true
)// Retrieve updated status list online (default behavior)
val credential = mobileCredentialHolder.getCredential(
credentialId = credentialId,
skipStatusCheck = false
)
// Use valid cached status list only (skip online update)
val credential = mobileCredentialHolder.getCredential(
credentialId = credentialId,
skipStatusCheck = true
)// Retrieve updated status list online (default behavior)
const credentialResult = await mobileCredentialHolder.getCredential(
credentialId,
{ skipStatusCheck: false }
)
if (credentialResult.isErr()) {
const { error } = credentialResult
// handle error scenarios
return
}
const credential = credentialResult.value
// Use valid cached status list only (skip online update)
const credentialResult = await mobileCredentialHolder.getCredential(
credentialId,
{ skipStatusCheck: true }
)
if (credentialResult.isErr()) {
const { error } = credentialResult
// handle error scenarios
return
}
const credential = credentialResult.valueImportant: Setting skipStatusCheck: true does not disable status checking. The SDK will still check the credential's status using the cached status list (as long as it is valid). This parameter only controls whether the SDK attempts to fetch an updated status list online.
Status check flow
- When
getCredentialis called, the SDK first checks theskipStatusCheckparameter. - If
skipStatusCheckistrue, the SDK uses the cached status list (skipping online update). - If
skipStatusCheckisfalse(the default), the SDK checks if the cached status list has passed its TTL. - If the TTL has passed, the SDK attempts to fetch an updated status list:
- If the fetch succeeds, the status is updated based on the new status list.
- If the fetch fails (e.g., device is offline), the SDK checks if the cached status list has expired.
- Whether using a cached status list or after a failed fetch, the SDK checks if the cached status list has expired:
- If not expired, the status is returned from the cache.
- If expired, the status becomes
Unknown.
Checking credential status
After retrieving a credential, you can check its revocation status to determine whether it should be displayed or presented based on verification failure types:
let credential = try await mobileCredentialHolder.getCredential(
credentialId: credentialId
)
if let verificationResult = credential.verificationResult, verificationResult.verified {
print("Credential is valid and can be presented")
// Display credential and allow presentation
} else {
switch credential.verificationResult?.failureType {
case .statusRevoked:
print("Credential has been permanently revoked")
// Display warning, prevent presentation
case .statusSuspended:
print("Credential has been temporarily suspended")
// Display warning, prevent presentation
case .statusUnknown:
print("Credential status cannot be determined")
// Display warning, handle according to your security requirements
default:
// Handle other failure types
break
}
}val verificationResult = credential.verificationResult
if (verificationResult?.verified == true) {
Log.d("Tag", "Credential is valid.")
} else {
when (verificationResult?.failureType) {
MobileCredentialVerificationFailureType.StatusRevoked -> {
Log.d("Tag", "Credential has been revoked.")
// Display warning, prevent presentation
}
MobileCredentialVerificationFailureType.StatusSuspended -> {
Log.d("Tag", "Credential has been suspended.")
// Display warning, prevent presentation
}
MobileCredentialVerificationFailureType.StatusUnknown -> {
Log.d("Tag", "Credential status is unknown.")
// Display warning, handle according to your security requirements
}
else -> {
// Handle other failure types
}
}
}const credentialResult = await mobileCredentialHolder.getCredential(
credentialId
)
if (credentialResult.isErr()) {
const { error } = credentialResult
// handle error scenarios
return
}
const credential = credentialResult.value
const verificationResult = credential.verificationResult
if (verificationResult?.verified === true) {
console.log("Credential is valid and can be presented")
// Display credential and allow presentation
} else {
switch (verificationResult?.failureType) {
case MobileCredentialVerificationFailureType.StatusRevoked:
console.log("Credential has been permanently revoked")
// Display warning, prevent presentation
break
case MobileCredentialVerificationFailureType.StatusSuspended:
console.log("Credential has been temporarily suspended")
// Display warning, prevent presentation
break
case MobileCredentialVerificationFailureType.StatusUnknown:
console.log("Credential status cannot be determined")
// Display warning, handle according to your security requirements
break
default:
// Handle other failure types
break
}
}Handling offline scenarios
- Cache reliance: When offline, the SDK relies on cached status lists.
- Cached status after TTL: If the device is offline and
ttlhas passed (but expiry hasn't), the cached status will be used. - Unknown status after expiry: If the device is offline and
exphas passed, the status will always be returned asUnknown.
- Cached status after TTL: If the device is offline and
- Unknown status handling: Define your application's policy for handling credentials with
Unknownstatus. Options include:- Preventing presentation.
- Allowing presentation with a warning.
- Allowing presentation only for recently checked credentials.
- User communication: Clearly inform users when status checks fail and what it means for their credentials.
How would you rate this page?