I have this common CUser class
public class CUser
{
public String Username { get; set; }
public String Password { get; set; }
}
Then I have this code on my client that uses RestSharp
public void CreateUser()
{
RestRequest request = new RestRequest(Method.POST);
request.RequestFormat = DataFormat.Json;
request.Resource = "user/{cUser}";
CUser user = new CUser
{
Username = "Foo",
Password = "BarBaz"
};
request.AddParameter("cUser", user, ParameterType.UrlSegment);
client.PostAsync<int>(request, (response, handler) =>
{
System.Diagnostics.Debug.WriteLine(response.StatusDescription);
System.Diagnostics.Debug.WriteLine("id: " + response.Data);
});
}
And this http route in my global.asax
routes.MapHttpRoute(
name: "CreateUser",
routeTemplate: "api/{controller}/{cUser}",
defaults: new
{
controller = "User",
action = "CreateUser",
cUser = RouteParameter.Optional
});
And this servercode to handle it
public int CreateUser(CUser cUser)
{
User user = new User(cUser);
LoginManager manager = new LoginManager();
return manager.CreateUser(user);
}
But everytime I run it cUser's values are null (Username and Password) so do I need to do something to restsharp to make it serialize it properly?
I think this is what you want:
request.AddParameter("username", user.Username);
request.AddParameter("password", user.Password);
What you're doing would result in cUser.ToString() being substituted for the {cUser} placeholder.
Related
I have created .NET Core Web Api with JWT authentication. Now, I am in the middle of creating web app using MVC Core. In MVC project I have API client wrapper:
Interface:
public interface IWebApiService
{
Task<T> AuthenticateAsync<T>(string userName);
Task<T> GetAsync<T>(string action, string authToken);
Task PutAsync<T>(string action, T data, string authToken);
Task PostAsync<T>(string action, T data, string authToken);
}
Implementation:
public class WebApiService : IWebApiService
{
private readonly WebApiSettings _webApiSettings;
public WebApiService(WebApiSettings webApiSettings)
{
_webApiSettings = webApiSettings;
}
public async Task<T> AuthenticateAsync<T>(string userName)
{
var client = new RestClient(_webApiSettings.BaseUri);
var request = new RestRequest("/Login", Method.POST)
{
RequestFormat = DataFormat.Json
};
request.AddBody(new { UserName = userName });
var response = await client.ExecuteTaskAsync(request);
if (response.IsSuccessful)
{
return JsonConvert.DeserializeObject<T>(response.Content);
}
throw new ApiException(response.StatusCode.ToString(), response.ErrorMessage);
}
public async Task<T> GetAsync<T>(string action, string authToken)
{
var client = new RestClient(_webApiSettings.BaseUri);
var request = new RestRequest(action, Method.GET)
{
RequestFormat = DataFormat.Json
};
request.AddHeader("Authorization", $"Bearer {authToken}");
var response = await client.ExecuteTaskAsync(request);
if (response.IsSuccessful)
{
return JsonConvert.DeserializeObject<T>(response.Content);
}
throw new ApiException(response.StatusCode.ToString(), response.ErrorMessage);
}
public Task PutAsync<T>(string action, T data, string authToken)
{
// TODO
throw new NotImplementedException();
}
public Task PostAsync<T>(string action, T data, string authToken)
{
// TODO
throw new NotImplementedException();
}
}
MVC Login Controller:
public class LoginController : Controller
{
private readonly IWebApiService _webApiService;
public LoginController(IWebApiService webApiService)
{
_webApiService = webApiService;
}
public async Task<IActionResult> Get(string redirectUrl)
{
var user = User.Identity.Name;
if(user == null)
throw new WebInterfaceException("Invalid username.");
var response = await _webApiService.AuthenticateAsync<JwtToken>(user);
HttpContext.Session.SetObjectAsJson("Token", response);
return Redirect(redirectUrl ?? "/Home/Index");
}
}
I keep JWT object in session as I didn't find better solution for storing tokens in MVC Core.
Below example controller:
public class ExampleController : Controller
{
private readonly IWebApiService _webApiService;
public ExampleController(IWebApiService webApiService)
{
_webApiService = webApiService;
}
[HttpGet]
public async Task<IActionResult> Browse()
{
var jwtToken = HttpContext.Session.GetObjectFromJson<JwtToken>("Token");
if (jwtToken == null)
{
return RedirectToAction("Get", "Login", new { redirectUrl = Request.Path});
}
var response = await _webApiService.GetAsync<IEnumerable<ExampleBrowseViewModel>>("/Examples", jwtToken.Token);
return Json(response);
}
}
My problem is that in every controller action I will have to check if token is not null. If it's null, I am redirecting to Login page where I am retrieving token from API and redirecting to originally requested page. I would like to have some token handler where so I will not repeat the same code over and over. Additionally in my JWT object I have token expiration time and I would like to refresh it once it will expire so user could continue sending requests to API.
Can you give me few advises so I could accomplish this?
I'm creating a web-api where I need to log people in using Facebook.
I'm following this guide.
Once I provide my credentials to Facebook, it should redirect to an Action but instead it says: "Too many redirects."
This is what I've got in my Startup.cs:
app.UseExternalSignInCookie(Microsoft.AspNet.Identity.DefaultAuthenticationTypes.ExternalCookie);
FacebookAuthenticationOptions facebookAuthOptions = new FacebookAuthenticationOptions()
{
AppId = "myAppId",
AppSecret = "myAppKey",
Provider = new FacebookAuthProvider()
};
app.UseFacebookAuthentication(facebookAuthOptions);
This is my FacebookAuthProvider: class:
public class FacebookAuthProvider : FacebookAuthenticationProvider
{
public override Task Authenticated(FacebookAuthenticatedContext context)
{
context.Identity.AddClaim(new System.Security.Claims.Claim("ExternalAccessToken", context.AccessToken));
return Task.FromResult<object>(null);
}
}
This is my ChallengeResult class:
public class ChallengeResult : IHttpActionResult
{
public string LoginProvider { get; set; }
public HttpRequestMessage Request { get; set; }
public ChallengeResult(string loginProvider, ApiController controller)
{
LoginProvider = loginProvider;
Request = controller.Request;
}
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
Request.GetOwinContext().Authentication.Challenge(LoginProvider);
HttpResponseMessage response = new HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized);
response.RequestMessage = Request;
return Task.FromResult<HttpResponseMessage>(response);
}
}
And this is the controller that I'm using to get the token from Facebook after user has logged in:
[HttpGet]
[OverrideAuthentication]
[HostAuthentication(DefaultAuthenticationTypes.ExternalCookie)]
[AllowAnonymous]
//[Route("ExternalLogin", Name = "ExternalLogin")]
public IHttpActionResult GetExternalLogin(string provider)
{
string redirectUri = string.Empty;
AppUserManager manager = new AppUserManager(new AppUserStore(new AppContext()));
if (!User.Identity.IsAuthenticated)
{
return new ChallengeResult(provider, this);
}
ExternalLoginData externalLogin = ExternalLoginData.FromIdentity(User.Identity as ClaimsIdentity);
UserLoginInfo loginInfo = new UserLoginInfo(externalLogin.LoginProvider, externalLogin.ProviderKey);
IdentityUser user = manager.Find(loginInfo);
bool hasRegistered = user != null;
ValidateRedirectUri(this.Request, ref redirectUri);
redirectUri = String.Format("{0}#external_access_token={1}&provider={2}&haslocalaccount={3}&external_user_name={4}",
redirectUri,
externalLogin.AccessToken,
externalLogin.LoginProvider,
hasRegistered.ToString(),
externalLogin.UserName);
return Redirect(redirectUri);
}
One thing that I'm really curious about, is that, if I uncomment this line:
[Route("ExternalLogin", Name = "ExternalLogin")]
And try to access that controller with that new route, it says that User (The one in the GetExternalLogin's if) is null.
This is the link that I'm using to test:
http://localhost:62887/api/ExternalAuth/GetExternalLogin?provider=Facebook&redirect_uri=http://localhost:62887/api/ExternalAuth/LoggedIn
And after the user has successfully logged in, this is the action that he's supposed to be redirected:
[HttpGet]
public IHttpActionResult LoggedIn()
{
return Ok(new { Message = "You've been successfully logged in! :)" });
}
I'm gonna kill myself, I finally got it working. I just had to update the NuGet Package from 2.1 to 3.1... >:/
I have a database, need web interface for administration and management (number of transactions, billing, and other administration) and to serve data from database (products) "manually" and API to serve data (products) to other bigger clients. All secured by SSL and https obviously.
I made a asp.net MVC 5 app (business logic and administration) and wanted to implement API (noob in API) for delivering data to users.
Have no idea how to implement security from MVC to API(same database).
The app is small and I can rewrite it. I'm thinking to try with core, but fear that i will be stuck with same problem.
Concrete question: What approach do I take and weather it should be in MVC 5 generation or .core (MVC 6) to be able to use one database for data, users and their authorizations?
(pushing everything true API is something I would like to avoid)
Ok, my project is done. I moved ahead on MVC 5.
(I apologize to you perfectionists, but I don't have the time now to strip unnecessary thing so I dumped whole files as they are :)
1st approach - abandoned
First I tried designing it as is recommended true the internet: .MVC solution, .DB for database and .API solution.
Conclusion - A lot of problems wit authentication and entity framework. At the end I abandoned this approach
2nd and successful approach
Just one solution .MVC
True NuGet installed .net Api, used integrated authorization extended using few tutorials (not single one worked). Note that I use ASP.NET Identity 2.0 Extending Identity Models and Using Integer Keys Instead of Strings and Implementing HTTPS Everywhere in ASP.Net MVC application.
Here are modifications and addons:
App_Start -> IdentityConfig.cs
public class ApplicationUserManager : UserManager<ApplicationUser, int>
{
// *** ADD INT TYPE ARGUMENT TO CONSTRUCTOR CALL:
public ApplicationUserManager(IUserStore<ApplicationUser, int> store)
: base(store)
{
}
public static ApplicationUserManager Create(
IdentityFactoryOptions<ApplicationUserManager> options,
IOwinContext context)
{
// *** PASS CUSTOM APPLICATION USER STORE AS CONSTRUCTOR ARGUMENT:
var manager = new ApplicationUserManager(
new ApplicationUserStore(context.Get<ApplicationDbContext>()));
// Configure validation logic for usernames
// *** ADD INT TYPE ARGUMENT TO METHOD CALL:
manager.UserValidator = new UserValidator<ApplicationUser, int>(manager)
{
AllowOnlyAlphanumericUserNames = false,
RequireUniqueEmail = true
};
// Configure validation logic for passwords
manager.PasswordValidator = new PasswordValidator
{
RequiredLength = 6,
RequireNonLetterOrDigit = false,
RequireDigit = true,
RequireLowercase = true,
RequireUppercase = true,
};
// other code removed for brevity
manager.UserLockoutEnabledByDefault = Convert.ToBoolean(ConfigurationManager.AppSettings["UserLockoutEnabledByDefault"].ToString());
manager.DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(Double.Parse(ConfigurationManager.AppSettings["DefaultAccountLockoutTimeSpan"].ToString()));
manager.MaxFailedAccessAttemptsBeforeLockout = Convert.ToInt32(ConfigurationManager.AppSettings["MaxFailedAccessAttemptsBeforeLockout"].ToString());
// 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 in here.
// *** ADD INT TYPE ARGUMENT TO METHOD CALL:
//manager.RegisterTwoFactorProvider("PhoneCode",
// new PhoneNumberTokenProvider<ApplicationUser, int>
// {
// MessageFormat = "Your security code is: {0}"
// });
//// *** ADD INT TYPE ARGUMENT TO METHOD CALL:
//manager.RegisterTwoFactorProvider("EmailCode",
// new EmailTokenProvider<ApplicationUser, int>
// {
// Subject = "SecurityCode",
// BodyFormat = "Your security code is {0}"
// });
//manager.EmailService = new EmailService();
//manager.SmsService = new SmsService();
var dataProtectionProvider = options.DataProtectionProvider;
if (dataProtectionProvider != null)
{
// *** ADD INT TYPE ARGUMENT TO METHOD CALL:
manager.UserTokenProvider =
new DataProtectorTokenProvider<ApplicationUser, int>(
dataProtectionProvider.Create("ASP.NET Identity"));
}
return manager;
}
}
// PASS CUSTOM APPLICATION ROLE AND INT AS TYPE ARGUMENTS TO BASE:
public class ApplicationRoleManager : RoleManager<ApplicationRole, int>
{
// PASS CUSTOM APPLICATION ROLE AND INT AS TYPE ARGUMENTS TO CONSTRUCTOR:
public ApplicationRoleManager(IRoleStore<ApplicationRole, int> roleStore)
: base(roleStore)
{
}
// PASS CUSTOM APPLICATION ROLE AS TYPE ARGUMENT:
public static ApplicationRoleManager Create(
IdentityFactoryOptions<ApplicationRoleManager> options, IOwinContext context)
{
return new ApplicationRoleManager(
new ApplicationRoleStore(context.Get<ApplicationDbContext>()));
}
}
public class EmailService : IIdentityMessageService
{
public Task SendAsync(IdentityMessage message)
{
// Plug in your email service here to send an email.
return Task.FromResult(0);
}
}
public class SmsService : IIdentityMessageService
{
public Task SendAsync(IdentityMessage message)
{
// Plug in your sms service here to send a text message.
return Task.FromResult(0);
}
}
//This is useful if you do not want to tear down the database each time you run the application.
//public class ApplicationDbInitializer : DropCreateDatabaseAlways<ApplicationDbContext>
//This example shows you how to create a new database if the Model changes
public class ApplicationDbInitializer : DropCreateDatabaseIfModelChanges<ApplicationDbContext>
{
protected override void Seed(ApplicationDbContext context)
{
//InitializeIdentityForEF(context); //- Do not Seed - IGOR
//base.Seed(context);
}
//Create User=Admin#Admin.com with password=Admin#123456 in the Admin role
//public static void InitializeIdentityForEF(ApplicationDbContext db)
//{
// var userManager = HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>();
// var roleManager = HttpContext.Current.GetOwinContext().Get<ApplicationRoleManager>();
// const string name = "igor#email.mail";
// const string password = "LolLol1";
// const string roleName = "lol";
// //Create Role Admin if it does not exist
// var role = roleManager.FindByName(roleName);
// if (role == null)
// {
// role = new ApplicationRole(roleName);
// var roleresult = roleManager.Create(role);
// }
// var user = userManager.FindByName(name);
// if (user == null)
// {
// user = new ApplicationUser { UserName = name, Email = name };
// var result = userManager.Create(user, password);
// result = userManager.SetLockoutEnabled(user.Id, false);
// }
// // Add user admin to Role Admin if not already added
// var rolesForUser = userManager.GetRoles(user.Id);
// if (!rolesForUser.Contains(role.Name))
// {
// var result = userManager.AddToRole(user.Id, role.Name);
// }
//}
}
public class ApplicationSignInManager : SignInManager<ApplicationUser, int>
{
public ApplicationSignInManager(ApplicationUserManager userManager, IAuthenticationManager authenticationManager) :
base(userManager, authenticationManager)
{ }
public override Task<ClaimsIdentity> CreateUserIdentityAsync(ApplicationUser user)
{
return user.GenerateUserIdentityAsync((ApplicationUserManager)UserManager);
}
public static ApplicationSignInManager Create(IdentityFactoryOptions<ApplicationSignInManager> options, IOwinContext context)
{
return new ApplicationSignInManager(context.GetUserManager<ApplicationUserManager>(), context.Authentication);
}
}
App_Start -> Startup.Auth.cs
public partial class Startup
{
public static OAuthAuthorizationServerOptions OAuthOptions { get; private set; }
public static string PublicClientId { get; private set; }
public void ConfigureAuth(IAppBuilder app)
{
// Configure the db context, user manager and role manager to use a single instance per request
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationRoleManager>(ApplicationRoleManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.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
// Configure the sign in cookie
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
// Enables the application to validate the security stamp when the user logs in.
// This is a security feature which is used when you change a password or add an external login to your account.
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser, int>(
validateInterval: TimeSpan.FromMinutes(2880),
regenerateIdentityCallback: (manager, user) => user.GenerateUserIdentityAsync(manager),
// Need to add THIS line because we added the third type argument (int) above:
getUserIdCallback: (claim) => int.Parse(claim.GetUserId()))
}
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
// Enables the application to temporarily store user information when they are verifying the second factor in the two-factor authentication process.
app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie, TimeSpan.FromMinutes(5));
// Enables the application to remember the second login verification factor such as phone or email.
// Once you check this option, your second step of verification during the login process will be remembered on the device where you logged in from.
// This is similar to the RememberMe option when you log in.
app.UseTwoFactorRememberBrowserCookie(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie);
// 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(
// clientId: "",
// clientSecret: "");
// Configure the application for OAuth based flow
PublicClientId = "self";
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Token"),
Provider = new ApplicationOAuthProvider(PublicClientId),
AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"), //TODO - makni ovo
AccessTokenExpireTimeSpan = TimeSpan.FromHours(1),
// In production mode set AllowInsecureHttp = false
AllowInsecureHttp = true
};
// Enable the application to use bearer tokens to authenticate users
app.UseOAuthBearerTokens(OAuthOptions);
}
}
//public partial class Startup
//{
// public void ConfigureAuth(IAppBuilder app)
// {
// // Enable the application to use a cookie to store information for the signed in user
// app.UseCookieAuthentication(new CookieAuthenticationOptions
// {
// ExpireTimeSpan = TimeSpan.FromHours(24),
// CookieSecure = CookieSecureOption.Never,
// CookieHttpOnly = false,
// SlidingExpiration = true,
// AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
// LoginPath = new PathString("/Account/Login")
// });
// // Use a cookie to temporarily store information about a user logging in with a third party login provider
// app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
// }
//}
App_Start -> WebApiConfig.cs
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// TODO: Add any additional configuration code.
// Web API routes
config.MapHttpAttributeRoutes();
//config.Routes.MapHttpRoute(
// name: "getkey",
// routeTemplate: "api/ApiKeys/Get/{term}"
//);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
// WebAPI when dealing with JSON & JavaScript!
// Setup json serialization to serialize classes to camel (std. Json format)
var formatter = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
formatter.SerializerSettings.ContractResolver =
new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver();
// make all web-api requests to be sent over https
config.MessageHandlers.Add(new EnforceHttpsHandler());
}
}
MySysAdmin controller that I use for initial insert and edit of roles and initial user.
public SysAdminController(ApplicationUserManager userManager,
ApplicationRoleManager roleManager)
{
UserManager = userManager;
RoleManager = roleManager;
}
private ApplicationUserManager _userManager;
public ApplicationUserManager UserManager
{
get
{
return _userManager ?? HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
}
set
{
_userManager = value;
}
}
private ApplicationRoleManager _roleManager;
public ApplicationRoleManager RoleManager
{
get
{
return _roleManager ?? HttpContext.GetOwinContext().Get<ApplicationRoleManager>();
}
private set
{
_roleManager = value;
}
}
public ActionResult RoleIndex()
{
return View(RoleManager.Roles);
}
public ActionResult RoleCreate()
{
return View();
}
[HttpPost]
public async Task<ActionResult> RoleCreate(SysAdminVM.RoleViewModel roleViewModel)
{
if (ModelState.IsValid)
{
// Use ApplicationRole, not IdentityRole:
var role = new ApplicationRole(roleViewModel.Name);
var roleresult = await RoleManager.CreateAsync(role);
if (!roleresult.Succeeded)
{
ModelState.AddModelError("", roleresult.Errors.First());
return View();
}
return RedirectToAction("RoleIndex");
}
return View();
}
public async Task<ActionResult> RoleEdit(int id)
{
if (id > 0)
{
var role = await RoleManager.FindByIdAsync(id);
if (role == null)
{
return HttpNotFound();
}
SysAdminVM.RoleViewModel roleModel = new SysAdminVM.RoleViewModel { Id = role.Id, Name = role.Name };
return View(roleModel);
}
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> RoleEdit([Bind(Include = "Name,Id")] SysAdminVM.RoleViewModel roleModel)
{
if (ModelState.IsValid)
{
var role = await RoleManager.FindByIdAsync(roleModel.Id);
role.Name = roleModel.Name;
await RoleManager.UpdateAsync(role);
return RedirectToAction("RoleIndex");
}
return View();
}
[AllowAnonymous]
public async Task<ActionResult> Initialize()
{
if (db.App.Where(x => x.Name.Contains("Initialize")).FirstOrDefault() == null)
{
await InitRoleCreate();
await InitUser();
db.App.Add(
new App { Name = "Initialize", Val = "true" }
);
db.SaveChanges();
return View();
}
return HttpNotFound();
}
private async Task InitRoleCreate()
{
var model = new List<string>()
{
"SysAdmin",
"Admin",
"User",
};
foreach (var item in model)
{
var role = new ApplicationRole(item);
await RoleManager.CreateAsync(role);
}
}
private async Task InitUser()
{
var user = new ApplicationUser
{
UserName = "HerGiz",
Email = "hergiz#outlook.com",
Name = "Igor Hermanović",
Contact = "098 185 3131",
TwoFactorEnabled = false,
LockoutEnabled = true,
EmailConfirmed = true
};
var adminResult = await UserManager.CreateAsync(user, "W7xtc2ywfb");
await UserManager.AddToRolesAsync(user.Id, "SysAdmin");
}
}
Entire API part that I need - controller and out of the box login (that is berried somewhere):
[Authorize]
public class ApiKeysController : ApiController
{
[Authorize]
[Route("api/getkey/{term}")]
public ShowFullKeyVM Get(string term)
{
if (User.Identity.IsAuthenticated == true)
{
if (!string.IsNullOrWhiteSpace(term) && (term.Length == 15 || term.Length == 16))
{
var lKey = new LKey();
var vm = lKey.Search(term);
if (vm != null)
{
return vm;
}
}
return new ShowFullKeyVM() { Error = "IMEI either is not valid :(", SearchIMEI = term };
}
return new ShowFullKeyVM() { Error = "Not Authenticated!!!", SearchIMEI = term };
}
}
Global.asax
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
MvcHandler.DisableMvcResponseHeader = true;
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
App_Start.AutoMapperConfig.DefineMaps();
ModelBinders.Binders.Add(typeof(decimal), new Extensions.DecimalModelBinder());
ModelBinders.Binders.Add(typeof(decimal?), new Extensions.DecimalModelBinder());
}
Web.config
<appSettings>
<add key="UserLockoutEnabledByDefault" value="true" />
<add key="DefaultAccountLockoutTimeSpan" value="30" />
<add key="MaxFailedAccessAttemptsBeforeLockout" value="4" />
</appSettings>
I'm making a registration form for the head of the family. So when someone registers a user and a family is made. The user is inserted in the DB by the Usermanager. After this i want to make a new family and add the user to this family. The Problem is, that with the code below he complains that the user allready exists.
Hope you guys can help me out. :)
Family class:
public class Familie
{
public int familieId { get; set; }
public virtual Adres adres { get; set; }
public virtual ICollection<ApplicationUser> contactPersonen { get; set;}
public virtual ICollection<ApplicationUser> kinderen { get; set; }
public ApplicationUser gezinsHoofd { get; set; }
}
Controller Code:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Registreer_Ouder(ContactPersViewModelmodel)
{
if (ModelState.IsValid)
{
var user = new ApplicationUser
{
UserName = model.email,
Email = model.email,
voorNaam = model.voorNaam,
achterNaam = model.achterNaam,
PhoneNumber = model.gsm,
PasswordHash = model.password,
};
Adres adres = new Adres
{
gemeente = db.Gemeente.Find(model.gemeente),
nummer = model.nummer,
straat = model.straat
};
Familie familie = new Familie
{
adres = adres
};
var result = await UserManager.CreateAsync(user, model.password);
if (result.Succeeded)
{
await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
var currentUser = UserManager.FindByName(user.UserName);
var roleresult = UserManager.AddToRole(currentUser.Id, "GezinsHoofd");
try {
db.Familie.Add(familie);
db.Familie.Find(familie.familieId).gezinsHoofd = user;
db.SaveChanges(); // ERROR IS HERE
}
catch(DbEntityValidationException ex)
{
// Just reading the exception for now..
throw;
}
// For more information on how to enable account confirmation and password reset please visit http://go.microsoft.com/fwlink/?LinkID=320771
// Send an email with this link
string code = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id);
var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme);
await UserManager.SendEmailAsync(user.Id, "Bevestig uw registratie", "Om te bevestigen klik hier");
return RedirectToAction("Welkom", "Home");
}
AddErrors(result);
}
// If we got this far, something failed, redisplay form
return View(model);
}
Execption:
In browser
Using ASP.NET identity is sometimes a little bit tricky... The point is that you are adding a user with UserManager.CreateAsync(user, model.password). This method adds a user to the database but there's no reference for the user in the current database context for now.
If you assign the user to an element in the model (db.Familie.Find(familie.familieId).gezinsHoofd = user), EF tries to add the user again to the database. What you need to do is to load the created user from the database and add the user to the family element:
// some code ignored for readability
try {
db.Familie.Add(familie);
// load user explicitly from database to include in the context
var dbUser = db.ApplicationUser.Find(currentUser.Id);
db.Familie.Find(familie.familieId).gezinsHoofd = dbUser;
db.SaveChanges(); // There should not be any error here :)
}
catch(DbEntityValidationException ex)
{
// Just reading the exception for now..
throw;
}
I have setup the project in Google and it gave me the appid and secret
I moved the id and secret to StartUp.Auth
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.CreatePerOwinContext<IdentityTestingDbContext>(IdentityTestingDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationRoleManager>(ApplicationRoleManager.Create);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
app.UseGoogleAuthentication(
clientId: "*********************.apps.googleusercontent.com ",
clientSecret: "**************");
}
}
Here are the actions for external login, i am following Identity Sample Application (install-package Microsoft.AspNet.Identity.Samples -Pre).
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult ExternalLogin(string provider, string returnUrl)
{
// Request a redirect to the external login provider
var challenge = new ChallengeResult(provider, Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl }));
return challenge;
}
// Used for XSRF protection when adding external logins
private const string XsrfKey = "XsrfId";
internal class ChallengeResult : HttpUnauthorizedResult
{
public ChallengeResult(string provider, string redirectUri)
: this(provider, redirectUri, null)
{
}
public ChallengeResult(string provider, string redirectUri, string userId)
{
LoginProvider = provider;
RedirectUri = redirectUri;
UserId = userId;
}
public string LoginProvider { get; set; }
public string RedirectUri { get; set; }
public string UserId { get; set; }
public override void ExecuteResult(ControllerContext context)
{
var properties = new AuthenticationProperties { RedirectUri = RedirectUri };
if (UserId != null)
{
properties.Dictionary[XsrfKey] = UserId;
}
context.HttpContext.GetOwinContext().Authentication.Challenge(properties, LoginProvider);
}
}
[AllowAnonymous]
public async Task<ActionResult> ExternalLoginCallback(string returnUrl)
{
var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();
if (loginInfo == null)
{
return RedirectToAction("Login");
}
var user = await UserManager.FindAsync(loginInfo.Login);
if (user == null)
{
user = new ApplicationUser
{
Email = loginInfo.Email,
UserName = loginInfo.DefaultUserName,
FirstName = string.Empty,
LastName = string.Empty
};
var result = await UserManager.CreateAsync(user);
if (!result.Succeeded)
{
return View("Error", result.Errors);
}
result = await UserManager.AddLoginAsync(user.Id, loginInfo.Login);
if (!result.Succeeded)
{
return View("Error", result.Errors);
}
}
var identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
identity.AddClaims(loginInfo.ExternalIdentity.Claims);
AuthenticationManager.SignIn(new AuthenticationProperties
{
IsPersistent = false
}, identity);
return Redirect(returnUrl ?? "/");
}
I get redirected to google but here i am getting an error. Looks like i am missing something but can't figure it out. I have been searching for almost 3 hours and couldn't find any thing to help with this issue.
Do you see any thing that i may be doing wrong?
Why redirect url in the image below is http://localhost:58286/signin-google
Following helped
http://www.asp.net/mvc/overview/security/create-an-aspnet-mvc-5-app-with-facebook-and-google-oauth2-and-openid-sign-on
Fix 1:
The authorized redirect url needs to be http://localhost:58286/signin-google, for the google setup screen shot in the above question thread. This isn't the callback method inside the accounts controller.
Fix 2:
I needed to enable Google+ API as well which i didn't during the setup