I am having some trouble accessing my userId after and creating my dictionary with connectionIds. I currently and using OAuthBearer for authentication of my users and saving information into ClaimsIdentity, something like below:
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
using (AuthRepository _repo = new AuthRepository())
{
IdentityUser user = await _repo.FindUser(context.UserName, context.Password);
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
identity.AddClaim(new Claim(ClaimTypes.Name, context.UserName));
identity.AddClaim(new Claim(ClaimTypes.Role, "user"));
identity.AddClaim(new Claim("sub", context.UserName));
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Id));
var props = new AuthenticationProperties(new Dictionary<string, string>
{
{
"as:client_id", (context.ClientId == null) ? string.Empty : context.ClientId
},
{
"userName", context.UserName
},
{
"userId", user.Id
}
});
var ticket = new AuthenticationTicket(identity, props);
context.Validated(ticket);
}
}
When I am in my startup/my signalR hub, when I try using the IRequest.user.identity.name or HttpContext.Current.User.Identity.GetUserId();, I get an empty value.
public string GetUserId(IRequest request)
{
var userId = request.User.Identity.Name;
return userId.ToString();
}
I'm thinking that something is wrong with my claims because I can't access the values of UserId and userName. If I run HttpContext.Current.User.Identity.GetUserId(); anywhere else, I can get the userId.
Any help would be greatly appreciated as I've been stuck on this for quite a while.
I had the same issue, try replacing HttpContext.Current with
Context.Request.GetHttpContext() inside your signalR hub methods.
According to MSDN the HubCallerContext returns the context of the client.
Related
I have a set of API's that I want to put some authentication on. I have added the authorization and authentication pieces to the project. I have added the context for the database and the application user. I can create users and log them and in and return JWT to the caller and validate the users based on the JWT. However, there are certain users that I want to create that are admins that will have elevated privileges. Here is the code I am user to Create a user:
async Task<Response> ICreateUser.CreateUser(RegisterModel model)
{
var userExists = await userManager.FindByNameAsync(model.UserName);
if (userExists != null)
{
return new Response { Status = "error", Message = "User already exists" };
}
ApplicationUser user = new ApplicationUser()
{
Email = model.Email,
SecurityStamp = Guid.NewGuid().ToString(),
UserName = model.UserName
};
var result = await userManager.CreateAsync(user, model.Password);
if (!result.Succeeded)
{
return new Response { Status = "Error", Message = "Unable to create user" };
}
else
{
var adminRole = await roleManager.FindByNameAsync("admin");
if(!await userManager.IsInRoleAsync(user, adminRole.Name))
{
await userManager.AddToRoleAsync(user, adminRole.Name);
}
}
return new Response { Status = "Success", Message = "User Created" };
}
This will add the user, and even add them to the role. But when I do a list on the claims all I see are the nameidentifier, jti, email, exp, iss, and aud values. Here is the code I am using to return the claims:
public IActionResult Index()
{
var claims = User.Claims.Select(claim => new { claim.Type, claim.Value }).ToArray();
return Json(claims);
}
When I created the Role for the admin I used this code:
public async Task<IActionResult> Create([Required] string name)
{
var adminRole = await roleManager.FindByNameAsync(name);
if (adminRole == null)
{
adminRole = new IdentityRole(name);
await roleManager.CreateAsync(adminRole);
await roleManager.AddClaimAsync(adminRole, new Claim(ClaimTypes.Role, name));
return Ok("Role Created");
}
else
{
return StatusCode(StatusCodes.Status500InternalServerError, new Response { Status = "error", Message = "Role Not created" });
}
}
Like I said, I can create the user, but I don't see the Admin claim added to user so I can't authenticate by the role. What am I missing?
When I created the JWT token I needed to add a call to GetRolesAsync. Once I got this list, I could loop through the array and add the claims.
var claim = new List<Claim>();
claim.Add(new Claim(JwtRegisteredClaimNames.Sub, user.UserName));
claim.Add(new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()));
claim.Add(new Claim(ClaimTypes.Email, model.Email));
var roles = userManager.GetRolesAsync(user).Result.ToArray();
foreach(var role in roles)
{
claim.Add(new Claim(ClaimTypes.Role, role));
}
This bit of code allows me to add the user to the roles they are assigned to when they are created.
I'm totally new to asp.net, and I'm trying to do a simple rest api.
i need to get the username of the current logged in user.
I have User.Identity.IsAuthenticated is true, but User.Identity.Name is null. I'm using jwt tokens for authentication.
here is my login method
[HttpPost]
public async Task<IActionResult> Login([FromBody] LoginData jsonUser) {
IdentityUser claimedUser;
try {
claimedUser = _userManager.Users.First(
user => user.Email == jsonUser.username || user.UserName == jsonUser.username);
} catch (Exception e) {
return NotFound("No user with this username : " + e);
}
bool isPasswordCorrect = await _userManager.CheckPasswordAsync(claimedUser, jsonUser.Password);
return isPasswordCorrect
? Ok(GenerateJwtToken(claimedUser.Email, claimedUser))
: StatusCode(401, "bad login or password");
}
//Génération du token JWT
private string GenerateJwtToken(string email, IdentityUser user) {
var claims = new List<Claim> {
new Claim(JwtRegisteredClaimNames.Sub, email),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
new Claim(ClaimTypes.NameIdentifier, user.Id),
new Claim(JwtRegisteredClaimNames.UniqueName, user.UserName),
new Claim(JwtRegisteredClaimNames.GivenName, user.UserName)
};
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["JwtKey"]));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var expires = DateTime.Now.AddDays(Convert.ToDouble(_configuration["JwtExpireDays"]));
var token = new JwtSecurityToken(
_configuration["JwtIssuer"],
_configuration["JwtIssuer"],
claims,
expires: expires,
signingCredentials: creds
);
return new JwtSecurityTokenHandler().WriteToken(token);
}
How can I get the current username ?
I am developing a web application, mobile application and desktop application which all can access the data with the help of a single API which can be developed by ASP.NET Web API.
In my Web API can I authenticate the user credentials and the consumer Application key with the help of OAuth?
Can you guys guide me to achieve the same with any examples?
Add the following lines in startup class
public class Startup
{
public void Configuration(IAppBuilder app)
{
var oauthProvider = new OAuthAuthorizationServerProvider
{
OnGrantResourceOwnerCredentials = async context =>
{
IsValid = true;
//You can get the username and password by context.username and context.password
if (IsValid)
{
var claimsIdentity = new ClaimsIdentity(context.Options.AuthenticationType);
claimsIdentity.AddClaim(new Claim("user", context.UserName));
context.Validated(claimsIdentity);
return;
}
context.Rejected();
},
OnValidateClientAuthentication = async context =>
{
string clientId;
string clientSecret;
if (context.TryGetBasicCredentials(out clientId, out clientSecret))
{
if (clientId == GlobalAppSettings.SystemSettings.ApplicationKey)
{
context.Validated();
}
}
}
};
var oauthOptions = new OAuthAuthorizationServerOptions
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/accesstoken"),
Provider = oauthProvider,
AuthorizationCodeExpireTimeSpan = TimeSpan.FromMinutes(1),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(3),
SystemClock = new SystemClock()
};
app.UseOAuthAuthorizationServer(oauthOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
var config = new HttpConfiguration();
config.MapHttpAttributeRoutes();
app.UseWebApi(config);
}
}
And invoke the startup method by start up the new OAuth APP from where you need the authorization and OAuth token
You can use your own host by using the self hosting namespace.
Otherwise you can use Microsoft.Owin.Host.HttpListener namespace and host the OAuth app in different host as below
var Basesite = "http://localhost:9327/";
var homeProcessorModel = new HomeProcessorModel();
using (WebApp.Start<Startup>(url: Basesite))
{
var client = new HttpClient();
var form = new Dictionary<string, string>
{
{"grant_type","password"},
{"userName",username},
{"passWord",password}
};//If you are using grant_type as password, you have to send the username and password to OAuth protocol.
var tokenResponse = client.PostAsync(Basesite + "accesstoken", new FormUrlEncodedContent(form)).Result;
var token = tokenResponse.Content.ReadAsAsync<Token>(new[] { new JsonMediaTypeFormatter() }).Result;
//You can get the token with token.AccessToken object
}
I have setup an ASP.NET WebApi project with support of bearer token like this:
var oAuthServerOptions = new OAuthAuthorizationServerOptions
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = new SimpleAuthorizationServerProvider()
};
var oAuthBearerOptions = new OAuthBearerAuthenticationOptions();
app.UseOAuthAuthorizationServer(oAuthServerOptions);
app.UseOAuthBearerAuthentication(oAuthBearerOptions);
When I make a request to the token endpoint I have this answer
{
"access_token":"GST9UwSuesiYhkezr94K4xwuzvNQ",
"token_type":"bearer",
"expires_in":86399
}
Is there any way to enrich the token with additional fields like this?
{
"access_token":"GST9UwSuesiYhkezr94K4xwuzvNQ",
"token_type":"bearer",
"expires_in":86399,
"username":"user#mail.com"
}
Well here is the solution:
In your class that inherits from OAuthAuthorizationServerProvider
public override async Task TokenEndpoint(OAuthTokenEndpointContext context)
{
context.AdditionalResponseParameters.Add("username", "user#mail.com");
return Task.FromResult<object>(null);
}
Please tell me what is wrong.
public void ConfigureAuth(IAppBuilder app)
{
var mo = new MicrosoftAccountAuthenticationOptions();
mo.ClientId = "xxxxxxxxxxxxxxxxx";
mo.ClientSecret = "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy";
mo.Scope.Add("wl.basic"); // No effect if this commented out
mo.Scope.Add("wl.emails");
// IF I COMMENT NEXT TWO PROPERTIES, USER IS AUTHENTICATED, BUT THE DB IS NOT
// UPDATED. LEAVE THEM AND THE REDIRECT FROM MSLIVE ENDS ON LOGIN PAGE
mo.SignInAsAuthenticationType = "External";
mo.Provider = new MicrosoftAccountAuthenticationProvider()
{
OnAuthenticated = (context) =>
{
// Set breakpoint here to see the context.Identity.Claims HAS CLAIMS DESIRED.
// SO IT APPEARS TO ME Nothing to do here but verify they exist in the debugger.
//(context.Identity.Claims).Items ARE:
//{http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier: xxxxxxxxx}
//{http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name: yyyy yyyyy}
//{urn:microsoftaccount:id: xxxxxxxx}
//{urn:microsoftaccount:name: yyyy yyyyy}
//{http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress: xxxxxxxx#hotmail.com}
return Task.FromResult(0);
}
};
// 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);
app.UseMicrosoftAccountAuthentication(mo);
}
A reasonable expectation asserts that the framwework will transparently handle the addition of a Scope to the default *AuthenticationOptions. Subsequently, wrt the MVC5 template, the developer can extract and persist Claims in ExternalLoginConfirmation code. Another reasonable expectation is that the framework will transform incoming standard ClaimTypes into Claims in the ClaimsIdentity exposed by the framework.
I am very glad source code is available MicrosoftAccountAutheticationHandler.cs, and I will check it to solve this; lacking a response. Best wishes to Katana as the documentation and the framework are maturing. Is there a way for the framework to help the developer spot config issues?.
I would have agreed with you if we both had not hit the same logical brick wall of reasonning ....
I think it is something to do with a detached Owin Security Context while the web application operates in a seperate Context and you have to 'seed' the web one. So what I've deduced is this:
in Startup.Auth.cs
var microsoftOptions =
new Microsoft.Owin.Security.MicrosoftAccount.MicrosoftAccountAuthenticationOptions
{
CallbackPath = new Microsoft.Owin.PathString("/Callbacks/External"),//register at oAuth provider
ClientId = "xxxx",
ClientSecret = "yyyyyyyyyyyyyyyyy",
Provider = new Microsoft.Owin.Security.MicrosoftAccount.MicrosoftAccountAuthenticationProvider
{
OnAuthenticated = (context) =>
{
context.Identity.AddClaim(new Claim(providerKey, context.Identity.AuthenticationType));
context.Identity.AddClaim(new Claim(ClaimTypes.Name, context.Identity.FindFirstValue(ClaimTypes.Name)));
return System.Threading.Tasks.Task.FromResult(0);
}
}
};
microsoftOptions.Scope.Add("wl.basic");
microsoftOptions.Scope.Add("wl.emails");
app.UseMicrosoftAccountAuthentication(microsoftOptions);
and in AccountController:
[AllowAnonymous]
public async Task<ActionResult> oAuthCallback(string returnUrl)
{
var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();
if (loginInfo == null)
{
if (User.Identity.IsAuthenticated)
return RedirectToAction("Index", "Manage");
else
return RedirectToAction("Login");
}
var currentUser = await UserManager.FindAsync(loginInfo.Login);
if (currentUser != null)
{
await StoreExternalTokensOnLocalContext(currentUser);
}
//.... rest as same as per AspNet Sample project.
}
private async Task StoreExternalTokensOnLocalContext(ApplicationUser user)
{
if (user == null)
return;
var externalIdentity = await AuthenticationManager.GetExternalIdentityAsync(DefaultAuthenticationTypes.ExternalCookie);
if (externalIdentity != null)
{
// Retrieve the existing claims for the user and add the FacebookAccessTokenClaim
var currentClaims = await UserManager.GetClaimsAsync(user.Id);
//var providerClaim = externalIdentity.FindFirstValue("provider") ?? string.Empty;
await StoreClaim("provider", user.Id, externalIdentity);
await StoreClaim("FacebookId", user.Id, externalIdentity);
await StoreClaim("image", user.Id, externalIdentity);
await StoreClaim("link", user.Id, externalIdentity);
await StoreClaim(ClaimTypes.Name, user.Id, externalIdentity);
await StoreClaim(ClaimTypes.Email, user.Id, externalIdentity);
var addedClaims = await UserManager.GetClaimsAsync(user.Id);
}
}
private async Task StoreClaim(string typeName, string userId, ClaimsIdentity externalIdentity)
{
var providerClaim = externalIdentity.Claims.FirstOrDefault(c => c.Type.Equals(typeName));
if (providerClaim == null)
return;
var previousClaims = await UserManager.GetClaimsAsync(userId);
if (previousClaims.IndexOf(providerClaim) >= 0)
return;
var idResult = await UserManager.AddClaimAsync(userId, providerClaim);
}