feat: SPOCP-based issuance policy evaluation with dynamic OIDC params#380
Open
leifj wants to merge 5 commits into
Open
feat: SPOCP-based issuance policy evaluation with dynamic OIDC params#380leifj wants to merge 5 commits into
leifj wants to merge 5 commits into
Conversation
Implement conditional credential issuance using the SPOCP policy engine, allowing authentic source business systems to pass dynamic parameters through the OIDC authorization flow. New components: - pkg/issuance/policy.go: PolicyEngine wrapping spocp.AdaptiveEngine with Evaluate() for checking claims against rules - pkg/issuance/parser.go: Human-readable S-expression parser supporting star forms (wildcard, prefix, suffix, set) - Model types: OIDCRequestParams, IssuancePolicy, ScopePolicyConfig in pkg/model/data_sources.go Integration points: - OIDC RP InitiateAuth/InitiateAuthForVCI accept dynamic params and OIDC request parameters with template resolution - OIDCRPCallback evaluates issuance policy against OIDC claims - PAR handler stores DynamicParams in AuthorizationContext - Consent flow forwards dynamic params to OIDC auth initiation Closes SUNET#379
Contributor
There was a problem hiding this comment.
Pull request overview
Implements per-credential-scope issuance gating and OIDC authorization-request customization by introducing (1) dynamic parameter propagation from PAR → auth context/session and (2) SPOCP-based post-login policy evaluation of OIDC claims in the OIDC RP callback.
Changes:
- Adds
DynamicParamsto PAR requests, authorization context, and OIDC sessions; forwards them into OIDC auth initiation for template substitution. - Introduces
OIDCRequestParamsandIssuancePolicyscope configuration (plus a cross-data-source lookup helper) to drive OIDC request customization and SPOCP rules. - Adds a new
pkg/issuancepackage with an “advanced-form” S-expression parser and policy engine wrapper; integrates evaluation into the OIDC callback path.
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| pkg/openid4vci/authoriziation.go | Extends PAR request model with DynamicParams. |
| pkg/model/data_sources.go | Adds per-scope OIDCRequestParams + IssuancePolicy config models and LookupScopePolicyConfig(). |
| pkg/issuance/policy.go | New SPOCP policy engine wrapper + query builder + optional rules file loading. |
| pkg/issuance/policy_test.go | Unit tests for policy engine behavior and query building. |
| pkg/issuance/parser.go | New “advanced form” S-expression parser with star-form support. |
| pkg/issuance/parser_test.go | Unit tests for the advanced S-expression parser. |
| pkg/cache/authcontext_types.go | Adds DynamicParams persistence to AuthorizationContext. |
| internal/apigw/httpserver/endpoints_oauth.go | Forwards per-scope OIDC params and stored dynamic params into OIDC auth initiation (VCI consent flow). |
| internal/apigw/auth_providers/oidcrp/session.go | Persists DynamicParams into the OIDC RP session model. |
| internal/apigw/auth_providers/oidcrp/service.go | Extends OIDC initiation to accept per-scope params + dynamic params; resolves templates into AuthCodeURL options. |
| internal/apigw/apiv1/handlers_oidcrp.go | Evaluates issuance policy after claims retrieval (pre-issuance). |
| internal/apigw/apiv1/handlers_oauth.go | Stores DynamicParams from PAR into the authorization context. |
Comments suppressed due to low confidence (1)
internal/apigw/auth_providers/oidcrp/service.go:152
- InitiateAuth’s signature change is a breaking API change; current repo call sites still use the old signature (e.g., internal/apigw/integration/oidc_integration_test.go calls InitiateAuth(ctx, "pid")). Update those callers (and any external consumers) or provide a backward-compatible wrapper to avoid build failures.
// InitiateAuth initiates an OIDC authentication flow.
// oidcParams and dynamicParams are optional: when non-nil, they customize the
// authorization request (e.g., acr_values, claims parameter, extra scopes).
func (s *Service) InitiateAuth(ctx context.Context, credentialType string, oidcParams *model.OIDCRequestParams, dynamicParams map[string]string) (*AuthRequest, error) {
s.log.Debug("Initiating OIDC auth",
"credential_type", credentialType)
// Create session with state, nonce, and PKCE verifier
session, err := s.createSession(ctx, credentialType)
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
added 4 commits
May 8, 2026 15:05
- Change QueryTemplate from map to ordered []QueryDimension slice to ensure deterministic SPOCP query dimension ordering (positional match) - Sort claim keys in default (no-template) query path - Cache PolicyEngine per scope via sync.Map to avoid per-request parsing - Merge session DynamicParams into policy evaluation claims - Add validation constraints on DynamicParams (max key=64, value=1024) - Fix CustomParams doc: keys are static, only values support templates
Move the duplicated advancedParser implementation from both pkg/issuance/parser.go and pkg/httphelpers/middleware_jwt.go into a new shared pkg/spocputil package. This eliminates 19.8% new-code duplication that was failing the SonarCloud quality gate (threshold: 3%).
Merge TestEvaluate_PrefixStarForm and TestEvaluate_SetStarForm into a single TestEvaluate_StarForms table-driven test to eliminate internal duplication flagged by SonarCloud CPD.
Resolve conflict in pkg/httphelpers/middleware_jwt.go: - Keep parser refactoring in pkg/spocputil (branch approach) - Adopt upstream's expanded BuildSPOCPQuery (6-param, exported) - Adopt upstream's extractSPOCPSubject helper - Add wildcard/prefix star form handling in spocputil.parseList() to match upstream's inline parser behaviour - Add starform import and spocputil import where needed
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Summary
Implements conditional credential issuance using the SPOCP policy engine, allowing authentic source business systems to pass dynamic parameters through the OIDC authorization flow.
Closes #379
Changes
New:
pkg/issuance/— Policy engine and S-expression parserpolicy.go:PolicyEnginewrappingspocp.AdaptiveEnginewithEvaluate(scope, claims, queryTemplate)for checking OIDC claims against SPOCP rules.BuildQuery()constructs S-expression queries from claims.parser.go: Human-readable S-expression parser (ParseAdvancedSExp) supporting star forms: wildcard(*), prefix(* prefix urn:example:), suffix(* suffix @example.com), set(* set loa3 loa4).policy_test.go+parser_test.go: 27 tests covering simple match/deny, wrong scope, wildcard rules, prefix/suffix/set star forms, multiple rules, missing claims, boolean coercion, query building, and parser edge cases.Model types in
pkg/model/data_sources.goOIDCRequestParams— per-scope OIDC authorization request customization (acr_values, claims, extra_scopes, custom_params)IssuancePolicy— SPOCP rules (inline + file) with query template mappingScopePolicyConfig— combined config structLookupScopePolicyConfig()helper onDataSourcesIntegration
service.go):InitiateAuth/InitiateAuthForVCIacceptOIDCRequestParams+DynamicParams;resolveOIDCRequestParams()buildsoauth2.AuthCodeOptionlist with Go template resolution for dynamic valueshandlers_oidcrp.go): Policy evaluation block after claims retrieval — denied claims return hard errorhandlers_oauth.go): StoresDynamicParamsfromPARRequestintoAuthorizationContextendpoints_oauth.go): Forwards dynamic params fromAuthorizationContexttoInitiateAuthForVCIDynamicParamsfield added to OIDC session andAuthorizationContextConfiguration Example