500 /signalr/negotiate for deployed app in Azure - azure

When deployed to Azure SignalR doens't work works on localhost but doesn't work when deployed to Azure
It responds with a 500 internal server error response when doing the signalr/negotiate request.
Navigating manually to the negotiate url, I got a more detailed error explanation. 'CryptographicException: The data protection operation was unsuccessful...'
Everything works fine locally, using IISExpress.
How do I fix this?

I think this is the solution. This worked for me without making any code changes:
Azure WebApps is configured to not load user profile by default and this causes the exception. In Azure App Settings, create an Application Setting called WEBSITE_LOAD_USER_PROFILE and set it to 1. This will load the user profile.
https://www.magnetismsolutions.com/blog/jaredjohnson/2015/12/18/resolving-cryptography-issues-with-the-dynamics-crm-sdk-in-azure-web-apps

Got it working. I needed to use appBuilder.SetDataProtectionProvider
app.UseAppBuilder(appBuilder =>
{
appBuilder.SetDataProtectionProvider(new MachineKeyProtectionProvider());
appBuilder.Map("/signalr", map =>
{
var hubConfiguration = new HubConfiguration
{
EnableDetailedErrors = true
};
map.RunSignalR(hubConfiguration);
});
});
I use katana extension methods to bridge the IAppBuilder to IApplicationBuilder.
This allows your owin middleware to connect to asp.net core. It is important to use the RunSignalr method.
internal class MachineKeyProtectionProvider : IDataProtectionProvider
{
public IDataProtector Create(params string[] purposes)
{
return new MachineKeyDataProtector(purposes);
}
}
internal class MachineKeyDataProtector : IDataProtector
{
private readonly string[] _purposes;
public MachineKeyDataProtector(string[] purposes)
{
_purposes = purposes;
}
public byte[] Protect(byte[] userData)
{
//return MachineKey.Protect(userData, _purposes);
return userData;
}
public byte[] Unprotect(byte[] protectedData)
{
//return System.Web.Security.MachineKey.Unprotect(protectedData, _purposes);
return protectedData;
}
}

Related

Redirect ends up outside of application

I am working on a .Net Core web application and we would like to be able to redirect a type of url to our custom error page. The site is hosted on Azure and it seems that this error is not being handled in the application. Here is the type of URL I am working with:
www.mywebsite.com/%22http://www.yahoo.com/%22
The error page that is presented is the following:
The page cannot be displayed because an internal server error has occurred.
In addition when I check the live HTTP traffic on Azure it does not show an error occurring.
Edit:
Apparently azure cannot handle this type request at all: https://azure.microsoft.com/%22/http://www.google.com/
It looks for the config file within the second url. Does anyone know where I can file a bug with Microsoft?
I haven't tested this on Azure, however it works on our server.
Configure exceptions handling in Startup class.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
// ...
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/error");
}
// ...
app.UseStatusCodePagesWithRedirects("/error/{0}");
// ...
}
And error controller.
[Route("[controller]")]
public class ErrorController : Controller
{
[Route("")]
public IActionResult Index()
{
return View();
}
[Route("{httpStatusCode}")]
public IActionResult Index(int httpStatusCode)
{
// handle by error code
switch (httpStatusCode)
{
case (int)HttpStatusCode.NotFound:
return View("NotFound");
default:
return View();
}
}
}

404 /signalr/negotiate for deployed app in Azure

SignalR works on localhost but doesn't work when is deployed in Azure
Asp.net Core 1.0.0 (.Net Framework 4.6.1)
SignalR.Core 2.2.1
public static void UseSignalR2(this IApplicationBuilder app)
{
app.UseAppBuilder(appBuilder => {
appBuilder.MapSignalR(new HubConfiguration());
});
GlobalHost.HubPipeline.AddModule(new ErrorHandlingPipelineModule());
GlobalHost.HubPipeline.AddModule(new LoggingPipelineModule());
}
SignalR.js 2.2.1 with default settings
$.connection.hub.url = '/signalr';
Expected behavior
200 for url:
https://(name).azurewebsites.com/signalr/negotiate?clientProtocol=1.5&connectionData=%5B%7B%22name%22%3A%22productsimporthub%22%7D%5D&_=1472811629592
Actual behavior
/signalr/negotiate - on localhost returns 200 but for deployed app in azure returns 404
/signalr - works on both - Protocol error: Unknown transport.
/signalr/hubs - works on both - returns the SignalR js correctly
To find out the real cause of the issue you need to navigate to the negotiate url, and look for the response.
If the response tells you something about a 'CryptographicException: The data protection operation was unsuccessful...'. This is how to fix it.
1) Create a custom IDataProtectionProvider
2) Configure signalr
internal class MachineKeyProtectionProvider : IDataProtectionProvider
{
public IDataProtector Create(params string[] purposes)
{
return new MachineKeyDataProtector(purposes);
}
}
internal class MachineKeyDataProtector : IDataProtector
{
private readonly string[] _purposes;
public MachineKeyDataProtector(string[] purposes)
{
_purposes = purposes;
}
public byte[] Protect(byte[] userData)
{
//return MachineKey.Protect(userData, _purposes);
return userData;
}
public byte[] Unprotect(byte[] protectedData)
{
//return System.Web.Security.MachineKey.Unprotect(protectedData, _purposes);
return protectedData;
}
}
I use katana extension methods to bridge the IAppBuilder to IApplicationBuilder.
This allows your owin middleware to connect to asp.net core. It is important to use the RunSignalr method
app.UseAppBuilder(appBuilder =>
{
appBuilder.SetDataProtectionProvider(new MachineKeyProtectionProvider());
appBuilder.Map("/signalr", map =>
{
var hubConfiguration = new HubConfiguration
{
EnableDetailedErrors = true
};
map.RunSignalR(hubConfiguration);
});
});

¿Why OAuth bearer auth not working in a MVC Web API when publishing to an API App in Azure?

I'm trying to use OAuth bearer authentication in a Web API application, everything works fine on my local IIS, I'm able to get the token as you can see here:
But when I publish my project to a ApiApp in Azure it doesn't work at all. I get the response:
{
"message": "No HTTP resource was found that matches the request URI 'https://mysite.azurewebsites.net/API/login'.",
"messageDetail": "No type was found that matches the controller named 'login'."
}
As shown in here:
My Startup.Auth class looks like:
public partial class Startup
{
public static OAuthAuthorizationServerOptions OAuthOptions { get; private set; }
public static string PublicClientId { get; private set; }
// For more information on configuring authentication, please visit http://go.microsoft.com/fwlink/?LinkId=301864
public void ConfigureAuth(IAppBuilder app)
{
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
// Configure the application for OAuth based flow
PublicClientId = "self";
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/login"),
Provider = new ApplicationOAuthProvider(),
AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
// In production mode set AllowInsecureHttp = false
//#if DEBUG
AllowInsecureHttp = true
//#endif
};
// Enable the application to use bearer tokens to authenticate users
app.UseOAuthAuthorizationServer(OAuthOptions);
}
}
I hope you can tell me what I'm doing wrong.
I've just find out that I was using an incorrect url. I was using /api/login instead of just /login.

Do the Request filters get run from BasicAppHost?

I know that the services get wired-up by instantiating the BasicAppHost, and the IoC by using the ConfigureContainer property, but where is the right place to add the filters? The test in question never fire the global filter:
[TestFixture]
public class IntegrationTests
{
private readonly ServiceStackHost _appHost;
public IntegrationTests()
{
_appHost = new BasicAppHost(typeof(MyServices).Assembly)
{
ConfigureContainer = container =>
{
//
}
};
_appHost.Plugins.Add(new ValidationFeature());
_appHost.Config = new HostConfig { DebugMode = true };
_appHost.GlobalRequestFilters.Add(ITenantRequestFilter);
_appHost.Init();
}
private void ITenantRequestFilter(IRequest req, IResponse res, object dto)
{
var forTennant = dto as IForTenant;
if (forTennant != null)
RequestContext.Instance.Items.Add("TenantId", forTennant.TenantId);
}
[TestFixtureTearDown]
public void TestFixtureTearDown()
{
_appHost.Dispose();
}
[Test]
public void CanInvokeHelloServiceRequest()
{
var service = _appHost.Container.Resolve<MyServices>();
var response = (HelloResponse)service.Any(new Hello { Name = "World" });
Assert.That(response.Result, Is.EqualTo("Hello, World!"));
}
[Test]
public void CanInvokeFooServiceRequest()
{
var service = _appHost.Container.Resolve<MyServices>();
var lead = new Lead
{
TenantId = "200"
};
var response = service.Post(lead); //Does not fire filter.
}
}
ServiceStack is set at 4.0.40
Updated
After perusing the ServiceStack tests (which I highly recommend BTW) I came across a few example of the AppHost being used AND tested. It looks like the "ConfigureAppHost" property is the right place to configure the filters, e.g.
ConfigureAppHost = host =>
{
host.Plugins.Add(new ValidationFeature());
host.GlobalRequestFilters.Add(ITenantRequestFilter);
},
ConfigureContainer = container =>
{
}
Updated1
And they still don't fire.
Updated2
After a bit of trial and error I think it's safe to say that NO, the filters are not hooked up while using the BasicAppHost. What I have done to solve my problem was to switch these tests to use a class that inherits from AppSelfHostBase, and use the c# servicestack clients to invoke the methods on my service. THIS does cause the global filters to be executed.
Thank you,
Stephen
No the Request and Response filters only fire for Integration Tests where the HTTP Request is executed through the HTTP Request Pipeline. If you need to test the full request pipeline you'd need to use a Self-Hosting Integration test.
Calling a method on a Service just does that, i.e. it's literally just making a C# method call on a autowired Service - there's no intermediate proxy magic intercepting the call in between.

Servicestack How to get the jsonserviceclient to work with custom authentication

Looking at the ServiceStack.UseCases example project. I am trying to use the jsonserviceclient to call the HelloRequest service after I have called the authentication service. No matter what I do it appears to be failing and returning the Not Found error message. Anyone know what I am doing wrong?
protected void Button1_Click(object sender, EventArgs e)
{
var baseUrl = Request.Url.GetLeftPart(UriPartial.Authority) + "/api";
var client = new JsonServiceClient(baseUrl);
client.UserName = "admin";
client.Password = "123";
client.SetCredentials("admin", "123");
client.AlwaysSendBasicAuthHeader = true;
client.Send(new HelloRequest { Name = "Mike" });
}
The server has the service configured like
public class AppHost : AppHostBase
{
public AppHost() : base("Custom Authentication Example", typeof(AppHost).Assembly) { }
public override void Configure(Container container)
{
// register storage for user sessions
container.Register<ICacheClient>(new MemoryCacheClient());
// Register AuthFeature with custom user session and custom auth provider
Plugins.Add(new AuthFeature(
() => new CustomUserSession(),
new[] { new CustomCredentialsAuthProvider() }
));
}
}
What I would really like is a good solution to the following problem I have. I have an existing system with an existing user database and custom authentication process. I am now trying to expose the functions of the system as web services using servicestack. I program using bog standard webforms so the MVC examples dont work too well for me. I am just looking for the best solution for my particular scenario where I can authenticate callers to my webservice with the majority of my clients using webforms in .NET
You also need to have Basic Auth enabled on the server you're trying to authenticate with.
See the SocialBootstrapApi AppHost for an example:
Plugins.Add(new AuthFeature(
() => new CustomUserSession(), //Use your own typed Custom UserSession type
new IAuthProvider[] {
new BasicAuthProvider(), //Sign-in with Basic Auth
}));

Resources