AuthenticatedProtocol¶
Overview¶
Reports protocol authentication and secure-session health independently from the connection retry lifecycle.
ConnectionBase answers "is the link up?". AuthenticatedProtocol answers "and did the device accept us?". The two occupy separate status slots on the same Thing, so an operator sees connection.retrying together with protocol.authentication.failed and knows the retry loop will never succeed without a credential change.
- Added in: PQ Framework 2.0
- Namespace:
Pq.Adapters.Framework
When to Use¶
Add this function to a connection-owning Thing when the protocol has an authenticated application session on top of the transport, and the adapter can tell an authentication failure apart from plain unreachability:
- login / credential exchange after the socket opens
- TLS, PSK or shared-secret handshake
- token or session-key negotiation
- SDK connect call that reports "bad credentials" distinctly from "host unreachable"
Do not add it when the protocol has no authentication step, or when every failure surfaces as the same opaque timeout — an always-Default slot is noise.
States¶
| State | Status id | Description |
|---|---|---|
Default |
(cleared) | Nothing asserted — the slot is empty |
Authenticated |
protocol.authenticated (hidden) |
Protocol session is authenticated |
AuthenticationFailed |
protocol.authentication.failed |
Device rejected the credentials or identity |
HandshakeFailed |
protocol.handshake.failed |
Secure-session handshake failed |
Authenticated is the hidden default state: SetAuthenticated(ct) publishes no status string, so a working device reports the function as cleared. Both failure states are Critical and mark the device unreliable.
The generated enum is AuthenticatedProtocolState.
Actions¶
| Method | Meaning |
|---|---|
SetAuthenticated(ct) |
Session authenticated — clears the failure status |
SetAuthenticationFailed(ct) |
Deterministic credential or identity rejection |
SetHandshakeFailed(ct) |
Secure handshake failed and no more precise credential error is available |
SetDefault(ct) |
Clears the slot without claiming success — use for transport-level failures |
All return ValueTask<bool> (true when the status was published) and take an optional CancellationToken.
Reach them through the Thing's function container:
Properties¶
None.
YAML Example¶
device_types:
- type_id: Panel
name: "Access Control Panel"
functions:
ConnectionBase:
AuthenticatedProtocol:
StatusPoll:
Code Usage¶
Classifying the connect path¶
The whole point of the function is the catch ladder: each failure class maps to a different slot value. Anything that is not provably an authentication problem must go to SetDefault — leaving a stale AuthenticationFailed on a device that simply lost its network lies to the operator.
/// <summary>Opens the authenticated session with the panel.</summary>
private async Task<bool> Connect(Panel panel, CancellationToken ct)
{
try
{
await _client.Open(_endpoint, ct);
await _client.Login(_userName, _password, ct);
await panel.Functions.AuthenticatedProtocol.SetAuthenticated(ct);
return true;
}
catch (BadCredentialsException)
{
// device answered and said no - retrying will not help
await panel.Functions.AuthenticatedProtocol.SetAuthenticationFailed(ct);
return false;
}
catch (SecureChannelException ex)
{
// TLS/PSK negotiation failed - certificate, cipher or shared secret
Logger.LogWarning(ex, "Secure handshake failed");
await panel.Functions.AuthenticatedProtocol.SetHandshakeFailed(ct);
return false;
}
catch (Exception ex)
{
// transport level - not an authentication verdict
Logger.LogWarning(ex, "Connection failed");
await panel.Functions.AuthenticatedProtocol.SetDefault(ct);
return false;
}
}
Session invalidated while running¶
A session that expires or is revoked mid-run is an authentication failure, not a disconnect:
/// <summary>Handles a session rejection reported on the live stream.</summary>
private async Task HandleSessionRejected(Panel panel, CancellationToken ct)
{
await panel.Functions.AuthenticatedProtocol.SetAuthenticationFailed(ct);
await panel.DisconnectedAsync();
}
The framework then drives the normal retry loop through ConnectionBase, and the protocol slot keeps explaining why each attempt fails until a successful login calls SetAuthenticated.
Status polling¶
AuthenticatedProtocol state comes from the connect and session path, not from a device register. When the Thing also has StatusPoll, exclude the state explicitly so the poll-coverage check stays honest:
[PollExcludes(typeof(AuthenticatedProtocolState), "Protocol authentication is reported by the connection handshake path.")]
public static Task PollStatus(Panel panel, Protocol protocol, CancellationToken ct) =>
protocol.PollAllStates(panel, ct);
Notes¶
ConnectionBasekeeps owningconnection.online/connection.offline/connection.retrying. This function never replaces them — it annotates them.- Set the state only from an explicit vendor error code, SDK exception type, or protocol response. A guess is worse than an empty slot.
- Never map a network timeout, refused port, or DNS failure to
AuthenticationFailedorHandshakeFailed— useSetDefault. - Prefer
AuthenticationFailedwhen the device distinguishes bad credentials; fall back toHandshakeFailedwhen the failure is only known to be inside the secure-session setup. - Both failure states drive the device to unreliable in reliability accounting, so they must clear as soon as a session succeeds.
See Also¶
- Rules: RULE-058 - an authentication verdict must come from the device, never from a transport failure
- Functions: ConnectionBase - connection lifecycle this function annotates
- Functions: StatusPoll - polling declared states
- Diagnostics: PQC251 - poll coverage and declaring
[PollExcludes] - Patterns: Connection Management - implementing device connections
- Concepts: Status - status slots, hidden states and aggregation