I have an ASP.NET Core 3.1 website that uses Identity for authentication on the actual customer-facing website. It also has an API backend that I want to secure with JWT for external user access.
However, using both authentication systems together is causing a problem. Setting the default authentication schemes to IdentityConstants.ApplicationScheme (or empty) causes JWT to fail authentication by routing API calls through the Identity scheme. Likewise, setting the schemes to JwtBearerDefaults.AuthenticationScheme causes the main site to fail by routing calls through the JWT scheme.
How can I separate the two so that anything routing through "/api" uses the JWT scheme and all other calls are handled by the default Identity scheme?
Here's part of the setup so far:
startup.cs
public void ConfigureServices(IServiceCollection services)
{
...
services.AddDefaultIdentity<IdentityUser>(options => {
options.SignIn.RequireConfirmedAccount = true;
options.SignIn.RequireConfirmedEmail = false;
options.Password.RequireDigit = true;
options.Password.RequiredLength = 8;
options.Password.RequireLowercase = true;
options.Password.RequireUppercase = true;
options.Password.RequireNonAlphanumeric = true;
options.Lockout.MaxFailedAccessAttempts = 5;
options.User.RequireUniqueEmail = true;
}).AddEntityFrameworkStores<ApplicationDbContext>();
...
services.AddAuthentication(x =>
{
// ** Playing with these settings **
//x.DefaultScheme = IdentityConstants.ApplicationScheme;
////x.DefaultAuthenticateScheme = IdentityConstants.ApplicationScheme;
////x.DefaultChallengeScheme = IdentityConstants.ApplicationScheme;
//x.DefaultForbidScheme = JwtBearerDefaults.AuthenticationScheme;
//x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
//x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(x =>
{
x.RequireHttpsMetadata = true;
x.SaveToken = true;
x.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = false,
IssuerSigningKey = new SymmetricSecurityKey(_SecretKey),
ValidateIssuer = false,
ValidateAudience = false,
ValidateLifetime = true,
ClockSkew = TimeSpan.Zero
};
x.IncludeErrorDetails = true;
});
Any ideas would be most welcome. I'd prefer not to have to resort to plaintext Licence keys for the API functions.
Related
I have the following set up: Authorization server (.NET 6 with MVC, port 7000), Client (.NET 6 with MVC, port 7001), Resource Server (.NET 6 API, port 7002).
Authorization server set up:
builder.Services.AddAuthentication()
.AddGoogle(options =>
{
options.ClientId = builder.Configuration["ClientId"];
options.ClientSecret = builder.Configuration["ClientSecret"];
});
builder.Services.Configure<IdentityOptions>(options =>
{
options.ClaimsIdentity.UserNameClaimType = Claims.Name;
options.ClaimsIdentity.UserIdClaimType = Claims.Subject;
options.ClaimsIdentity.RoleClaimType = Claims.Role;
options.ClaimsIdentity.EmailClaimType = Claims.Email;
options.SignIn.RequireConfirmedAccount = false;
});
builder.Services.AddOpenIddict()
.AddCore(options =>
{
options.UseEntityFrameworkCore()
.UseDbContext<AuthorizationContext>();
})
.AddServer(options =>
{
options.SetAuthorizationEndpointUris("/connect/authorize")
.SetLogoutEndpointUris("/connect/logout")
.SetTokenEndpointUris("/connect/token")
.SetUserinfoEndpointUris("/connect/userinfo")
.SetIntrospectionEndpointUris("/connect/introspect");
options.RegisterScopes(Scopes.Email, Scopes.Profile, Scopes.Roles);
options.AllowAuthorizationCodeFlow();
options.AddDevelopmentEncryptionCertificate()
.AddDevelopmentSigningCertificate();
options.UseAspNetCore()
.EnableAuthorizationEndpointPassthrough()
.EnableLogoutEndpointPassthrough()
.EnableTokenEndpointPassthrough()
.EnableUserinfoEndpointPassthrough()
.EnableStatusCodePagesIntegration();
})
.AddValidation(options =>
{
options.UseLocalServer();
options.UseAspNetCore();
});
builder.Services.AddHostedService<Worker>();
The seeded clients:
await manager.CreateAsync(new OpenIddictApplicationDescriptor
{
ClientId = "mvc",
ClientSecret = "901564A5-E7FE-42CB-B10D-61EF6A8F3654",
ConsentType = ConsentTypes.Explicit,
DisplayName = "MVC client application",
PostLogoutRedirectUris =
{
new Uri("https://localhost:7001/signout-callback-oidc")
},
RedirectUris =
{
new Uri("https://localhost:7001/signin-oidc")
},
Permissions =
{
Permissions.Endpoints.Authorization,
Permissions.Endpoints.Logout,
Permissions.Endpoints.Token,
Permissions.GrantTypes.AuthorizationCode,
Permissions.GrantTypes.RefreshToken,
Permissions.ResponseTypes.Code,
Permissions.Scopes.Email,
Permissions.Scopes.Profile,
Permissions.Scopes.Roles,
Permissions.Prefixes.Scope + "api1"
},
Requirements =
{
Requirements.Features.ProofKeyForCodeExchange
}
});
// resource server
if (await manager.FindByClientIdAsync("resource_server_1") == null)
{
var descriptor = new OpenIddictApplicationDescriptor
{
ClientId = "resource_server_1",
ClientSecret = "846B62D0-DEF9-4215-A99D-86E6B8DAB342",
Permissions =
{
Permissions.Endpoints.Introspection
}
};
await manager.CreateAsync(descriptor);
}
Client config:
builder.Services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddCookie(options =>
{
options.LoginPath = "/login";
options.ExpireTimeSpan = TimeSpan.FromMinutes(50);
options.SlidingExpiration = false;
})
.AddOpenIdConnect(options =>
{
options.ClientId = "mvc";
options.ClientSecret = "901564A5-E7FE-42CB-B10D-61EF6A8F3654";
options.RequireHttpsMetadata = false;
options.GetClaimsFromUserInfoEndpoint = true;
options.SaveTokens = true;
options.ResponseType = OpenIdConnectResponseType.Code;
options.AuthenticationMethod = OpenIdConnectRedirectBehavior.RedirectGet;
options.Authority = "https://localhost:7000/";
options.Scope.Add("email");
options.Scope.Add("roles");
options.Scope.Add("api1");
options.MapInboundClaims = false;
options.TokenValidationParameters.NameClaimType = "name";
options.TokenValidationParameters.RoleClaimType = "role";
});
Resource Server config:
builder.Services.AddOpenIddict()
.AddValidation(options =>
{
options.SetIssuer("https://localhost:7000/");
options.AddAudiences("resource_server_1");
options.UseIntrospection()
.SetClientId("resource_server_1")
.SetClientSecret("846B62D0-DEF9-4215-A99D-86E6B8DAB342");
options.UseSystemNetHttp();
options.UseAspNetCore();
});
builder.Services.AddAuthentication(OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme);
This is how the client makes request to resource server:
[Authorize, HttpPost("~/")]
public async Task<ActionResult> Index(CancellationToken cancellationToken)
{
var token = await HttpContext.GetTokenAsync(CookieAuthenticationDefaults.AuthenticationScheme, OpenIdConnectParameterNames.AccessToken);
if (string.IsNullOrEmpty(token))
{
throw new InvalidOperationException("The access token cannot be found in the authentication ticket. " +
"Make sure that SaveTokens is set to true in the OIDC options.");
}
using var client = _httpClientFactory.CreateClient();
using var request = new HttpRequestMessage(HttpMethod.Get, "https://localhost:7002/api/message");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
using var response = await client.SendAsync(request, cancellationToken);
var content = await response.Content.ReadAsStringAsync();
response.EnsureSuccessStatusCode();
return View("Home", model: await response.Content.ReadAsStringAsync());
}
The problem is following when I set up those 3 instances (auth server, client, resource server) and I am NOT authenticated in the client (no cookies). I can authenticate on the client (and therefore on auth server). Then I make the request from the client to the resource server and it returns 200.
But then I stop all 3 instances and try to do it again.
At that time I'm already authenticated in the client (cookies) and can extract token (FYI the tokens are the same between requests before stopping instances and after). But this token is invalid and the response code from the resource server is 401.
On the resource server logs I can see the following logs: "OpenIddict.Validation.AspNetCore was not authenticated. Failure message: An error occurred while authenticating the current request", and "invalid_token, the specified token is invalid"
The question: is it expected behavior? I assume the reason is that data protection changed key ring or something like that. If it is expected - then how to do redeploys without reauthenticating all the users?
I'm pretty sure the problem is with this line
options.AddDevelopmentEncryptionCertificate()
.AddDevelopmentSigningCertificate();
Those certificates (keys) are changed when you restart the application. You need to have production encryption/signing keys in place.
See options.AddEncryptionKey and options.AddSigningKey
Key can be created like this
var rsa = RSA.Create(2048);
var key = new RsaSecurityKey(rsa);
You can get the XML for the key and save it somewhere PRIVATE
var xml = key.Rsa.ToXmlString(true);
When you start the application, you load the key using the XML
var rsa = RSA.Create();
rsa.FromXmlString(xml);
Then you add the key to openiddict
options.AddEncryptionKey(rsa);
options.AddSigningKey(rsa);
You may also want to use the following methods instead
options.AddEncryptionCredentials
options.AddSigningCredentials
options.AddEncryptionCertificate
options.AddSigningCertificate
It depends of what you have available.
We have a solution today where we use EWS's basic authentication (username and password) with .net Core 2.1, and it works. The problem is that basic authentication will expire in 2020. Therefore, we will transition to the OAuth solution that will work after 2020.
We have tried multiple solutions for this problem, including this: https://learn.microsoft.com/en-us/exchange/client-developer/exchange-web-services/how-to-authenticate-an-ews-application-by-using-oauth, but some of the methods have been updated (AcquireToken -> AcquireTokenAsync).
It's important that the authentication against azure is not client-based, since everything will happen in backend (web api).
Does anyone have a solution to this problem?
This is our current solution:
ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2007_SP1);
service.Credentials = new WebCredentials(<email>, <password>);
service.TraceEnabled = true;
service.TraceFlags = TraceFlags.All;
service.Url = new Uri("https://outlook.office365.com/EWS/Exchange.asmx");
This is an example of what we have tried:
public class Program
{
public static void Run()
{
//tried this as well: string authority = "https://login.windows.net/<devAccountName>.onmicrosoft.com";
string authority = "https://login.microsoftonline.com/<tenantId>/OAuth2/Token";
string clientId = "<clientId>"; // Application ID from Azure
Uri clientAppUri = new Uri("http://localhost:55424/");
Uri resourceHostUri = new Uri("https://outlook.office365.com/EWS/Exchange.asmx");
AuthenticationResult authenticationResult = null;
AuthenticationContext authenticationContext = new AuthenticationContext(authority, false);
string errorMessage = null;
try
{
Console.WriteLine("Trying to acquire token");
PlatformParameters platformParams = new PlatformParameters(PromptBehavior.Auto);
authenticationResult = authenticationContext.AcquireTokenAsync("https://outlook.office365.com/EWS/Exchange.asmx", clientId, clientAppUri, platformParams).Result;
}
catch (AdalException ex)
{
errorMessage = ex.Message;
if (ex.InnerException != null)
{
errorMessage += "\nInnerException : " + ex.InnerException.Message;
}
}
catch (ArgumentException ex)
{
errorMessage = ex.Message;
}
if (!string.IsNullOrEmpty(errorMessage))
{
Console.WriteLine("Failed: {0}" + errorMessage);
return;
}
Console.WriteLine("\nMaking the protocol call\n");
ExchangeService exchangeService = new ExchangeService(ExchangeVersion.Exchange2013);
exchangeService.Url = resourceHostUri;
exchangeService.TraceEnabled = true;
exchangeService.TraceFlags = TraceFlags.All;
exchangeService.Credentials = new OAuthCredentials(authenticationResult.AccessToken);
exchangeService.FindFolders(WellKnownFolderName.Root, new FolderView(10));
}
}
We receive this error message after we log in:
AADSTS50001: The application named
https://outlook.office365.com/EWS/Exchange.asmx was not found in the tenant named <tenantId>. This can happen if the application has not been installed by the administrator of the tenant or consented to by any user in the tenant. You might have sent your authentication request to the wrong tenant.
I need to create automaion client for Azure webhook.
Following code is written by me to get AutomationManagementClient Value.
var cert = new X509Certificate2(Convert.FromBase64String(ConfigurationManager.AppSettings["CertBase64String"]));
var creds[![enter image description here][1]][1] = new CertificateCloudCredentials(ConfigurationManager.AppSettings["SubscriptionId"], cert);
AutomationManagementClient automationManagementClient = new AutomationManagementClient(creds);
I need that certificate string i.e. CertBase64String value as I don't know from where I will get that value.
Help me...
This error I am getting after updating as per your answer.
If you want to create the automation client, I suggest you try to use the ARM way to operate the automation. The following is the demo code works correctly on my side.
Prepare: Registry an AD application and assign role to applcation, more details please refer to Azure official tutorials. After that we can get tenantId, appId, secretKey from the Azure Portal.
We could use the following code to get the token
var tenantId = "tenantId";
var context = new AuthenticationContext($"https://login.windows.net/{tenantId}");
var clientId = "application Id";
var clientSecret = "client secret";
var resourceGroup = "resource group";
var automationAccount = "automationAccount";
var subscriptionId = "susbscriptionId";
var token = context.AcquireTokenAsync(
"https://management.azure.com/",
new ClientCredential(clientId, clientSecret)).Result.AccessToken;
if you use the Microsoft.Azure.Management.Automation Version <= 2.0.4 please try the following code.
var automationClient = new AutomationManagementClient(new TokenCloudCredentials(subscriptionId,token));
var webhook = automationClient.Webhooks.CreateOrUpdate(resourceGroup, automationAccount,new WebhookCreateOrUpdateParameters
{
Properties = new WebhookCreateOrUpdateProperties
{
ExpiryTime = DateTimeOffset.Now.AddDays(1),
IsEnabled = false,
Parameters = parameters,
Runbook = new RunbookAssociationProperty
{
Name = "xxxx"
},
Name = "xxxx",
Uri = "https://xxxx.xx"
}
});
if use the Microsoft.Azure.Management.Automation Version 3.0.0-preview, please try to the following case.
var automationClient = new AutomationClient(new TokenCredentials(token)) {SubscriptionId = subscriptionId};
var webhook = automationClient.Webhook.CreateOrUpdate(resourceGroup, automationAccount, "webhookName",
new WebhookCreateOrUpdateParameters
{
ExpiryTime = DateTime.Now.AddDays(1),
IsEnabled = false,
Parameters = parameters,
Name = "xxxxx",
Runbook = new RunbookAssociationProperty
{
Name = "xxxxx"
},
Uri = "https://xxx.xxx"
});
Update:
You could set the Parameters = null or if you have parameter, you could define the parameters as dictionary. Please also add the Name = "xxxx" in the code.
var parameters = new Dictionary<string, string> {{"test", "test"}};
var webhook = automationClient.Webhooks.CreateOrUpdate(resourceGroup, automationAccount,new WebhookCreateOrUpdateParameters
{
Properties = new WebhookCreateOrUpdateProperties
{
ExpiryTime = DateTimeOffset.Now.AddDays(1),
IsEnabled = false,
Parameters = parameters,
Runbook = new RunbookAssociationProperty
{
Name = "xxxx"
},
Name = "xxxx",
Uri = "https://xxxx.xx"
}
});
I test it on my side, it works correctly
"CertBase64String" will get by passing thumb-print of that certificate to following fucntion.
internal static X509Certificate2 GetCertificateFromthumbPrint(String certThumbPrint) {
X509Store certStore = new X509Store(StoreName.My, StoreLocation.CurrentUser);
certStore.Open(OpenFlags.ReadOnly);
//Find the certificate that matches the thumbprint.
X509Certificate2Collection certCollection = certStore.Certificates.Find(X509FindType.FindByThumbprint, certThumbPrint, false);
certStore.Close();
//Get the first cert with the thumbprint
X509Certificate2 cert = (certCollection.Count > 0) ? certCollection[0] : null;
return cert;
}
I'm taking a look at the source code of the JwtFormat class and I'm wondering why does it add the Issuer it recovers from the token to the list of ValidIssuers. Does that mean that it will accept all issuers as valid if I don't specify a key or provide a IssueValidator handler to the TokenValidationParameters that are being used?
Btw, I'm lookit at this class because I'm investigating an issue regarding the use of JWT tokens (azure ad v2.0) in a web api app that seems to be ignoring the ValidIssuer property:
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions {
AccessTokenFormat = new JwtFormat(
GetTokenValidationParameters(),
new OpenIdConnectCachingSecurityTokenProvider(authority)),
Provider = new OAuthBearerAuthenticationProvider {
OnValidateIdentity = ValidateIdentity
}
});
private TokenValidationParameters GetTokenValidationParameters() {
return new TokenValidationParameters {
ValidAudience = ConfigData.ClientId,
ValidIssuer = "nobody",
ValidIssuers = null,
IssuerValidator = ValidateIssuer
};
}
I'm editing this to give more information about what's going on.
According to the source code, ValidateIssuer is true by default, so there's no need to set it again. Just to be sure, here's the source code:
public TokenValidationParameters()
{
this.RequireExpirationTime = true;
this.RequireSignedTokens = true;
this.SaveSigninToken = false;
this.ValidateActor = false;
this.ValidateAudience = true;
this.ValidateIssuer = true;
this.ValidateIssuerSigningKey = false;
this.ValidateLifetime = true;
}
I'm setting up the IssuerValidator because I want to make sure that if the ValidIssuer is set, then I want to compare the token's issuer with that value (and don't want to check against the ValidIssuers collection when the ValidIssuer's validation fails).
In case you're wondering where the ValidIssuers is being filled (and yes, even in my example, it's being automatically populated, even though I've set it explicityl to null), it's happening in JwtFormat's Unprotect method:
public AuthenticationTicket Unprotect(string protectedText)
{
if (string.IsNullOrWhiteSpace(protectedText))
throw new ArgumentNullException(nameof (protectedText));
if (!(this.TokenHandler.ReadToken(protectedText) is JwtSecurityToken))
throw new ArgumentOutOfRangeException(nameof (protectedText), Microsoft.Owin.Security.Jwt.Properties.Resources.Exception_InvalidJwt);
TokenValidationParameters validationParameters = this._validationParameters;
if (this._issuerCredentialProviders != null)
{
validationParameters = validationParameters.Clone();
IEnumerable<string> second1 = this._issuerCredentialProviders.Select<IIssuerSecurityTokenProvider, string>((Func<IIssuerSecurityTokenProvider, string>) (provider => provider.Issuer));
validationParameters.ValidIssuers = validationParameters.ValidIssuers != null ? validationParameters.ValidIssuers.Concat<string>(second1) : second1;
IEnumerable<SecurityToken> second2 = this._issuerCredentialProviders.Select<IIssuerSecurityTokenProvider, IEnumerable<SecurityToken>>((Func<IIssuerSecurityTokenProvider, IEnumerable<SecurityToken>>) (provider => provider.SecurityTokens)).Aggregate<IEnumerable<SecurityToken>>((Func<IEnumerable<SecurityToken>, IEnumerable<SecurityToken>, IEnumerable<SecurityToken>>) ((left, right) => left.Concat<SecurityToken>(right)));
validationParameters.IssuerSigningTokens = validationParameters.IssuerSigningTokens != null ? validationParameters.IssuerSigningTokens.Concat<SecurityToken>(second2) : second2;
}
SecurityToken validatedToken;
ClaimsIdentity identity = (ClaimsIdentity) this.TokenHandler.ValidateToken(protectedText, validationParameters, out validatedToken).Identity;
AuthenticationProperties properties = new AuthenticationProperties();
if (this.UseTokenLifetime)
{
DateTime validFrom = validatedToken.ValidFrom;
if (validFrom != DateTime.MinValue)
properties.IssuedUtc = new DateTimeOffset?((DateTimeOffset) validFrom.ToUniversalTime());
DateTime validTo = validatedToken.ValidTo;
if (validTo != DateTime.MinValue)
properties.ExpiresUtc = new DateTimeOffset?((DateTimeOffset) validTo.ToUniversalTime());
properties.AllowRefresh = new bool?(false);
}
return new AuthenticationTicket(identity, properties);
}
Btw, this method ends up being called (indirectly) by the AuthenticateCoreAsync method when it needs to deserialize the token:
protected override async Task<AuthenticationTicket> AuthenticateCoreAsync()
{
try
{
string requestToken = (string) null;
string authorization = this.Request.Headers.Get("Authorization");
if (!string.IsNullOrEmpty(authorization) && authorization.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
requestToken = authorization.Substring("Bearer ".Length).Trim();
OAuthRequestTokenContext requestTokenContext = new OAuthRequestTokenContext(this.Context, requestToken);
await this.Options.Provider.RequestToken(requestTokenContext);
if (string.IsNullOrEmpty(requestTokenContext.Token))
return (AuthenticationTicket) null;
AuthenticationTokenReceiveContext tokenReceiveContext = new AuthenticationTokenReceiveContext(this.Context, this.Options.AccessTokenFormat, requestTokenContext.Token);
await this.Options.AccessTokenProvider.ReceiveAsync(tokenReceiveContext);
if (tokenReceiveContext.Ticket == null)
tokenReceiveContext.DeserializeTicket(tokenReceiveContext.Token);
//remaining code removed
}
Since I really haven't read the specs, I was wondering if anyone could explain me this behavior (of always adding the token's issuer to the ValidIssuers collection and checking if the token's issuer is in the ValidIssuers - which will always be true!)
Final edit
Ok, my bad...Not enough coffee, I think...In fact, the issuer is being added not from the token itself, but from the IIssuerSecurityTokenProvider that is passed to the JwtFormat ctor (gets it from the metadata endpoint)...
Sorry guys...
Thanks.
Luis
I'm currently without Vs, because I'm writing on my mobile phone, there should be a ValidateIssuer Property in the TokenValidationParameters, but it looks like you set the IssuerValidator to ValidateIssuer, which should be true, so try it that way:
private TokenValidationParameters GetTokenValidationParameters() {
return new TokenValidationParameters {
ValidAudience = ConfigData.ClientId,
ValidIssuer = "nobody",
ValidateIssuer = true
};
}
I'm new at Neo4j and I'm going to use neo4j.AspNet.Identity for my authentication and authorization. I'm using neo4jUserManager and also neo4jUserStore.But when I run the application, in Neo4jUserManager create method I'll face with NullExceptionReference and I don't know why? Can Anybody help me?
Below is my code
public class Neo4jUserManager : UserManager<ApplicationUser>
{
public Neo4jUserManager(IUserStore<ApplicationUser> store)
: base(store)
{
}
public async Task SetLastLogin()
{
// Store.FindByIdAsync()
}
public static Neo4jUserManager Create(IdentityFactoryOptions<Neo4jUserManager> options, IOwinContext context)
{
var graphClientWrapper = context.Get<GraphClientWrapper>();
var manager = new Neo4jUserManager(new Neo4jUserStore<ApplicationUser>(graphClientWrapper.GraphClient));
// Configure validation logic for usernames
// manager.UserValidator = new UserValidator<Neo4jUser>(manager)
// {
// AllowOnlyAlphanumericUserNames = false,
// RequireUniqueEmail = true
// };
manager.PasswordValidator = new PasswordValidator
{
RequiredLength = 6,
RequireNonLetterOrDigit = true,
RequireDigit = true,
RequireLowercase = true,
RequireUppercase = true
};
// Configure user lockout defaults
manager.UserLockoutEnabledByDefault = false;
manager.DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(5);
manager.MaxFailedAccessAttemptsBeforeLockout = 5;
// Register two factor authentication providers. This application uses Phone and Emails as a step of receiving a code for verifying the user
// You can write your own provider and plug it in here.
// manager.RegisterTwoFactorProvider("Phone Code", new PhoneNumberTokenProvider<Neo4jUser>
// {
// MessageFormat = "Your security code is {0}"
// });
// manager.RegisterTwoFactorProvider("Email Code", new EmailTokenProvider<Neo4jUser>
// {
// Subject = "Security Code",
// BodyFormat = "Your security code is {0}"
// });
// manager.EmailService = new EmailService();
// manager.SmsService = new SmsService();
var dataProtectionProvider = options.DataProtectionProvider;
if (dataProtectionProvider != null)
{
manager.UserTokenProvider =
new DataProtectorTokenProvider<ApplicationUser>(dataProtectionProvider.Create("ASP.NET Identity"));
}
return manager;
}
}
To setup the graphClientWrapper, you can do this:
var gc = new GraphClient(new Uri("http://localhost.:7474/db/data"));
gc.Connect();
var graphClientWrapper = new GraphClientWrapper(gc);
var manager = new Neo4jUserManager(new Neo4jUserStore<ApplicationUser>(graphClientWrapper.GraphClient));
Also, as a side I would read up on how to use Owin. It should have been obvious to you that you're trying to pull from an IOwinContext object, and that if you're getting null you should investigate how to set that up. I imagine less than 5 minutes on Google would have helped you. OR just looking at what calls that method would have shown you how the EntityFramework was being setup in the template you're modifying.