Skip to content

feat: add POST /api/v1/datastore/preauth_offer endpoint - #493

Merged
masv3971 merged 7 commits into
SUNET:mainfrom
sirosfoundation:feat/datastore-preauth-offer
Jul 29, 2026
Merged

feat: add POST /api/v1/datastore/preauth_offer endpoint#493
masv3971 merged 7 commits into
SUNET:mainfrom
sirosfoundation:feat/datastore-preauth-offer

Conversation

@leifj

@leifj leifj commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Summary

Add a new endpoint POST /api/v1/datastore/preauth_offer that 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/notification endpoint and the QR field from the CompleteDocument model.

Closes #492

What it does

The endpoint accepts {authentic_source, scope, document_id} and:

  1. Looks up the document from the datastore
  2. Generates a credential offer with a pre-authorized code (via openid4vci.NewCredentialOffer)
  3. Creates and persists an authorization context in the cache (5-minute expiry)
  4. Caches the document data for the credential endpoint (keyed by pre-auth code)
  5. Returns the credential offer, a credential_offer_url, and a QR code

Design

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 types
  • internal/apigw/httpserver/endpoints_datastore.go — HTTP endpoint wrapper
  • internal/apigw/httpserver/service.go — route registration
  • internal/apigw/httpserver/api.go — interface method
  • internal/apigw/apiv1/handlers_datastore_test.go — unit tests (4 test cases)

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
@s-jairl

s-jairl commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Could you make swagger to get the API docs updated as well?

@s-jairl

s-jairl commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

There also seems to be some build errors with the tests:

Error: internal/apigw/httpserver/endpoints_admin_test.go:141:66: cannot use mock (variable of type *adminStatusMock) as Apiv1 value in argument to testSetupAdminStatus: *adminStatusMock does not implement Apiv1 (missing method DatastorePreAuthOffer)

Leif Johansson added 5 commits June 17, 2026 11:09
…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.
leifj added a commit to sirosfoundation/facetec-api that referenced this pull request Jun 23, 2026
…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
@kushaldas
kushaldas requested a review from Copilot July 9, 2026 14:36

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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_offer handler + HTTP wiring and registers the route.
  • Persists a pre-auth AuthorizationContext and caches the selected datastore document for later /token + /credential redemption.
  • Extends issuance logic to recognize the new datastore auth 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.

Comment thread internal/apigw/apiv1/handlers_datastore.go
Comment thread internal/apigw/apiv1/handlers_datastore.go
Comment thread internal/apigw/apiv1/handlers_datastore.go
Comment thread internal/apigw/apiv1/handlers_datastore_test.go
…n test

- Distinguish ErrNoDocumentFound from internal store failures instead
  of wrapping all errors as 'document not found'
- Use assert.ErrorIs in test for robustness
@sonarqubecloud

sonarqubecloud Bot commented Jul 9, 2026

Copy link
Copy Markdown

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)
	}

@masv3971
masv3971 merged commit 3625750 into SUNET:main Jul 29, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Pre-authorized credential offer endpoint missing since API refactoring

4 participants