Can IUserMapper be used to Change User Details - asp.net

In Nancy FX how can I use the IUserMapper (if at all) to change a logged in users account details (name, email, password)?
// registering is straight forward
Post["/register", true] = async(parameters, ct) =>
{
var user = this.BindAndValidate<UserRegistration>();
var response = await mapper.RegisterUser(user); // user is registered
...
}
// but how can I change a registered user's details?
Post["/profile", true] = async(parameters, ct) =>
{
this.RequiresAuthenticationAndLogOut();
var user = this.BindAndValidate<UserRegistration>();
var response = await mapper.?????(user);
...
}

You wouldn't use the IUserMapper at all, this really only exists for authentication purposes and nothing more.
When a user is authenticated then you get access to the UserName property. If you setup your mapper to assign the user's Id to the UserName then you can load your user, modify, and commit.
i.e:
Post["/profile", true] = async(parameters, ct) =>
{
this.RequiresAuthenticationAndLogOut();
var user = this.BindAndValidate<UserRegistration>();
var existingUser = await db.LoadAsync(int.Parse(CurrentUser.UserName));
existingUser.Name = user.Name;
...
return ...;
}
Also, you should never persist an object that's been bound from a client. The user may submit additional information you don't want them to.
Also I don't know where you got your IUserMapper from because in Nancy there is no Register.

Related

Is there any possibility return the access_token with only user object?

I am implementing a functionality, where access_token will be sent via email, in this case I need to generate this token with a logic to authenticate the user when accessing the link passed via email.
public async Task<IActionResult> GetLink ()
{
var user = await userManager.FindByEmailAsync("eduardo#test.com.br"); // is active user created
if (user != null)
{
var ident = await userManager.GetAuthenticationTokenAsync(user, "Test", "access_token");
return Ok(ident);
}
return NoContent();
}
Based on the research expected would be something like this, but this is not done with persisted data and my model is not allowing this, anyone have any idea how to persist? Or even just return the token?
I think it is a bad behavior not is not acceptable, but, my user dont have a password for access in this case, maybe is necessary using the token or another mode to login.
It is a very simple flow, this link would be one for a granted action (it will only have read access, basically), and this link will be sent only to a user via email.
The above problem can be solved as follows:
[HttpGet("get_token")]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(string))]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<IActionResult> GetUserToken([FromServices] ITokenService TS, [FromServices] IUserClaimsPrincipalFactory<EkyteUser> principalFactory,
[FromServices] IdentityServerOptions options)
{
var Request = new TokenCreationRequest();
var user = await userManager.FindByIdAsync(User.GetSubjectId());
var IdentityPricipal = await principalFactory.CreateAsync(user);
var IdServerPrincipal = IdentityServerPrincipal.Create(user.Id.ToString(), user.UserName);
Request.Subject = IdServerPrincipal;
Request.IncludeAllIdentityClaims = true;
Request.ValidatedRequest = new ValidatedRequest();
Request.ValidatedRequest.Subject = Request.Subject;
Request.ValidatedRequest.SetClient(Config.GetClient());
Request.Resources = new Resources(Config.GetResources(), Config.GetApiResources());
Request.ValidatedRequest.Options = options;
var Token = await TS.CreateAccessTokenAsync(Request);
Token.Issuer = "http://" + HttpContext.Request.Host.Value;
var TokenValue = await TS.CreateSecurityTokenAsync(Token);
return Ok(TokenValue);
}
It is necessary to identify the user, set the necessary resources and consequently the client that is accessing. After that, just include the access host to generate the token.

asp.net core identity extract and save external login tokens and add claims to local identity

I am a stackoverflow noob so please go easy if I am doing this wrong.
I am using asp.net core with the default core identity template (local accounts).
I have accertained how to add claims to user principal when they login locally like so
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(LoginInputModel model)
{
if (ModelState.IsValid)
{
// This doesn't count login failures towards account lockout
// To enable password failures to trigger account lockout, set lockoutOnFailure: true
var user = await _userManager.FindByNameAsync(model.Email);
await _userManager.AddClaimAsync(user, new Claim("your-claim", "your-value"));
And I have figured out how to get claims returned from the external login but I cannot figure out how I would add these before the user principal gets created in the ExternalLoginCallback function
public async Task<IActionResult> ExternalLoginCallback(string returnUrl = null, string remoteError = null)
{
if (remoteError != null)
{
ModelState.AddModelError(string.Empty, $"Error from external provider: {remoteError}");
return View(nameof(Login));
}
var info = await _signInManager.GetExternalLoginInfoAsync();
if (info == null)
{
return RedirectToAction(nameof(Login));
}
else {
// extract claims from external token here
}
// assume add claims to user here before cookie gets created??
// Sign in the user with this external login provider if the user already has a login.
var result = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: false);
if (result.Succeeded)
I am assuming the the _signInManager.ExternalLoginSignInAsync function works similar to the local login _signInManager.PasswordSignInAsync in the sense that once it is called, the cookie will be created. But I am just not sure.
Essentially what I am hoping to achieve, is understanding of how to add custom claims into the cookie that gets created regardless of how to user logins in (local or external), and how to persist these claims to the database if required.
I am planning on doing some work where if I have a user login using say google auth, I need to save that access_token from google, because I wish to call into the Google APIs later with it. So I need to be able to include this access_token in with the User Principal that gets created, and I would hope the cookie would have a claim on it I could use at the front end as well.
This might be out of scope on this question but I would also like when the google token expires, for some-how it to use the refresh token and go get a new one, or force the user to relogin.
Any help on this would be super appreciated, I have really tried hard to understand this without posting this question to stackoverflow. I have read many articles with lots of useful info, but does not provide the answers this specific question is asking. So Thank you very much in advance.
cheers
When you use await _userManager.AddClaimAsync(user, new Claim("your-claim", "your-value")); that actually updates the Identity's aspnetuserclaims table.
Whenever you sign in (by using _signInManager.PasswordSignIn or _signInManager.ExternalLoginSignInAsync) the claims from that table are read and added to the cookie that on every request becomes the Principal.
So you probably don't want to be calling the AddClaimAsync method from UserManager on every login.
Regarding external login providers, you have access to the claims when you call (in ExternalCallback and ExternalCallbackConfirmation if you are using the default templates) here:
var info = await _signInManager.GetExternalLoginInfoAsync();
The claims are in info.Principal.Claims.
The access token is not included by default. When it is, it will be here (along with the type and expiry date):
var accessToken = info.AuthenticationTokens.Single(f => f.Name == "access_token").Value;
var tokenType = info.AuthenticationTokens.Single(f => f.Name == "token_type").Value;
var expiryDate = info.AuthenticationTokens.Single(f => f.Name == "expires_at").Value;
To have the access token be included in the AuthenticationTokens collection, when you are configuring the GoogleAuthentication middleware set the SaveTokens flag to true:
app.UseGoogleAuthentication(new GoogleOptions{
ClientId = "...",
ClientSecret = "...",
SaveTokens = true
Now, if you want to have control over which claims go in the cookie you have to "take over" the process of creating the claims principal.
This is done for you when you use _signInManager.PasswordSignIn/ExternalLoginSignInAsync.
So, for example, for ExternalLoginSignInAsync replace:
var result = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: false);
With:
var user = await this._userManager.FindByLoginAsync(info.LoginProvider, info.ProviderKey);
var claimsPrincipal = await this._signInManager.CreateUserPrincipalAsync(user);
((ClaimsIdentity)claimsPrincipal.Identity).AddClaim(new Claim("accessToken", info.AuthenticationTokens.Single(t => t.Name == "access_token").Value));
await HttpContext.Authentication.SignInAsync("Identity.Application", claimsPrincipal);
"Identity.Application" is the default cookie name. You can change it in Startup's ConfigureServices method, for example to MainCookie:
services.Configure<IdentityOptions>(options => {
options.Cookies.ApplicationCookie.AuthenticationScheme = "MainCookie";
});
You still need to handle the ExternalCallbackConfirmation action in the AccountController. It will be similar to the example above.

AutoMapper: Keep Destination Value if the property doesnt exists in source

I tried to search a lot, and try different options but nothing seems to work.
I am using ASP.net Identity 2.0 and I have UpdateProfileViewModel . When Updating the User info, I want to map the UpdateProfileViewModel to ApplicationUser (i.e. the Identity Model); but I want to keep the values, I got from the db for the user. i.e. the Username & Email address, that doesn't needs to change.
I tried doing :
Mapper.CreateMap<UpdateProfileViewModel, ApplicationUser>()
.ForMember(dest => dest.Email, opt => opt.Ignore());
but I still get the Email as null after mapping:
var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
user = Mapper.Map<UpdateProfileViewModel, ApplicationUser>(model);
I also tried this but doesn't works:
public static IMappingExpression<TSource, TDestination> IgnoreAllNonExisting<TSource, TDestination>(this IMappingExpression<TSource, TDestination> expression)
{
var sourceType = typeof(TSource);
var destinationType = typeof(TDestination);
var existingMaps = Mapper.GetAllTypeMaps().First(x => x.SourceType.Equals(sourceType) && x.DestinationType.Equals(destinationType));
foreach (var property in existingMaps.GetUnmappedPropertyNames())
{
expression.ForMember(property, opt => opt.Ignore());
}
return expression;
}
and then:
Mapper.CreateMap<UpdateProfileViewModel, ApplicationUser>()
.IgnoreAllNonExisting();
All you need is to create a mapping between your source and destination types:
Mapper.CreateMap<UpdateProfileViewModel, ApplicationUser>();
and then perform the mapping:
UpdateProfileViewModel viewModel = ... this comes from your view, probably bound
ApplicationUser user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
Mapper.Map(viewModel, user);
// at this stage the user domain model will only have the properties present
// in the view model updated. All the other properties will remain unchanged
// You could now go ahead and persist the updated 'user' domain model in your
// datastore

Asp.net identity get facebook information

I have searched online on the following code for getting code information, but I have some questions
1) facebook.Options.Scope.Add("email") : How to determine what to put in email? For example what should I put if I need name or first name?
2) ext.Claims.First(x => x.Type.Contains("email")).Value;
How do I determine the email here? Let's say I want first name, how do I know the first name is first_name of first-name of firstname
var facebookOptions = new Microsoft.Owin.Security.Facebook.FacebookAuthenticationOptions()
{
AppId = "AppId",
AppSecret = "AppSecret"
};
facebookOptions.Scope.Add("email");
app.UseFacebookAuthentication(facebookOptions);
ClaimsIdentity ext = await AuthenticationManager.GetExternalIdentityAsync(DefaultAuthenticationTypes.ExternalCookie);
var email = ext.Claims.First(x => x.Type.Contains("email")).Value;
You can't directly get all of Facebook profile information from claims added through facebookOptions scope. You have to add scopes like below and use FacebookClient as explain.
var facebookOptions = new Microsoft.Owin.Security.Facebook.FacebookAuthenticationOptions()
{
AppId = "AppId",
AppSecret = "AppSecret"
};
facebookOptions.Scope.Add("email");
facebookOptions.Scope.Add("public_profile");
app.UseFacebookAuthentication(facebookOptions);
Then you can get Facebook user information using FacebookClient.
[Authorize]
public async Task<ActionResult> FacebookInfo()
{
var claimsforUser = await UserManager.GetClaimsAsync(User.Identity.GetUserId());
var access_token = claimsforUser.FirstOrDefault(x => x.Type == "FacebookAccessToken").Value;
var fb = new FacebookClient(access_token);
dynamic myInfo = fb.Get("/me"); **// Check this with Facebook api to get profile information.**
}
Note that: If you only need to get Facebook user full name, then check your claims, you'll see user's full name is there.
More information
Hope this helps,

how to skip facebook app permissions dialog

Here, I am trying to authenticate user via login and after that I want to skip permissions dialog. But I am unable to achieve this, as it always asking for permissions for app to the user. My intention is if user is not logged into the facebook he/she should be prompted for facebook login and then I will fetch public information by using method Get("/me"). Let me know what I am doing wrong here.
public string GetFBAccessToken(string strAppID, string strAppSecret, string strUrl)
{
// Declaring facebook client type
var vFB = new FacebookClient();
string strAccessTok = string.Empty;
try
{
if (!string.IsNullOrEmpty(strAppID) && !string.IsNullOrEmpty(strAppSecret) && !string.IsNullOrEmpty(strUrl))
{
// Getting login url for facebook
var loginUrl = vFB.GetLoginUrl(new
{
client_id = strAppID,
client_secret = strAppSecret,
redirect_uri = strUrl,
response_type = "code",
state = "returnUrl",
//scope = "",
display = "popup"
});
// Redirecting the page to login url
if (HttpContext.Current.Request.QueryString["code"] == null)
{
HttpContext.Current.Response.Redirect(loginUrl.AbsoluteUri);
}
// Fetching the access token from query string
if (HttpContext.Current.Request.QueryString["code"] != null)
{
dynamic result = vFB.Post("oauth/access_token", new
{
client_id = strAppID,
client_secret = strAppSecret,
redirect_uri = strUrl,
code = HttpContext.Current.Request.QueryString["code"]
});
// Getting access token and storing in a variable
strAccessTok = result.access_token;
}
}
return strAccessTok;
}
catch (Exception ex)
{
//if (HttpContext.Current.Request.QueryString["response_type"] == "code")
//{
// var fb = new FacebookClient();
// var details = fb.Get("/me");
//}
return strAccessTok;
}
}
Regardless to the platform/ language you are using; solution can be as follows.
check use's logged in status. https://developers.facebook.com/docs/reference/javascript/FB.getLoginStatus/
based on Response status, forcefully call your action (i.e. Log in, Get Permission or any additional action if user is already connected). For Log in check this reference document from FB. https://developers.facebook.com/docs/facebook-login/login-flow-for-web/
No. You cannot skip the Login Dialog.
In fact, it is really important for an APP owner to build a trust relationship with your users. I would recommend you to follow the Login Best Practices while authenticating the users using your APP.

Resources