"AmbiguousActionException: Multiple actions matched" when using only 1 GET - c#

I'm getting this error even though I only have one GET on the page. So I'm thoroughly confused.
Here's what the whole login page looks like:
public class LoginModel : PageModel
{
private UserAuthenticationService _userAuthenticationService { get; set; }
private ClaimService _claimService { get; set; }
public LoginModel(UserAuthenticationService userAuthenticationService, ClaimService claimService)
{
_userAuthenticationService = userAuthenticationService;
_claimService = claimService;
}
public async Task OnGetAsync()
{
var user = _userAuthenticationService.Login();
if (user == null)
{
//TODO: Login failed
}
else
{
var claims = _claimService.GetClaimsFromUserModel(user);
var identity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
var authProperties = new AuthenticationProperties()
{
ExpiresUtc = DateTime.UtcNow.AddDays(6),
IsPersistent = true,
};
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(identity), authProperties);
}
}
And here's the error. I only have one OnGet, so I don't know why it's saying there are multiple. Any ideas?

Related

ASP.NET Core Web API - How to include User Details and roles in in Login response

In ASP.NET Core-6 Web API, I have this code:
Models:
public class AuthResult
{
public string AccessToken { get; set; }
public string TokenType { get; set; }
public int ExpiresIn { get; set; }
public string RefreshToken { get; set; }
}
public class JwtTokenManager : IJwtTokenManager
{
private readonly JwtSettings _jwtSettings;
private readonly UserManager<ApplicationUser> _userManager;
private readonly TokenValidationParameters _tokenValidationParameters;
public async Task<AuthResult> GenerateClaimsTokenAsync(string username, CancellationToken cancellationToken)
{
var user = await _userManager.FindByNameAsync(username);
var roles = await _userManager.GetRolesAsync(user);
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes(_jwtSettings.Secret);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new[]
{
new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()), // TODO: encrypt user id for added security
new Claim(ClaimTypes.Name, username),
new Claim(JwtRegisteredClaimNames.Sub, username),
new Claim(JwtRegisteredClaimNames.Nbf, new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds().ToString()),
new Claim(JwtRegisteredClaimNames.Exp, new DateTimeOffset(DateTime.Now.AddMinutes(5)).ToUnixTimeSeconds().ToString()),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
}),
Expires = DateTime.UtcNow.Add(_jwtSettings.Expiration),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
};
var refreshTokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new[]
{
new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()), // TODO: encrypt user id for added security
new Claim(ClaimTypes.Name, username),
new Claim(JwtRegisteredClaimNames.Iss, _jwtSettings.Issuer),
new Claim(JwtRegisteredClaimNames.Iat, new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds().ToString()),
new Claim(JwtRegisteredClaimNames.Nbf, new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds().ToString()),
new Claim(JwtRegisteredClaimNames.Exp, new DateTimeOffset(DateTime.Now.AddMinutes(30)).ToUnixTimeSeconds().ToString()),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
}),
Expires = DateTime.UtcNow.Add(_jwtSettings.Expiration),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
};
// Create JWT tokens
var token = tokenHandler.CreateToken(tokenDescriptor);
var refreshtoken = tokenHandler.CreateToken(refreshTokenDescriptor);
return new AuthResult
{
AccessToken = tokenHandler.WriteToken(token),
TokenType = "Bearer",
ExpiresIn = _jwtSettings.Expiration.Seconds,
RefreshToken = tokenHandler.WriteToken(refreshtoken)
};
}
}
Identity:
public class ApplicationUser : IdentityUser
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string MobileNumber { get; set; }
}
UserDto:
public class UserDto
{
public long Id { get; set; }
public string Firstname { get; set; }
public string Lastname { get; set; }
public string Email { get; set; }
public string UserName { get; set; }
}
Then, finally I have SignInCommandHandler
public class SignInCommandHandler : IRequestHandler<SignInCommand, AuthResult>
{
private readonly ISignInManager _signInManager;
private readonly IJwtTokenManager _jwtTokenManager;
public SignInCommandHandler(ISignInManager signInManager, IJwtTokenManager jwtTokenManager)
{
_signInManager = signInManager;
_jwtTokenManager = jwtTokenManager;
}
public async Task<AuthResult> Handle(SignInCommand request, CancellationToken cancellationToken)
{
// validate username & password
var result = await _signInManager.PasswordSignInAsync(request.Username, request.Password, false, false);
// Throw exception if credential validation failed
if (!result.Successful)
{
throw new UnauthorizedException("Invalid username or password.");
}
// Generate JWT token response if validation successful
AuthResult response = await _jwtTokenManager.GenerateClaimsTokenAsync(request.Username, cancellationToken);
return response;
}
}
I have done something like:
var user = await _userManager.FindByNameAsync(username);
var roles = await _userManager.GetRolesAsync(user);
in JwtTokenManager
How do I involve the detail of the logged in user and his roles in the
return response?
Thank you

AspNetUsers custom columns don't appear in intellisense

Am trying to create a user database that I can modify to suit what my users will need to submit when registering for my service, I've created the database and am able to modify it and include whatever columns I want but I can't seem to access them in my c# code, the only fields that appear are those native to AspNetUsers, I've tried looking at similar questions but I can't seem to grasp the concepts specific to what I need, anyone that can help me get some clarity on this cause am a bit new to working with IdentityUser.
//Registration/Login
public class Identify : IIdentify
{
private readonly UserManager<IdentityUser> _manager;
private readonly Mystery _jwtset;
private readonly DataContext _personality;
public Identify(UserManager<IdentityUser> userManager, Mystery jW, DataContext users)
{
_manager = userManager;
_jwtset = jW;
_personality = users;
}
public async Task<Authentication_result> RegisterAsync(string email, string password, string Username)
{
var exists = await _manager.FindByEmailAsync(email);
if (exists != null)
{
return new Authentication_result
{
Errors = new[] { "User with this email already exists" }
};
}
var newPerson = new IdentityUser()
{
Email = email,
UserName = Username
};
var Creation = await _manager.CreateAsync(newPerson, password);
if (!Creation.Succeeded)
{
return new Authentication_result
{
Errors = new[] { "Invalid user!" }
};
}
return Generate_Authentication_Result(newPerson);
}
public async Task<Authentication_result> LoginAsync(string email, string Password)
{
var exists = await _manager.FindByEmailAsync(email);
if (exists == null)
{
return new Authentication_result
{
Errors = new[] { "User does not exists" }
};
}
var pass_validation = await _manager.CheckPasswordAsync(exists, Password);
if (!pass_validation)
{
return new Authentication_result
{
Errors = new[] { "f78wrvep034rf wrong" }
};
}
return Generate_Authentication_Result(exists);
}
private Authentication_result Generate_Authentication_Result(IdentityUser newPerson)
{
var Tokenhandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes(_jwtset.Secret);
var TokenDescripter = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new[]
{
new Claim(JwtRegisteredClaimNames.Sub, newPerson.UserName),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
new Claim(JwtRegisteredClaimNames.Email, newPerson.Email),
new Claim("id",newPerson.Id)
}),
Expires = DateTime.UtcNow.AddHours(2),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
};
var token = Tokenhandler.CreateToken(TokenDescripter);
return new Authentication_result
{
Success = true,
Token = Tokenhandler.WriteToken(token)
};
}
}
//Controller for the above
public class IdentifyMe : Controller
{
private readonly IIdentify _identify;
public IdentifyMe(IIdentify identifying)
{
_identify = identifying;
}
[HttpPost(Api_Routes.Identity.Register)]
public async Task<IActionResult> Register(UserRegistration register)
{
if (!ModelState.IsValid)
{
return BadRequest(new Unauthenticated
{
Errors = ModelState.Values.SelectMany(x => x.Errors.Select(xx => xx.ErrorMessage))
});
}
var authresponce = await _identify.RegisterAsync(register.Email, register.Password, register.User_Name);
if (!authresponce.Success)
{
return BadRequest(new Unauthenticated
{
Errors = authresponce.Errors
});
}
return Ok(new Authenticated
{
Token = authresponce.Token
});
}
[HttpPost(Api_Routes.Identity.Login)]
public async Task<IActionResult> LoginAsync(User_login login)
{
var authresponce = await _identify.LoginAsync(login.email, login.Password);
if (!authresponce.Success)
{
return BadRequest(new Unauthenticated
{
Errors = authresponce.Errors
});
}
return Ok(new Authenticated
{
Token = authresponce.Token
});
}
}
//Domain object, these are the values I would like to be able to access
public class Users : IdentityUser
{
public string PreferredNet { get; set; }
public int Inactive { get; set; }
public int Active { get; set; }
public int Max_Return { get; set; }
public DateTime Time { get; set; }
}
//Other controller
public ActionResult <IEnumerable<Time_dto>> Getitem(string usernum, int amt, string user, string server)
{
var Total = caller.Getusers();
//This is my attempt to acces the domain object, pitcture below[![Intelisense does not display fields in domain object][1]][1]
var container=Total.Select(x=>x.)
var totalin = _digital.Map<IEnumerable<User_dto>>(Total).Count(x => x.PreferredNet == user);
var totalout= _digital.Map<IEnumerable<User_dto>>(Total).Count(x=>x.PreferredNet== server);
int factor = 1;
var HCD = caller.rates(factor, user, server);
var result = shift;
int retrive = caller.Total(amt, user, server, HCD);
var serials = caller.cards(retrive);
int differential = retrive > serials.Sum() ? retrive serials.Sum() : serials.Sum() - retrive;
int number = serials.Count();
IEnumerable<int> Real_cards=new List<int>();
}
```
[1]: https://i.stack.imgur.com/HEenG.png

UserProfile state is null on webchat but ok on directline and emulator

I am passing a StateBotAccessor around to my multi-dialog bot. I noticed that on webchat, retrieving userProfile via _stateBotAccessor.UserProfileAccessor.GetAsync() returns null but conversationData is persisted. Testing via emulator and directline shows a properly filled userProfile.
I created a git project built from EnterpriseBot template to show the problem.
Here is my simple UserProfile class:
public class UserProfile
{
public UserProfile(string name)
{
this.UserName = name;
}
public string UserName { get; set; }
}
Here is my simple ConversationData class:
public class ConversationData
{
public ConversationData(string channelId)
{
ChannelId = channelId;
}
// The ID of the user's channel.
public string ChannelId { get; set; }
}
Here is my StateBotAccessor class:
public class StateBotAccessors
{
public StateBotAccessors(ConversationState conversationState, UserState userState)
{
ConversationState = conversationState ?? throw new ArgumentNullException("UserProfile");
UserState = userState ?? throw new ArgumentNullException("ConversationData");
}
public IStatePropertyAccessor<UserProfile> UserProfileAccessor { get; set; }
public IStatePropertyAccessor<ConversationData> ConversationDataAccessor { get; set; }
public IStatePropertyAccessor<DialogState> DialogStateAccessor { get; set; }
public ConversationState ConversationState { get; }
public UserState UserState { get; }
}
On Startup.cs I register StateBotAccessors. I use inMemory storage here and I did test with blob storage but the results are the same.
var dataStore = new MemoryStorage();
var userState = new UserState(dataStore);
var conversationState = new ConversationState(dataStore);
services.AddSingleton(dataStore);
var stateBotAccessors = new StateBotAccessors(conversationState, userState)
{
DialogStateAccessor = conversationState.CreateProperty<DialogState>("DialogState"),
ConversationDataAccessor = conversationState.CreateProperty<ConversationData>("ConversationData"),
UserProfileAccessor = userState.CreateProperty<UserProfile>("UserProfile"),
};
services.AddSingleton(stateBotAccessors);
On my MainDialog.OnStartAsync() I immediately set both userProfile and conversationData:
protected override async Task OnStartAsync(DialogContext dc, CancellationToken cancellationToken = default(CancellationToken))
{
var context = dc.Context;
// set userprofile and conversationdata
await _accessors.ConversationDataAccessor.SetAsync(context, new ConversationData(dc.Context.Activity.ChannelId));
await _accessors.UserProfileAccessor.SetAsync(context, new UserProfile("Elena"));
await dc.Context.SendActivityAsync($"ConversationData and UserProfile were set in MainDialog.OnStartAsync()");
// easier testing, I have this as a convenience method
var reply = context.Activity.CreateReply();
reply.Text = "Say 'print out state' or click the button.";
reply.SuggestedActions = new SuggestedActions()
{
Actions = new List<CardAction>()
{
new CardAction(type: ActionTypes.ImBack, title: "Test State", value: "print out state"),
}
};
await context.SendActivityAsync(reply);
}
I call SecondDialog if user's text input is "print out state" then I print out the contents of both conversationData and userProfile but userProfile is always NULL on webchat client:
var conversationState = await _accessors.ConversationDataAccessor.GetAsync(sc.Context, () => null);
var userProfile = await _accessors.UserProfileAccessor.GetAsync(sc.Context, () => null);
await sc.Context.SendActivityAsync($"conversationState.ChannelId: {conversationState?.ChannelId}");
await sc.Context.SendActivityAsync($"userProfile.UserName: {userProfile?.UserName}");
The test result looks like this:
Sample webchat and directline clients are under /Clients folder in the git project.
My webchat client is simple:
const res = await fetch('https://directline.botframework.com/v3/directline/conversations',
{
method: 'POST',
headers: {
"Authorization": "Bearer mySecretKey",
},
});
const { token } = await res.json();
var dl = window.WebChat.createDirectLine({ token });
window.WebChat.renderWebChat({
directLine: dl,
user : user,
}, document.getElementById('webchat'));
document.querySelector('#webchat > *').focus();
I want userProfile to persist on webchat channel like it does on emulator and directline channel. Is there something I'm missing? Some special handling for webchat channel perhaps?
Webchat client is very particular with the userId - sessions are not persisted unless a specific user id is properly configured.
...
var user = {id: 'test#test.com',
name: 'You'
};
....
var dl = window.WebChat.createDirectLine({ token });
window.WebChat.renderWebChat({
directLine: dl,
userId : user.id,
}, document.getElementById('webchat'));
document.querySelector('#webchat > *').focus();

How to get access token in Web Api OAuth?

I have a Web Application that generates the link to get an access token against a web API 2.
Basically, the following controller action is called:
GetExternalLogin at AccountController:
ApplicationUser user = await UserManager.FindAsync(new UserLoginInfo(externalLogin.LoginProvider,
externalLogin.ProviderKey));
bool hasRegistered = user != null;
if (hasRegistered)
{
Authentication.SignOut(DefaultAuthenticationTypes.ExternalCookie);
ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(UserManager,
OAuthDefaults.AuthenticationType);
ClaimsIdentity cookieIdentity = await user.GenerateUserIdentityAsync(UserManager,
CookieAuthenticationDefaults.AuthenticationType);
AuthenticationProperties properties = ApplicationOAuthProvider.CreateProperties(user.UserName);
Authentication.SignIn(properties, oAuthIdentity, cookieIdentity);
}
else
{
// as user is not registered, this block is hit
IEnumerable<Claim> claims = externalLogin.GetClaims();
ClaimsIdentity identity = new ClaimsIdentity(claims, OAuthDefaults.AuthenticationType);
Authentication.SignIn(identity);
}
return Ok();
Now, this return Ok line simply returns to my Web API base url and add a token after this:
https://localhost:44301/#access_token=iPl1MSgnjI3oXgDxuCH9_t5I1SsELUH-v_vNXdehGpNWsCWsQaX7csWWadWRq4H2uZ0BB8zZm2s0xOI8TSOfgzH7QbFVko4Ui8jM5SylhPgkC7eiQG-kChDfa5HMlxKF1JvRg9Kvs40rPGqsC22uel-Gi2QZlrMh_5M0NT06QOOMv4bDTAFljKw9clsMiHidX4TPfQ6UmhROMIo8FcBDlAfH7wZbSQZjFAWm4Mub-oMoUxUOzAVxJrjGiM9gxwk4iqLqGbcFVl6AncJnFO_YDtmWH_sRBvmbfzpQ6GiB10eyY-hA_L-sWtQbX8IPPtOKuWGbyg0_MfaWBfAJfUiNjH6_VjcOfPEdwUPEvbnR8vw&token_type=bearer&expires_in=1209600&state=Qvlzg__CCwjCjaqEOInQw0__FprOykwROuAciRgDlIQ1
and that's all.
How I get these parameters from the URL and process them?
If I change the base URL to any other action I get the "invalid_request" error caused by calling uri is different that redirect_uri.
So, how a client app gets the access token ?
Any help or clarification will be really helpful.
1. create class for Token
public class Token
{
[JsonProperty("access_token")]
public string AccessToken { get; set; }
[JsonProperty("token_type")]
public string TokenType { get; set; }
[JsonProperty("expires_in")]
public int ExpiresIn { get; set; }
[JsonProperty("refresh_token")]
public string RefreshToken { get; set; }
}
2. Startup class
[assembly: OwinStartup(typeof(ProjectName.API.Startup))]
namespace ProjectName.API
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
var oauthProvider = new OAuthAuthorizationServerProvider
{
OnGrantResourceOwnerCredentials = async context =>
{
if (context.UserName == "xyz" && context.Password == "xyz#123")
{
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 == "xyz" && clientSecret == "secretKey")
{
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);
}
}
}
3 . Add a controller
[Authorize]
public class TestController : ApiController
{
[Route("test")]
public HttpResponseMessage Get()
{
return Request.CreateResponse(HttpStatusCode.OK, "hello !");
}
}
4. Now check the authorization on the basis of the token
static void Main()
{
string baseAddress = "http://localhost:/";
// Start OWIN host
using (WebApp.Start<Startup>(url: baseAddress))
{
var client = new HttpClient();
var response = client.GetAsync(baseAddress + "test").Result;
Console.WriteLine(response);
Console.WriteLine();
var authorizationHeader = Convert.ToBase64String(Encoding.UTF8.GetBytes("xyz:secretKey"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", authorizationHeader);
var form = new Dictionary<string, string>
{
{"grant_type", "password"},
{"username", "xyz"},
{"password", "xyz#123"},
};
var tokenResponse = client.PostAsync(baseAddress + "accesstoken", new FormUrlEncodedContent(form)).Result;
var token = tokenResponse.Content.ReadAsAsync<Token>(new[] { new JsonMediaTypeFormatter() }).Result;
Console.WriteLine("Token issued is: {0}", token.AccessToken);
Console.WriteLine();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.AccessToken);
var authorizedResponse = client.GetAsync(baseAddress + "test").Result;
Console.WriteLine(authorizedResponse);
Console.WriteLine(authorizedResponse.Content.ReadAsStringAsync().Result);
}
}

Is this the correct way of populating user's roles using ServiceStack to a custom IPrincipal for autowired authorization in Forms Authentication

I'm using Servicestack for the middle tier logic/web services part of my asp.net mvc app. On the front-end, I'm using FormsAuthentication + "auth/credentials" to enable authentication/authorization to both asp.net and servicestack.
In some of my mvc controllers, Im using the built-in User (IPrincipal) for roles/permission checking,
[Authorize]
public ActionResult Index()
{
if (!User.IsInRole("admin"))
ViewBag.ErrorMessage = "Access Denied";
return View();
}
I learned that the roles in the default User don't get populated during forms authentication, thus I created my custom class, based from IPrincipal, and set that as HttpContext.Current.User during calls to PostAuthenticateRequest
Is this the correct way to get/set the roles/permission from servicestack into the HttpContext.Current.User?
public class CustomPrincipal : IPrincipal
{
//private readonly IEnumerable<string> _roles;
public IIdentity Identity { get; private set; }
public bool IsInRole(string role)
{
// retrieve roles from servicestack cache
var key = ServiceStack.ServiceInterface.SessionFeature.GetSessionKey() ?? "";
var sess = fubar.web.App_Start.AppHost.Resolve<ServiceStack.CacheAccess.ICacheClient>().Get<ServiceStack.ServiceInterface.Auth.AuthUserSession>(key);
if (sess != null)
return sess.Roles.Contains(role);
return false;
}
public CustomPrincipal(string name)
{
Identity = new GenericIdentity(name);
//_roles = roles;
}
public long Id { get; set; }
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string LastName { get; set; }
}
Btw, here's my Login method, and pls do check if this is also the correct way of dual auth to asp.net formsauthentication and servicestack. :)
try
{
var authService = AppHostBase.Resolve<AuthService>();
authService.RequestContext = System.Web.HttpContext.Current.ToRequestContext();
var response = authService.Authenticate(new Auth
{
UserName = model.UserName,
Password = model.Password,
RememberMe = model.RememberMe
});
var version = 1;
var now = DateTime.Now;
var timeout = FormsAuthentication.Timeout;
var expire = now.Add(timeout);
var user = new User
{
FirstName = "first",
MiddleName = "middle",
LastName = "last"
};
var serializer = new JavaScriptSerializer();
var userData = serializer.Serialize(user);
var ticket = new FormsAuthenticationTicket(
version,
model.UserName,
now,
expire,
model.RememberMe,
userData);
Response.Cookies.Add(new HttpCookie(
FormsAuthentication.FormsCookieName, FormsAuthentication.Encrypt(ticket))
{
Secure = FormsAuthentication.RequireSSL
});
FormsAuthentication.GetRedirectUrl(model.UserName, model.RememberMe);
// add ASP.NET auth cookie
//FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
return Url.IsLocalUrl(returnUrl) ? Redirect(returnUrl) : Redirect("~/");
}
catch (HttpError ex)
{
ModelState.AddModelError("", "Error occured.\n" + ex.Message);
}
Many thanks!

Categories