feat: SPOCP-based issuance policy evaluation with dynamic OIDC params#380
feat: SPOCP-based issuance policy evaluation with dynamic OIDC params#380leifj wants to merge 5 commits into
Conversation
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.
|
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
- 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.
The spocputil parser was missing the bare * → Wildcard and trailing * → Prefix conversions inside tagged lists. This meant rules like (scope *) or (path /api/*) were parsed as plain atoms instead of star forms, causing SPOCP matching failures. Also update test imports to use the exported spocputil.ParseAdvancedSExp.
71920c4 to
42459b9
Compare
|



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