I am building a fairly small sized mvc4 application. I want to use the simple membership provided as in time I can it being useful for the social stuff.
I want to be able to take advantage of the webapi within the project as I am building an ios application and would like for it to be able to use the same registration/account details. I have added an API area to the site.
I am using ninject for my injection so I have created a repository and have taken the default membership registration component and put it into this.
My Account controller and the apicontroller use the same repository.
public RegisterModel RegisterLocalUser(RegisterModel model)
{
try
{
WebSecurity.CreateUserAndAccount(model.UserName, model.Password,
new
{
Mobile = model.Mobile,
FirstName = model.FirstName,
LastName = model.LastName,
Email = model.Email,
});
Creating a user using the site works fine however when I try to use the api via fiddler to create a user I run into an issue of
{"Message":"An error has occurred.","ExceptionMessage":"You must call the \"WebSecurity.InitializeDatabaseConnection\" method before you call any other method of the \"WebSecurity\" class. This call should be placed in an _AppStart.cshtml file in the root of your site.","ExceptionType":"System.InvalidOperationException","StackTrace":" at WebMatrix.WebData.SimpleMembershipProvider.VerifyInitialized()\r\n at WebMatrix.WebData.WebSecurity.VerifyProvider()\r\n at WebMatrix.WebData.WebSecurity.CreateUserAndAccount(String userName, String password, Object propertyValues, Boolean requireConfirmationToken)\r\n at MySite.Web.Repository.AccountRepository.RegisterLocalUser(RegisterModel model) in c:\#Projects\Site\Site\Site.Consumer.Web\Repository\AccountRepository.cs:line 28\r\n at MySite.Web.Areas.API.Controllers.AccountController.RegisterLocal(RegisterModel data) in c:\#Projects\Site\Site\Site.Consumer.Web\Areas\API\Controllers\AccountController.cs:line 26\r\n at lambda_method(Closure , Object , Object[] )\r\n at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.<>c_DisplayClass13.b_c(Object instance, Object[] methodParameters)\r\n at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.Execute(Object instance, Object[] arguments)\r\n at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.<>c_DisplayClass5.b_4()\r\n at System.Threading.Tasks.TaskHelpers.RunSynchronously[TResult](Func`1 func, CancellationToken cancellationToken)"}
Adding a breakpoint within the repository the CreateUserAndAccount is never completed.
I have a custom DependancyResolver needed to allow the api and mvc stuff to work
public class NinjectDependencyScope : IDependencyScope
{
private IResolutionRoot resolver;
internal NinjectDependencyScope(IResolutionRoot resolver)
{
Contract.Assert(resolver != null);
this.resolver = resolver;
}
public void Dispose()
{
IDisposable disposable = resolver as IDisposable;
if (disposable != null)
disposable.Dispose();
resolver = null;
}
public object GetService(Type serviceType)
{
if (resolver == null)
throw new ObjectDisposedException("this", "This scope has already been disposed");
return resolver.TryGet(serviceType);
}
public IEnumerable<object> GetServices(Type serviceType)
{
if (resolver == null)
throw new ObjectDisposedException("this", "This scope has already been disposed");
return resolver.GetAll(serviceType);
}
}
public class NinjectDependencyResolver : NinjectDependencyScope, IDependencyResolver
{
private IKernel kernel;
public NinjectDependencyResolver(IKernel kernel)
: base(kernel)
{
this.kernel = kernel;
}
public IDependencyScope BeginScope()
{
return new NinjectDependencyScope(kernel.BeginBlock());
}
}
Has someone else been able to find a work around to get these components working together?
Thank you
Your error dump suggests you are not using the InitializeSimpleMembershipAttribute for your AccountController.
Either decorate your new account controller with InitializeSimpleMembershipAttribute or move that Websecurity.InitializeDatabaseConnection() to your Global.asax startup code.
Of course, this ignores the dependency issue and I don't have a good answer for that as I don't bother injecting WebSecurity.
WebSecurity is a static class
You'd only need to call InitializeDatabaseConnection once at startup. With the attribute usage it's called each time the controller is accessed.
Which means you must inject an initialized WebSecurity into your repository.
Related
I have a Blazor WebAssembly (WASM) app that authenticates users using Okta. After they successfully log in via Okta, I want to authorize the user by calling an API that I wrote to retrieve that users roles and other general user info that we store. This call to get user info must also include the access token retrieved from the Okta log in.
The authentication piece with Okta works fine.
I'm not sure how to correctly call our API to get user info/roles as part of the login process, so that the roles can be added as claims BEFORE being redirected to any other page.
The Okta log in piece is set up using the RemoteAuthenticatorView and added in Program.Main as:
builder.Services.AddOidcAuthentication(options =>
{
options.ProviderOptions.Authority = builder.Configuration.GetValue<string>("Okta:Authority");
options.ProviderOptions.ClientId = builder.Configuration.GetValue<string>("Okta:ClientId");
options.ProviderOptions.ResponseType = "code";
});
What I've tried so far:
Using the OnLogInSucceeded event callback of the RemoteAuthenticatorView. This doesn't work because the user will be redirected to the page they tried to access before the api call completes. Therefore if the page has any Authorize(Roles="Admin") type of restrictions on it, those roles haven't been populated yet.
Using a factory that inherits from AccountClaimsPrincipalFactory. This seems like the correct way, however I'm getting runtime errors anytime I inject certain classes or services into my factory. I think I've narrowed it down to being an issue with an injected service using the IHttpClientFactory. Here's my factory code:
public class ClaimsPrincipalFactory : AccountClaimsPrincipalFactory
{
private IUserService userService { get; set; }
public ClaimsPrincipalFactory(
IAccessTokenProviderAccessor accessor,
IUserService userService
)
: base(accessor)
{
this.userService = userService;
}
public async override ValueTask<ClaimsPrincipal> CreateUserAsync(
RemoteUserAccount account, RemoteAuthenticationUserOptions options)
{
var user = await base.CreateUserAsync(account, options);
var userInfo = await userService.UserInfo();
var identity = user.Identity as ClaimsIdentity;
if (userInfo != null)
{
foreach (var role in userInfo.UserRoles)
{
identity.AddClaim(new Claim(ClaimsIdentity.DefaultRoleClaimType, role.ApplicationRole.Name));
}
}
return user;
}
}
Here is the constructor of my UserService:
public UserService(IHttpClientFactory clientFactory)
{
http = clientFactory.CreateClient("BlazorClient.ServerApi");
}
The CreateClient line causes this runtime error:
crit: Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100]
Unhandled exception rendering component: ValueFactory attempted to access the Value property of this instance.
System.InvalidOperationException: ValueFactory attempted to access the Value property of this instance.
at System.Lazy`1[[Microsoft.Extensions.Http.ActiveHandlerTrackingEntry, Microsoft.Extensions.Http, Version=6.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60]].ViaFactory(LazyThreadSafetyMode mode)
at System.Lazy`1[[Microsoft.Extensions.Http.ActiveHandlerTrackingEntry, Microsoft.Extensions.Http, Version=6.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60]].ExecutionAndPublication(LazyHelper executionAndPublication, Boolean useDefaultConstructor)
Here is how the httpFactory is set up in my Program file:
builder.Services
.AddHttpClient("BlazorClient.ServerApi", client => client.BaseAddress = new Uri(builder.Configuration.GetValue<string>("ServerApi:BaseAddress")))
.AddHttpMessageHandler<CorsRequestAuthorizationMessageHandler>();
builder.Services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>().CreateClient("BlazorClient.ServerApi"));
Here is how the Factory is added in Program:
builder.Services.AddApiAuthorization()
.AddAccountClaimsPrincipalFactory<ClaimsPrincipalFactory>();
What is the correct way of doing this? I've been stuck on this issue for literally days and it doesn't seem like it should be this hard (and so hard to find documented info on it).
I was strugling with the same issue and based on your code snippet I might solved it.
What I did is to pass a HttpClientFactory to the generator of the CustomUserFactory, then in the CreateUser func I can create my userService with this factory.
Hope it's an ok solution and helps you as well.
public class CustomUserFactory : AccountClaimsPrincipalFactory<CustomUserAccount>
{
private IUserService _userService { get; set; }
private IHttpClientFactory _httpClientFactory { get; set; }
public CustomUserFactory(IAccessTokenProviderAccessor accessor, IHttpClientFactory httpClientFactory)
: base(accessor)
{
_httpClientFactory = httpClientFactory;
}
public async override ValueTask<ClaimsPrincipal> CreateUserAsync(CustomUserAccount account, RemoteAuthenticationUserOptions options)
{
var initialUser = await base.CreateUserAsync(account, options);
_userService = new UserService(_httpClientFactory);
...
I'm trying to use the ResourceAuthorize attribute from Thinktecture.IdentityModel, but everything stops because there is no owin context.
I have a owin startup class which setups the authorization manager
[assembly: OwinStartup(typeof(My.WebApi.Startup))]
namespace My.WebApi
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
AuthConfig.Configure(app);
}
}
}
public class AuthConfig
{
public static void Configure(IAppBuilder app)
{
app.UseResourceAuthorization(new ResourceAuthorizationMiddlewareOptions
{
Manager = GlobalConfiguration.Configuration.DependencyResolver.GetService(typeof(IResourceAuthorizationManager)) as IResourceAuthorizationManager
});
}
}
and I know that it is detected and invoked. But later on, when hitting the following code from IdentityModel, I get a null pointer exception:
public static Task<bool> CheckAccessAsync(this HttpRequestMessage request, IEnumerable<Claim> actions, IEnumerable<Claim> resources)
{
var authorizationContext = new ResourceAuthorizationContext(
request.GetOwinContext().Authentication.User ?? Principal.Anonymous,
actions,
resources);
return request.CheckAccessAsync(authorizationContext);
}
I have stepped through and sees that it's caused by the GetOwinContext() returning null, since there is no MS_OwinContext or MS_OwinEnvironment property on the request.
What am I missing?
UPDATE:
I have found that i have an owin.environment property available, but it's part of the `HttpContextWrapper, not the request.
By searching around, I found some code inside of System.Web.Http.WebHost.HttpControllerHandler that looks like it should have converted the owin.environment to an MS_OwinEnvironment, but apparently, that code is never called in my case...
internal static readonly string OwinEnvironmentHttpContextKey = "owin.Environment";
internal static readonly string OwinEnvironmentKey = "MS_OwinEnvironment";
internal static HttpRequestMessage ConvertRequest(HttpContextBase httpContextBase, IHostBufferPolicySelector policySelector)
{
HttpRequestBase requestBase = httpContextBase.Request;
HttpRequestMessage httpRequestMessage = new HttpRequestMessage(HttpMethodHelper.GetHttpMethod(requestBase.HttpMethod), requestBase.Url);
bool bufferInput = policySelector == null || policySelector.UseBufferedInputStream((object) httpContextBase);
httpRequestMessage.Content = HttpControllerHandler.GetStreamContent(requestBase, bufferInput);
foreach (string str in (NameObjectCollectionBase) requestBase.Headers)
{
string[] values = requestBase.Headers.GetValues(str);
HttpControllerHandler.AddHeaderToHttpRequestMessage(httpRequestMessage, str, values);
}
HttpRequestMessageExtensions.SetHttpContext(httpRequestMessage, httpContextBase);
HttpRequestContext httpRequestContext = (HttpRequestContext) new WebHostHttpRequestContext(httpContextBase, requestBase, httpRequestMessage);
System.Net.Http.HttpRequestMessageExtensions.SetRequestContext(httpRequestMessage, httpRequestContext);
IDictionary items = httpContextBase.Items;
if (items != null && items.Contains((object) HttpControllerHandler.OwinEnvironmentHttpContextKey))
httpRequestMessage.Properties.Add(HttpControllerHandler.OwinEnvironmentKey, items[(object) HttpControllerHandler.OwinEnvironmentHttpContextKey]);
httpRequestMessage.Properties.Add(HttpPropertyKeys.RetrieveClientCertificateDelegateKey, (object) HttpControllerHandler._retrieveClientCertificate);
httpRequestMessage.Properties.Add(HttpPropertyKeys.IsLocalKey, (object) new Lazy<bool>((Func<bool>) (() => requestBase.IsLocal)));
httpRequestMessage.Properties.Add(HttpPropertyKeys.IncludeErrorDetailKey, (object) new Lazy<bool>((Func<bool>) (() => !httpContextBase.IsCustomErrorEnabled)));
return httpRequestMessage;
}
UPDATE 2:
Inside of mvc controllers, the context is available. But not in webapi controllers.
A team mate found a solution. He simply added the following line to the owin startup class:
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
Why this solves the issue is another mystery, though. But we are using wsFederation, so I guess it's needed some how. But what if we didn't use wsFed? Would we still need it to get a context? Who knows...
When a Record gets updated in my Database, I need to be able to save who edited it.
Currently in my Repository I do this
pt.ModifiedBy = HttpContext.Current.User.Identity.Name;
There has to be a better way of doing this or is this only method?
By using HttpContext.Current.User you're tightly coupling your DbContext with HttpContext which is not a good idea in case you'll expose your DbContext to a non-web environment (UnitTesting, WCF, WPF etc).
You can use System.Security.Principal.IIdentity instead, just like exposed in ASP.NET (System.Web.HttpContext.Current.User.Identity), WCF (System.ServiceModel.OperationContext.Current.ServiceSecurityContext.PrimaryIdentity) and Thread (Thread.CurrentPrincipal.Identity).
Then, have your DbContext accept IIdentity in its constructor, and whenever the context initialized pass the appropriate IIdentity (from your current context).
For example (based on #qujck answer):
public class MyContext : DbContext
{
private readonly IIdentity _identity;
public DbContext(IIdentity identity)
{
this._identity = identity;
}
public override int SaveChanges()
{
//you may need this line depending on your exact configuration
//ChangeTracker.DetectChanges();
foreach (DbEntityEntry o in GetChangedEntries())
{
IEntity entity = o.Entity as IEntity;
entity.ModifiedBy = this._identity.Name;
}
return base.SaveChanges();
}
}
// Usage (ASP.NET):
var context = new DbContext(System.Web.HttpContext.Current.User.Identity);
IMO the best option is to handle all the auditing in one place - your unit of work (DbContext). This is easily achieved by having all of your Poco objects implement a common interface, such as IEntity.
Here's an example:
public class MyContext : DbContext
{
public override int SaveChanges()
{
//you may need this line depending on your exact configuration
//ChangeTracker.DetectChanges();
foreach (DbEntityEntry o in GetChangedEntries())
{
IEntity entity = o.Entity as IEntity;
entity.ModifiedBy = HttpContext.Current.User.Identity.Name;
}
return base.SaveChanges();
}
private IEnumerable<DbEntityEntry> GetChangedEntries()
{
return new List<DbEntityEntry>(
from e in ChangeTracker.Entries()
where e.State != System.Data.EntityState.Unchanged
select e);
}
}
I am creating a website using Orchard CMS and I have an external .NET project written with Ninject for dependency injection which I would like to use together with a module within Orchard CMS. I know that Orchard uses Autofac for dependency injection and this is causing me problems since I never worked with DI before.
I have created an Autofac module, UserModule, which registers the a source, UserRegistrationSource, like this:
UserModule.cs
public class UserModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterSource(new UserRegistrationSource());
}
}
UserRegistrationSource.cs
public class UserRegistrationSource : IRegistrationSource
{
public bool IsAdapterForIndividualComponents
{
get { return false; }
}
public IEnumerable<IComponentRegistration> RegistrationsFor(Service service, Func<Service, IEnumerable<IComponentRegistration>> registrationAccessor)
{
var serviceWithType = service as IServiceWithType;
if (serviceWithType == null)
yield break;
var serviceType = serviceWithType.ServiceType;
if (!serviceType.IsInterface || !typeof(IUserServices).IsAssignableFrom(serviceType) || serviceType != typeof(IUserServices))
yield break;
var registrationBuilder = // something...
yield return registrationBuilder.CreateRegistration();
}
}
UserServices.cs
public interface IUserServices : IDependency
{
void Add(string email, string password);
}
public class UserServices : IUserServices
{
private readonly EFMembershipManager _manager;
public UserServices(EFMembershipManager manager)
{
_manager = manager;
}
public void Add(string email, string password)
{
_manager.createUser(email, password);
}
}
EFMembershipManager.cs constructor
public EFMembershipManager(ServerRepository db,
ServerRepositoryMembershipProvider membershipProvider,
string testUsername,
string serverUsername)
{
...
}
EFMembershipManager is a class from the external project which uses Ninject for DI's and uses ServerRepository and ServerRepositoryMembershipProvider whom also are injected using Ninject.
And now I'm stuck...
Should UserRegistrationSource take the Ninject container (kernel) as a constructor argument and try to find the IUserServices service and then mediate the resolves to the Ninject kernel and return an empty Enumerable so that Autofac doesn't try to resolve anything related to IUserServices or is this the wrong approach?
Autofac supports registration sources (and more on registration sources here). A registration source is a service that the container will consult when trying to resolve a type. The source can respond, either with a means to build the type, or an empty list which indicates that the source is not able to provide the requested type.
In your case, a registration source could be implemented that will try to resolve the requested type from your Ninject container.
I'm not too familiar with Orchard but I'm guessing that it uses configuration files to configure Autofac. My suggestion is that you create a simple Autofac module that registers your registration source implementation, and that you configure Orchard to load the module from config.
I need to create Unit Tests for an ASP.NET MVC 2.0 web site. The site uses Windows Authentication.
I've been reading up on the necessity to mock the HTTP context for code that deals with the HttpContext. I feel like I'm starting to get a handle on the DI pattern as well. (Give the class an attribute of type IRepository and then pass in a Repository object when you instantiate the controller.)
What I don't understand, however, is the proper way to Mock the Windows Principal object available through User.Identity. Is this part of the HttpContext?
Does any body have a link to an article that demonstrates this (or a recommendation for a book)?
Thanks,
Trey Carroll
I've used IoC to abstract this away with some success. I first defined a class to represent the currently logged in user:
public class CurrentUser
{
public CurrentUser(IIdentity identity)
{
IsAuthenticated = identity.IsAuthenticated;
DisplayName = identity.Name;
var formsIdentity = identity as FormsIdentity;
if (formsIdentity != null)
{
UserID = int.Parse(formsIdentity.Ticket.UserData);
}
}
public string DisplayName { get; private set; }
public bool IsAuthenticated { get; private set; }
public int UserID { get; private set; }
}
It takes an IIdentity in the constructor to set its values. For unit tests, you could add another constructor to allow you bypass the IIdentity dependency.
And then I use Ninject (pick your favorite IoC container, doesn't matter), and created a binding for IIdentity as such:
Bind<IIdentity>().ToMethod(c => HttpContext.Current.User.Identity);
Then, inside of my controller I declare the dependency in the constructor:
CurrentUser _currentUser;
public HomeController(CurrentUser currentUser)
{
_currentUser = currentUser;
}
The IoC container sees that HomeController takes a CurrentUser object, and the CurrentUser constructor takes an IIdentity. It will resolve the dependencies automatically, and voila! Your controller can know who the currently logged on user is. It seems to work pretty well for me with FormsAuthentication. You might be able to adapt this example to Windows Authentication.
I don't know for MVC 2.0, but in newer versions you can mock the ControllerContext:
// create mock principal
var mocks = new MockRepository(MockBehavior.Default);
Mock<IPrincipal> mockPrincipal = mocks.Create<IPrincipal>();
mockPrincipal.SetupGet(p => p.Identity.Name).Returns(userName);
mockPrincipal.Setup(p => p.IsInRole("User")).Returns(true);
// create mock controller context
var mockContext = new Mock<ControllerContext>();
mockContext.SetupGet(p => p.HttpContext.User).Returns(mockPrincipal.Object);
mockContext.SetupGet(p => p.HttpContext.Request.IsAuthenticated).Returns(true);
// create controller
var controller = new MvcController() { ControllerContext = mock.Object };
see also How to unit-test an MVC controller action which depends on authentification in c#?
Scott Hanselman shows in his blog how to use IPrincipal and ModelBinder to make easier to test the controller by mocking IPrincipal.
Example for mocking username and SID on MVC4.
The username and SID (Windows Authentication) in the following action should be tested:
[Authorize]
public class UserController : Controller
{
public ActionResult Index()
{
// get Username
ViewBag.Username = User.Identity.Name;
// get SID
var lIdentity = HttpContext.User.Identity as WindowsIdentity;
ViewBag.Sid = lIdentity.User.ToString();
return View();
}
}
I use Moq and Visual Studio Test Tools. The test is implemented as follows:
[TestMethod]
public void IndexTest()
{
// Arrange
var myController = new UserController();
var contextMock = new Mock<ControllerContext>();
var httpContextMock = new Mock<HttpContextBase>();
var lWindowsIdentity = new WindowsIdentity("Administrator");
httpContextMock.Setup(x => x.User).Returns(new WindowsPrincipal(lWindowsIdentity));
contextMock.Setup(ctx => ctx.HttpContext).Returns(httpContextMock.Object);
myController.ControllerContext = contextMock.Object;
// Act
var lResult = myController.Index() as ViewResult;
// Assert
Assert.IsTrue(lResult.ViewBag.Username == "Administrator");
Assert.IsTrue(lResult.ViewBag.Sid == "Any SID Pattern");
}
I've changed dev environment global.asax and Web.Config for use FormsAuth for force a specific user. The username uses the same WindowsAuth format. See:
public override void Init()
{
base.Init();
this.PostAuthenticateRequest +=
new EventHandler(MvcApplication_PostAuthenticateRequest);
}
void MvcApplication_PostAuthenticateRequest(object sender, EventArgs e)
{
FormsAuthentication.SetAuthCookie("Domain\\login", true);
}
The Windows or Forms Auth shares the same login patterns.
The application will work with both Windows authentication and Form authentication.
To mock WindowsIdentity you can do the following:
var mockedPrincipal = new Mock<WindowsPrincipal>(WindowsIdentity.GetCurrent());
mockedPrincipal.SetupGet(x => x.Identity.IsAuthenticated).Returns(true);
mockedPrincipal.SetupGet(x => x.Identity.Name).Returns("Domain\\User1");
mockedPrincipal.Setup(x => x.IsInRole("Domain\\Group1")).Returns(true);
mockedPrincipal.Setup(x => x.IsInRole("Domain\\Group2")).Returns(false);
then use mockedPrincipal.Object to get the actual WindowsIdentity