# Description
Introduces a pluggable user-identity provider model covering all OPC UA
Part 6 §6.5 mechanisms (Anonymous, UserName, X509, IssuedToken/JWT) on
both client and server, with full
`Microsoft.Extensions.DependencyInjection` integration, plus the Part 12
§9 `AuthorizationService` two-phase token-request flow **including
refresh-token rotation**, the Part 12 §8 KeyCredential Push model, and a
clean provider model for OPC 10000-12 §7.2 `ApplicationSelfAdmin`. The
work is split across phase-by-phase commits so it can be reviewed
independently; nothing here changes wire-level behaviour or forces
existing applications to migrate.
### Documentation
All under `Docs/`:
- `IdentityProviders.md` — full developer guide: three-layer model,
recipes, configuration reference, "what gets auto-wired" matrix, JWS
algorithm table, identity-augmenters section, **"Implementing your own
provider"** how-to subsections for Entra / OIDC / Windows / AspNetCore
(sibling files merged in per review feedback).
- `DependencyInjection.md` — `Identity` sections with `appsettings.json`
examples, identity-extension matrix, `WithAuthorizationService` +
`WithKeyCredentialPush` + `AddIdentityAugmenter` extensions documented.
- `RoleBasedUserManagement.md` — claim-criteria callout describing
`IIdentityClaims` probe surface.
- `AuthorizationService.md` — Architecture diagram +
`IAccessTokenProvider` conceptual narrative restored from master; modern
`StartRequestToken` / `FinishRequestToken` flow leads; refresh-token
recipe added.
- `KeyCredentialService.md` — Push model + Pull vs Push vs Bridge
decision matrix + Hybrid (Push + Pull) section; explicit experimental
warning for the bridge.
- `GDS.md` — KeyCredential Push, `StartRequestToken`,
`GdsApplicationSelfAdminProvider`, **shipped Refresh-token support with
rotation policy and audit semantics** all documented.
- `MigrationGuide.md` — User Identity Providers section listing
`[Obsolete]` markers + mechanical recipes; SelfAdmin migration via
`IIdentityAugmenter`; `EnableRefreshTokens` default note.
- `README.md` index entries.
## Checklist
- [x] I have signed the
[CLA](https://opcfoundation.org/license/cla/ContributorLicenseAgreementv1.0.pdf)
and read the
[CONTRIBUTING](https://github.com/OPCFoundation/UA-.NETStandard/blob/master/CONTRIBUTING.md)
doc.
- [x] I have added tests that prove my fix is effective or that my
feature works and increased code coverage.
- [x] I have added all necessary documentation.
- [x] I have verified that my changes do not introduce (new) build or
analyzer warnings.
- [x] I ran **all** tests locally using the **UA.slnx** solution against
at least .net **framework** and .net **10**, and all passed.
- [x] I fixed **all** failing and flaky tests in the CI pipelines and
**all** CodeQL warnings.
- [x] I have addressed **all** PR feedback received.
### Test project reorganization
Splits the integration- vs unit-test boundary along **project
boundaries**
and introduces a runtime-tier split for subscription tests so the
mainline
CI no longer drags in stress / durable suites.
**New projects:**
- `Opc.Ua.Stress.Tests` ΓÇö cross-area stress tier (designed to
absorb
session / channel / pub-sub stress in future). Seeded with the 6
subscription stress classes (`SubscriptionDepthTests`,
`SubscriptionBasicDepthTests`, `MonitoredItemDepthTests`,
`SubscriptionTransferDepthTests`, `SubscriptionPublishTooManyTests`,
`SubscriptionMultipleTests`). **Excluded from mainline CI; runs
nightly.**
- `Opc.Ua.Subscriptions.Durable.Tests` ΓÇö durable-subscription tier
(needs `DurableSubscriptionsEnabled` + storage-backed lifetime).
Receives `SubscriptionDurableTests` plus the migrated
`DurableSubscriptionTest` and `DurableSubscriptionTestDebug`.
**Excluded from mainline CI; runs nightly.**
- `Opc.Ua.Features.Tests` ΓÇö cross-feature integration tier.
Receives the
`[OneTimeSetUp]`-bootstraps-its-own-server integrations for AliasNames
(Part 17), Roles (Part 18), FileSystem (Part 5 §C / Part 20), and
NodeCache. Also absorbs the misplaced
`Tests/Opc.Ua.History.Tests/FileSystemTests.cs`.
**Client.Tests cleanup (~28 files moved):**
- `Opc.Ua.Sessions.Tests` (existing) gains 10 session integration files
plus the 4 session benchmark files inline (`LoadTest`,
`RequestHeaderTest`, `TraceableRequestHeaderTest`,
`SecurityPolicyBenchmarks`).
- `Opc.Ua.Subscriptions.Tests` (existing) gains 4 subscription
integration files plus `PooledNotificationBenchmarks` inline.
- `Opc.Ua.Client.ComplexTypes.Tests` (existing) gains all
`ComplexTypes/*` integration files. The Client.Tests copy of
`TypeSystemClientTest.cs` (a stale near-duplicate) is removed.
- Benchmarks **stay inline** as `[Test]` + BenchmarkDotNet dual-use; no
new benchmark project (per the approved plan).
**CI pipeline:**
- `.azurepipelines/test.yml` now filters
`TestCategory!=LongRunning&TestCategory!=Stress` on the mainline run.
- `-ExcludeFileName` now accepts a comma-separated list. The mainline
matrix excludes the 3 nightly-tier project files alongside the
existing `Opc.Ua.Aot.Tests.csproj`.
- `get-matrix.ps1` enhanced to support multi-pattern excludes.
`InternalsVisibleTo` extended on `Opc.Ua.Client` and `Opc.Ua.Server` to
grant the new test projects access. All affected projects build clean
(`0 Error`) for `net10.0`.
### Fix 8 skipped tests flagged as suspected bugs
Triage of every `[Ignore]` / `[Explicit]` test in `Tests/**` surfaced 8
entries labelled as suspected production bugs. After inspection:
- **1 real production bug** ΓÇö the
`CancellationTokenSource.CancelAsync`
polyfill for netstandard2.0 / netfx was running cancellation callbacks
inline on the caller's thread, deadlocking dispose paths
(`SubscriptionManager.DisposeAsync`, etc.) when a callback awaits on
the caller's `SynchronizationContext`. Fixed at the polyfill root by
hoisting `Cancel()` onto the ThreadPool via `Task.Run`, matching real
.NET 8+ semantics. Caveat (deferred `IsCancellationRequested`
transition) documented inline; all in-repo callers use
`await cts.CancelAsync()` so the deviation is invisible. Test
`DrainAsyncWaitsForInFlightPublishToCompleteAsync` un-ignored ΓÇö
verified passes on net48 (943 ms) and net10.0 (1 s).
- **2 flaky tests stabilized** ΓÇö both
`ValidateMetaDataUpdateTimeNonZeroIsSentPeriodically` (MQTT JSON &
UADP) now use `Stopwatch.GetTimestamp()` instead of `DateTime.Now`,
compute deviation against the **median** interval (not the configured
cadence, so a startup hiccup doesn't skew the run), and use a
proportional `max(interval / 4, 50 ms)` tolerance. `[Ignore]` removed,
`[Category("LongRunning")]` retained.
- **1 mis-labelled "bug"** ΓÇö
`UnorderedMessagesWouldBeLostForSequentialPublishingAsync` asserts the
*documented* lossy trade-off of strict sequential publishing.
`[Explicit]`
removed and renamed to
`OldMessagesAbandonedAfterRepublishTimeoutInSequentialModeAsync`; it's
now a CI-verified regression test for the WAI behavior.
- **4 false positives** ΓÇö `JsonEncoderTestsOld.cs` (16 kLOC) was
excluded from compilation via `<Compile Remove>` and never reached the
runner. The 4 `[Ignore("ProductionBugSuspected")]` markers inside were
orphaned source from a previous JsonEncoder API. Active coverage lives
in `JsonEncoderTests.cs` and `JsonValidationData` fixtures. Deleted
the orphan file and the now-redundant `<Compile Remove>` line.
Drive the solution to zero warnings / zero errors with analyzers on,
promote the consistently-fixed rules to error, and lock in coverage
on the only line of new logic.
* New certificate and secrete abstraction with certificate infrastructure and certificate/secret stores.
* leak testing for certificate resources to validate new ref counting in every test assembly
* Migration guide updated and documentation added for all
* Remove IDisposable from Nodestate and UserIdentity.
* UserCertificateTokenHandler now just points into certificate registry/stores, no dispose required
* Fix CA2000 warnings and cleanup of other diagnostics, increase warning levels in .editorconfig
- Add new managed session, wrapping session and providing connection management and failover handling
- Add new Subscription engine that can act as alternative to current engine, but is based on channels with worker tasks
- Add a fluent builder for managed session clients
- Replace NodeCache with LruNodeCache, rename ILruNodeCache and LruNodeCache to INodeCache and NodeCache
- Update migration guide and document new functionality.
* Fixed timer leaks in ChannelAsyncOperation.EndAsync (added delay task cancellation)
* Added WaitAsync with timeout PolyFill extension for versions <= .NET 6.0
* The ChannelAsyncOperation.EndAsync has been unified across all .NET versions using polyfills for older versions (< .NET 6.0)
* Fixed missing "using" operator
* make most ptojects aot compatible
* Remove newtonsoft
* Migrate all projects from Mono.Options to System.CommandLine 2.0.5
* Fix array clone
* Replace serializers for storage using new attribute driven type geberator
* Add new AOT integration tests covering all functional areas
* Documentation updates
This pull request modernizes the OPC UA .NET codebase and tests, removes .NET Standard 2.0 support from certificate/security code, and updates test code to use C# 12 and .NET 8+ features for clarity and maintainability.
- Certificate and BouncyCastle interop files (e.g., CertificateBuilder.cs, X509Extensions.cs, X509SignatureFactory.cs) are refactored to drop NETSTANDARD2_0 support and use modern .NET APIs and conditional compilation.
- Test code across the codebase is updated to use default, strongly-typed Parse methods, collection expressions, and improved async/await patterns.
- Exception handling and assertions in tests are modernized, and test method naming is made consistent with Async suffixes.
- The project and build files are updated to clarify Roslynator analyzer usage and remove obsolete netstandard tags.
- General code and test cleanup includes improved null safety, type safety, and removal of deprecated or unused code.
This change adds a **new Opc.Ua.Types** nuget that contains core types that do not rely on generated code. Why the name? Because almost all code that was in the Types subfolder is part of it, and because we do not want to change the name of the Opc.Ua.Core nuget/assembly because code relies on it. Any other name like base or bootstrap felt out of place due to the current structure already defining the name "types". Key design is to depend on only a small set of libraries with limited transitive dependencies, that includes hashcode, logging.abstraction, and for non standard20 builds System.Text.Json (needed for localizedText mul parsing). Also, key is to support .netstd 2.0 to support source generators and to allow removal of EEC_SUPPORT defines by removing std2.0 from core and all libraries.
The changes are mostly restructuring, and thus small but contain some key changes:
- **Status code** struct now comes with a Symbol field, which is used to carry symbols in case the code is not generated. Currently (until generated) the status code struct must be manually created with a symbol or the ToSymbolicId extension must be called in the core project. Future will generate Status code constants just like NodeId that contain the symbol which also removes the reflection-based lookup. Several tests needed to be adjusted as they checked the status code symbol strings instead of the code.
- **Core identifiers** are copied as they were needed for code to compile, that includes attributes, a subset of status codes and node identifiers. These will never change so no need to generate them.
- The types folder now contains **encoders**, except for json which is still in core. Once the json encoder is ported to system.text.json it will also be included and Newtonsoft.Json dropped as dependency (-> #3329).
- Since types relies on **IOperationContext** and **ISystemContext**, and these export IUserIdentity which relies on crypto, these were split and extended in core with ISessionOperationContext and ISessionSystemContext, which mean changes to ServerSystemContext and introduction of SessionOperationContext and SessionSystemContext classes that extend OperationContext and SystemContext. Some runtime checks in the server are therefore needed to get at the session and auth info, however, those are anyway optional fields and are not on the critical paths, and thus a type check is in order (and was done in the past already, but against OperationContext class).
- **Manifest resources** are now split between Core (nodeset zip, some generated resoures) and Types (built in bsd/xsds). This needs to be revisited later if the bsd/xsd need to be regenerated during type generation. Note that the embedded resource names changed as a result. Since these are only used internally and do not constitute public API this feels reasonable vs duplication of resources in 2 assemblies.
- Reflection however now needs to load not just the built in **encodeables** from Opc.Ua.Types assembly, but also the generated encodeables from Opc.Ua.Core assembly. For this a new helper Utils method was added to get the Core assembly when linked against Opc.Ua.Core otherwise null.
- Whatever utils methods needed to be split out into the new Opc.Ua.Types assembly, they are now in **CoreUtils** static class in Opc.Ua.Types, and Utils.xxxx forwards to them to ensure minimal changes. Later we would want to obsolete them in favor of CoreUtils, although it might make sense to split them again into domain specific utilities, such as networking, files, etc