How to use Linq to Entities in ApplicationOAuthProvider asp.net web api2 - asp.net-mvc

this code return error does not support linq to entities, please help me
error return is
LINQ to Entities does not recognize the method 'System.String get_Item(System.String)' method, and this method cannot be translated into a store expression.
problem in this line
ApplicationUser user = dbContext.Users.Where(s => s.UserName == context.UserName && s.Password == context.Password && s.CodeID == data["CodeID"]);
public class ApplicationOAuthProvider : OAuthAuthorizationServerProvider
{
private readonly string _publicClientId;
public ApplicationOAuthProvider(string publicClientId)
{
if (publicClientId == null)
{
throw new ArgumentNullException("publicClientId");
}
_publicClientId = publicClientId;
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();
var data = await context.Request.ReadFormAsync();
ApplicationUser user = await userManager.FindAsync(context.UserName, context.Password);
ApplicationDbContext dbContext = new ApplicationDbContext();
//here problem
//does not support linq to entities
ApplicationUser user = dbContext.Users.Where(s => s.UserName == context.UserName && s.Password == context.Password && s.CodeID == data["CodeID"]);
//here problem
if (user == null)
{
context.SetError("invalid_grant", Resources.Resources.Invalid_UsernamePassword);
return;
}
ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager,
OAuthDefaults.AuthenticationType);
ClaimsIdentity cookiesIdentity = await user.GenerateUserIdentityAsync(userManager,
CookieAuthenticationDefaults.AuthenticationType);
AuthenticationProperties properties = CreateProperties(user.UserName);
AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties);
context.Validated(ticket);
context.Request.Context.Authentication.SignIn(cookiesIdentity);
}
public override Task TokenEndpoint(OAuthTokenEndpointContext context)
{
foreach (KeyValuePair<string, string> property in context.Properties.Dictionary)
{
context.AdditionalResponseParameters.Add(property.Key, property.Value);
}
return Task.FromResult<object>(null);
}
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
// Resource owner password credentials does not provide a client ID.
if (context.ClientId == null)
{
context.Validated();
}
return Task.FromResult<object>(null);
}
public override Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context)
{
if (context.ClientId == _publicClientId)
{
Uri expectedRootUri = new Uri(context.Request.Uri, "/");
if (expectedRootUri.AbsoluteUri == context.RedirectUri)
{
context.Validated();
}
}
return Task.FromResult<object>(null);
}
public static AuthenticationProperties CreateProperties(string Username)
{
IDictionary<string, string> data = new Dictionary<string, string>
{
{ "Username", Username }
};
return new AuthenticationProperties(data);
}
}

Linq to entities cannot convert this part:
s.CodeID == data["CodeID"]
Which retrieves the CodeID value from the data collection into a meaningful SQL statement.
What you have to do is store this value in a variable before your where expression like so:
var codeId = data["CodeID"];
and then modify your where to use it:
var codeId = data["CodeID"];
ApplicationUser user = dbContext.Users.Where(s => s.UserName == context.UserName && s.Password == context.Password && s.CodeID == codeId);
Now Linq can properly generate the SQL statement.

Related

Identity Server - how to I include with token additional claims (from external identity provider)

I am having a hard time finding solid information about how to send additional claims to my client application from identity server.
At the current moment, I am using the following to get a local claim (that I captured within ProcessLoginCallbackForOidc) and added to the claims being returned during authentication.
Is this the best approach?
public class ProfileService : IProfileService
{
public Task GetProfileDataAsync(ProfileDataRequestContext context)
{
//var sub = context.Subject.GetSubjectId();
var claims = context.Subject.Claims.ToList();
if (claims.Count > 0)
{
var emailClaim = claims.FirstOrDefault(x => x.Type == "email");
if (emailClaim == null)
{
var emailAddressClaim = context.Subject.Claims.FirstOrDefault(x => x.Type == "emails");
if (emailAddressClaim != null)
{
claims.Add(new Claim("email", emailAddressClaim.Value));
}
}
}
// Set returned claims (System.Security.Claims.Claim) by setting context.IssuedClaims
context.IssuedClaims = claims;
return Task.CompletedTask;
}
public Task IsActiveAsync(IsActiveContext context)
{
context.IsActive = true;
return Task.CompletedTask;
}
}
Yes this is proper solution. ProfileService is an extensibility point to add extra claims for a user.
Its called when creating token for the user.
Read more here
Edit:
Here is sample code for profile service:
public class ProfileService : IProfileService
{
private readonly IUserClaimsPrincipalFactory<ApplicationUser> _claimsFactory;
private readonly UserManager<ApplicationUser> _userManager;
public ProfileService(UserManager<ApplicationUser> userManager, IUserClaimsPrincipalFactory<ApplicationUser> claimsFactory)
{
_userManager = userManager;
_claimsFactory = claimsFactory;
}
public async Task GetProfileDataAsync(ProfileDataRequestContext context)
{
var sub = context.Subject.GetSubjectId();
var user = await _userManager.FindByIdAsync(sub);
var principal = await _claimsFactory.CreateAsync(user);
var claims = principal.Claims.ToList();
if (claims.Count > 0)
{
var emailClaim = claims.FirstOrDefault(x => x.Type == "email");
if (emailClaim == null)
{
var emailAddressClaim = context.Subject.Claims.FirstOrDefault(x => x.Type == "emails");
if (emailAddressClaim != null)
{
claims.Add(new Claim("email", emailAddressClaim.Value));
}
}
}
context.IssuedClaims = claims;
return Task.CompletedTask;
}
public async Task IsActiveAsync(IsActiveContext context)
{
var sub = context.Subject.GetSubjectId();
var user = await _userManager.FindByIdAsync(sub);
context.IsActive = user != null;
}
}

Flow of authorize attribute in rest API controller for debugging using OAuth and OWIN and identity

Hi i am making a rest web api project in which i selected individual user account.
By Default its created account controller and ApplicationOAuthProvider and Startup.Auth.cs .
And Values controller is decorated with [Authorize] attribute which is authorizing request using bearer token is valid or not.
So i want to know answer of below question
what is the flow of Authorize attribute, how Authorize attribute flow
decide that token is valid or expire. where or in which class and
method its check that. for example i am making a request with bearer
token how my application decide that it is valid token or its expiry
in any class
how i can capture bearer token in database and based on this token
and its expiry i want to customize authorize attribute on
authorization policy like if token is valid in database i need to do some operation
i am using visual studio 2017 and latest owin packages
my Startup.Auth.cs class
public partial class Startup
{
public static OAuthAuthorizationServerOptions OAuthOptions { get; private set; }
public static string PublicClientId { get; private set; }
// For more information on configuring authentication, please visit https://go.microsoft.com/fwlink/?LinkId=301864
public void ConfigureAuth(IAppBuilder app)
{
// Configure the db context and user manager to use a single instance per request
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
// Enable the application to use a cookie to store information for the signed in user
// and to use a cookie to temporarily store information about a user logging in with a third party login provider
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
// Configure the application for OAuth based flow
PublicClientId = "self";
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Token"),
Provider = new ApplicationOAuthProvider(PublicClientId),
AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
// In production mode set AllowInsecureHttp = false
AllowInsecureHttp = true
};
//OAuthOptions = new OAuthAuthorizationServerOptions
//{
// TokenEndpointPath = new PathString("/api/Account/Login"),
// Provider = new ApplicationOAuthProvider(PublicClientId),
// RefreshTokenProvider = new SimpleRefreshTokenProvider(),
// AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(5),
// AllowInsecureHttp = true,
//};
// Enable the application to use bearer tokens to authenticate users
app.UseOAuthBearerTokens(OAuthOptions);
// Uncomment the following lines to enable logging in with third party login providers
//app.UseMicrosoftAccountAuthentication(
// clientId: "",
// clientSecret: "");
//app.UseTwitterAuthentication(
// consumerKey: "",
// consumerSecret: "");
//app.UseFacebookAuthentication(
// appId: "",
// appSecret: "");
//app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions()
//{
// ClientId = "",
// ClientSecret = ""
//});
}
}
my ApplicationOAuthProvider.cs
public class ApplicationOAuthProvider : OAuthAuthorizationServerProvider
{
private readonly string _publicClientId;
public ApplicationOAuthProvider(string publicClientId)
{
if (publicClientId == null)
{
throw new ArgumentNullException("publicClientId");
}
_publicClientId = publicClientId;
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();
ApplicationUser user = await userManager.FindAsync(context.UserName, context.Password);
if (user == null)
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager,
OAuthDefaults.AuthenticationType);
ClaimsIdentity cookiesIdentity = await user.GenerateUserIdentityAsync(userManager,
CookieAuthenticationDefaults.AuthenticationType);
AuthenticationProperties properties = CreateProperties(user.UserName);
AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties);
context.Validated(ticket);
context.Request.Context.Authentication.SignIn(cookiesIdentity);
}
public override Task TokenEndpoint(OAuthTokenEndpointContext context)
{
foreach (KeyValuePair<string, string> property in context.Properties.Dictionary)
{
context.AdditionalResponseParameters.Add(property.Key, property.Value);
}
return Task.FromResult<object>(null);
}
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
// Resource owner password credentials does not provide a client ID.
if (context.ClientId == null)
{
context.Validated();
}
return Task.FromResult<object>(null);
}
//public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
//{
// string clientId;
// string clientSecret;
// if (context.TryGetBasicCredentials(out clientId, out clientSecret))
// {
// if (clientSecret == "secret")
// {
// context.OwinContext.Set<string>("as:client_id", clientId);
// context.Validated();
// }
// }
// return Task.FromResult<object>(null);
//}
public override Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context)
{
if (context.ClientId == _publicClientId)
{
Uri expectedRootUri = new Uri(context.Request.Uri, "/");
if (expectedRootUri.AbsoluteUri == context.RedirectUri)
{
context.Validated();
}
}
return Task.FromResult<object>(null);
}
public static AuthenticationProperties CreateProperties(string userName)
{
IDictionary<string, string> data = new Dictionary<string, string>
{
{ "userName", userName }
};
return new AuthenticationProperties(data);
}
}
and Account controller
[Authorize]
[RoutePrefix("api/Account")]
public class AccountController : ApiController
{
private const string LocalLoginProvider = "Local";
private ApplicationUserManager _userManager;
public AccountController()
{
}
public AccountController(ApplicationUserManager userManager,
ISecureDataFormat<AuthenticationTicket> accessTokenFormat)
{
UserManager = userManager;
AccessTokenFormat = accessTokenFormat;
}
public ApplicationUserManager UserManager
{
get
{
return _userManager ?? Request.GetOwinContext().GetUserManager<ApplicationUserManager>();
}
private set
{
_userManager = value;
}
}
public ISecureDataFormat<AuthenticationTicket> AccessTokenFormat { get; private set; }
// GET api/Account/UserInfo
[HostAuthentication(DefaultAuthenticationTypes.ExternalBearer)]
[Route("UserInfo")]
public UserInfoViewModel GetUserInfo()
{
ExternalLoginData externalLogin = ExternalLoginData.FromIdentity(User.Identity as ClaimsIdentity);
return new UserInfoViewModel
{
Email = User.Identity.GetUserName(),
HasRegistered = externalLogin == null,
LoginProvider = externalLogin != null ? externalLogin.LoginProvider : null
};
}
// POST api/Account/Logout
[Route("Logout")]
public IHttpActionResult Logout()
{
Authentication.SignOut(CookieAuthenticationDefaults.AuthenticationType);
//HttpContext.Current.GetOwinContext().Authentication.SignOut(Microsoft.AspNet.Identity.DefaultAuthenticationTypes.ApplicationCookie);
// Request.GetOwinContext().Authentication.SignOut();
// Request.GetOwinContext().Authentication.SignOut(Microsoft.AspNet.Identity.DefaultAuthenticationTypes.ApplicationCookie);
return Ok();
}
// GET api/Account/ManageInfo?returnUrl=%2F&generateState=true
[Route("ManageInfo")]
public async Task<ManageInfoViewModel> GetManageInfo(string returnUrl, bool generateState = false)
{
IdentityUser user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
if (user == null)
{
return null;
}
List<UserLoginInfoViewModel> logins = new List<UserLoginInfoViewModel>();
foreach (IdentityUserLogin linkedAccount in user.Logins)
{
logins.Add(new UserLoginInfoViewModel
{
LoginProvider = linkedAccount.LoginProvider,
ProviderKey = linkedAccount.ProviderKey
});
}
if (user.PasswordHash != null)
{
logins.Add(new UserLoginInfoViewModel
{
LoginProvider = LocalLoginProvider,
ProviderKey = user.UserName,
});
}
return new ManageInfoViewModel
{
LocalLoginProvider = LocalLoginProvider,
Email = user.UserName,
Logins = logins,
ExternalLoginProviders = GetExternalLogins(returnUrl, generateState)
};
}
// POST api/Account/ChangePassword
[Route("ChangePassword")]
public async Task<IHttpActionResult> ChangePassword(ChangePasswordBindingModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
IdentityResult result = await UserManager.ChangePasswordAsync(User.Identity.GetUserId(), model.OldPassword,
model.NewPassword);
if (!result.Succeeded)
{
return GetErrorResult(result);
}
return Ok();
}
// POST api/Account/SetPassword
[Route("SetPassword")]
public async Task<IHttpActionResult> SetPassword(SetPasswordBindingModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
IdentityResult result = await UserManager.AddPasswordAsync(User.Identity.GetUserId(), model.NewPassword);
if (!result.Succeeded)
{
return GetErrorResult(result);
}
return Ok();
}
// POST api/Account/AddExternalLogin
[Route("AddExternalLogin")]
public async Task<IHttpActionResult> AddExternalLogin(AddExternalLoginBindingModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
Authentication.SignOut(DefaultAuthenticationTypes.ExternalCookie);
AuthenticationTicket ticket = AccessTokenFormat.Unprotect(model.ExternalAccessToken);
if (ticket == null || ticket.Identity == null || (ticket.Properties != null
&& ticket.Properties.ExpiresUtc.HasValue
&& ticket.Properties.ExpiresUtc.Value < DateTimeOffset.UtcNow))
{
return BadRequest("External login failure.");
}
ExternalLoginData externalData = ExternalLoginData.FromIdentity(ticket.Identity);
if (externalData == null)
{
return BadRequest("The external login is already associated with an account.");
}
IdentityResult result = await UserManager.AddLoginAsync(User.Identity.GetUserId(),
new UserLoginInfo(externalData.LoginProvider, externalData.ProviderKey));
if (!result.Succeeded)
{
return GetErrorResult(result);
}
return Ok();
}
// POST api/Account/RemoveLogin
[Route("RemoveLogin")]
public async Task<IHttpActionResult> RemoveLogin(RemoveLoginBindingModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
IdentityResult result;
if (model.LoginProvider == LocalLoginProvider)
{
result = await UserManager.RemovePasswordAsync(User.Identity.GetUserId());
}
else
{
result = await UserManager.RemoveLoginAsync(User.Identity.GetUserId(),
new UserLoginInfo(model.LoginProvider, model.ProviderKey));
}
if (!result.Succeeded)
{
return GetErrorResult(result);
}
return Ok();
}
// GET api/Account/ExternalLogin
[OverrideAuthentication]
[HostAuthentication(DefaultAuthenticationTypes.ExternalCookie)]
[AllowAnonymous]
[Route("ExternalLogin", Name = "ExternalLogin")]
public async Task<IHttpActionResult> GetExternalLogin(string provider, string error = null)
{
if (error != null)
{
return Redirect(Url.Content("~/") + "#error=" + Uri.EscapeDataString(error));
}
if (!User.Identity.IsAuthenticated)
{
return new ChallengeResult(provider, this);
}
ExternalLoginData externalLogin = ExternalLoginData.FromIdentity(User.Identity as ClaimsIdentity);
if (externalLogin == null)
{
return InternalServerError();
}
if (externalLogin.LoginProvider != provider)
{
Authentication.SignOut(DefaultAuthenticationTypes.ExternalCookie);
return new ChallengeResult(provider, this);
}
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
{
IEnumerable<Claim> claims = externalLogin.GetClaims();
ClaimsIdentity identity = new ClaimsIdentity(claims, OAuthDefaults.AuthenticationType);
Authentication.SignIn(identity);
}
return Ok();
}
// GET api/Account/ExternalLogins?returnUrl=%2F&generateState=true
[AllowAnonymous]
[Route("ExternalLogins")]
public IEnumerable<ExternalLoginViewModel> GetExternalLogins(string returnUrl, bool generateState = false)
{
IEnumerable<AuthenticationDescription> descriptions = Authentication.GetExternalAuthenticationTypes();
List<ExternalLoginViewModel> logins = new List<ExternalLoginViewModel>();
string state;
if (generateState)
{
const int strengthInBits = 256;
state = RandomOAuthStateGenerator.Generate(strengthInBits);
}
else
{
state = null;
}
foreach (AuthenticationDescription description in descriptions)
{
ExternalLoginViewModel login = new ExternalLoginViewModel
{
Name = description.Caption,
Url = Url.Route("ExternalLogin", new
{
provider = description.AuthenticationType,
response_type = "token",
client_id = Startup.PublicClientId,
redirect_uri = new Uri(Request.RequestUri, returnUrl).AbsoluteUri,
state = state
}),
State = state
};
logins.Add(login);
}
return logins;
}
// POST api/Account/Register
[AllowAnonymous]
[Route("Register")]
public async Task<IHttpActionResult> Register(RegisterBindingModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var user = new ApplicationUser() { UserName = model.Email, Email = model.Email };
IdentityResult result = await UserManager.CreateAsync(user, model.Password);
if (!result.Succeeded)
{
return GetErrorResult(result);
}
return Ok();
}
// POST api/Account/RegisterExternal
[OverrideAuthentication]
[HostAuthentication(DefaultAuthenticationTypes.ExternalBearer)]
[Route("RegisterExternal")]
public async Task<IHttpActionResult> RegisterExternal(RegisterExternalBindingModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var info = await Authentication.GetExternalLoginInfoAsync();
if (info == null)
{
return InternalServerError();
}
var user = new ApplicationUser() { UserName = model.Email, Email = model.Email };
IdentityResult result = await UserManager.CreateAsync(user);
if (!result.Succeeded)
{
return GetErrorResult(result);
}
result = await UserManager.AddLoginAsync(user.Id, info.Login);
if (!result.Succeeded)
{
return GetErrorResult(result);
}
return Ok();
}
protected override void Dispose(bool disposing)
{
if (disposing && _userManager != null)
{
_userManager.Dispose();
_userManager = null;
}
base.Dispose(disposing);
}
#region Helpers
private IAuthenticationManager Authentication
{
get { return Request.GetOwinContext().Authentication; }
}
private IHttpActionResult GetErrorResult(IdentityResult result)
{
if (result == null)
{
return InternalServerError();
}
if (!result.Succeeded)
{
if (result.Errors != null)
{
foreach (string error in result.Errors)
{
ModelState.AddModelError("", error);
}
}
if (ModelState.IsValid)
{
// No ModelState errors are available to send, so just return an empty BadRequest.
return BadRequest();
}
return BadRequest(ModelState);
}
return null;
}
private class ExternalLoginData
{
public string LoginProvider { get; set; }
public string ProviderKey { get; set; }
public string UserName { get; set; }
public IList<Claim> GetClaims()
{
IList<Claim> claims = new List<Claim>();
claims.Add(new Claim(ClaimTypes.NameIdentifier, ProviderKey, null, LoginProvider));
if (UserName != null)
{
claims.Add(new Claim(ClaimTypes.Name, UserName, null, LoginProvider));
}
return claims;
}
public static ExternalLoginData FromIdentity(ClaimsIdentity identity)
{
if (identity == null)
{
return null;
}
Claim providerKeyClaim = identity.FindFirst(ClaimTypes.NameIdentifier);
if (providerKeyClaim == null || String.IsNullOrEmpty(providerKeyClaim.Issuer)
|| String.IsNullOrEmpty(providerKeyClaim.Value))
{
return null;
}
if (providerKeyClaim.Issuer == ClaimsIdentity.DefaultIssuer)
{
return null;
}
return new ExternalLoginData
{
LoginProvider = providerKeyClaim.Issuer,
ProviderKey = providerKeyClaim.Value,
UserName = identity.FindFirstValue(ClaimTypes.Name)
};
}
}
private static class RandomOAuthStateGenerator
{
private static RandomNumberGenerator _random = new RNGCryptoServiceProvider();
public static string Generate(int strengthInBits)
{
const int bitsPerByte = 8;
if (strengthInBits % bitsPerByte != 0)
{
throw new ArgumentException("strengthInBits must be evenly divisible by 8.", "strengthInBits");
}
int strengthInBytes = strengthInBits / bitsPerByte;
byte[] data = new byte[strengthInBytes];
_random.GetBytes(data);
return HttpServerUtility.UrlTokenEncode(data);
}
}
#endregion
}
the logic to check the token should be somewhere in your startup.cs of the API project. Have a solution search for UseJwtBearerAuthentication and there the details should be specified.
The flow for the Authorize decoration goes 'automatically', using the properties set in UseJwtBearerAuthentication. If you want to do some additional checks, you are in need of a custom authorize attribute.
You should create a new class that inherits the interface AuthorizeAttribute. Implement the methods and you can do custom checks (such as checking against DB). Check this link to have a good example of custom authorize attributes:
https://mycodepad.wordpress.com/2014/05/17/mvc-custom-authorizeattribute-for-custom-authentication/
Be aware that with each call, your DB will be contacted which may imply some extra load.

Getting User in web api controller using Asp.net Identity 2 - User not authenticated

I am using Asp.net Identity 2 in my new MVC 5 project. After much work I was able to implement my own custom user store etc and authenticate my users at log in. However, when I call User.Identity.Name inside my Web API controller, the name is always null and the user is not authenticated. I can see the authorization header and cookies in the request object, but no authorization seems to be occurring. If I add an "authorize" attribute, I am denied access.
EDIT:
Here is the OAuthProvider class:
public class MyOAuthProvider : OAuthAuthorizationServerProvider
{
private readonly string publicClientId;
private readonly Func<UserManager<CustomUser, int>> userManagerFactory;
public MyOAuthProvider(string publicClientId, Func<UserManager<CustomUser, int>> userManagerFactory)
{
if (publicClientId == null)
{
throw new ArgumentNullException("publicClientId");
}
if (userManagerFactory == null)
{
throw new ArgumentNullException("userManagerFactory");
}
this.publicClientId = publicClientId;
this.userManagerFactory = userManagerFactory;
}
public static AuthenticationProperties CreateProperties(CustomUser user, IEnumerable<string> roles)
{
//var roles = string.Join(",", user.Roles.Select(iur => iur.RoleId));
IDictionary<string, string> data = new Dictionary<string, string>
{
{ "userName", user.UserName },
{ "userRoles", string.Join(",", roles.ToArray()) }
};
return new AuthenticationProperties(data);
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
using (var userManager = this.userManagerFactory())
{
var user = await userManager.FindAsync(context.UserName, context.Password);
if (user == null)
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
ClaimsIdentity oAuthIdentity = await userManager.CreateIdentityAsync(user, context.Options.AuthenticationType);
ClaimsIdentity cookiesIdentity = await userManager.CreateIdentityAsync(user, CookieAuthenticationDefaults.AuthenticationType);
var roles = userManager.GetRoles(user.Id).ToArray();
AuthenticationProperties properties = CreateProperties(user, roles);
AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties);
context.Validated(ticket);
context.Request.Context.Authentication.SignIn(cookiesIdentity);
}
}
public override Task TokenEndpoint(OAuthTokenEndpointContext context)
{
foreach (var property in context.Properties.Dictionary)
{
context.AdditionalResponseParameters.Add(property.Key, property.Value);
}
return Task.FromResult<object>(null);
}
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
// Resource owner password credentials does not provide a client ID.
if (context.ClientId == null)
{
context.Validated();
}
return Task.FromResult<object>(null);
}
public override Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context)
{
if (context.ClientId == this.publicClientId)
{
var expectedRootUri = new Uri(context.Request.Uri, "/");
if (expectedRootUri.AbsoluteUri == context.RedirectUri)
{
context.Validated();
}
}
return Task.FromResult<object>(null);
}
}
What might I be doing wrong?

Debugging WebApi2 Login (/Token) endpoint

Using .NET WebApi2:
I am getting an 500 internal server error when attempting to log in to a WebApi, at the /Token
endpoint.
I know there are some configuration parameters in Startup.Auth, but is there anyway I can set a breakpoint, or otherwise see what is showing up on the server? (say, before the authentication check happens, and after -- not looking for a Fiddler type intercept solution).
debug GrantResourceOwnerCredentials method in Providers/ApplicationOAuthProvider.cs
You can configure Visual Studio to break on all exceptions and then obtain more information about your error when it throws.
In Visual Studio 2013, debug menu, Exceptions, mark both ckecks related to CLR exceptions as shown in the image:
you can debug it... but you need to implement your own ApplicationOAuthProvider which should inherit from this class: OAuthAuthorizationServerProvider
For example:
public class MyTokenProvider : OAuthAuthorizationServerProvider
{
private readonly string _publicClientId;
public MyTokenProvider(string publicClientId)
{
if (publicClientId == null)
{
throw new ArgumentNullException("publicClientId");
}
_publicClientId = publicClientId;
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();
ApplicationUser user = await userManager.FindAsync(context.UserName, context.Password);
if (user == null)
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager,
OAuthDefaults.AuthenticationType);
ClaimsIdentity cookiesIdentity = await user.GenerateUserIdentityAsync(userManager,
CookieAuthenticationDefaults.AuthenticationType);
AuthenticationProperties properties = CreateProperties(user.UserName);
AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties);
context.Validated(ticket);
context.Request.Context.Authentication.SignIn(cookiesIdentity);
}
public override Task TokenEndpoint(OAuthTokenEndpointContext context)
{
foreach (KeyValuePair<string, string> property in context.Properties.Dictionary)
{
context.AdditionalResponseParameters.Add(property.Key, property.Value);
}
return Task.FromResult<object>(null);
}
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
// Resource owner password credentials does not provide a client ID.
if (context.ClientId == null)
{
context.Validated();
}
return Task.FromResult<object>(null);
}
public override Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context)
{
if (context.ClientId == _publicClientId)
{
Uri expectedRootUri = new Uri(context.Request.Uri, "/");
if (expectedRootUri.AbsoluteUri == context.RedirectUri)
{
context.Validated();
}
}
return Task.FromResult<object>(null);
}
public static AuthenticationProperties CreateProperties(string userName)
{
IDictionary<string, string> data = new Dictionary<string, string>
{
{ "userName", userName }
};
return new AuthenticationProperties(data);
}
}
Obviously you will need to set it as your default provider, and that could be done from your Startup.Auth.cs
Example:
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Token"),
Provider = new MyTokenProvider(PublicClientId),
AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
AllowInsecureHttp = true
};
Then you can debug it :)

asp.net mvc, multiple CustomAuthorize Role Providers

I am trying to lock my application down to each method where by users and admin can see the whole controller but only administrators can create, delete, and edit.
I have created a CustomAuthorize class which holds the method:
public class CustomAuthorize : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
Debug.WriteLine("Show me the filterContext " + filterContext);
if (filterContext.Result is HttpUnauthorizedResult)
{
Debug.WriteLine("Why is this going to the redirect to AccessDenied?");
filterContext.Result = new RedirectResult("~/AccessDenied/Index");
}
}
}
My RoleProvider Class:
namespace Office.WebUI.Security {
public class OfficeRoleProvider : RoleProvider
{
public override void AddUsersToRoles(string[] usernames, string[] roleNames)
{
throw new NotImplementedException();
}
public override string ApplicationName
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
public override void CreateRole(string roleName)
{
throw new NotImplementedException();
}
public override bool DeleteRole(string roleName, bool throwOnPopulatedRole)
{
throw new NotImplementedException();
}
public override string[] FindUsersInRole(string roleName, string usernameToMatch)
{
throw new NotImplementedException();
}
public override string[] GetAllRoles()
{
throw new NotImplementedException();
}
public override string[] GetRolesForUser(string username)
{
Debug.WriteLine("Get the username" + username);
using (EFDbContext db = new EFDbContext()) >
{
User user = db.Users.FirstOrDefault(u => u.UserName.Equals(username, >stringComparison.CurrentCultureIgnoreCase) || u.Email.Equals(username, StringComparison.CurrentCultureIgnoreCase));
//User user = db.TaskPrivilege.FirstOrDefault(u => >u.UserName.Equals(username, StringComparison.CurrentCultureIgnoreCase) || u.Email.Equals(username, StringComparison.CurrentCultureIgnoreCase));
if (user != null)
{
var roles = from ur in user.UserRoles
from r in db.Roles
where user.role.RoleID == r.RoleID
//where ur.RoleID == r.RoleID
select r.Name;
if (roles != null)
return roles.ToArray();
else
return new string[] { }; ;
}
else
{
return new string[] { }; ;
}
}
}
public override string[] GetUsersInRole(string roleName)
{
throw new NotImplementedException();
}
public override bool IsUserInRole(string username, string roleName)
{
Debug.WriteLine("The username is " + username + " And the Role it is using is >" + roleName);
using (EFDbContext db = new EFDbContext())
{
User user = db.Users.FirstOrDefault(u => u.UserName.Equals(username, StringComparison.CurrentCultureIgnoreCase) || u.Email.Equals(username, StringComparison.CurrentCultureIgnoreCase));
if (user != null)
{
var roles = from ur in user.UserRoles
from r in db.Roles
where user.role.RoleID == r.RoleID
//where ur.RoleID == r.RoleID
select r.Name;
if (user != null)
return roles.Any(r => r.Equals(roleName, >StringComparison.CurrentCultureIgnoreCase));
else
return false;
}
else
{
return false;
}
}
}
public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames)
{
throw new NotImplementedException();
}
public override bool RoleExists(string roleName)
{
throw new NotImplementedException();
}
}
}
In my Database I have a Roles table that holds Role and RoleID as columns, with 2 values Administrator and User as roles with a roleID as the PK, 1 = Administrator & 2 = User.
Another table called Users, that contains UserID, Username and role_RoleID
When I assign a user to role_RoleID as 1 (administrator) and lock down a controller using
[CustomAuthorize(Roles = "Administrator"]
This works.
When I assign a role_RoldID = 2 and lock down using:
[CustomAuthorize(Roles = "User"]
This doesn't work?
Also another question is, how do I assign 2 roles to a controller?, I then want to lock down each method to 1 User Role.
Hope that makes sense and I hope someone can help me please.
Thanks
Steven
Basically I have a controller which I have put
[CustomAuthorize(Roles = "Administrators, Support")]
to lock it down so both roles can see the controller.
Then I have create/delete and edit methods I have:
[CustomAuthorize(Roles = "Administrators")]
so basically I want to show the whole controller to both roles but only administrators can create/edit/delete.
When I assign a roleID in the user table = 2(User) the whole application breaks for that user. Assigning RoldID = 1 (Administators) it works for all the people that is assigned roldID 1.
Currently I have the methods
public override string[] GetRolesForUser(string username)
{
using (EFDbContext db = new EFDbContext())
{
User user = db.Users.FirstOrDefault(u => u.UserName.Equals(username, StringComparison.CurrentCultureIgnoreCase) || u.Email.Equals(username, StringComparison.CurrentCultureIgnoreCase));
if (user != null)
{
var roles = from ur in user.UserRoles
from r in db.Roles
where user.role.RoleID == r.RoleID
select r.Name;
if (roles != null)
return roles.ToArray();
else
return new string[] { }; ;
}
else
{
return new string[] { }; ;
}
}
}
> public override bool IsUserInRole(string username, string roleName)
> {
>
> using (EFDbContext db = new EFDbContext())
> {
> User user = db.Users.FirstOrDefault(u => u.UserName.Equals(username, StringComparison.CurrentCultureIgnoreCase)
> || u.Email.Equals(username,
> StringComparison.CurrentCultureIgnoreCase));
>
> if (user != null)
> {
> var roles = from ur in user.UserRoles
> from r in db.Roles
> where user.role.RoleID == r.RoleID
> //where ur.RoleID == r.RoleID
> select r.Name;
> if (user != null)
> return roles.Any(r => r.Equals(roleName, >StringComparison.CurrentCultureIgnoreCase));
> else
> return false;
> }
> else
> {
> return false;
> }
> }
> }
I know I have to change the IsUserInRole method so it handles a array parameter like so, public override bool IsUserInRole(string username, string[] roleName)
But don't know how to handle that in the linq and mainly in the IsUserInRole method:
return roles.Any(r => r.Equals(roleName, >StringComparison.CurrentCultureIgnoreCase));
Currently it is failing at the GetRolesForUser method, at the where clause i believe,
where user.role.RoleID == r.RoleID
the error message is displayed below:
There is already an open DataReader associated with this Command which must be closed first.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.InvalidOperationException: There is already an open DataReader associated with this Command which must be closed first.
Source Error:
Line 66: var roles = from ur in user.UserRoles Line 67: from r in db.Roles Line 68: where user.role.RoleID == r.RoleID
Thanks
Multiple roles can be done as such:
[CustomAuthorize(Roles = "Administrator, User")]
But seeing you've written this code I'd think you'd know this already?
If all you want to do is keeps Users from getting in your create/edit/delete functions, why not use the default authorisation functions? meaning, put [CustomAuthorize(Roles = "Administrator")] above edit/create/delete and [CustomAuthorize(Roles = "Administrator, User")] above anything else...
I'm guessing the IsUserInRole gets called on authorisation? This method should take a string[] roleName array, and not just a string.
Edit:
change
var roles = from ur in user.UserRoles
from r in db.Roles
where user.role.RoleID == r.RoleID
//where ur.RoleID == r.RoleID
select r.Name;
to
var roles = user.UserRoles.select(q=>q.Roles.Name);

Resources