How to retrive profile photo,email from facebook graph version V 2.4 in asp.net MVC 5 - asp.net-mvc-5

i need to facebook profile photo and email in purpose of asp.mvc 5 web application. but facebook graph api version 2.4 email permission accessed. but .net mvc 5 code email value is null.and profile photo.
startup.Auth
var facebookAuthenticationOptions = new FacebookAuthenticationOptions()
{
AppId = "1655122338065609",
AppSecret = "5e858655ce82fad864e3991ae00db87a"
};
facebookAuthenticationOptions.Scope.Add("email");
app.UseFacebookAuthentication(facebookAuthenticationOptions);
ExternalLoginCallback
public async Task<ActionResult> ExternalLoginCallback(string returnUrl)
{
var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();
if (loginInfo == null)
{
return RedirectToAction("Login");
}
var externalIdentity = HttpContext.GetOwinContext().Authentication.GetExternalIdentityAsync(DefaultAuthenticationTypes.ExternalCookie);
var emailClaim = externalIdentity.Result.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Email);
var email = emailClaim.Value;
// Sign in the user with this external login provider if the user already has a login
var result = await SignInManager.ExternalSignInAsync(loginInfo, isPersistent: false);
switch (result)
{
case SignInStatus.Success:
return RedirectToLocal(returnUrl);
case SignInStatus.LockedOut:
return View("Lockout");
case SignInStatus.RequiresVerification:
return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = false });
case SignInStatus.Failure:
default:
// 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,Email=loginInfo.Email });
}
}
externallogoncallbacck method var emailcliam value is null return from startup.auth ???

Related

Unable to redirect uri after cas authentication with WSO2 provider using asp.net core authorization

[enter image description here][1]I am trying to authenticate my .net core application using cas with Wso2 identity provider although the autentication is succesful but on redirect uri i am getting webpage not found error with cas ticket not able to figure out what is the issue any guidance would be helpful
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
options.LoginPath = "/Login";
options.Events.OnSigningOut = context =>
{
var redirectContext = new RedirectContext<CookieAuthenticationOptions>(
context.HttpContext,
context.Scheme,
context.Options,
context.Properties,
"/"
);
if (builder.Configuration.GetValue("Authentication:CAS:SingleSignOut", false))
{
// Single Sign-Out
var casUrl = new Uri(builder.Configuration["Authentication:CAS:ServerUrlBase"]);
var links = context.HttpContext.RequestServices.GetRequiredService<LinkGenerator>();
var serviceUrl = context.Properties.RedirectUri ?? links.GetUriByPage(context.HttpContext, "/Index");
redirectContext.RedirectUri = UriHelper.BuildAbsolute(
casUrl.Scheme,
new HostString(casUrl.Host, casUrl.Port),
casUrl.LocalPath, "/logout",
QueryString.Create("service", serviceUrl!));
}
context.Options.Events.RedirectToLogout(redirectContext);
return Task.CompletedTask;
};
})
.AddCAS(o =>
{
o.CasServerUrlBase = builder.Configuration["Authentication:CAS:ServerUrlBase"]; // Set in `appsettings.json` file.
o.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
o.CasValidationUrl = "/";
});
Login controller
[HttpGet("login")]
public IActionResult Login(string returnUrl)
{
// var props = new AuthenticationProperties { RedirectUri = "/" };
return Challenge(new AuthenticationProperties { RedirectUri = returnUrl }, "CAS");
// return Redirect("http://localhost:5095/");
}
[Web Response](https://i.stack.imgur.com/1LFOP.png)
[1]: https://i.stack.imgur.com/v3fKm.png

Both OIDC and ADFS in same application (OWIN)

I am working on an ASP.NET MVC 5 website using EPiServer CMS.
The requirement is that we have admin (back-end users) log in using ADFS (working) and OIDC for front-end.
Both are set to passive mode and are called through action methods in a controller.
The issue I am facing is that front-end users OIDC is set to (Authentication type ="Federation") when it returns from external call instead of external cookie.
public void Configuration(IAppBuilder app)
{
//IdentityModelEventSource.ShowPII = true;
ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11;
ConfigureMemberAuth(app);
ConfigureAdminAuth(app);
}
private void ConfigureMemberAuth(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/logmein/"),
LogoutPath = new PathString("/"),
AuthenticationMode = AuthenticationMode.Active
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
_ = app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
ClientId = ApplicationSettings.OIDCClientId,
ClientSecret = ApplicationSettings.OIDCClientSecret,
Authority = ApplicationSettings.OIDCAuthority,
RedirectUri = $"{ApplicationSettings.Domain}{_oidcRedirectMethod}",
PostLogoutRedirectUri = ApplicationSettings.Domain,
ResponseType = OpenIdConnectResponseType.CodeIdToken,
Scope = "xxx",
SaveTokens = true,
AuthenticationMode = AuthenticationMode.Passive,
AuthenticationType = OpenIdConnectAuthenticationDefaults.AuthenticationType,
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = notification =>
{
if (string.Equals(notification.ProtocolMessage.Error, "xxx", StringComparison.Ordinal))
{
notification.HandleResponse();
if (string.Equals(notification.ProtocolMessage.ErrorDescription, "xxx", StringComparison.Ordinal))
notification.Response.Redirect(ApplicationSettings.Domain);
else
{
var errorPage = UrlResolver.GetUrl(PageHelper.GetAdminPage().LoginBox.NemIDErrorPage.GetFriendlyUrl());
notification.Response.Redirect(errorPage);
}
}
return Task.CompletedTask;
},
// Retrieve an access token from the remote token endpoint
// using the authorization code received during the current request.
AuthorizationCodeReceived = async notification =>
{
using (var client = new HttpClient())
{
var configuration = await notification.Options.ConfigurationManager.GetConfigurationAsync(notification.Request.CallCancelled);
var tokenEndpointResult = await ExchangeCodeForTokens(notification, client, configuration);
// Add the identity token to the returned ClaimsIdentity to make it easier to retrieve.
notification.AuthenticationTicket.Identity.AddClaim(new Claim(
type: OpenIdConnectParameterNames.IdToken,
value: tokenEndpointResult.Value<string>(OpenIdConnectParameterNames.IdToken)));
// Retrieve the claims from UserInfo endpoint using the access token as bearer token.
var accesstoken = tokenEndpointResult.Value<string>(OpenIdConnectParameterNames.AccessToken);
var userInfoEndpointResult = await UserInfoEndpointClaims(notification, client, configuration, accesstoken);
//Security note: It is important to verify that the sub claim from ID token matches the sub claim in the UserInfo response
var userinfoSub = userInfoEndpointResult["xx"].Value<string>();
var idtokenSub = notification.AuthenticationTicket.Identity.FindFirst("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier").Value;
if (userinfoSub == idtokenSub)
{
//add claims from UserInfo endpoint to identity
foreach (var entry in userInfoEndpointResult)
{
if (!notification.AuthenticationTicket.Identity.HasClaim(c => c.Type == entry.Key))
{
notification.AuthenticationTicket.Identity.AddClaim(new Claim(
type: entry.Key,
value: entry.Value.ToString()));
}
}
// Add access token to claims.
notification.AuthenticationTicket.Identity.AddClaim(new Claim(
OpenIdConnectParameterNames.AccessToken,
accesstoken));
}
}
},
// Attach the id_token stored in the authentication cookie to the logout request.
RedirectToIdentityProvider = notification =>
{
if (notification.ProtocolMessage.RequestType == OpenIdConnectRequestType.Logout)
{
var token = notification.OwinContext.Authentication.User?.FindFirst(OpenIdConnectParameterNames.IdToken);
if (token != null)
{
notification.ProtocolMessage.IdTokenHint = token.Value;
}
notification.Response.Redirect(ApplicationSettings.Domain);
}
return Task.CompletedTask;
},
}
});
}
private void ConfigureAdminAuth(IAppBuilder app)
{
//Enable cookie authentication, used to store the claims between requests
app.SetDefaultSignInAsAuthenticationType(WsFederationAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = WsFederationAuthenticationDefaults.AuthenticationType,
CookieName = WsFederationAuthenticationDefaults.CookieName,
AuthenticationMode = AuthenticationMode.Active,
CookieHttpOnly = true,
});
//Enable federated authentication
_ = app.UseWsFederationAuthentication(new WsFederationAuthenticationOptions()
{
//Trusted URL to federation server meta data
MetadataAddress = ApplicationSettings.MetaDataAddress,
//Value of Wtreal must *exactly* match what is configured in the federation server
Wtrealm = ApplicationSettings.RelyPartyUri,
AuthenticationMode = AuthenticationMode.Passive,
Notifications = new WsFederationAuthenticationNotifications()
{
RedirectToIdentityProvider = (ctx) =>
{
// To avoid a redirect loop to the federation server send 403 when user is authenticated but does not have access
if (ctx.OwinContext.Response.StatusCode == 401 && (ctx.OwinContext.Authentication.User.Identity.AuthenticationType == WsFederationAuthenticationDefaults.AuthenticationType && ctx.OwinContext.Authentication.User.Identity.IsAuthenticated))
{
ctx.OwinContext.Response.StatusCode = 403;
ctx.HandleResponse();
}
//XHR requests cannot handle redirects to a login screen, return 401
if (ctx.OwinContext.Response.StatusCode == 401 && IsXhrRequest(ctx.OwinContext.Request))
{
ctx.HandleResponse();
}
return Task.FromResult(0);
},
SecurityTokenValidated = (ctx) =>
{
//Ignore scheme/host name in redirect Uri to make sure a redirect to HTTPS does not redirect back to HTTP
var redirectUri = new Uri(ctx.AuthenticationTicket.Properties.RedirectUri, UriKind.RelativeOrAbsolute);
if (redirectUri.IsAbsoluteUri)
{
ctx.AuthenticationTicket.Properties.RedirectUri = redirectUri.PathAndQuery;
}
//Sync user and the roles to EPiServer in the background
ServiceLocator.Current.GetInstance<ISynchronizingUserService>().SynchronizeAsync(ctx.AuthenticationTicket.Identity);
return Task.FromResult(0);
},
}
});
//Add stage marker to make sure WsFederation runs on Authenticate (before URL Authorization and virtual roles)
app.UseStageMarker(PipelineStage.Authenticate);
// Remap logout to a federated logout
app.Map(LogoutUrl, map =>
{
map.Run(ctx =>
{
ctx.Authentication.SignOut();
ctx.Response.Redirect(ApplicationSettings.Domain);
return Task.FromResult(0);
});
});
}
When I out comment this line: app.SetDefaultSignInAsAuthenticationType(WsFederationAuthenticationDefaults.AuthenticationType);
in admin auth then client-login works as intended and authentication type is set to "ExternalCookie" when call return but then admin login stops working ?
Any help would be appreciated

MVC 5 create user using Identity 2 in synchronous way

I'm trying to create new user and add him to selected role. I want to do it sync way, but always get stacked at point of creating in part ModelState. If I make it without ModelState then method will stack at point adminresult where is user created with UserManager. It looks like there is a problem with adding user to selected role, but I'm not sure. I'm using Identity 2. Is it possible to create user sync way or is it totally async process?
[HttpPost]
public ActionResult Create(User userViewModel, params string[] selectedRoles)
{
if (ModelState.IsValid)
{
var user = new User()
{
UserName = userViewModel.UserName,
Email = userViewModel.Email,
FirstName = userViewModel.FirstName,
LastName = userViewModel.LastName,
Password = userViewModel.Password
};
var adminresult = UserManager.Create(user, userViewModel.Password); // without using ModelState stack here
var roleStore = new RoleStore<IdentityRole>(context);
var roleManager = new RoleManager<IdentityRole>(roleStore);
var userStore = new UserStore<User>(context);
var userManager = new UserManager<User>(userStore);
var result = userManager.AddToRoles(user.Id, selectedRoles);
if (adminresult.Succeeded)
{
if (selectedRoles != null)
{
if (!result.Succeeded)
{
ModelState.AddModelError("", result.Errors.First());
// gets all names of roles to list
ViewBag.RoleId = new SelectList(RoleManager.Roles.ToList(), "Name", "Name");
return View("_Create");
}
}
return RedirectToAction("UserWizardIndex");
}
return View("_Create");
}
I would recommend you to use group based roles instead of giving multiple roles at login.
Your a sync will work fine in this way if single role is at login..
one thing that I saw in your code is if you have selected multiple
roles why there isn't any way to add every role against that user...
// POST: /Account/Register
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
var roleManager = new RoleManager<IdentityRole>
(
new RoleStore<IdentityRole>
(
new ApplicationDbContext()
)
);
if (!roleManager.RoleExists(model.RoleName))
{
roleManager.Create(new IdentityRole(model.RoleName));
}
var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
var role = Db.GetUserRoleId(model.RoleName);
var strRole = role.FirstOrDefault().ToList();
var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
var userInfo = new UserInformation
{
FirstName = model.FirstName,
LastName = model.LastName,
GUID = user.Id,
RoleId = model.RoleId
};
Db.UserInformations.Add(userInfo);
UserManager.AddToRoles(userInfo.GUID, model.RoleName);
await UserManager.UpdateSecurityStampAsync(user.Id);
Db.SaveChanges();
await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
// 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, "Confirm your account", "Please confirm your account by clicking here");
return RedirectToAction("Index", "Home");
}
AddErrors(result);
}
// If we got this far, something failed, redisplay form
return View(model);
}

Can`t login with seeded database configuration using asp.net identity

I have a problem with login, even though i see the tables are filled with my seeded user info from Configuration.cs:
protected override void Seed(www.Models.ApplicationDbContext context)
{
if (!context.Roles.Any(x => x.Name == "admin"))
{
var roleStore = new RoleStore<IdentityRole>(context);
var roleManager = new RoleManager<IdentityRole>(roleStore);
var role = new IdentityRole { Name = "admin" };
roleManager.Create(role);
}
if (!context.Users.Any(x => x.UserName == "admin" && x.Email == "admin#admin.com"))
{
var userStore = new UserStore<ApplicationUser>(context);
var userManager = new UserManager<ApplicationUser>(userStore);
var user = new ApplicationUser { UserName = "admin", Email = "admin#admin.com" };
var hasher = new PasswordHasher();
userManager.Create(user, "MySecret5");
userManager.AddToRole(user.Id, "admin");
}
}
and when i try to login i get error "Invalid login attempt".
What am i missing?
EDIT:
Im in the process of learning all stuff about asp.net so im pretty big noob now :( so i found this example to be working for me, and if anyone else needs it here it is:
protected override void Seed(www.Models.ApplicationDbContext context)
{
var userStore = new UserStore<ApplicationUser>(context);
var userManager = new UserManager<ApplicationUser>(userStore);
if (!context.Users.Any(x => x.UserName == "admin#v.com"))
{
var user = new ApplicationUser { UserName = "admin#v.com", Email = "admin#v.com" };
userManager.Create(user, "Password5%");
context.Roles.AddOrUpdate(x => x.Name, new IdentityRole { Name = "admin" });
context.SaveChanges();
userManager.AddToRole(user.Id, "admin");
}
}
And thanks for all your help and time.
It seems that the problem with logging in with seeded users is associated with MVC inaccuracy, look at:
var result = await SignInManager.PasswordSignInAsync(model.Emali, model.Password, model.RememberMe, shouldLockout: false);
if we seed this user:
var user = new ApplicationUser { UserName = "SomeName", Email = "admin#v.com" };
result will be == false, but if we change model.Emali in result to model.UserName then login end with success - of course now when we log in, we must give the UserName e.g:
UserName: SomeName;
Passwort: Password5%;
This worked flawlessly for me.
protected override void Seed(www.Models.ApplicationDbContext context)
{
var userStore = new UserStore<ApplicationUser>(context);
var userManager = new UserManager<ApplicationUser>(userStore);
if (!context.Users.Any(x => x.UserName == "admin#v.com"))
{
var user = new ApplicationUser { UserName = "admin#v.com", Email = "admin#v.com" };
userManager.Create(user, "Password5%");
context.Roles.AddOrUpdate(x => x.Name, new IdentityRole { Name = "admin" });
context.SaveChanges();
userManager.AddToRole(user.Id, "admin");
}
}

How can I change UserManager logic so that user must exist in database before they register

I am customizing the MVC5 registration process so that when users are registering they must enter two custom fields 'MyNewField1' and 'MyNewField2' which will be then checked against the user context to ensure if they exist in which case the registration can succeed by updating that current user.
public async Task<ActionResult> CustomRegister(CustomRegisterViewModel model)
{
if (ModelState.IsValid)
{
var context = new ApplicationDbContext();
ApplicationUser user = context.Users.Where(a => a.MyNewField1== model.MyNewField1& a.MyNewField2== a.MyNewField2).SingleOrDefault();
if(user != null)
{
var emailCheck = await UserManager.FindByNameAsync(model.Email);
if (emailCheck == null)
{
//We have found a user and email address has not been already assigned to another
//assign the email entered for this user in place of the username and email place
//holders and update the user before saving to the database
user.UserName = model.Email;
user.Email = model.Email;
var hasher = new PasswordHasher();
user.PasswordHash = hasher.HashPassword(model.Password);
context.SaveChanges();
var 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, "Budget Energy Email Verification", "Please confirm your account by clicking this link: link");
ViewBag.Link = callbackUrl;
ViewBag.Message = "Check your email and confirm your account, you must be confirmed before you can log in.";
return View("Info");
}
else
{
//This email address is already assigned to a user
return View(model);
}
}
else
{
//No user exists with these details so redisplay form
return View(model);
}
}
}
This method is passing off successfully and I am being informed that an email has been sent however when I click on this email link I am taken to an error page with the error being Invalid Token. Because I have changed the logic here do I have to create a token in a different manner?
I was able to solve this as follows:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> BillpayRegister(BillpayRegisterViewModel model)
{
if (ModelState.IsValid)
{
var context = new ApplicationDbContext();
ApplicationUser customer = context.Users.Where(a => a.MyNewField1 == model.MyNewField1 & a.MyNewField2 == model.MyNewField2).SingleOrDefault();
if(customer != null)
{
var emailCheck = await UserManager.FindByNameAsync(model.Email);
if (emailCheck == null)
{
//We have found a user and email address has not been already assigned to another
//assign the email entered for this user in place of the username and email place
//holders and update the user before saving to the database
var user = UserManager.FindById(customer.Id);
user.UserName = model.Email;
UserManager.SetEmail(user.Id, model.Email);
string hashedNewPassword = UserManager.PasswordHasher.HashPassword(model.Password);
user.PasswordHash = hashedNewPassword;
var result = UserManager.Update(user);
if (result.Succeeded)
{
var 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, "Email Verification", "Please confirm your account by clicking this link: link");
ViewBag.Link = callbackUrl;
ViewBag.Message = "Check your email and confirm your account, you must be confirmed before you can log in.";
return View("Info");
}
}
else
{
//This email address is already assigned to a user
return View(model);
}
}
else
{
//No user exists with these details so redisplay form
return View(model);
}
}
// If we got this far, something failed, redisplay form
return View(model);
}

Resources