Home Terms & Conditions Privacy Policy Documentation Contact About
  1. 0. Choose a use caseHow the issuance starts
  2. 1. Discover the issuerIssuer + authorization server metadata
  3. 2. Authorize & approveAuthenticate, review tokens, request it
  4. 3. The credentialThe issued SD-JWT VC and its disclosures
  5. 4. Refresh itRefresh Token, then the issuer again

← Back to step 3 (the credential)

SD-JWT VC Issuance — Step 4 of 4: Refresh the Credential

A credential does not stay current: its claim values go out of date and its validity window runs out. OID4VCI section 14.5, Refreshing Issued Credentials, names two ways to deal with that. The first needs no End-User interaction at all: the wallet obtains a fresh Access Token with its Refresh Token (RFC 6749 section 6) and asks the Credential Endpoint again. The second is to start the issuance over from step 1, which does involve the End-User — and is the only route left once the Refresh Token has expired too. This page is the first: the two calls it is made of, and then what the issuer actually changed.

Tip: click a pane's title to collapse/expand it.

The Credential You Hold

What there is to refresh, and why anyone would: the validity window is the usual reason, but not the only one — a claim the issuer knows has changed is another, and the wallet cannot tell from the credential which of those applies.

 
What kind of credential this is. A refresh asks for the same one — this is not a way to obtain a different credential.
nbf and exp from the issuer-signed JWT, next to now. An expired credential is the plainest case for refreshing; a valid one may still be refreshed (section 14.3).
The holder key this credential is bound to. Refreshing it in place needs the private half of that key, because the Credential Request has to carry a proof of possession for it.
Which credential configuration was asked for, from which issuer, when — and how many times it has been refreshed already.
Credential History

Every attempt made on this page, newest first — not only the ones that worked. Each access token refresh, each Credential Request, each poll of the Deferred Credential Endpoint, and what you then decided about what came back, is one row with what the issuer actually said. (It is this workflow's counterpart of the Token History pane on debugger2.html, which does the same for token sets — and it records more, because “I tried to refresh and it did not work” is the case a debugger is most needed for.)

The # column numbers the attempts in the order they were made, so every row has one; Gen is the generation number, which only the rows the wallet holds have — those are the credentials there is something to go back to. The newest 100 attempts are kept, in a scrolling list of fixed height so the panes below it do not move as the log grows.

Attempt is what was tried; Outcome is what came of it: success (an access token came back), FAILED (with the refusal in the next column), deferred (the issuer needs more time), returned — not kept yet (a credential is in hand awaiting your decision), kept, or discarded. Only the kept rows are generations the wallet holds — they carry a Gen number and can be activated; everything else is there as a record and says log only.

Activate makes a generation the credential in hand again, together with the holder key it is bound to (without that key the credential could not be presented at all), so you can move backwards as well as forwards: a refresh that turned out worse than what it replaced is a real outcome, and going back to the credential that was working is what a wallet does about it.

 
 
Refresh the Access Token

Plain OAuth 2.0: grant_type=refresh_token at the authorization server's Token Endpoint. Nothing about this call is OID4VCI-specific — which is section 14.5's point. What is specific is what the token is for: it is about to be spent on the Credential Endpoint again. If the access token in hand has not expired, this step can be skipped entirely; section 14.3 permits asking the Credential Endpoint again with the token you already have.

token_endpoint from the authorization server metadata retrieved in step 1 — the same endpoint the OIDC leg used to exchange the authorization code.
The access token the workflow currently holds, with its own expiry — the thing this call is about to replace.
Issued alongside the access token, and the whole reason a credential can be refreshed without troubling the End-User. An authorization server is not obliged to issue one.

Inspect

OPTIONAL, and narrowing only: RFC 6749 section 6 allows a scope no broader than the one originally granted. Leave it empty to keep the scope the credential was authorized with.
The Token Request exactly as it will be sent: method, URL, and the form-encoded body. A confidential client's client_secret appears here too, because it is part of the call.
Exchange the refresh token for a fresh access token. No credential is issued by this call — that is the pane below.
 
The token endpoint's answer, verbatim. Watch whether a new refresh_token comes back: a rotating server invalidates the old one.
Ask the Credential Endpoint Again

The same Credential Request step 2 makes — a fresh c_nonce, a proof of possession signed by the holder key, the credential named the way section 8.2 requires — sent with the access token from above. Section 14.3 is explicit that the endpoint may be asked again, and equally explicit that the issuer decides what comes back: the same credential, or an updated one. It may also decide the access token is no longer good enough and answer 401, which is its right.

credential_endpoint from the credential issuer metadata retrieved in step 1.
The access token this request will carry as a Bearer credential — the refreshed one if the pane above produced one.
A refreshed credential normally stays bound to the key the old one used, which is what makes it a replacement. Binding a new key produces a credential a verifier will demand a different proof of possession for — a legitimate thing to want, but not a replacement.
 
A fresh nonce from the issuer's Nonce Endpoint. The one step 2 used was spent on the original issuance — a c_nonce is single use.
typ openid4vci-proof+jwt, signed by the holder key, naming the credential issuer as its audience and carrying the c_nonce above.
The JSON that will be POSTed. Identical in shape to step 2's — a refresh is not a different request, it is the same request made again.
 
The fully assembled HTTP request: method, full URL, headers including the Bearer access token, and the body.
Send it. Nothing the wallet already holds is touched by this: what comes back is compared with the credential in hand, and replaces it only if you say so.
 
The Credential Response, verbatim.