feat: add POST /api/v1/datastore/preauth_offer endpoint - #493
Conversation
Add a new endpoint that generates a pre-authorized credential offer for a specific document in the datastore. This restores the capability that was lost when PR SUNET#375 removed the old /api/v1/notification endpoint and the QR field from the CompleteDocument model. The endpoint: 1. Looks up the document by (authentic_source, scope, document_id) 2. Generates a credential offer with a pre-authorized code 3. Creates and persists an authorization context in the cache 4. Caches the document data for the credential endpoint 5. Returns the credential offer, credential_offer_url, and a QR code This follows the same pre-authorized code flow pattern used by the OIDC and SAML standalone authentication flows, but sources the document data from the datastore instead of from authentication claims. Closes SUNET#492
|
Could you |
|
There also seems to be some build errors with the tests: |
…ate swagger docs - Add missing DatastorePreAuthOffer stub to unimplementedApiv1 struct to fix build error in admin status tests - Add pkg/openid4vp to swagger-apigw parse directories so openid4vp.QRReply type is resolved - Regenerate apigw swagger docs
Per reviewer feedback (issue SUNET#492 comment), QR code generation is unnecessary server-side complexity that clients can handle more flexibly (custom size, branding, etc). The endpoint now returns only the credential_offer and credential_offer_url. Clients can generate QR codes from the URL as needed.
When AuthorizationDetails is set on the AuthorizationContext, the token endpoint returns authorization_details with credential_identifiers in the token response. Per OID4VCI spec, this forces the wallet to use credential_identifier (not credential_configuration_id) in the credential request. Most wallets use credential_configuration_id for pre-auth flows, causing a 400 error. Remove AuthorizationDetails from the pre-auth context. The scope is already conveyed via the Scopes field, and the credential offer itself contains the credential_configuration_id.
The credential endpoint dispatches on AuthProvider to retrieve cached documents. The datastore pre-auth handler was not setting AuthProvider, causing 'unsupported or missing auth provider' errors when the wallet redeemed the offer. Add AuthProviderDatastore constant and set it on the authorization context. The credential handler now recognizes it alongside the existing OIDC/SAML/OpenID4VP providers.
The credential endpoint's requireIdentifier check rejected datastore pre-auth sessions because they don't have an authenticated identifier. Like assertion-based issuance, datastore issuance sources its data from pre-uploaded documents, not identity-mapped lookups, so an empty identifier is valid. Downstream code already guards identifier usage with != "" checks.
…er (#33) The /api/v1/notification endpoint was removed in VC 0.6.0 (SUNET/vc#492). A new endpoint /api/v1/datastore/preauth_offer was introduced in VC 0.6.5-sirosid.3 (SUNET/vc#493) to provide pre-authorized credential offer generation. - Replace NotificationRequest/NotificationReply/Notification() in issuerclient with PreauthOfferRequest/PreauthOfferReply/PreauthOffer() targeting the new endpoint - Update issueCredential() in apiv1/client.go to call PreauthOffer instead of Notification - The new endpoint takes authentic_source, scope, and document_id (no VCT needed) - The response returns credential_offer_url directly (no nested data wrapper) Closes #32
There was a problem hiding this comment.
Pull request overview
This PR adds a new admin/API endpoint for generating OpenID4VCI pre-authorized credential offers backed by documents already stored in the datastore, restoring functionality removed during the earlier API refactor.
Changes:
- Introduces
POST /api/v1/datastore/preauth_offerhandler + HTTP wiring and registers the route. - Persists a pre-auth
AuthorizationContextand caches the selected datastore document for later/token+/credentialredemption. - Extends issuance logic to recognize the new
datastoreauth provider and updates Swagger artifacts.
Reviewed changes
Copilot reviewed 12 out of 13 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| pkg/model/auth_providers.go | Adds AuthProviderDatastore constant. |
| Makefile | Includes additional package(s) in swagger generation inputs. |
| internal/apigw/httpserver/service.go | Registers the /datastore/preauth_offer route. |
| internal/apigw/httpserver/endpoints_datastore.go | Adds HTTP endpoint wrapper calling apiv1 client method. |
| internal/apigw/httpserver/endpoints_datastore_test.go | Extends unimplemented apiv1 stub with new method. |
| internal/apigw/httpserver/api.go | Extends Apiv1 interface with DatastorePreAuthOffer. |
| internal/apigw/apiv1/handlers_issuer.go | Allows datastore data source to have empty identifier; treats datastore as session-based for doc cache lookup. |
| internal/apigw/apiv1/handlers_issuer_assertion_test.go | Updates tests for the new datastore identifier behavior. |
| internal/apigw/apiv1/handlers_datastore.go | Implements DatastorePreAuthOffer request/reply types and handler. |
| internal/apigw/apiv1/handlers_datastore_test.go | Adds unit tests for the new datastore preauth offer behavior. |
| docs/apigw/swagger.yaml | Updates Swagger YAML with new endpoint and schemas. |
| docs/apigw/swagger.json | Updates Swagger JSON with new endpoint and schemas. |
| docs/apigw/docs.go | Updates embedded Swagger template with new endpoint and schemas. |
Files not reviewed (1)
- docs/apigw/docs.go: Generated file
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…n test - Distinguish ErrNoDocumentFound from internal store failures instead of wrapping all errors as 'document not found' - Use assert.ErrorIs in test for robustness
|
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 12 out of 13 changed files in this pull request and generated no new comments.
Files not reviewed (1)
- docs/apigw/docs.go: Generated file
Comments suppressed due to low confidence (1)
internal/apigw/apiv1/handlers_datastore.go:592
- DatastorePreAuthOffer can generate offers for expired documents. LookupDatastoreByIdentity filters out docs where Meta.ValidNotAfter is in the past; this endpoint should enforce the same rule so issuance can’t happen from stale datastore data (and to keep behavior consistent across datastore issuance paths).
// Look up the document from the datastore
doc, err := c.datastoreStore.GetByKey(ctx, req.AuthenticSource, req.Scope, req.DocumentID)
if err != nil {
if errors.Is(err, helpers.ErrNoDocumentFound) {
return nil, helpers.ErrNoDocumentFound
}
return nil, fmt.Errorf("failed to retrieve document: %w", err)
}



Summary
Add a new endpoint
POST /api/v1/datastore/preauth_offerthat generates a pre-authorized credential offer for a specific document in the datastore.This restores the capability that was lost when PR #375 removed the old
/api/v1/notificationendpoint and theQRfield from theCompleteDocumentmodel.Closes #492
What it does
The endpoint accepts
{authentic_source, scope, document_id}and:openid4vci.NewCredentialOffer)credential_offer_url, and a QR codeDesign
This follows the same pre-authorized code flow pattern used by the OIDC (
handlers_oidcrp.go) and SAML (endpoints_saml.go) standalone authentication flows, but sources the document data from the datastore instead of from authentication claims.The wallet can redeem the offer via the standard token + credential endpoints, just as it would for an OIDC/SAML-initiated offer.
Files changed
internal/apigw/apiv1/handlers_datastore.go— new handler + request/reply typesinternal/apigw/httpserver/endpoints_datastore.go— HTTP endpoint wrapperinternal/apigw/httpserver/service.go— route registrationinternal/apigw/httpserver/api.go— interface methodinternal/apigw/apiv1/handlers_datastore_test.go— unit tests (4 test cases)