I was following up this great tutorial from Tims Persistance Ignorance with Asp.NET Identity and Patterns Everything was working fine until I found I've got an error when rendering a partial View with Ajax. In this case, App redirect to LogOff and then, of course, to Login View. I can't trace the problem, I couldn't find anything wrong
My Startup class looks like this
public void ConfigureAuth(IAppBuilder app)
{
// Enable the application to use a cookie to store information for the signed in user
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
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);
This is the controller
[HttpPost]
public ActionResult IndexMedico(MedicoVM vm)
{
if (vm.ConsultorioSeleccionadoId != 0)
{
Session["ConsultorioId"] = vm.ConsultorioSeleccionadoId;
}
else
{
if (Session["ConsultorioId"] != null)
{
vm.ConsultorioSeleccionadoId = Int64.Parse(Session["ConsultorioId"].ToString());
}
}
vm.Medico = CurrentUser.Medico;
TurnosManager manager = new TurnosManager(unitofwork);
manager.Manage(vm);
vm.UsuarioEsMedico = CurrentUser.EsMedico;
if(Request.IsAjaxRequest())
{
return PartialView("_PanelMedico", vm);
}
return View(vm);
}
The problem ocurred after return PartialView. And the partial View is this
@model TurnosMedicos.Domain.Business.ViewModels.MedicoVM
<div id="panellistado" class="panel panel-primary">
<div class="panel panel-body">
<div class="panel panel-title">
<span id="dias" class="panel">
@switch(@Model.DiaSeleccionadoId)
{
case 1:
<b>TURNOS PARA HOY</b>
break;
case 2:
<b>TURNOS PARA MAÑANA</b>
break;
case 3:
<b>TURNOS TODA LA SEMANA</b>
break;
}
</span>
</div>
<!-- Esto es para actualizar el Summary con la cantidad de turnos. -->
<div id="PacientesHoy" name="PacientesHoy" style="visibility: hidden">
@Model.CantidadPacientes
</div>
<!-- Fin de Div para actualizar el Summary-->
<table class="table">
<thead>
<tr>
<th>FECHA</th>
<th>HORA</th>
<th class="col-md-1">PACIENTE</th>
<th class="col-md-1">RANKING</th>
<th>ACCIONES</th>
<th></th>
</tr>
</thead>
@foreach (var t in Model.Medico.TurnosDisponibles)
{
<tr>
<th>@t.Fecha.ToShortDateString()</th>
<th>@t.Hora</th>
<th class="col-md-1">
@if (t.Paciente != null && t.Estado == TurnosMedicos.Entities.EstadoTurno.Reservado)
{
<button type="button" class="btn btn-sm btn-info"
data-toggle="popover" title="Teléfono"
data-content="@t.Paciente.TelefonosRapido()">
<i class="fa fa-user"> </i> @t.Paciente.ToString()
</button>
}
else
{
<a onclick="ObtenerTurnoId(@t.Id);" href="#" data-toggle="modal" data-target="#NuevoTurnoModal"><span class="glyphicon glyphicon-plus-sign"></span> Agregar Paciente</a>
}
</th>
<th class="col-md-1">
@if(t.Paciente != null)
{
if (t.Paciente.Ranking == 100)
{
<i title="@t.Paciente.Ranking %" class="fa fa-thumbs-up fa-2x btn-success"></i>
}
if (t.Paciente.Ranking < 100 && t.Paciente.Ranking >= 75)
{
<i title="@t.Paciente.Ranking %" class="fa fa-arrow-circle-left fa-2x btn-info"></i>
}
if (t.Paciente.Ranking < 75 && t.Paciente.Ranking >= 50)
{
<i title="@t.Paciente.Ranking %" class="fa fa-arrow-circle-down fa-2x btn-warning"></i>
}
if (t.Paciente.Ranking < 50)
{
<i title="@t.Paciente.Ranking %" class="fa fa-thumbs-down fa-2x btn-danger"></i>
}
}
</th>
<th>
<a title="Anular este turno y ponerlo NO Disponible" class="fa fa-remove fa-2x btn btn-danger" onclick="AnularTurno(@t.Id);"></a>
@if (t.Paciente != null && t.Estado == TurnosMedicos.Entities.EstadoTurno.Reservado)
{
<a title="Paciente Ausente" class="fa fa-user-times fa-2x btn" onclick="PacienteAusente(@t.Id);"><span class="glyphicon glyphicon-"></span> </a>
<a title="Cancelar y Reprogramar" class="fa fa-random fa-2x btn" onclick="CancelarTurno(@t.Id);"><span class="glyphicon glyphicon-"></span> </a>
}
</th>
</tr>
}
</table>
</div>
<div id="NuevoTurnoModal" class="modal fade" role="form">
<div class="modal-dialog">
<!-- Contenido Modal -->
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">×</button>
<h4 class="modal-title">NUEVO TURNO</h4>
</div>
<div class="modal-body">
<p>Ingrese el número de documento del paciente</p>
<input id="txtDNI" type="text" />
</div>
<div class="modal-footer">
<button id="btnAsignar" type="button" onclick="TurnoExpress()" class="btn btn-default" data-dismiss="modal">Asignar</button>
</div>
</div>
</div>
</div>
<div class="panel panel-body">
<div class="panel panel-heading">TURNOS DISPONIBLES</div>
</div>
The ViewModel I am using is
namespace TurnosMedicos.Domain.Business.ViewModels
{
public class MedicoVM: BaseViewModel
{
public MedicoVM()
{
Medico = new Medico();
}
public Medico Medico { get; set; }
public int DiaSeleccionadoId { get; set; }
public Int64 ConsultorioSeleccionadoId { get; set; }
public int CantidadPacientes { get; set; }
}
public enum Dias
{
Hoy = 1,
Maniana = 2,
Semana = 3,
ProximaSemana = 4,
Todo = 5
}
}
The Account Controller
public class AccountController : Controller
{
private readonly UserManager<IdentityUser, Guid> _userManager;
public AccountController(UserManager<IdentityUser, Guid> userManager)
{
_userManager = userManager;
}
// GET: /Account/Login
[AllowAnonymous]
public ActionResult Login(string returnUrl)
{
ViewBag.ReturnUrl = returnUrl;
var vm = new LoginViewModel();
return View(vm);
}
//
// POST: /Account/Login
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
if (ModelState.IsValid)
{
var user = await _userManager.FindAsync(model.Email, model.Password);
if (user != null)
{
await SignInAsync(user, model.RememberMe);
if (user.EsMedico)
{
model.UsuarioEsMedico = true;
}
else
{
model.UsuarioEsMedico = false;
}
return RedirectToLocal(returnUrl);
}
else
{
ModelState.AddModelError("", "Invalid username or password.");
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
//
// GET: /Account/Register
[AllowAnonymous]
public ActionResult Register()
{
var vm = new RegisterViewModel();
vm.CanalesCaptacion.Clear();
vm.CanalesCaptacion.Add("Búsqueda en Internet (google, yahoo, etc)");
vm.CanalesCaptacion.Add("Me lo recomendo un amigo/colega");
vm.CanalesCaptacion.Add("Recibí un Email de promoción");
vm.CanalesCaptacion.Add("Lo ví en Facebook");
vm.CanalesCaptacion.Add("Expomedical");
vm.CanalesCaptacion.Add("Otras redes sociales (Linkedin, etc)");
vm.CanalesCaptacion.Add("Twitter");
vm.CanalesCaptacion.Add("Visitador medico");
vm.CanalesCaptacion.Add("Por mi médico");
vm.CanalesCaptacion.Add("Otro");
return View(vm);
}
//
// POST: /Account/Register
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
TurnosMedicos.UI.Web.Models.IdentityUser user = new TurnosMedicos.UI.Web.Models.IdentityUser();
user.Email = model.Email;
user.UserName = model.UserName;
user.CanalSeleccionado = model.CanalSeleccionado;
user.Nombres = model.Nombres;
user.Apellido = model.Apellido;
user.EsMedico = model.EsMedico;
user.Matricula = model.Matricula;
user.NroDocumento = model.NroDocumento;
user.FechaNacimiento = model.FechaNacimiento;
var result = await _userManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
RegistrarMedicoOPaciente(user);
await SignInAsync(user, isPersistent: false);
return RedirectToAction("Index", "Home");
}
else
{
AddErrors(result);
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
private void RegistrarMedicoOPaciente(IdentityUser user)
{
if(user.EsMedico)
{
user.Medico = new TurnosMedicos.Entities.Medico();
user.Medico.Apellido = user.Apellido;
user.Medico.Email = user.Email;
user.Medico.Nombres = user.Nombres;
user.Medico.Matricula = user.Matricula;
user.Medico.FechaNacimiento = user.FechaNacimiento;
user.Medico.NroDocumento = user.NroDocumento;
user.Medico.PrecioTurno = ObtenerPrecioDefault();
}
//Todos somos pacientes
user.Paciente = new TurnosMedicos.Entities.Paciente();
user.Paciente.Apellido = user.Apellido;
user.Paciente.Nombres = user.Nombres;
user.Paciente.Email = user.Email;
user.Paciente.FechaNacimiento = user.FechaNacimiento;
user.Paciente.NroDocumento = user.NroDocumento;
_userManager.Update(user);
}
private decimal ObtenerPrecioDefault()
{
decimal precio = 0;
if(System.Configuration.ConfigurationManager.AppSettings["Precio"] != null)
{
precio = decimal.Parse(System.Configuration.ConfigurationManager.AppSettings["Precio"].ToString());
}
return precio;
}
//
// POST: /Account/Disassociate
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Disassociate(string loginProvider, string providerKey)
{
ManageMessageId? message = null;
IdentityResult result = await _userManager.RemoveLoginAsync(getGuid(User.Identity.GetUserId()), new UserLoginInfo(loginProvider, providerKey));
if (result.Succeeded)
{
message = ManageMessageId.RemoveLoginSuccess;
}
else
{
message = ManageMessageId.Error;
}
return RedirectToAction("Manage", new { Message = message });
}
//
// GET: /Account/Manage
public ActionResult Manage(ManageMessageId? message)
{
ViewBag.StatusMessage =
message == ManageMessageId.ChangePasswordSuccess ? "Your password has been changed."
: message == ManageMessageId.SetPasswordSuccess ? "Your password has been set."
: message == ManageMessageId.RemoveLoginSuccess ? "The external login was removed."
: message == ManageMessageId.Error ? "An error has occurred."
: "";
ViewBag.HasLocalPassword = HasPassword();
ViewBag.ReturnUrl = Url.Action("Manage");
return View();
}
//
// POST: /Account/Manage
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Manage(ManageUserViewModel model)
{
bool hasPassword = HasPassword();
ViewBag.HasLocalPassword = hasPassword;
ViewBag.ReturnUrl = Url.Action("Manage");
if (hasPassword)
{
if (ModelState.IsValid)
{
IdentityResult result = await _userManager.ChangePasswordAsync(getGuid(User.Identity.GetUserId()), model.OldPassword, model.NewPassword);
if (result.Succeeded)
{
return RedirectToAction("Manage", new { Message = ManageMessageId.ChangePasswordSuccess });
}
else
{
AddErrors(result);
}
}
}
else
{
// User does not have a password so remove any validation errors caused by a missing OldPassword field
ModelState state = ModelState["OldPassword"];
if (state != null)
{
state.Errors.Clear();
}
if (ModelState.IsValid)
{
IdentityResult result = await _userManager.AddPasswordAsync(getGuid(User.Identity.GetUserId()), model.NewPassword);
if (result.Succeeded)
{
return RedirectToAction("Manage", new { Message = ManageMessageId.SetPasswordSuccess });
}
else
{
AddErrors(result);
}
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
//
// POST: /Account/ExternalLogin
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult ExternalLogin(string provider, string returnUrl)
{
// Request a redirect to the external login provider
return new ChallengeResult(provider, Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl }));
}
//
// GET: /Account/ExternalLoginCallback
[AllowAnonymous]
public async Task<ActionResult> ExternalLoginCallback(string returnUrl)
{
var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();
if (loginInfo == null)
{
return RedirectToAction("Login");
}
// Sign in the user with this external login provider if the user already has a login
var user = await _userManager.FindAsync(loginInfo.Login);
if (user != null)
{
await SignInAsync(user, isPersistent: false);
return RedirectToLocal(returnUrl);
}
else
{
// If the user does not have an account, then prompt the user to create an account
ViewBag.ReturnUrl = returnUrl;
ViewBag.LoginProvider = loginInfo.Login.LoginProvider;
return View("ExternalLoginConfirmation", new ExternalLoginConfirmationViewModel { UserName = loginInfo.DefaultUserName });
}
}
//
// POST: /Account/LinkLogin
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult LinkLogin(string provider)
{
// Request a redirect to the external login provider to link a login for the current user
return new ChallengeResult(provider, Url.Action("LinkLoginCallback", "Account"), User.Identity.GetUserId());
}
//
// GET: /Account/LinkLoginCallback
public async Task<ActionResult> LinkLoginCallback()
{
var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync(XsrfKey, User.Identity.GetUserId());
if (loginInfo == null)
{
return RedirectToAction("Manage", new { Message = ManageMessageId.Error });
}
var result = await _userManager.AddLoginAsync(getGuid(User.Identity.GetUserId()), loginInfo.Login);
if (result.Succeeded)
{
return RedirectToAction("Manage");
}
return RedirectToAction("Manage", new { Message = ManageMessageId.Error });
}
//
// POST: /Account/ExternalLoginConfirmation
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> ExternalLoginConfirmation(ExternalLoginConfirmationViewModel model, string returnUrl)
{
if (User.Identity.IsAuthenticated)
{
return RedirectToAction("Manage");
}
if (ModelState.IsValid)
{
// Get the information about the user from the external login provider
var info = await AuthenticationManager.GetExternalLoginInfoAsync();
if (info == null)
{
return View("ExternalLoginFailure");
}
var user = new IdentityUser() { UserName = model.UserName };
var result = await _userManager.CreateAsync(user);
if (result.Succeeded)
{
result = await _userManager.AddLoginAsync(user.Id, info.Login);
if (result.Succeeded)
{
await SignInAsync(user, isPersistent: false);
return RedirectToLocal(returnUrl);
}
}
AddErrors(result);
}
ViewBag.ReturnUrl = returnUrl;
return View(model);
}
//
// POST: /Account/LogOff
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult LogOff()
{
AuthenticationManager.SignOut();
Session["Usuario"] = null;
return RedirectToAction("Index", "Home");
}
//
// GET: /Account/ExternalLoginFailure
[AllowAnonymous]
public ActionResult ExternalLoginFailure()
{
var vm = new BaseViewModel();
return View(vm);
}
[ChildActionOnly]
public ActionResult RemoveAccountList()
{
var linkedAccounts = _userManager.GetLogins(getGuid(User.Identity.GetUserId()));
ViewBag.ShowRemoveButton = HasPassword() || linkedAccounts.Count > 1;
return (ActionResult)PartialView("_RemoveAccountPartial", linkedAccounts);
}
protected override void Dispose(bool disposing)
{
if (disposing && _userManager != null)
{
_userManager.Dispose();
}
base.Dispose(disposing);
}
#region Helpers
// Used for XSRF protection when adding external logins
private const string XsrfKey = "XsrfId";
private IAuthenticationManager AuthenticationManager
{
get
{
return HttpContext.GetOwinContext().Authentication;
}
}
private async Task SignInAsync(IdentityUser user, bool isPersistent)
{
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
var identity = await _userManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity);
}
private void AddErrors(IdentityResult result)
{
foreach (var error in result.Errors)
{
ModelState.AddModelError("", error);
}
}
private bool HasPassword()
{
var user = _userManager.FindById(getGuid(User.Identity.GetUserId()));
if (user != null)
{
return user.PasswordHash != null;
}
return false;
}
public enum ManageMessageId
{
ChangePasswordSuccess,
SetPasswordSuccess,
RemoveLoginSuccess,
Error
}
private ActionResult RedirectToLocal(string returnUrl)
{
if (Url.IsLocalUrl(returnUrl))
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction("Index", "Home");
}
}
private 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);
}
}
private Guid getGuid(string value)
{
var result = default(Guid);
Guid.TryParse(value, out result);
return result;
}
#endregion
}
And here I use a CurrentUser, that is defined in Session variable, created in HomeController
Entities.UserInfo.User _user = null;
public Entities.UserInfo.User CurrentUser
{
get
{
if (Session["Usuario"] == null)
{
_user = repositoryUser.FindByUserName(User.Identity.GetUserName());
Session["Usuario"] = _user;
return _user;
}
return (Entities.UserInfo.User)Session["Usuario"];
}
}
Just before calling the logOff action, I can check HttpContext.User.Identity and the property "IsAuthenticated" is True and the name propery has correct value.
Any Idea?
Aucun commentaire:
Enregistrer un commentaire