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?
Related
I have a web application targeting .NET 5.0 and I am trying to implement Windows Authentication along with some policies for authorization. We have a database table from another application that holds info on user roles, so I am using that to set permissions for my users.
I created a ClaimsTransformer class:
ClaimsTransformer.cs
public class ClaimsTransformer : IClaimsTransformation
{
// snip constructor which pulls in my DbContext from DI
public async Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
{
var id = ((ClaimsIdentity) principal.Identity);
var ci = new ClaimsIdentity(id.Claims, id.AuthenticationType, id.NameClaimType, id.RoleClaimType);
// snip call to DbContext to get user's role from database
if (roleId == 1 || roleId == 7)
{
ci.AddClaim(new Claim("user-role", "admin"));
}
return new ClaimsPrincipal(ci);
}
}
I have my authentication/authorization setup like this:
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(IISDefaults.AuthenticationScheme);
services.AddTransient<IClaimsTransformation, ClaimsTransformer>();
services.AddAuthorization(options =>
{
options.AddPolicy("Admin", policy =>
policy.RequireClaim("user-role", "admin"));
});
// snip rest of method
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// snip unrelated code
app.UseAuthentication();
app.UseAuthorization();
}
My first use of this is to hide a menu in my _Layout.cshtml:
#if ((await AuthorizationService.AuthorizeAsync(User, "admin").Succeeded)
{
// Admin nav link
}
The issue is, since AuthorizeAsync is running on every HTTP request, my ClaimsTransformer also runs each time, hitting the database to check the user's roles on every request. I'd like to avoid this, but I'm not sure of the best way to do so.
Basically, I'd like the system to check the roles only once, when the user is first authenticated. I read that this is what is supposed to happen when using Windows Authentication with IIS, but I am seeing the roles query running on every request when deployed to my IIS server also.
I could easily add a check in my ClaimsTransformer to see if the "user-role" claim exists, and only hit the DB if it is not present, but is there a better way? Should I be overriding something like UserClaimsPrincipalFactory instead of using a ClaimsTransformer?
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.
Anyone using the new 2.0 version of the Azure AD Graph Client?
I started fooling around with it yesterday but can't get it to work. The GraphConnection class is marked deprecated and replaced with ActiveDirectoryClient. In addition all of a sudden it's all Office 365 while I just want to limit my trials to Azure Active Directory without O365. Documentation is hard to find, at least when you don't want to use the O365 and O365 API Tools. The AD samples on GitHub seem to be updated as well but code is still using GraphConnection class. Go figure.
Not much samples/guidance on using ActiveDirectory client yet so below code using for now
public async Task<ActionResult> Index()
{
List<Exception> exceptions = new List<Exception>();
ProfileViewModel model = new ProfileViewModel();
string userObjectID = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
AuthenticationContext authContext = new AuthenticationContext(SecurityConfiguration.Authority, new NaiveSessionCache(userObjectID));
ClientCredential credential = new ClientCredential(SecurityConfiguration.ClientId, SecurityConfiguration.AppKey);
try
{
var ServiceUri = new Uri(SecurityConfiguration.GraphUrl);
ActiveDirectoryClient client = new ActiveDirectoryClient(ServiceUri, async () =>
{
var result = await authContext.AcquireTokenSilentAsync(SecurityConfiguration.GraphUrl, credential, new UserIdentifier(userObjectID, UserIdentifierType.UniqueId));
return result.AccessToken;
});
try
{
var users = await client.Users.ExecuteAsync();
var user = await client.Users[userObjectID].ExecuteAsync();
}
catch (Exception exc)
{
exceptions.Add(exc);
}
}
catch (AdalSilentTokenAcquisitionException exc)
{
exceptions.Add(exc);
}
ViewBag.Exceptions = exceptions;
return View(model);
}
client.Users.ExecuteAsync() throws exceptions
The response payload is a not a valid response payload. Please make sure that the top level element is a valid Atom or JSON element or belongs to 'http://schemas.microsoft.com/ado/2007/08/dataservices' namespace.
client.Users[userObjectID].ExecuteAsync() throws
System.Reflection.TargetInvocationException with Innerexpection
Expected a relative URL path without query or fragment.
Parameter name: entitySetName
UPDATE 2/11
Spooky resolution: without changing one line of code client.Users.ExecuteAsync() worked as expected. My thought is that folks at MSFT changed some stuff on the API so that response payload is now correct. They could have mentioned that.
To get user details using v2.0 code below does the trick
var userFetcher = client.Users.Where(u => u.ObjectId == userObjectID);
var user = await userFetcher.ExecuteAsync();
If you are using razor to display content of the user you'll probably get razor exceptions when trying to go through collection like AssignedPlans
The type 'System.Object' is defined in an assembly that is not referenced. You must add a reference to assembly 'System.Runtime, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'.
Resolution is to change compilation settings in your web.config as outlined in http://www.lyalin.com/2014/04/25/the-type-system-object-is-defined-in-an-assembly-that-is-not-reference-mvc-pcl-issue/
<compilation debug="true" targetFramework="4.5" >
<assemblies>
<add assembly="System.Runtime, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
</assemblies>
</compilation>
For retrieving a user entity by Id, rather than:
var userFetcher = client.Users.Where(u => u.ObjectId == userObjectID);
var user = await userFetcher.ExecuteAsync();
you can just use getByObjectId directly:
var user = await client.Users.GetByObjectId(userObjectID).ExecuteAsync();
Using the ADAL library for getting a token for WAAD i would like to know how I can get more control over the login flow.
var ac = new AuthenticationContext("https://login.windows.net/" + ActiveDirectoryTenantId);
AuthenticationInfo = ac.AcquireToken(
resource: "https://management.core.windows.net/",
clientId: "1950a258-227b-4e31-a9cf-717495945fc2",
redirectUri: new Uri("urn:ietf:wg:oauth:2.0:oob"));
The user is prompted to login. For me it's via Live Id, for my customer's computer it's via an organizational account, and there is no way to switch between them. It seems to be controlled by how/what current sessions the computer might have running already logged into azure.
Can I do anything in the AcquireToken call to control this?
It would be best if I could trigger the normal flow when people log into Azure where they get to select if its a live id or a organizational login.
I have tried this:
ac.AcquireToken("https://management.core.windows.net/",
"1950a258-227b-4e31-a9cf-717495945fc2",
new Uri("urn:ietf:wg:oauth:2.0:oob"), PromptBehavior.Always,"wtrealm=urn:federation:MicrosoftOnline");
with no luck.
I found some magic tricks that seems to give some more control.
// ID for site to pass to enable EBD (email-based differentiation)
// This gets passed in the call to get the azure branding on the
// login window. Also adding popup flag to handle overly large login windows.
internal const string EnableEbdMagicCookie = "site_id=501358&display=popup";
private void ClearCookies()
{
NativeMethods.InternetSetOption(IntPtr.Zero, NativeMethods.INTERNET_OPTION_END_BROWSER_SESSION, IntPtr.Zero, 0);
}
private static class NativeMethods
{
internal const int INTERNET_OPTION_END_BROWSER_SESSION = 42;
[DllImport("wininet.dll", SetLastError = true)]
internal static extern bool InternetSetOption(IntPtr hInternet, int dwOption, IntPtr lpBuffer,
int lpdwBufferLength);
}
There is a login form with any authentication logic. I enter login and password, then I click "Login" and get error "The method or operation is not implemented" in this code line:
SecurityToken tk = SPSecurityContext.SecurityTokenForFormsAuthentication
(
new Uri(SPContext.Current.Web.Url),
"MyUserProvider",
"MyRoleProvider",
this.txLogin.Text,
this.txPassword.Text
);
================================================
Server Error in '/' Application.
The method or operation is not implemented.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.ServiceModel.FaultException`1[[System.ServiceModel.ExceptionDetail, System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]: The method or operation is not implemented.
Stack Trace:
[FaultException`1: The method or operation is not implemented.]
Microsoft.IdentityModel.Protocols.WSTrust.WSTrustChannel.ReadResponse(Message response) +1161013
Microsoft.IdentityModel.Protocols.WSTrust.WSTrustChannel.Issue(RequestSecurityToken rst, RequestSecurityTokenResponse& rstr) +73
Microsoft.IdentityModel.Protocols.WSTrust.WSTrustChannel.Issue(RequestSecurityToken rst) +36
Microsoft.SharePoint.SPSecurityContext.SecurityTokenForContext(Uri context, Boolean bearerToken, SecurityToken onBehalfOf, SecurityToken actAs, SecurityToken delegateTo) +26193297
Microsoft.SharePoint.SPSecurityContext.SecurityTokenForFormsAuthentication(Uri context, String membershipProviderName, String roleProviderName, String username, String password) +26189452
Microsoft.SharePoint.IdentityModel.Pages.FormsSignInPage.GetSecurityToken(Login formsSignInControl) +188
Microsoft.SharePoint.IdentityModel.Pages.FormsSignInPage.AuthenticateEventHandler(Object sender, AuthenticateEventArgs formAuthenticateEvent) +123
System.Web.UI.WebControls.Login.AttemptLogin() +152
But I have assembly with custom Membership and Roles Provider and all methods are implemented! Where is a mistake?
You may be calling base membership functions directly from your custom membership and roles provider, like:
Membership.FindUsersByEmail("myemail#here.com");
These will get handled by the default membership provider, which won't be your membership provider, but will be the SPClaimsAuthMembershipProvider. SPClaimsAuthMembershipProvider doesn't implement a lot of the base methods - they will return a not implemented exception.
If you want to get the web app's selected membership provider to reference, you can use the following code:
public static string GetMembershipProvider(SPSite site)
{
// get membership provider of whichever zone in the web app is fba enabled
SPIisSettings settings = GetFBAIisSettings(site);
return settings.FormsClaimsAuthenticationProvider.MembershipProvider;
}
public static SPIisSettings GetFBAIisSettings(SPSite site)
{
SPIisSettings settings = null;
// try and get FBA IIS settings from current site zone
try
{
settings = site.WebApplication.IisSettings[site.Zone];
if (settings.AuthenticationMode == AuthenticationMode.Forms)
return settings;
}
catch
{
// expecting errors here so do nothing
}
// check each zone type for an FBA enabled IIS site
foreach (SPUrlZone zone in Enum.GetValues(typeof(SPUrlZone)))
{
try
{
settings = site.WebApplication.IisSettings[(SPUrlZone)zone];
if (settings.AuthenticationMode == AuthenticationMode.Forms)
return settings;
}
catch
{
// expecting errors here so do nothing
}
}
// return null if FBA not enabled
return null;
}
Some things to try:
Did you register the providers in the web application through the Cental Admin?
Did you register the providers in your web.config?
What happens if you use SPClaimsUtility.AuthenticateFormsUser instead?