Reputation: 137
I'm working in C# on a web application. At the moment the (bearer) authentication and token generation all happens in one place.
After the claims are done, we have the following code to get the ticket: -
var ticket = new AuthenticationTicket(identity, properties);
context.Validated(ticket);
Later on, we examine the ticket that's passed back to us using the following code to get the ticket: -
OAuthAuthenticationOptions.AccessTokenFormat.Unprotect(token);
This all works fine when the code is all hosted on the one machine.
When I split the code to work on different machines, I can't get the AuthenticationTicket back by calling the AccessTokenFormat.Unprotect method.
After reading this article OWIN Bearer Token Authentication - I tried setting the MachineKey in the web.config file of the new machine to match the MachineKey of the existing server.
The result is that the decryption process no longer throws errors, but it returns null for the token.
(When I did not have the correct machineKey, I got a decryption run-time error.)
Please could some-one let me know if there's an obvious mistake I'm making here?
Also, since I'm new to working with the OWIN pipeline; there may be a configuration step that I'm missing in the new project.
Thanks, David :-)
2016-05-23: Code from Startup.Configuration
public class Startup
{
public void Configuration(IAppBuilder app)
{
// Build IoC Container
var container = new Container().Initialize();
// Initialize Logging and grab logger.
MyCustomLogger.Configure();
var logger = container.GetInstance<IMyCustomLogger>();
var userIdProvider = container.GetInstance<IUserIdProvider>();
var azureSignalRInterface = new SignalRInterface();
GlobalHost.DependencyResolver.Register(typeof(ITokenService), container.GetInstance<ITokenService>);
GlobalHost.DependencyResolver.Register(typeof(IMyCustomLogger), () => logger);
GlobalHost.DependencyResolver.Register(typeof(IUserIdProvider), () => userIdProvider);
GlobalHost.DependencyResolver.Register(typeof(IExternalMessageBus), () => azureSignalRInterface);
GlobalHost.DependencyResolver.Register(typeof(ISerializer<>), () => typeof(JsonSerializer<>));
app.Use<ExceptionHandlerMiddleware>(logger, container);
app.Use<StructureMapMiddleware>(container);
// Setup Authentication
var authConfig = container.GetInstance<OwinAuthConfig>();
authConfig.ConfigureAuth(app);
// Load SignalR
app.MapSignalR("/signalR", new HubConfiguration()
{
EnableDetailedErrors = false,
EnableJSONP = true,
EnableJavaScriptProxies = true
});
}
}
The Container().Initialize just sets up some registries for StructureMap's dependence injection with the following code: -
public static IContainer Initialize(this IContainer container)
{
container.Configure(x => {
x.AddRegistry<ServiceRegistry>();
x.AddRegistry<AlertsRegistry>();
x.AddRegistry<SignalRRegistry>();
});
return container;
}
Also, my Global.asax.cs file looks like this: -
protected void Application_Start()
{
//GlobalConfiguration.Configure(WebApiConfig.Register);
GlobalConfiguration.Configure(config =>
{
AuthConfig.Register(config);
WebApiConfig.Register(config);
});
}
And the AuthConfig class looks like this: -
public static class AuthConfig
{
/// <summary>
/// Registers authorization configuration with global HttpConfiguration.
/// </summary>
/// <param name="config"></param>
public static void Register(HttpConfiguration config)
{
// Forces WebApi/OAuth to handle authentication.
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
}
}
Where OAuthDefaults.AuthenticationType
is string constant.
Finally, my OwinAuthConfig code is as follows: -
public class OwinAuthConfig
{
public static OAuthAuthorizationServerOptions OAuthAuthorizationOptions { get; private set; }
public static OAuthBearerAuthenticationOptions OAuthAuthenticationOptions { 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)
{
// Configure the application for OAuth based flow
PublicClientId = "MyCustom.SignalRMessaging";
OAuthAuthorizationOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Authenticate"), // PathString.FromUriComponent("https://dev.MyCustom-api.com/Authenticate"),
Provider = new MyCustomDbLessAuthorizationProvider(
PublicClientId),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
// TODO: change when we go to production.
AllowInsecureHttp = true
};
// Enable the application to use bearer tokens to authenticate users
app.UseOAuthAuthorizationServer(OAuthAuthorizationOptions);
OAuthAuthenticationOptions = new OAuthBearerAuthenticationOptions
{
Provider = new MyCustomDbLessAuthenticationProvider()
};
app.UseOAuthBearerAuthentication(OAuthAuthenticationOptions);
}
public static AuthenticationTicket UnprotectToken(string token)
{
return OAuthAuthenticationOptions.AccessTokenFormat.Unprotect(token);
}
public void ConfigureHttpAuth(HttpConfiguration config)
{
config.Filters.Add(new AuthorizeAttribute());
}
}
2016-05-26: Added config file snippets. So here's the config on the server that generates the tokens: -
<system.web>
<machineKey
validationKey="..."
decryptionKey="..." validation="SHA1" decryption="AES" />
<authentication mode="None" />
<compilation debug="true" targetFramework="4.5" />
<httpRuntime targetFramework="4.5" />
<customErrors mode="Off" />
</system.web>
and here's the config on the SignalR server that tries to use the token: -
<system.web>
<machineKey
validationKey="..."
decryptionKey="..." validation="SHA1" decryption="AES" />
<authentication mode="None" />
<compilation debug="true" targetFramework="4.5.2" />
<httpRuntime targetFramework="4.5.2" />
<customErrors mode="Off" />
</system.web>
Upvotes: 4
Views: 3243
Reputation: 317
Another possibilities is the machine key in the web.config has changed after the web has been deployed, causing the machine key in the compiled dll doesn't match the machine key in the dll.
Upvotes: 0
Reputation: 1532
In the resource server you should use OAuthBearerAuthenticationOptions.AccessTokenFormat
property instead of OAuthAuthorizationServerOptions.AccessTokenFormat
. See links for documentation.
For AuthenticationTokenReceiveContext
in IAuthenticationTokenProvider.Receive()
method you also could do context.DeserializeTicket(context.Token);
.
As you pointed, the MachineKey should be the same in both servers.
I hope this helps.
EDIT (2016-05-24)
public async Task ReceiveAsync(AuthenticationTokenReceiveContext context)
{
context.DeserializeTicket(context.Token);
// Now you can access to context.Ticket
...
}
Upvotes: 1