Add endpoint to list (about-to-)expired wallet credentials#4224
Add endpoint to list (about-to-)expired wallet credentials#4224reinkrul wants to merge 3 commits into
Conversation
Adds GET /internal/vcr/v2/holder/expiring which aggregates credentials across all wallets on the node and returns a JSON object grouping expiring credentials by subject ID. Operators can poll a single URL to monitor and refresh credentials before they expire (closes #4217). The response is a focused monitoring DTO (id, holder, issuer, type, expirationDate) rather than the raw VC, so the shape stays uniform regardless of whether the underlying credential is JSON-LD or JWT-encoded. Assisted by AI
1 new issue
|
|
TODO / open question — filtering by credential type: Some credential types are expected to expire and shouldn't trigger refresh alerts, but operators still want to keep them in the wallet for audit / paper-trail purposes (e.g. We may want to add type-based filtering to this endpoint, e.g. Assisted by AI |
|
Coverage Impact ⬆️ Merging this pull request will increase total coverage on Modified Files with Diff Coverage (1)
🤖 Increase coverage with AI coding...🚦 See full report on Qlty Cloud » 🛟 Help
|
stevenvegt
left a comment
There was a problem hiding this comment.
Didn't look into to the implementation yet, first lets discuss the API and how people might want to use this feature.
| in: query | ||
| description: | | ||
| Time window relative to now in which a credential's `expirationDate` falls for it to be considered | ||
| expiring. Accepts a Go duration string (e.g. `24h`, `720h`, `30m`). Must be non-negative. |
There was a problem hiding this comment.
Most devs probably are not familiar with go duration strings, so better to take the relevant parts from the spec:
Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
| summary: List credentials across all wallets on this node that are expired or about to expire. | ||
| description: | | ||
| Returns all credentials held by any subject on this node whose `expirationDate` is at or before | ||
| `now + within`. This includes credentials that are already expired. Credentials without an |
There was a problem hiding this comment.
Since eOverdracht (and others) uses NutsAuthVCs, this list will quickly become longer and longer. I think you might want to add some additional filtering here to include or exclude certain types. Also a param to ignore already expired VCs might be a good idea since you probably want to signal for upcoming expiration?
There was a problem hiding this comment.
Since eOverdracht (and others) uses NutsAuthVCs, this list will quickly become longer and longer. I think you might want to add some additional filtering here to include or exclude certain types.
That's right, we need something like that. I'm leaning towards an exclude model, because an include model quickly becomes out of date if a new credential is introduced. So you'd have something like:
/expiring?within=30d&exclude=NutsAuthorizationCredential
The downside is that you have to explicitly (in many cases, always) exclude certain types, but at least it'll be visible for operators if the configuration is off.
Also a param to ignore already expired VCs might be a good idea since you probably want to signal for upcoming expiration?
Maybe... You don't want to keep being bothered if you don't clean up expired VCs, on the other hand you could've missed/ignored (and forgot) about renewing it. We could make it more flexible (at the cost of a more complex API), by adding a parameter which specifies for how long we'll keep returning it, after it expired. E.g., return VCs that expired less than a week ago.
Now I think of it, you also don't want to send e-mails (if your monitoring system does that) every day for the same VC, 30 days straight (if you're checking for VCs that expire within 30 days, every day). Not sure if we should solve that here, but it complicates things.
Proposal: keep this feature simple at first;
- Add
excludeTypesparameter - Let the monitoring system deal with not sending too many notifications for the same VC every hour/day (we're not building a monitoring system here, just feeding it with data)
There was a problem hiding this comment.
Thinking about it, lets include expired credentials, since those should be visible so vendors can clean them up.
|
We also need to add this to the official monitoring documentation. |
|
We'll be adding |
Allows operators to suppress credentials that are expected to expire and are kept for audit purposes (e.g. NutsAuthorizationCredential). A credential is excluded if any of its types matches any supplied value. Assisted by AI
TODO: push filtering down to the SQL storeNeither For larger wallets this means a full load + JSON parse of every credential of every subject on the node on every call. Both filters should be propagated all the way down to the SQL store before this merges. What that needs:
Assisted by AI |
|
Given the previous comment, I think this PR needs some more work? I've converted it to state "draft". |

Closes #4217.
Summary
GET /internal/vcr/v2/holder/expiring?within=<duration>— aggregates across all wallets on the node, returns a JSON object keyed by subject ID with the list of expired or about-to-expire credentials per subject.withinis720h(30 days);0sreturns only already-expired credentials. Negative or unparseable values give400.id,holder,issuer,type,expirationDate) rather than the raw VC, so the shape stays uniform regardless of whether the underlying credential is JSON-LD or JWT-encoded. Operators needing the full VC can fetch it by id via existing wallet endpoints.{ "90BC1AE9-752B-432F-ADC3-DD9F9C61843C": [ { "id": "did:web:issuer.example.com#abc", "holder": "did:web:example.com:iam:123", "issuer": "did:web:issuer.example.com", "type": ["NutsOrganizationCredential"], "expirationDate": "2026-05-15T12:00:00Z" } ] }Subjects with no expiring credentials are omitted from the response.
Test plan
within,within=0(only already-expired), no subjects (empty map), invalidwithin, negativewithin, subject manager errorgo build ./...go test ./vcr/api/vcr/v2/...Assisted by AI