How to override ServiceStack RegistrationService Validator? - servicestack

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>>();
}
}

Related

Setting up AAD Authentication in a Project with Autofac

I have a web API that has AAD auth (in code because it runs in IaaS not PaaS) it works well, but if I add Autofac configuration to the Startup.cs, the Authentication breaks, (if I put Autofac after Auth inizialization Autofac breaks) which makes me think that the configurations are overwriting eachother.
I have tried to find any documentation on how to use both of them together but I have not been able to find any information. One Uses HttpConfiguration and the other uses the IAppBuilder and I don't know how to combine them for them to work together.
here is my Authentication code:
public void ConfigureAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.Map("/api", inner =>
{
inner.UseWindowsAzureActiveDirectoryBearerAuthentication(new WindowsAzureActiveDirectoryBearerAuthenticationOptions()
{
Tenant = tenant,
TokenValidationParameters = new Tokens.TokenValidationParameters
{
ValidAudience = Audience
}
});
});
}
and here is the Autofac Code
public static void Register(HttpConfiguration configuration)
{
var builder = new ContainerBuilder();
Bootstrapper.Configure(builder);
var container = builder.Build();
configuration.DependencyResolver = new AutofacWebApiDependencyResolver(container);
}
what are the best practices for using these two tools together?
I was not properly setting all the WebAPI autofac references to get all the dependencies I followed this Quick start and then Added my references. Bellow is the new ConfigureAutofac function (the configure auth stayed the same)
private void ConfigureAutofac(IAppBuilder app)
{
//Autofac info from https://autofaccn.readthedocs.io/en/latest/integration/webapi.html#quick-start
var builder = new ContainerBuilder();
// STANDARD WEB API SETUP:
// Get your HttpConfiguration. In OWIN, you'll create one
// rather than using GlobalConfiguration.
var config = new HttpConfiguration();
// Register your Web API controllers.
builder.RegisterApiControllers(Assembly.GetExecutingAssembly()); //Register WebApi Controllers
builder.RegisterType<AutofacManager>().As<IAutofacManager>();
builder.RegisterSource(new AnyConcreteTypeNotAlreadyRegisteredSource());
var container = builder.Build();
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
GlobalConfiguration.Configuration.DependencyResolver = new AutofacWebApiDependencyResolver((IContainer)container); //Set the WebApi DependencyResolver
// and finally the standard Web API middleware.
app.UseAutofacMiddleware(container);
app.UseAutofacWebApi(config);
app.UseWebApi(config);
}

ServiceStack MQ: how to populate data in RequestContext

I'm developing a JWT-based multi-tenancy system using ServiceStack. The JWT token contains shard information, and I use JwtAuthProvider to translate the JWT token to session object following instructions at http://docs.servicestack.net/jwt-authprovider.
Now, I want to use ServiceStack MQ for asynchronous processing. The MQ request needs to be aware of the shard information, so I populate the request context before executing it as follow
mqServer.RegisterHandler<EmployeeAssignedToProject>(m =>
{
var req = new BasicRequest { Verb = HttpMethods.Post };
var sessionKey = SessionFeature.GetSessionKey(m.GetBody().SessionId);
var session = HostContext.TryResolve<ICacheClient>().Get<Context>(sessionKey);
req.Items[Keywords.Session] = session;
var response = ExecuteMessage(m, req);
return response;
});
Here, Context is my custom session class. This technique is stemmed from the instruction at http://docs.servicestack.net/messaging#authenticated-requests-via-mq. Since I execute the message within the context of req, I reckon that I should then be able to resolve Context as follow
container.AddScoped<Context>(c =>
{
var webRequest = HostContext.TryGetCurrentRequest();
if (webRequest != null)
{
return webRequest.SessionAs<Context>();
} else
{
return HostContext.RequestContext.Items[Keywords.Session] as Context;
}
});
However, HostContext.RequestContext.Items is always empty. So the question is, how to populate HostContext.RequestContext.Items from within message handler registration code?
I've tried to dig a little bit into ServiceStack code and found that the ExecuteMessage(IMessage dto, IRequest req) in ServiceController doesn't seem to populate data in RequestContext. For my case, it is a bit too late to get session inside service instance, as a service instance depends on some DB connections whose shard info is kept in session.
The same Request Context instance can't be resolved from the IOC. The Request Context instance is created in the MQ's RegisterHandler<T>() where you can add custom data in the IRequest.Items property, e.g:
mqServer.RegisterHandler<EmployeeAssignedToProject>(m =>
{
var req = new BasicRequest { Verb = HttpMethods.Post };
req.Items[MyKey] = MyValue; //Inject custom per-request data
//...
var response = ExecuteMessage(m, req);
return response;
});
This IRequest instance is available throughout the Request pipeline and from base.Request in your Services. It's not available from your IOC registrations so you will need to pass it in as an argument when calling your dependency, e.g:
public class MyServices : Service
{
public IDependency MyDep { get; set; }
public object Any(MyRequest request) => MyDep.Method(base.Request, request.Id);
}

Azure Active Directory: Where can I insert a function to run after a user is signed in?

I've been following this example https://azure.microsoft.com/en-us/documentation/articles/active-directory-b2c-devquickstarts-web-dotnet/ and I'm pretty certain that in the SignIn method I can put my function there to run, but I'm not sure how you would go
In that top portion of the AccountController.cs example:
namespace WebApp_OpenIDConnect_DotNet_B2C.Controllers
{
public class AccountController : Controller
{
public void SignIn()
{
if (!Request.IsAuthenticated)
{
// To execute a policy, you simply need to trigger an OWIN challenge.
// You can indicate which policy to use by adding it to the AuthenticationProperties using the PolicyKey provided.
HttpContext.GetOwinContext().Authentication.Challenge(
new AuthenticationProperties (
new Dictionary<string, string>
{
{Startup.PolicyKey, Startup.SignInPolicyId}
})
{
RedirectUri = "/",
}, OpenIdConnectAuthenticationDefaults.AuthenticationType);
}
}
Where do I insert toDoAfterSignedIn(); method ? or is this the wrong way of doing it, is there some listener or event emitter that will notify me when a user has successfully signed in?
https://github.com/AzureADQuickStarts/B2C-WebApp-OpenIdConnect-DotNet
The correct place to plug-in yourself is the Startup.Auth.cs file. And register yourself for one of the OpenIdConnectNotifications. The notification you need is SecurityTokenValidated. And you will need to add it similarly to the other two notifications which are already in the sample. Or just add the following line within the the OpenIdConnectnotifications initializer:
SecurityTokenValidated = (notification) => {
return Task.Delay(0);
}
The notification parameter you get contains everything you need to know about the end user and his/her successfull authentication.

ASP.net Identity 2 sign in continue to use old password after changing password

I am using the Change Password functionality that visual studio generated for the accountcontroller. I am able to change the password without errors but when I go to login using the new password, I get a login error but if I use the old password, it works.
If I restart the app then the newly changed password takes effect. I am also using Autofac, may be I am not configuring the container correctly.
var builder = new ContainerBuilder();
builder.Register(c => new ApplicationDataContext(connectionString)).InstancePerLifetimeScope();
builder.RegisterType<ApplicationUserManager>().AsSelf();
builder.RegisterType<ApplicationRoleManager>().AsSelf();
builder.Register(c => new UserStore<ApplicationUser>(c.Resolve<ApplicationDataContext>())).AsImplementedInterfaces();
builder.Register(c => new RoleStore<IdentityRole>(c.Resolve<ApplicationDataContext>())).AsImplementedInterfaces();
builder.Register(c => HttpContext.Current.GetOwinContext().Authentication).As<IAuthenticationManager>();
builder.Register(c => new IdentityFactoryOptions<ApplicationUserManager>
{
DataProtectionProvider = new DpapiDataProtectionProvider("Application​")
});
builder.Register(c => new ApplicationOAuthProvider(publicClientId, c.Resolve<ApplicationUserManager>())).As<IOAuthAuthorizationServerProvider>();
Any help will be much appreciated.
Thanks
--------UPDATED----------
ContanierConfig.cs
public static void Configure(HttpConfiguration config)
{
// Configure the application for OAuth based flow
const string publicClientId = "self";
// ContainerConfig Config
var connectionString = ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString;
var elasticsearchUrl = ConfigurationManager.AppSettings["ElasticSearchUrl"];
var elasticSearchName = ConfigurationManager.AppSettings["ElasticSearchName"];
var builder = new ContainerBuilder();
builder.Register(c => new BimDataContext(connectionString)).InstancePerRequest();
builder.RegisterType<ApplicationUserManager>().AsSelf().InstancePerRequest();
builder.RegisterType<ApplicationRoleManager>().AsSelf().InstancePerRequest();
builder.Register(c => new UserStore<ApplicationUser>(c.Resolve<BimDataContext>())).AsImplementedInterfaces().InstancePerRequest();
builder.Register(c => new RoleStore<IdentityRole>(c.Resolve<BimDataContext>())).AsImplementedInterfaces().InstancePerRequest();
builder.Register(c => HttpContext.Current.GetOwinContext().Authentication).As<IAuthenticationManager>().InstancePerRequest();
builder.Register(c => new IdentityFactoryOptions<ApplicationUserManager>
{
DataProtectionProvider = new DpapiDataProtectionProvider("Application​")
}).InstancePerRequest(); ;
builder.RegisterType<SimpleRefreshTokenProvider>().As<IAuthenticationTokenProvider>().InstancePerRequest();
builder.RegisterType<AuthRepository>().As<IAuthRepository>().InstancePerRequest();
builder.Register(c => new ApplicationOAuthProvider(
publicClientId,
c.Resolve<ApplicationUserManager>(),
c.Resolve<IAuthRepository>()))
.As<IOAuthAuthorizationServerProvider>().InstancePerRequest();
// Register your Web API controllers.
builder.RegisterApiControllers(Assembly.GetExecutingAssembly()).InstancePerRequest();
// UoW registration: being explicit
builder.RegisterType<UnitOfWork>().As<IUnitOfWork>().InstancePerRequest();
// Repositories registration
builder.RegisterAssemblyTypes(typeof(ClientRepository).Assembly)
.AsImplementedInterfaces()
.InstancePerRequest();
// Services registration
builder.RegisterAssemblyTypes(typeof(ClientService).Assembly)
.AsImplementedInterfaces()
.InstancePerRequest();
builder.RegisterAssemblyTypes(typeof(ClientSearchService).Assembly)
.AsImplementedInterfaces()
.InstancePerRequest();
builder.RegisterType<IfcFileImportTask>().As<IIfcFileImportTask>().InstancePerRequest();
builder.RegisterType<COBieFileImportTask>().As<ICOBieFileImportTask>().InstancePerRequest();
// Hangfire registration
builder.RegisterType<BackgroundJobClient>().As<IBackgroundJobClient>().InstancePerRequest();
// OPTIONAL: Register the Autofac filter provider.
builder.RegisterWebApiFilterProvider(config);
// Set the dependency resolver to be Autofac.
var container = builder.Build();
JobActivator.Current = new AutofacJobActivator(container);
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
}
Startup.Auth.Cs
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);
// Configure the db context and user manager to use a single instance per request
//app.CreatePerOwinContext(BimDataContext.Create);
//app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
// 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);
// Configure the application for OAuth based flow
PublicClientId = "self";
var oAuthAuthorizationServerProvider = GlobalConfiguration.Configuration.DependencyResolver.GetRequestLifetimeScope().Resolve<IOAuthAuthorizationServerProvider>();
var authenticationTokenProvider = GlobalConfiguration.Configuration.DependencyResolver.GetRequestLifetimeScope().Resolve<IAuthenticationTokenProvider>();
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Token"),
Provider = oAuthAuthorizationServerProvider,
AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(5),
RefreshTokenProvider = authenticationTokenProvider,
// In production mode set AllowInsecureHttp = false,
AllowInsecureHttp = true
};
// Enable the application to use bearer tokens to authenticate users
app.UseOAuthBearerTokens(OAuthOptions);
}
Getting error
"value cannot be null. parameter name context autofac" on line var oAuthAuthorizationServerProvider = GlobalConfiguration.Configuration.DependencyResolver.GetRequestLifetimeScope().Resolve<IOAuthAuthorizationServerProvider>();
I was missing a key component of oauth2, the solution to this problem is refresh_tokens. On change password, invalidate the refresh token and force user to log out.
http://bitoftech.net/2014/07/16/enable-oauth-refresh-tokens-angularjs-app-using-asp-net-web-api-2-owin/
If using ASP.NET (this includes MVC and Web API, Web Forms, etc) and AutoFac you should register all your components using the extension method .InstancePerRequest(). The only exception is for components that are thread safe and where you do not have to worry about errors/unexpected results occurring from one request accessing the same (stale) data as another. An example might be a Factory or a Singleton.
Example of use on a line of code:
builder.Register(c => new UserStore<ApplicationUser>(c.Resolve<ApplicationDataContext>())).AsImplementedInterfaces().InstancePerRequest();
This ensures that every new incoming Http Request will get its own copy of that implementation (resolved and injected hopefully via an interface). Autofac will also cleanup the Disposable instances at the end of each request.
This is the behavior you need. It ensures that there is no cross request interference (like one request manipulating data on a shared dbcontext on another request). It also ensures that data is not stale as it is cleaned up after each request ends.
See the Autofac documentation for more details (here an excerpt).
Instance Per Request
Some application types naturally lend themselves to “request” type semantics, for example ASP.NET web forms and MVC applications. In these application types, it’s helpful to have the ability to have a sort of “singleton per request.”
Instance per request builds on top of instance per matching lifetime scope by providing a well-known lifetime scope tag, a registration convenience method, and integration for common application types. Behind the scenes, though, it’s still just instance per matching lifetime scope.
Changing your DI definitions above to include this should resolve your issues (I think based on what you have provided). If not then it might be a problem with your Identity registration in which case you should post that code so it can be scrutinized.

How to enable basic authentication without user sessions with ServiceStack?

According ServiceStack github wiki In order to add/enable basic authentication in ServiceStack following lines of code are required:
Plugins.Add(new AuthFeature(() => new AuthUserSession(),
new IAuthProvider[] {
new BasicAuthProvider(), //Sign-in with Basic Auth
new CredentialsAuthProvider(), //HTML Form post of UserName/Password credentials
}));
But how can I add basic authentication without user sessions?
If you want to perform the authentication without using sessions then you can create a simple request filter that performs the basic authentication yourself.
You can then authenticate the credentials either against your own database or repositor, or you can authenticate against the standard ServiceStack repository shown below:
public class MyAuthenticateAttribute : RequestFilterAttribute
{
public override void Execute(IRequest req, IResponse res, object requestDto)
{
// Determine if request has basic authentication
var authorization = req.GetHeader(HttpHeaders.Authorization);
if(!String.IsNullOrEmpty(authorization) && authorization.StartsWith("basic", StringComparison.OrdinalIgnoreCase))
{
// Decode the credentials
var credentials = Encoding.UTF8.GetString(Convert.FromBase64String(authorization.Substring(6))).Split(':');
if(credentials.Length == 2)
{
// Perform authentication checks. You could do so against your own database
// or you may wish to use the ServiceStack authentication repository IUserAuthRepository
// If you want to check against ServiceStacks authentication repository
var repository = HostContext.TryResolve<IUserAuthRepository>();
if(repository == null)
throw new Exception("Authentication Repository is not configured");
// Try authenticate the credentials
IUserAuth user;
if(repository.TryAuthenticate(credentials[0], credentials[1], out user))
{
// Authenticated successfully
// If you need the user details available in your service method
// you can set an item on the request and access it again in your service
// i.e. req.SetItem("user", user);
// In your service: Request.GetItem("user") as IUserAuth
return;
}
}
}
// User requires to authenticate
res.StatusCode = (int)HttpStatusCode.Unauthorized;
res.AddHeader(HttpHeaders.WwwAuthenticate, "basic realm=\"My Secure Service\"");
res.EndRequest();
}
}
So instead of using the [Authenticate] attribute you would use the [MyAuthenticate] attribute.
In your AppHost Configure method do not add the AuthFeature plugin. You do still however need to add the repository, if that's how you choose to authenticate the credentials against.
container.Register<ICacheClient>(new MemoryCacheClient());
var userRep = new InMemoryAuthRepository();
container.Register<IUserAuthRepository>(userRep);
I hope this helps.

Resources