Definition
Assembly: Microsoft.OData.Mcp.Authentication.dll Namespace: Microsoft.OData.Mcp.Authentication.Outbound Inheritance: System.ObjectSyntax
Summary
The outbound OAuth state machine: it owns one OData service root’s cached token, decides whether that token can still be attached, refreshes it exactly once when several callers race, and runs the advertised grant when there is nothing left to refresh.Remarks
Nothing here ever logs an access token, a refresh token, a device code, an authorization code, or a PKCE code verifier: every token that reaches a log goes through TokenContainer) first. There is no background refresh — an idle process sends no token requests at all — because the send path checks remaining lifetime against ODataMcpAuthConstants.TokenRefreshSkew and a401 invalid_token remains the fallback for a token whose lifetime the client cannot compute.
The credential store is proved usable inside
CancellationToken) rather than at host
construction, and only when no explicit credential was configured, so a service that needs no sign-in at
all never touches the operating system keyring.
Examples
Constructors
.ctor
Initializes a new instance of the OutboundOAuthClient class.Syntax
Parameters
Exceptions
Remarks
The token endpoint client, every grant, the dynamic client registrar, and the scope resolver are composed here rather than injected: they are implementation detail of this state machine and registering half a dozen more services would let a caller wire an inconsistent set..ctor Inherited
Inherited from
objectSyntax
Methods
AcquireAsync
Runs discovery and the advertised grant to obtain a brand new token, and persists it.Syntax
Parameters
Returns
Type:System.Threading.Tasks.Task<ModelContextProtocol.Authentication.TokenContainer>
The acquired token, already written to the cache.
Exceptions
Examples
Remarks
The whole interactive wait runs under a linked cancellation source that fires after OutboundOAuthOptions.AuthTimeout, so a human who walks away cannot pin the send path open forever; the caller’s own cancellation still surfaces as OperationCanceledException rather than a timeout. The client credentials grant takes none of that path: it needs no human, so it neither starts a timeout nor consults a presenter. When a presenter is configured and the wait fails for any reason — the presenter threw, the timeout fired, the end user refused — the pending grant is cleared, so the next call starts a fresh one instead of failing forever with “an interactive sign-in is already pending”. The deliberate OAuthConsentRequiredException thrown when no presenter is configured is the one path that keeps the pending state, because a caller that elicits the URL finishes that same grant later.CompleteInteractiveGrantAsync
Finishes the interactive grant a previous CancellationToken) left pending: polls the token endpoint or waits on the loopback redirect, persists the result, and clears the pending state.Syntax
Parameters
Returns
Type:System.Threading.Tasks.Task<ModelContextProtocol.Authentication.TokenContainer>
The acquired token, already written to the cache.
Exceptions
Remarks
The pending state is cleared — and the loopback listener disposed — in afinally, so a refusal
or a timeout leaves this client ready to start a fresh grant rather than wedged on a device code the
authorization server has forgotten or a socket nothing is reading.
Equals Inherited Virtual
Inherited from
objectSyntax
Parameters
Returns
Type:bool
Equals Inherited
Inherited from
objectSyntax
Parameters
Returns
Type:bool
ForceRefreshAsync
Refreshes the cached token even when its remaining lifetime says it is still good, for the401 invalid_token path where the resource server disagrees with the token’s own
expires_in.
Syntax
Parameters
Returns
Type:System.Threading.Tasks.Task<ModelContextProtocol.Authentication.TokenContainer?>
The refreshed token, or null when there is nothing cached to refresh or the
authorization server rejected the refresh token.
Examples
Remarks
Coalesced through the same gate as the lifetime-driven refresh, so a burst of simultaneousinvalid_token answers costs one token POST rather than one per request. A client credentials
token has no refresh token to spend, so this returns null and the caller falls
through to a fresh grant.
GetHashCode Inherited Virtual
Inherited from
objectSyntax
Returns
Type:int
GetType Inherited
Inherited from
objectSyntax
Returns
Type:System.Type
GetValidTokenAsync
Returns a token that can be attached to the next"OData" request, refreshing it first when its
remaining lifetime has fallen inside ODataMcpAuthConstants.TokenRefreshSkew.
Syntax
Parameters
Returns
Type:System.Threading.Tasks.Task<ModelContextProtocol.Authentication.TokenContainer?>
The token to attach, or null when nothing is cached, nothing can be refreshed, or
the refresh failed — in which case the caller runs
CancellationToken) off the resulting
challenge.
Examples
Remarks
A token whoseexpires_in the authorization server omitted is attached as-is: the client cannot
prove it has expired, and a 401 invalid_token is a cheaper way to find out than throwing away
a token that may still have hours left.
MemberwiseClone Inherited
Inherited from
objectSyntax
Returns
Type:object
ReferenceEquals Inherited
Inherited from
objectSyntax
Parameters
Returns
Type:bool
StartInteractiveGrantAsync
Starts an interactive grant and returns the consent request a human — or an MCP elicitation — has to act on, without waiting for them to act.Syntax
Parameters
Returns
Type:System.Threading.Tasks.Task<Microsoft.OData.Mcp.Authentication.Outbound.OutboundConsentRequest>
The consent request, carrying the URL to open, the user code to type when the grant uses one, and a
fresh D-format GUID OutboundConsentRequest.ElicitationId.
Exceptions
Remarks
Split from CancellationToken) so an MCP host never polls a token endpoint — or blocks on a loopback socket — on the thread that is servicing an OData request.ToString Inherited Virtual
Inherited from
objectSyntax
Returns
Type:string?