License error attempting to implement AppHostHttpListenerBase - servicestack

I'm attempting to create a second App host for self-hosting, so my unit tests are in the same process as the service, to aid debugging. I created the new app host as follows. When my Unit test calls the .Init() method, I receive the following error:
ServiceStack.LicenseException was unhandled by user code
HResult=-2146233088
Message=The free-quota limit on '10 ServiceStack Operations' has been reached. Please see https://servicestack.net to upgrade to a commercial license or visit https://github.com/ServiceStackV3/ServiceStackV3 to revert back to the free ServiceStack v3.
Source=ServiceStack.Text
The class below is in the same assembly as my real AppHost (my main ASP.NET service project)., so there is definitely a license key in the web.config. file.
public class ServiceTestAppHost : AppSelfHostBase
{
public const string BaseUrl = "http://localhost/dvsvc";
public ServiceTestAppHost()
: base("Test Web Services", typeof(DV.Svc.Interface.HelloService).Assembly) { }
public override void Configure(Funq.Container container)
{
ServiceStack.Text.JsConfig.IncludeNullValues = true;
ServiceStack.Text.JsConfig.DateHandler = ServiceStack.Text.DateHandler.ISO8601;
ServiceStack.Text.JsConfig.ExcludeTypeInfo = true; //exclude the type specification
ServiceStack.Formats.HtmlFormat.Humanize = false;
//most apps use credentials auth. the TVTI player uses Basic auth
Plugins.Add(new AuthFeature(() =>
new DVAuthUserSession(),
new ServiceStack.Auth.IAuthProvider[] { new DVCredentialsAuthProvider(), new DVBasicAuthProvider() })
/*{ HtmlRedirect = null }*/
);
//in memory cache
container.RegisterAs<MemoryCacheClient, ICacheClient>();
SetConfig(new HostConfig { DebugMode = true });
}
}

Self hosted applications don't read from Web.config, they read from the app config App.config, so you would have to create an appropriate config file for the host executable.
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<add key="servicestack:license" value="{licenseKeyText}" />
</configuration>

Related

Different authentication schema (Windows, Bearer) for each route

I need to add single-sign-on using Windows Authentication to my intranet Angular web application (hosted on IIS) which uses a JWT Bearer token for authentication. The controllers are secured using the [Authorize] attribute and JWT Bearer token authentication is working. All of the controllers are exposed under the api/ route.
The idea is to publish a new SsoController under the sso/ route, which should be secured with Windows Authentication and that exposes a WindowsLogin action that returns a valid bearer token for the application.
Back when I was using ASP.net Web Forms it was quite easy, you only had to enable Windows Authentication in the web.config/system.webServer section, disable it application-wide in the system.web section and then enable it again under a <location path="sso"> tag. This way ASP.net generated the NTLM/Negotiate challenges only for requests under the sso route.
I got it almost working - the SsoController gets the Windows user name and creates the JWT token just fine, but the pipeline is still generating the WWW-Authenticate: NTLM and WWW-Authenticate: Negotiate headers for all HTTP 401 responses, not just for the ones under the sso route.
How can I tell the pipeline that I want only Anonymous or Bearer auth for all of the api/ requests?
Thanks in advance for your help.
Program.cs
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.UseIISIntegration();
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
// Set up data directory
services.AddDbContext<AuthContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("AuthContext")));
services.AddAuthentication(IISDefaults.AuthenticationScheme);
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = "AngularWebApp.Web",
ValidAudience = "AngularWebApp.Web.Client",
IssuerSigningKey = _signingKey,
ClockSkew = TimeSpan.Zero //the default for this setting is 5 minutes
};
options.Events = new JwtBearerEvents
{
OnAuthenticationFailed = context =>
{
if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
{
context.Response.Headers.Add("Token-Expired", "true");
}
return Task.CompletedTask;
}
};
});
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
// In production, the Angular files will be served from this directory
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/dist";
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseSpaStaticFiles();
app.UseAuthentication();
app.UseWhen(context => context.Request.Path.StartsWithSegments("/sso"),
builder => builder.UseMiddleware<WindowsAuthMiddleware>());
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller}/{action=Index}/{id?}");
});
app.UseSpa(spa =>
{
// To learn more about options for serving an Angular SPA from ASP.NET Core,
// see https://go.microsoft.com/fwlink/?linkid=864501
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment())
{
spa.UseAngularCliServer(npmScript: "start");
}
});
}
WindowsAuthMiddleware.cs
public class WindowsAuthMiddleware
{
private readonly RequestDelegate next;
public WindowsAuthMiddleware(RequestDelegate next)
{
this.next = next;
}
public async Task Invoke(HttpContext context)
{
if (!context.User.Identity.IsAuthenticated)
{
await context.ChallengeAsync(IISDefaults.AuthenticationScheme);
return;
}
await next(context);
}
}
web.config
<system.webServer>
<aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="true"/>
<security>
<authentication>
<anonymousAuthentication enabled="true" />
<windowsAuthentication enabled="true" />
</authentication>
</security>
</system.webServer>
So, I spent the last few days investigating this problem and I got a working - if a bit hacky - solution.
It turns out that the main problem is that IIS will handle the Windows Authentication negotiation for all 401 responses sent by the application. It's something that's done at a lower level as soon as you enable Windows Authentication in IIS (or in the system.webServer section), and I haven't been able to find a way to bypass this behaviour. I actually did a test with a classic Web Form app and it works the same - the reason I never noticed this is that classic Forms Authentication rarely generates 401 responses, rather it uses redirects (30x) to take the user to the login page.
This gave me an idea: I could add another middleware to the pipeline that rewrites 401 responses generated by the authorization infrastructure to another, rarely used HTTP code, and detect that in my client Angular app to make it behave as a 401 (by refreshing an access token, or denying router navigation, etc). I used HTTP error 418 "I'm a teapot" since it's an existing but unused code. Here is the code:
ReplaceHttp401StatusCodeMiddleware.cs
public class ReplaceHttp401StatusCodeMiddleware
{
private readonly RequestDelegate next;
public ReplaceHttp401StatusCodeMiddleware(RequestDelegate next)
{
this.next = next;
}
public async Task Invoke(HttpContext context)
{
await next(context);
if (context.Response.StatusCode == 401)
{
// Replace all 401 responses, except the ones under the /sso paths
// which will let IIS trigger the Windows Authentication mechanisms
if (!context.Request.Path.StartsWithSegments("/sso"))
{
context.Response.StatusCode = 418;
context.Response.Headers["X-Original-HTTP-Status-Code"] = "401";
}
}
}
}
Startup.cs
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
...
// Enable the SSO login using Windows Authentication
app.UseWhen(
context => context.Request.Path.StartsWithSegments("/sso"),
builder => builder.UseMiddleware<WindowsAuthMiddleware>());
app.UseMiddleware<ReplaceHttp401StatusCodeMiddleware>();
...
}
The middleware also injects the original status code in the response for further reference.
I also applied to my code the suggestion from Mickaƫl Derriey to use Authorization policies because it makes the controllers cleaner, but it's not necessary for the solution to work.
Welcome to StackOverflow! That's an interesting quesiton you have here.
First, let me state that I didn't test any of the content in this answer.
Using authorization policies to drive sources of authentication
I like the idea behind the WindowsAuthMiddleware you created, and how it's conditionally inserted in the pipeline if the URL starts with /sso.
MVC integrated with the authorization system and provides the same capabilities with authorization policies. The result is the same, and prevents you from having to write low-level code.
You can define authorization policies in the ConfigureServices method. In your case, if I'm not mistaken, there are two policies:
all requests to /sso should be authenticated with Windows authenticated; and
all other requests should be authenticated with JWTs
services.AddAuthorization(options =>
{
options.AddPolicy("Windows", new AuthorizationPolicyBuilder()
.AddAuthenticationSchemes(IISDefaults.AuthenticationScheme)
.RequireAuthenticatedUser()
.Build());
options.AddPolicy("JWT", new AuthorizationPolicyBuilder()
.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme)
.RequireAuthenticatedUser()
.Build());
});
You can then reference those policies by name in the [Authorize] attributes used to decorate your controllers and/or actions.
[Authorize("Windows")]
public class SsoController : Controller
{
// Actions
}
[Authorize("JWT")]
public class ApiController : Controller
{
// Actions
}
Doing so means that the Windows authentication handler will not run against /api requests, hence the responses should not contain the WWW-Authenticate: NTLM and WWW-Authenticate: Negotiate headers.
Removing automatic authentication of all requests
When you pass an authentication scheme as an argument of AddAuthentication, this means the authentication middleware will try to authenticate every request against that scheme.
This is useful when you have one authentication scheme, but in this case, you could think about removing it, as even for requests to /sso, the JWT handler will analyze the request for a token.
Two calls to AddAuthentication
You should only have one call to AddAuthentication:
the first one sets the IIS authentication scheme as a default so the handler should run on every request;
the second call overwrites that setting and set the JWT scheme as the default one
Let me know how you go!

Functioning App on local server fails on Azure

I have an Angular Web app with an API that is functioning perfectly (well, as perfectly as any app under development functions) on my local server, but when I migrate it and its associated databases to an App Service on Azure every /api call fails with 500.
So thinking that the problem was with the databases I altered the connection strings on my local development server to point to the Azure databases. I found one small problem this way, I has mispelled the username in the connection string. SO I fixed that and it runs perfectly on my local server while accessing the Azure databases, but as soon as I run it on the Azure App Service using the same connection strings every call to /api fails with Internal Server Error 500.
All regular pages are served perfectly and Angular routing works just fine. Only accessing content from the DB fails. I have been at this for a few days and have no idea what to do next. Any advice welcomed.
I am using OpenIddict for authentication so I tagged that, but I can't see anyway that is relevant. Oddly though, and there is a clue here somewhere, the authentication call to "/connect/token" works and returns a valid token, but "/api/..." URLs do not.
I am using Asp Net Core 2.1 if that is relevant.
More Information
I tried the detailed logs as suggested, but they were hardly detailed. But I did note one interesting item. In the error there was the following information:
Requested URL: https://mysite.azuurewebsites.net/api/accounts/getusers
Physical Path: D:\home\site\wwwroot\api\accounts\getusers
Now this app is using MVC so there is no such Physical Path. The Controller is decorated with:
[Route("api/accounts")]
and the Action is decorated as:
[Authorize(Roles = "Somerole")]
[HttpGet("GetUsers"), Produces("application/json")]
It seems to me the route mapping is failing. But this works beautifully on my local development computer. What could be different on the Azure App Service? Is there some special setting I need to set in the portal to allow MVC? I can't imagine why the portal should care about such matters.
Even More Information
Using Postman, if I access /api/someValidUrl with a valid Bearer token I get a 500 error. If I remove the Authorization header then I get a 401 returned.
I started off by saying I didn't think it had anything to do with OpenIddict, but maybe I was wrong. My Authorization Controller simply creates the token. All the checking for validity is done by OpenIddict.
A Huge Clue
I added an ExceptionHandler and then used Postman to make an API request and that yielded the following exception:
<h1>Error: IDX20803: Unable to obtain configuration from: '[PII is hidden by default. Set the 'ShowPII' flag in IdentityModelEventSource.cs to true to reveal it.]'.</h1>
at Microsoft.IdentityModel.Protocols.ConfigurationManager`1.GetConfigurationAsync(CancellationToken cancel)
at Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler.HandleAuthenticateAsync()
at Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler.HandleAuthenticateAsync()
at Microsoft.AspNetCore.Authentication.AuthenticationHandler`1.AuthenticateAsync()
at Microsoft.AspNetCore.Authentication.AuthenticationService.AuthenticateAsync(HttpContext context, String scheme)
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Cors.Infrastructure.CorsMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware.Invoke(HttpContext context)
I found an explanation here but I don't fully understand this but it looks as though MS middleware on Azure is incorrectly trying to interpret it as an Azure AD request. The only thing I know for sure is I do not have a file called IdentityModelEventSource.cs in my project.
For reference https://mywebsite.azurewebsites.net/.well-known/openid-configuration returns:
{
"issuer": "https://mywebsite.azurewebsites.net/",
"token_endpoint": "https://mywebsite.azurewebsites.net/connect/token",
"jwks_uri": "https://mywebsite.azurewebsites.net/.well-known/jwks",
"grant_types_supported": [
"password"
],
"scopes_supported": [
"openid",
"email",
"profile",
"roles"
],
"claims_supported": [
"aud",
"exp",
"iat",
"iss",
"jti",
"sub"
],
"subject_types_supported": [
"public"
],
"token_endpoint_auth_methods_supported": [
"client_secret_basic",
"client_secret_post"
],
"claims_parameter_supported": false,
"request_parameter_supported": false,
"request_uri_parameter_supported": false
}
Perhaps with this information someone can point me in the right direction.
New Startup.cs
I took Pinpoint's advice and changed from JWT. The new Startup follows:
using AspNet.Security.OpenIdConnect.Primitives;
using Microsoft.AspNetCore.Builder;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using SIAngular.DBContexts;
using SIAngular.Models;
using SIAngular.Services;
using OpenIddict.Abstractions;
using Microsoft.IdentityModel.Tokens;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Identity;
using System;
using Microsoft.Extensions.Logging;
namespace SIAngular
{
public class Startup
{
private readonly IHostingEnvironment env;
public Startup(IHostingEnvironment env, IConfiguration configuration)
{
Configuration = configuration;
this.env = env;
SIDBConnectionString = Configuration.GetConnectionString("SIDB");
}
public static string SIDBConnectionString;
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
SymmetricSecurityKey _ssk = new SymmetricSecurityKey(Convert.FromBase64String(Configuration["Jwt:Key"]));
services.AddDbContext<ApplicationDbContext>(options =>
{
options.UseSqlServer(Configuration.GetConnectionString("SqlConnection"));
options.UseOpenIddict();
});
services.AddCors();
// Register the Identity services.
services.AddIdentityCore<ApplicationUser>(config =>
{
config.SignIn.RequireConfirmedEmail = true;
config.Password.RequireDigit = true;
config.Password.RequiredLength = 8;
config.Password.RequireLowercase = true; config.Password.RequireNonAlphanumeric = true;
config.User.RequireUniqueEmail = true;
})
.AddRoles<IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders()
.AddRoleValidator<RoleValidator<IdentityRole>>()
.AddRoleManager<RoleManager<IdentityRole>>()
.AddSignInManager<SignInManager<ApplicationUser>>();
// Configure Identity to use the same JWT claims as OpenIddict instead
// of the legacy WS-Federation claims it uses by default (ClaimTypes),
// which saves you from doing the mapping in your authorization controller.
services.Configure<IdentityOptions>(options =>
{
options.ClaimsIdentity.UserNameClaimType = OpenIdConnectConstants.Claims.Name;
options.ClaimsIdentity.UserIdClaimType = OpenIdConnectConstants.Claims.Subject;
options.ClaimsIdentity.RoleClaimType = OpenIdConnectConstants.Claims.Role;
});
services.AddOpenIddict()
// Register the OpenIddict core services.
.AddCore(options =>
{
// Configure OpenIddict to use the Entity Framework Core stores and models.
options.UseEntityFrameworkCore()
.UseDbContext<ApplicationDbContext>();
})
// Register the OpenIddict server services.
.AddServer(options =>
{
// Register the ASP.NET Core MVC services used by OpenIddict.
// Note: if you don't call this method, you won't be able to
// bind OpenIdConnectRequest or OpenIdConnectResponse parameters.
options.UseMvc();
// Enable the token endpoint.
options.EnableTokenEndpoint("/connect/token");
options.AcceptAnonymousClients();
options.DisableScopeValidation();
// Note: the Mvc.Client sample only uses the code flow and the password flow, but you
// can enable the other flows if you need to support implicit or client credentials.
options.AllowPasswordFlow();
// Mark the "email", "profile" and "roles" scopes as supported scopes.
options.RegisterScopes(OpenIdConnectConstants.Scopes.Email,
OpenIdConnectConstants.Scopes.Profile,
OpenIddictConstants.Scopes.Roles);
// During development, you can disable the HTTPS requirement.
if (env.IsDevelopment())
options.DisableHttpsRequirement();
options.AddSigningKey(_ssk);
})
.AddValidation();
services.AddSingleton<IConfiguration>(Configuration);
services.AddScoped<IPasswordHasher<ApplicationUser>, SqlPasswordHasher>();
services.AddMvc();
services.AddTransient<IEmailSender, AuthMessageSender>();
services.AddTransient<ISmsSender, AuthMessageSender>();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
app.UseCors(builder =>
builder.AllowAnyOrigin()
.AllowAnyHeader()
.AllowAnyMethod()
);
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
//app.UseWebpackDevMiddleware(new Microsoft.AspNetCore.SpaServices.Webpack.WebpackDevMiddlewareOptions { HotModuleReplacement = true });
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseAuthentication();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
app.MapWhen(x => !x.Request.Path.Value.StartsWith("/api"), builder =>
{
builder.UseMvc(routes =>
{
routes.MapSpaFallbackRoute(
name: "spa-fallback",
defaults: new { controller = "Home", action = "Index" });
});
});
}
}
}
Now the problem is an exception:
InvalidOperationException: No authenticationScheme was specified, and there was no DefaultChallengeScheme found.

IIS Basic Auth Configuration .NET Core 2.1 application

I have a .NET Core 2.1 application that is running on IIS with Basic Authentication and Windows Authentication configured. Windows Authentication is working as expected. When I disable Windows Authentication and only use Basic Authentication the application does not work as expected. It prompts for credentials when visiting the index.html page and prompts again when you go to the swashbuckle UI of the API. When you try to use any of the API Endpoints it prompts for credentials and continues to prompt for credentials like it doesn't remember the user.
I have this Middleware logging what is happening
public class UserInfoMiddleWare
{
private readonly ILogger<UserInfoMiddleWare> _logger;
private readonly RequestDelegate _next;
public UserInfoMiddleWare(ILogger<UserInfoMiddleWare> logger, RequestDelegate next)
{
_logger = logger;
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
this._logger.LogError("UserInfoMiddleWare!!!!!!");
try
{
var userIdentity = context.User?.Identity;
this._logger.LogError($"user exists {(userIdentity != null)}");
var loginName = context.User?.LoginName();
this._logger.LogError($"LoginName {loginName}");
var name = userIdentity?.Name;
this._logger.LogError($"name {name}");
var identityIsAuthenticated = userIdentity?.IsAuthenticated;
this._logger.LogError($"identityIsAuthenticated {identityIsAuthenticated}");
var identityAuthenticationType = userIdentity?.AuthenticationType;
this._logger.LogError($"identityAuthenticationType {identityAuthenticationType}");
}
catch (Exception e)
{
this._logger.LogError(e, "bad middleware!");
}
await _next(context);
}
This is a sample output I am getting in my logs
[Error] UserInfoMiddleWare!!!!!!
[Error] user exists True
[Error] LoginName
[Error] name
[Error] identityIsAuthenticated False
[Error] identityAuthenticationType
Edit:
After doing some reading it looks like .NET Core does not support Basic Auth even if IIS is the proxy. If someone could confirm that with some documentation I would appreciate it.
ASP.NET Core Module is designed to only forward Windows authentication token,
https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/aspnet-core-module?view=aspnetcore-2.1
Not sure if in .NET Core 2.2 they plan to change that.

The data protection operation was unsuccessful on Azure using OWIN / Katana

I'm trying to implement password reset on an OWIN/Katana based ASP.NET MVC website running in Azure.
It works fine when run locally but fails in production.
I create a UserToken Provider
userManager.UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser>(provider.Create("PasswordReset"))
But when I attempt to generate the token as follows
var resetToken = await UserManager.GeneratePasswordResetTokenAsync(user.Id);
I get following exception.
System.Security.Cryptography.CryptographicException: The data
protection operation was unsuccessful. This may have been caused by
not having the user profile loaded for the current thread's user
context, which may be the case when the thread is impersonating.
at System.Security.Cryptography.ProtectedData.Protect(Byte[] userData, Byte[] optionalEntropy, DataProtectionScope scope)
at System.Security.Cryptography.DpapiDataProtector.ProviderProtect(Byte[]
userData)
at System.Security.Cryptography.DataProtector.Protect(Byte[] userData)
at Microsoft.Owin.Security.DataProtection.DpapiDataProtector.Protect(Byte[]
userData)
at Microsoft.AspNet.Identity.Owin.DataProtectorTokenProvider 2.d__0.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task
task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task
task)
at Microsoft.AspNet.Identity.UserManager`2.d__e9.MoveNext()
If the host server is a virtual machine it could be exactly what the error message says. Check if your Application Pool in IIS really has Load User Profile set to true like the exception says:
In the Connections pane, expand the server name, and then click Application Pools.
Right click on you Pool
Advanced Settings
I have the same problem when I try to generate token with ASP .Net identity and custom login function in Web API.
"The data protection operation was unsuccessful. This may have been
caused by not having the user profile loaded for the current thread's
user context, which may be the case when the thread is impersonating."
What I did is just simply create an Application Setting called WEBSITE_LOAD_USER_PROFILE in Microsoft Azure and set it to 1. That solution works for me.
You can see the detail here
Please see my my answer to this question. A much simpler solution can be achieved by utilizing IAppBuilder.GetDataProtectionProvider()
I found a solution. I'm not exactly sure if all steps are necessary to it work, but now my app works perfectly:
1.- Update your web.config to support securityTokenHandlers
<section name="system.identityModel" type="System.IdentityModel.Configuration.SystemIdentityModelSection, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
<section name="system.identityModel.services" type="System.IdentityModel.Services.Configuration.SystemIdentityModelServicesSection, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
in the configSections node. And
<securityTokenHandlers>
<remove type="System.IdentityModel.Tokens.SessionSecurityTokenHandler,
System.IdentityModel, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=B77A5C561934E089" />
<add
type="System.IdentityModel.Services.Tokens.MachineKeySessionSecurityTokenHandler,
System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=B77A5C561934E089">
<sessionTokenRequirement lifetime="00:30:00"></sessionTokenRequirement>
</add>
</securityTokenHandlers>
</identityConfiguration>
as a regular node.
2.- In your Startup.Auth.cs file, update your ConfigureAuth(IAppBuilder app) like this:
public void ConfigureAuth(IAppBuilder app)
{
UserManagerFactory = () =>
{
var userManager = new UserManager<SIAgroUser>(new UserStore<UserType>(new SIAgroUserDbContext()));
IDataProtectionProvider provider = app.GetDataProtectionProvider();
//userManager.UserTokenProvider = new Microsoft.AspNet.Identity.Owin.DataProtectorTokenProvider<UserType>(provider.Create("PasswordReset") );
if (provider != null)
{
userManager.UserTokenProvider = new DataProtectorTokenProvider<UsertType, string>(provider.Create("PasswordReset"));
}
return userManager;
};
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Token"),
Provider = new ApplicationOAuthProvider(PublicClientId, UserManagerFactory),
AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
AllowInsecureHttp = true
};
// Enable the application to use a cookie to store information for the signed in user
// and to use a cookie to temporarily store information about a user logging in with a third party login provider
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
// Enable the application to use bearer tokens to authenticate users
app.UseOAuthBearerTokens(OAuthOptions);
// Uncomment the following lines to enable logging in with third party login providers
//app.UseMicrosoftAccountAuthentication(
// clientId: "",
// clientSecret: "");
//app.UseTwitterAuthentication(
// consumerKey: "",
// consumerSecret: "");
//app.UseFacebookAuthentication(
// appId: "",
// appSecret: "");
//app.UseGoogleAuthentication();
}
3.- Clean up the constructor of your Startup class like this:
static Startup()
{
PublicClientId = "self";
}
That worked for me :) I hope it works for you too
This error happens for me on a shared hosting provider, at the line:
var provider = new DpapiDataProtectionProvider("SITENAME");
The solution was quite simple. First change the above line to this:
var provider = new MachineKeyProtectionProvider();
Then create a new file, which I have in my Utilities namespace, like so:
using Microsoft.Owin.Security.DataProtection;
using System.Web.Security;
namespace <yournamespace>.Utilities
{
public class MachineKeyProtectionProvider : IDataProtectionProvider
{
public IDataProtector Create(params string[] purposes)
{
return new MachineKeyDataProtector(purposes);
}
}
public class MachineKeyDataProtector : IDataProtector
{
private readonly string[] _purposes;
public MachineKeyDataProtector(string[] purposes)
{
_purposes = purposes;
}
public byte[] Protect(byte[] userData)
{
return MachineKey.Protect(userData, _purposes);
}
public byte[] Unprotect(byte[] protectedData)
{
return MachineKey.Unprotect(protectedData, _purposes);
}
}
}
Et voila! Problem solved. Just remember, in your password reset controller method, you will also have to use this provider, otherwise you will get an Invalid Token error.
I put this one on ice for a while but was forced to come back to it. I found the solution here:
Generating reset password token does not work in Azure Website
Getting the UserManager from the Owin Pipeline, as its set in App_Start/Startup.Auth.cs, works on Azure.
I'm unsure as to how this works specifically.
The DpApi should work in Azure with the solution described in the first link.
If the DpApi has a static machine key set in Web.config all server machines will be able to decrypt the encrypted data created by another machine in your webfarm is the understanding behind this.
(code as given in the standard template - from AccountController.cs)
private UserManager userManager;
public UserManager UserManager
{
get { return userManager ?? HttpContext.GetOwinContext().GetUserManager<UserManager>(); }
private set { userManager = value; }
}
After me and two other people have messing with this error for dayS we discovered something intresting in the IIS. If the Load User Profile is switched following is created in applicationhost.config
loadUserProfile="true"
but when you turn it off it also works, but now the line
loadUserProfile="false"
has been added. So difference was that default value had to be written in applicationhost.config to make it work. Some cache is recreated?

How to override ServiceStack RegistrationService Validator?

How to override ServiceStack RegistrationService Validator and add some new rules to it?
And what needs to be done to intercept the UserAuthService validation?
Here is the AppHost Config:
Plugins.Add(new CorsFeature()); //Registers global CORS Headers
RequestFilters.Add((httpReq, httpRes, requestDto) =>
{
// Handles Request and closes Responses after emitting global HTTP Headers
if (httpReq.HttpMethod == "OPTIONS")
httpRes.EndRequest();
});
// Enable the validation feature
Plugins.Add(new ValidationFeature());
// This method scans the assembly for validators
container.RegisterValidators(typeof(AppHost).Assembly);
container.Register<ICacheClient>(new MemoryCacheClient());
//var dbFactory = new OrmLiteConnectionFactory(connectionString, SqlServerDialect.Provider);
var dbFactory = new OrmLiteConnectionFactory(connectionString, SqliteDialect.Provider);
container.Register<IDbConnectionFactory>(dbFactory);
// Enable Authentication
Plugins.Add(new AuthFeature(() => new CustomUserSession(),
new IAuthProvider[] {
new CustomAuthProvider(),
}));
// Provide service for new users to register so they can login with supplied credentials.
Plugins.Add(new RegistrationFeature());
// Override the default registration validation with your own custom implementation
container.RegisterAs<CustomRegistrationValidator, IValidator<Registration>>();
container.Register<IUserAuthRepository>(c => new CustomAuthRepository(c.Resolve<IDbConnectionFactory>()));
ServiceStack validators are pretty easy to use. The 'SocialBootstrap' example shows how to use custom validators for registration in its AppHost.cs.
//Provide extra validation for the registration process
public class CustomRegistrationValidator : RegistrationValidator
{
public CustomRegistrationValidator()
{
RuleSet(ApplyTo.Post, () => {
RuleFor(x => x.DisplayName).NotEmpty();
});
}
}
Remember to register your custom validator as well.
//override the default registration validation with your own custom implementation
container.RegisterAs<CustomRegistrationValidator, IValidator<Registration>>();
Add more rules by using 'RuleSet'. Hope that helps.
EDIT
It seems there might be a bug in the current v3 version of ServiceStack that is preventing the validator from being called. I did a quick test with the Social Bootstrap project and could reproduce what you are experiencing, eg the CustomRegistrationValidator not firing its rules. Other validators seem to be working fine, so not sure what the cause might be at the moment. I will pull down the source to debug when I get time. If you happen to do it before hand, please post up what you find as it might help others.
Update
This problem is due to the order of ops for plugins and registration. The Registration plugin is running it's Register function after the CustomRegistrationValidator has been registered and overrides the type registered as IValidator<Registration>.
Simplest way around this is to creator your own RegistrationFeature as it is pretty simple in itself.
public class MyRegistrationFeature : IPlugin
{
public string AtRestPath { get; set; }
public RegistrationFeature()
{
this.AtRestPath = "/register";
}
public void Register(IAppHost appHost)
{
appHost.RegisterService<RegisterService>(AtRestPath);
appHost.RegisterAs<CustomRegistrationValidator, IValidator<Registration>>();
}
}

Resources