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

Sync call freezes with CreateChannelWithIssuedToken #5100

Closed
martintro opened this issue Apr 16, 2023 · 12 comments · Fixed by #5136
Closed

Sync call freezes with CreateChannelWithIssuedToken #5100

martintro opened this issue Apr 16, 2023 · 12 comments · Fixed by #5136
Assignees
Labels

Comments

@martintro
Copy link

Describe the bug
I use a binding with SecureConversation over NetTcp and IssuedTokenOverTransport as secureConversationBootstrap authentication method (see discussions in #5029). To pass the token I have based my code on discussions here #4837 and #4866 as well as the .NET framework implementation. I have used dotnet-svcutil to generate the service reference (client side).

What I have is

public static TChannel CreateChannelWithIssuedToken<TChannel>(this ChannelFactory<TChannel> factory, SecurityToken token)
{
  SamlClientCredentials clientCredentials = new SamlClientCredentials(token);
  factory.Endpoint.EndpointBehaviors.Remove(typeof (ClientCredentials));
  factory.Endpoint.EndpointBehaviors.Add(clientCredentials);
  return factory.CreateChannel();
}

private class SamlSecurityTokenProvider : SecurityTokenProvider
{
  private readonly SecurityToken _securityToken;

  public SamlSecurityTokenProvider(SecurityToken securityToken) => this._securityToken = securityToken;

  protected override SecurityToken GetTokenCore(TimeSpan timeout) => this._securityToken;
}

private class SamlSecurityTokenManager : ClientCredentialsSecurityTokenManager
{
  private readonly SamlClientCredentials _samlClientCredentials;

  public SamlSecurityTokenManager(SamlClientCredentials samlClientCredentials) : base((ClientCredentials) samlClientCredentials)
  {
	this._samlClientCredentials = samlClientCredentials;
  }

  public override SecurityTokenProvider CreateSecurityTokenProvider(SecurityTokenRequirement tokenRequirement)
  {
    if (IsSecureConversation(tokenRequirement))
    {
      return base.CreateSecurityTokenProvider(tokenRequirement);
    }
    return new SamlSecurityTokenProvider(this.samlClientCredentials.ProofToken);
  }

  private bool IsSecureConversation(SecurityTokenRequirement requirement) => requirement.TokenType == "http://schemas.microsoft.com/ws/2006/05/servicemodel/tokens/SecureConversation";
}

private class SamlClientCredentials : ClientCredentials
{
  public SamlClientCredentials(SecurityToken securityToken) => this.SecurityToken = securityToken;

  private SamlClientCredentials(SamlClientCredentials other) : base((ClientCredentials) other)
  {
	this.SecurityToken = other.SecurityToken;
  }

  public SecurityToken SecurityToken { get; }

  protected override ClientCredentials CloneCore() => (ClientCredentials) new SamlClientCredentials(this);

  public override SecurityTokenManager CreateSecurityTokenManager() => (SecurityTokenManager) new SamlSecurityTokenManager(this);
}

When I then use it, I do:

var factory = new ChannelFactory<IService>(binding, endpointAddress);
var channel = factory.CreateChannelWithIssuedToken(token);
var response = channel.Test(new TestRequest());

which just freezes the client forever until it is stopped. I can see the SCT/RST in svclog for the service, but then nothing more happens. If I switch to the async generated method like this, it works:

var factory = new ChannelFactory<IService>(binding, endpointAddress);
var channel = factory.CreateChannelWithIssuedToken(token);
var response = channel.TestAsync(new TestRequest());
response.Wait();

To Reproduce
Steps to reproduce the behavior:
See above in the description.

Expected behavior
To get response from service even when sync method is used.

@mconnew Maybe you have an idea here? :)

@mconnew
Copy link
Member

mconnew commented Apr 18, 2023

Would you be able to test with the latest 6.0-rc pre-release packages? I recently fixed a problem where mixing sync and async calls could cause the request to freeze and might be the cause of your problem. I also fixed some sync over async issues which might also contribute to the problem. If you still have problems, I'll investigate further.

@martintro
Copy link
Author

We have already switched to the new rc packages, but sadly it didn’t help. The problem is still there.

@mconnew
Copy link
Member

mconnew commented Apr 19, 2023

There's only one thing I can think of which might be causing this, and it's a long shot, try overriding SecurityTokenProvider.GetTokenCoreAsync as well.

If that doesn't help, is it possible to create a minimal reproduction app? If I have an app which demonstrates the problem, I can work out what the problem is. We still have a little bit of time (not much) to get last minute fixes into the 6.0 release.

@martintro
Copy link
Author

We tried to override SecurityTokenProvider.GetTokenCoreAsync as well, sadly without success.

We understand that you need a reproduction app, but we cannot find out an easy way to get a SecurityToken that can be passed from the example client and be accepted by the example WCF service. Do you have some idea how to create such token without creating a minimal STS service?

@mconnew
Copy link
Member

mconnew commented Apr 21, 2023

You could override the authenticator on the service side to accept any token and basically skip validation. Then you can provide a stale/dummy on the client side.

@martintro
Copy link
Author

Alright, so I have a small repro solution. What I found out during the creation is that it might have something to do with the WPF button click event, since it did work when calling it from a regular console app. There are some shortcuts in the code, especially regarding token handling, so please ignore the details :)

Review that you have a cert that matches criteria in DotnetWcfIssueReproServiceHost/app.config (element serviceCertificate). MainWindow.xaml.cs has comments for the line that works and the one that hangs.

Here is the source code: https://github.com/martintro/DotnetWcfIssueRepro

Hopefully you'll find something, thanks in advance!

@mconnew
Copy link
Member

mconnew commented May 2, 2023

Thanks for the repro. I see the problem. It should be quite easy to fix. As a short term work around you can do one of the following:

var response = Task.Factory.StartNew(() => channel.GetData(1), TaskCreationOptions.PreferFairness).GetAwaiter().GetResult();

or

var response = channel.GetDataAsync(1).ConfigureAwait(false).GetAwaiter().GetResult(); 

or

            string response;
            var syncContext = SynchronizationContext.Current;
            try
            {
                SynchronizationContext.SetSynchronizationContext(null);
                response = channel.GetData(1);
            }
            finally
            {
                SynchronizationContext.SetSynchronizationContext(syncContext);
            }

That last one is effectively what the fix will be doing that I make to WCF.

@martintro
Copy link
Author

Great to hear that you solved it! Do you think the fix will be included in the 6.0.0 release? Is there any approx. date for the release yet?

@mconnew
Copy link
Member

mconnew commented May 2, 2023

I don't know if it will be in 6.0.0, but we can release a servicing release with the fix after we've released the 6.0.0 final release. I suspect we've missed the window, but I'll check and get it in if it's possible.

@martintro
Copy link
Author

I understand. What is the normal time in days/weeks/months to release a servicing release?

@mconnew
Copy link
Member

mconnew commented May 3, 2023

Turns out we can get it in time for the GA release as we have a small delay before the final package validation checks will be started so can I can sneak this in.

@martintro
Copy link
Author

That is good news. Good job!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants