iiamhuman Developer Docsv1

PERSONHOOD CREDENTIAL INTEGRATION · STEP 10

Check the State of a Stored Personhood Credential

Check expiration, revocation, and status-list lifecycle before each comment.

Check the state of a stored personhood credential

Use PresentationService.checkCredentialState when your backend has stored a credential JWT and needs a lightweight state check before deciding what the user may do.

const state = await presentation.checkCredentialState({
  credentialJwt: storedCredential.credentialJwt,
});

checkCredentialState returns a state object instead of throwing for expected status-list or parsing problems:

type CredentialState = {
  expiration: boolean;
  revocation: 'not_revoked' | 'revoked' | 'unknown';
  statusListLifecycle: 'active' | 'retired' | 'sealing' | 'sealed' | 'unknown';
  checkedAt: string;
};

The method checks whether the credential is expired. When the credential has a usable status-list reference and the SDK has a status-list client, it also checks whether the credential is revoked. Expiration and revocation are independent: an expired credential may also be revoked.

checkCredentialState is not a replacement for full credential verification. Use verifyCredential or verifySubmission when issuing or first accepting a credential, and use the state check for subsequent stored-credential decisions.

Apply the state to app logic

Credential revocation is one-way. Once a credential is revoked, it cannot become not_revoked again.

How your application uses a credential state depends on its risk model. In the general case, a credential is valid only when it is neither expired nor revoked. The App Registry developer gate follows this model: a test wallet is accepted only while its gate credential remains valid.

Journal’s comment policy

Journal checks the stored credential immediately before every comment. A valid seven-day browser session and a saved public name are also required. Reading articles does not require verification.

Credential stateJournal behavior
Expired or revokedDelete the browser’s backend session and require verification again (verification_required, HTTP 401).
Revocation or status-list lifecycle is unknownTemporarily block the comment (unavailable, HTTP 503); do not treat an outage as credential rejection.
Not expired, not_revoked, known lifecycleContinue comment authorization and record checkedAt.

This condensed service excerpt assumes app-owned deleteSession and appError helpers, not SDK methods:

const state = await presentation.checkCredentialState({
  credentialJwt: session.credentialJwt,
});
if (state.expiration || state.revocation === 'revoked') {
  await deleteSession(sessionId);
  throw appError('verification_required', 401);
}
if (state.revocation !== 'not_revoked' || state.statusListLifecycle === 'unknown') {
  throw appError('unavailable', 503);
}
// Continue publication using the server's saved user ID and public name.
const credentialCheckedAt = state.checkedAt;

Journal’s comment endpoint is POST /api/articles/{articleId}/comments/. It validates the session and saved name, checks credential state, and applies comment validation and quotas before publishing. It takes the author identity, public name, and verification marker from server state rather than trusting the browser payload. Anonymous submissions cannot publish comments.

The SDK initialization uses statusListTtlMs: 0 so mutable status-list results are refreshed. A previously accepted presentation or an enabled comment form is not sufficient authorization. Journal’s policy also accepts a known sealed list with a not_revoked credential; requiring a still-revocable credential would be a different product policy.

Other products can define different authorization requirements, but must make those requirements explicit.

Status-List Lifecycle and Its Effect on the Credential

statusListLifecycle describes the lifecycle of the status list used by the credential:

When statusListLifecycle is sealed, the status list is final and no longer accepts revocations. If a credential is not_revoked when its list is sealed, its revocation state will remain not_revoked for that list. If your application requires a credential that can still be revoked, ask the user to submit a newly issued credential.

The SDK returns unknown when the credential cannot be parsed, has no usable status-list reference, no statusListUrl or status-list client is configured, or status-list retrieval or verification fails. If this continues to occur, check the statusListUrl in the PresentationService configuration. It may have been removed or changed accidentally.

Store and re-check state

Store the original JWT only on the backend. Re-run checkCredentialState immediately before a protected action, and record checkedAt in your authorization audit record. A previous not_revoked result is not permanent: issuing a replacement credential can revoke an older credential in the same context.

Credential state and same-context revocation

When the Credential Issue Server creates a new presentation-definition credential, it first checks whether an existing credential can be reused. If it creates a replacement, matching active credentials in the same context are revoked before the new credential is saved. The replacement then becomes the current credential for that context.

Your application supplies the following context attributes. Use a stable value for subject when later requests should replace the credential for the same account or resource.

AttributeWhat it represents
appIdThe registered application that requested the credential. Configure it in the App Registry.
subjectA stable, app-owned identifier for the account, resource, or authorization scope being verified. Journal uses <appId>:journal-comments, a stable authorization scope established before the holder is known.
pdRequestTypeThe registered request type or purpose, such as BasicAccessRequestVerifiableCredential. Configure it in the App Registry.
targetVcTypeThe credential type being issued. Select Human or Uniqueness for each credential request type in the App Registry.

The Credential Issue Server also uses server-derived tenant and credential-anchor values when it identifies matching credentials. Your application does not need to configure those values.