Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Authentication validation failed with error - InvalidToken exception on .net #110184

Open
danghyan opened this issue Nov 26, 2024 · 7 comments
Open
Labels
area-System.Net.Http untriaged New issue has not been triaged by the area owner

Comments

@danghyan
Copy link

danghyan commented Nov 26, 2024

Description

Hello. My team has partially encountered an InvalidToken error during POST requests to the server. This error appears sequentially for different developers, and once it occurs, it doesn’t go away. Currently, half of the team has no issues, while the other half consistently encounters this error during POST requests.

The client typically makes two requests. The first one, a handshake response, returns 401, and the second request receives a 200 response. The server processes the request correctly and returns the appropriate response. However, the client throws an InvalidToken error. I checked the headers through Fiddler, and they seem fine.

If the Kerberos authentication header is explicitly specified for the HTTP client, everything starts working correctly. In this case, the client skips the first handshake (401 request, which makes sense) and sends the request with the Kerberos header directly. Again, I checked the headers through Fiddler, and they are identical to those used in unsuccessful operations. The first 7700 characters of the header are entirely identical.

Decompiled code didn’t provide any useful information either. I understand this is a common issue, so are there any suggestions or advice?

All the threads found here and on other resources did not provide a clear answer to the issue. In our case, the server is managed by our team, so we might be able to dig a bit deeper into solving the problem.

Reproduction Steps

It won't be possible to reproduce it explicitly.

Expected behavior

Currect 200 response

Actual behavior

System.Net.Http.HttpRequestException: Authentication validation failed with error - InvalidToken.
at System.Net.Http.AuthenticationHelper.SendWithNtAuthAsync(HttpRequestMessage request, Uri authUri, Boolean async, ICredentials credentials, Boolean isProxyAuth, HttpConnection connection, HttpConnectionPool connectionPool, CancellationToken cancellationToken)
at System.Net.Http.HttpConnectionPool.SendWithVersionDetectionAndRetryAsync(HttpRequestMessage request, Boolean async, Boolean doRequestAuth, CancellationToken cancellationToken)
at System.Net.Http.AuthenticationHelper.SendWithAuthAsync(HttpRequestMessage request, Uri authUri, Boolean async, ICredentials credentials, Boolean preAuthenticate, Boolean isProxyAuth, Boolean doRequestAuth, HttpConnectionPool pool, CancellationToken cancellationToken)
at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
at Microsoft.Extensions.Http.Logging.LoggingHttpMessageHandler.g__Core|5_0(HttpRequestMessage request, Boolean useAsync, CancellationToken cancellationToken)
at Microsoft.Extensions.Http.Logging.LoggingScopeHttpMessageHandler.g__Core|5_0(HttpRequestMessage request, Boolean useAsync, CancellationToken cancellationToken)
at System.Net.Http.HttpClient.g__Core|83_0(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationTokenSource cts, Boolean disposeCts, CancellationTokenSource pendingRequestsCts, CancellationToken originalCancellationToken)

Regression?

No response

Known Workarounds

Cusom kerberos header for HttpClient

Configuration

.Net 7, .Net 8
Client - Windows 10
Server - K8S

Other information

No response

@dotnet-policy-service dotnet-policy-service bot added the untriaged New issue has not been triaged by the area owner label Nov 26, 2024
Copy link
Contributor

Tagging subscribers to this area: @dotnet/ncl
See info in area-owners.md if you want to be subscribed.

@danghyan
Copy link
Author

@filipnavara You were extremely helpful and provided a lot of advice in a similar thread where the server was not managed by the developers and was written in Java. Perhaps you could offer some guidance here as well, please?

@filipnavara
Copy link
Member

@danghyan I can analyze Fiddler/Proxyman/Charles traces with the Authorization / WWW-Authentication headers included. Feel free to share them with me on my email (link on my profile) and I will post the results back here.

@ogguevara07
Copy link

ogguevara07 commented Nov 26, 2024

@danghyan Review how you declared the [Authorize]. At one point, I had issues with the front-end returning a 401 error on some occasions. I identified that the problem was how I had configured my JWT and how the back-end was processing it. What I did was change [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] to [Authorize].

@filipnavara
Copy link
Member

filipnavara commented Nov 26, 2024

Just a summary of the private conversation from email.

I briefly checked the tokens. The server responds with 200 OK and WWW-Authentication header. The value of the header seems to be syntactically correct according to RFC 4178 (https://datatracker.ietf.org/doc/html/rfc4178) specification of NegTokenResp and contains the following values:

There's no MIC (Message Integrity Check) in the response. That seems to be valid in this case according to the specification:

Otherwise, if the accepted mechanism is the most preferred mechanism of both the initiator and the acceptor, then the MIC token exchange, as described later in this section, is OPTIONAL.

The value of responseToken seems to be a Kerberos 5 KRB_AP_REP packet. Whether the token is actually valid response I am not sure. Just in case, this is the decoded ASN.1 structure:

OCTET_STRING @0+156 (encapsulates): (156 byte)|[...]
  Application_0 @3+153 (constructed): (3 elem)
    OBJECT_IDENTIFIER @6+9: 1.2.840.113554.1.2.2
    INTEGER @17+0: 0
    Application_15 @19+137 (constructed): (1 elem)
      SEQUENCE @22+134 (constructed): (3 elem)
        [0] @25+3 (constructed): (1 elem)
          INTEGER @27+1: 5
        [1] @30+3 (constructed): (1 elem)
          INTEGER @32+1: 15
        [2] @35+122 (constructed): (1 elem)
          SEQUENCE @37+120 (constructed): (2 elem)
            [0] @39+3 (constructed): (1 elem)
              INTEGER @41+1: 18
            [2] @44+113 (constructed): (1 elem)
              OCTET_STRING @46+111: (111 byte)|[...]

/cc @SteveSyfuhs in case he has tips on getting more info from the Windows auth stack.

@SteveSyfuhs
Copy link

Structure looks about right:

AP-REP          ::= [APPLICATION 15] SEQUENCE {
        pvno            [0] INTEGER (5), --> 5
        msg-type        [1] INTEGER (15), --> 15
        enc-part        [2] EncryptedData -- EncAPRepPart ----->
}

EncryptedData   ::= SEQUENCE {
        etype   [0] Int32, --> 18 (AES)
        kvno    [1] UInt32 OPTIONAL, --> NULL
        cipher  [2] OCTET STRING --> 111 bytes
}

It's unlikely a structural issue and more likely the client and server not agreeing on the session key. I'm not sure how the client gets to InvalidToken but I'd guess it's translated from SEC_E_INVALID_TOKEN. Looking at the code there's a handful of places where this is set:

ISC:

  • Missing output token buffer
  • Missing input token buffer && missing mechlist buffer
  • Internal state machine failure -- unlikely

Which none of these come from session key disagreement.

Might try enabling logging on the client and see if the provides anything more specific: https://learn.microsoft.com/en-us/troubleshoot/windows-server/active-directory/enable-kerberos-event-logging. Otherwise we might need to dig into the internal tracing and see what its complaining about.

@danghyan
Copy link
Author

I’ve discovered something that might be useful.

The source code for the location where the error occurs is here - https://github.com/dotnet/runtime/blob/main/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/AuthenticationHelper.NtAuth.cs

If the Kerberos authentication header is set manually, the check passes at line 89 without validating the token. It simply checks the status. The authContext.GetOutgoingBlob(challengeData, out statusCode) method is not even called.

However, if the header is not set manually, the first request is made at line 89, where we get a NeedContinue response and proceed further. At line 209, we then receive an InvalidToken error.

In both cases, the tokens are sent and generated correctly. The difference lies in how the token is verified.

It’s also unclear why it works fine for half of my colleagues. It could be related to a Windows update or some corrupted system package, such as sspicli.dll, which is used to calculate the InvalidToken error code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-System.Net.Http untriaged New issue has not been triaged by the area owner
Projects
None yet
Development

No branches or pull requests

4 participants