invalid_grant of OAuthAuthorizationServerProvider - asp.net-web-api

I'm working on writing fully customized ASP.NET Identity for my WebAPi.
I have rewritten my own derived OAuthAuthorizationServerProvider in this way:
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
context.Validated();
return Task.FromResult<object>(null);
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
// Check User availability ...
//ApplicationUser user = await userManager.FindAsync(context.UserName, context.Password);
// if i couldn't found user in my DataBase ...
//if (user == null)
//{
//context.SetError("invalid_grant", "The user name or password is incorrect.");
// return;
//}
context.Validated();
}
}
GrantResourceOwnerCredentials just returns an invalid_grant error for each calls. i want to handle it but, i don't know how.

ValidateClientAuthentication is where you would do your authentication checks and this is where you throw errors if anything doesn't match.
move your code there and do the checks before you call context.Validated(). You only call the Validate method once you make sure everything is validated correctly.
here is an example of such an implementation I did a while back:
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
string clientId;
string clientSecret;
//first try to get the client details from the Authorization Basic header
if (!context.TryGetBasicCredentials(out clientId, out clientSecret))
{
//no details in the Authorization Header so try to find matching post values
context.TryGetFormCredentials(out clientId, out clientSecret);
}
if (string.IsNullOrWhiteSpace(clientId) || string.IsNullOrWhiteSpace(clientSecret))
{
context.SetError("client_not_authorized", "invalid client details");
return Task.FromResult<object>(null);
}
var dataLayer = new RepoManager(new DataLayerDapper()).DataLayer;
var audienceDto = dataLayer.GetAudience(clientId);
if (audienceDto == null || !clientSecret.Equals(audienceDto.Secret))
{
context.SetError("unauthorized_client", "unauthorized client");
return Task.FromResult<object>(null);
}
context.Validated();
return Task.FromResult<object>(null);
}
Notice how the checks happen in order and certain errors are raised with some appropriate errors.
This code takes a client id and client secret from an authorization header but you can easily drop all that and replace it with your own checks and database calls.
The important part is that this is where you deal with stuff like this and this is where you set the errors so your clients know what's going on.
GrantResourceOwnerCredentials this is where you get once the call is properly authenticated, at which point you can start creating tokens, adding claims and creating the authentication ticket. This method does not get hit if the previous one fails to authenticate the request.
Here is a working example:
public override Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
var identity = new ClaimsIdentity("JWT");
identity.AddClaim(new Claim("clientID", context.ClientId));
var props = new AuthenticationProperties(new Dictionary<string, string>
{
{
"audience", context.ClientId
}
});
var ticket = new AuthenticationTicket(identity, props);
context.Validated(ticket);
return Task.FromResult<object>(null);
}
Now, if you get an invalid grant error that usually happens because you either didn't set up the grant_type in your initial call or you set up the wrong value.
in my case I had to setup this:
"grant_type", "password"

Related

SignInStatus always returns Success on TwoFactorAuthentication is enabled in webapi using asp.net identity

I am implementing 2 factor authentication in WebApi, asp.net identity and OWIN. Every time I log in, I get SignInStatus = Success never reaches to SignInStatus = RequiresVerification though user TwoFactorAuthentication is enabled.
Below are some code snippets,
Startup.cs:
private void ConfigureAuth(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie, TimeSpan.FromMinutes(5));
app.UseTwoFactorRememberBrowserCookie(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie);
app.UseOAuthBearerTokens(OAuthOptions);
}
Action method for enabling two factor authentication,
[HttpPost]
public async Task<IHttpActionResult> EnableTwoFactorAuthentication()
{
var user = await this.AppUserManager.FindByIdAsync(User.Identity.GetUserId());
if (user != null)
{
IdentityResult result = await this.AppUserManager.SetTwoFactorEnabledAsync(User.Identity.GetUserId(), true);
await this.AppSignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
if (!result.Succeeded)
{
return GetErrorResult(result);
}
}
return Ok();
}
Please suggest a solution.
If you get stuck here, one way to solve the problem is to copy the methods from SignInManager directly into your code and call those instead so you can step through the methods and see why you are getting the wrong status. For me the problem ended up being that I instantiated my UserManager with:
new MyUserManager()
instead of the right way:
HttpContext.GetOwinContext().Get<MyUserManager>()
I was using this as my template for setting it up:
https://github.com/adamtuliper/ASP.NET-Identity-Samples/tree/master/BasicTemplate%20-%20Two%20Factor/BasicTemplate
SignInManager return RequiresVerification if :
dbo.ASpnetUsers has for user set to true TwoFactorEnabled and EmailConfirmed and user email should be confirmed, email not be empty or null.
var result = SignInManager.PasswordSignIn(usernameIdentity, model.Password, model.RememberMe, shouldLockout: true);
switch (result)
{
case SignInStatus.Success:
return RedirectToLocal(returnUrl);
case SignInStatus.RequiresVerification:
return RedirectToAction("SendCode", returnUrl);
case SignInStatus.Failure:
default:
ModelState.AddModelError("", "Invalid username or password.");
return View(model);
}

Why GrantRefreshToken method is not called - Oauth2 ASP.NET Web API

I send request to oauth2 server with request body :
grant_type=refresh_token&refresh_token=abc
I save refresh_token in database.
ReceiveAsync method :
public async Task ReceiveAsync(AuthenticationTokenReceiveContext context)
{
var allowedOrigin = context.OwinContext.Get<string>("clientAllowedOrigin");
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { allowedOrigin });
string hashTokenName = Helper.GetHash(context.Token);
var refreshToken = await repo.FindRefreshToken(hashTokenName);
if (refreshToken != null)
{
//Get protectedTicket from refreshToken class
context.DeserializeTicket(refreshToken.ProtectedTicket);
var result = await repo.RemoveRefreshToken(hashTokenName);
}
}
GrantRefreshToken method :
public override Task GrantRefreshToken(OAuthGrantRefreshTokenContext context)
{
var originClient = context.Ticket.Properties.Dictionary["client_id"];
var currenClient = context.ClientId;
if (originClient != currenClient)
{
context.SetError("Error");
return Task.FromResult<object>(null);
}
var newIdentity = new ClaimsIdentity(context.Ticket.Identity);
newIdentity.AddClaim(new Claim(ClaimTypes.Name, context.ClientId));
var newTicket = new AuthenticationTicket(newIdentity, context.Ticket.Properties);
context.Validated(newTicket);
return Task.FromResult<object>(null);
}
Why GrantRefreshToken is not called when ReceiveAsync finished ?
I had been facing the same problem for two days.
the issue causes by ReceiveAsync method does not set OAuthGrantRefreshTokenContext context.ticket
properly.
to diagnose that, use postman to send the refresh_token request. an invalid_grant error will be prompted.
the minimum requirement for generate a ticket is to set IssuedUtc & ExpiresUtc and leave other properties to default.
the code below can simply replicate the issue.
public async Task ReceiveAsync(AuthenticationTokenReceiveContext context)
{
AuthenticationTicket ticket = null;
var identity = new ClaimsIdentity();
var props = new AuthenticationProperties();
ticket = new AuthenticationTicket(identity, props);
context.SetTicket(ticket);
//context.Ticket.Properties.IssuedUtc = DateTime.UtcNow;
//context.Ticket.Properties.ExpiresUtc = DateTime.UtcNow;
}
once IssuedUtc & ExpiresUtc are set, GrantRefreshToken will be
invoked.
solution: put a breakpoint by the end of ReceiveAsync method, check
whether context.ticket is set properly.
IssuedUtc & ExpiresUtc these two properties can not be null.
ReceiveAsync signature should be: public Task ReceiveAsync(AuthenticationTokenReceiveContext context)
And you should return a Task in that method.
I've faced the exact same problem, and the root cause in my case was the machine key, the engine use the machine key in serializing the ticket, so if you don't configure the machine key in web.config then one key could be used in the serialization process and another one in the deserialization.
So try to configure the machine key in the web.cofig. Hope this helps

HttpClient always do Basic Authentication check after provide an authorization header?

The web api control, UserControl, has two methods, RetrieveUserID which needs Basic Authorization check
[HttpGet]
[Route("RetrieveUserID/{strUsername}")]
[Authorize]
public string RetrieveUserID(string strUsername)
{
//retrieve userID and return it
......
return strUserID;
}
Another method, FailAuthenticationReason, is used if fail to retrieve userID, it returns the detail fail info such as wrong username, wrong password, account is locked out, etc. which doesn't need to do any authentication check
[HttpGet]
[Route("FailAuthenticationReason/{strUsername}")]
public string FailAuthenticationReason(string strUsername)
{
//retrieve detail failed reason
......
return strFailedReason;
}//End of
It works well when I use a browser to check them. But when I use it in my app, after I provide the authorization header and fail to retrieve userID because of incorrect username and/or password, it also do the authorization check when it call FailAuthenticationReason
var authData = string.Format("{0}:{1}", entUsername.Text,entPassword.Text);
var authHeaderValue = Convert.ToBase64String(Encoding.UTF8.GetBytes(authData));
App.httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", authHeaderValue);
var uri = new Uri(string.Format(App.strWebAPIURI + "/RetrieveUserID/{0}", entUsername.Text));
try
{
var response = await App.httpClient.GetAsync(uri);
if (response.IsSuccessStatusCode)
{
......
}
else
{
//Fail to pass authorization
uri = new Uri(string.Format(App.strWebAPIURI + "/FailAuthenticationReason/{0}", entUsername.Text));
response = await App.httpClient.GetAsync(uri);
......
}
How can the program call FailAuthenticationReason without the authorization check?

Custom Async Action Filter for Web API 2

I have a web api to consume the data coming from android mobile. This web api will consume the multi part file from along with the form data the web api request. I followed this article to archive.
[CustAuthAsync]
public async Task<HttpResponseMessage> SaveEHSInspectionData()
{
try
{
string root = HttpContext.Current.Server.MapPath("~/App_Data");
MultipartFormDataStreamProvider provider = new MultipartFormDataStreamProvider(root);
//do stuff
var res = await Request.Content.ReadAsMultipartAsync(provider);
// DO SOME STUFF
}
catch (Exception exp)
{
}
return Request.CreateResponse(HttpStatusCode.OK, result);
}
I wanted to do the custom access validation for this web api, so implemented a filter to validate the request.
I have the filter like below
public class CustAuthAsyncAttribute : ActionFilterAttribute
{
public override async Task OnActionExecutingAsync(HttpActionContext actionContext, CancellationToken cancellationToken)
{
InternalOnExecutingAsync(actionContext);
}
}
The internal method like this
protected void InternalOnExecutingAsync(HttpActionContext actionContext)
{
var authValue = actionContext.Request.Headers;
if (authValue.Contains("CustomAccessToken"))
{
string token = authValue.GetValues("CustomAccessToken").First();
var result = // doing some decription
if (result != null)
{
bool validationResult = // validation with database
if (!validationResult)
{
actionContext.Response = new HttpResponseMessage(HttpStatusCode.Unauthorized)
{ ReasonPhrase = "Invalid token" };
}
}
else
{
actionContext.Response = new HttpResponseMessage(HttpStatusCode.Unauthorized)
{ ReasonPhrase = "Invalid token" };
}
}
else
{
actionContext.Response = new HttpResponseMessage(HttpStatusCode.Unauthorized)
{ ReasonPhrase = "Unauthorized Request" };
}
These implementations are working fine in API Client Tools (Example: Postman) if the validation passes, allows the request to the method.
Postman Response screen shot
This is not working in mobile app, Saying the response message as Unauthorized Access. and not allowing the request to the method even the custom access validations are passed.
FYI : This method is working fine in mobile without filter
Help me to get this works in mobile app also.
Thanks in advance.
Your using the wrong type of filter to manage access. You should use an authorization filter. Besides you can't have an async method to authorize. You have to make the calling client wait for clearance. This may cause the side effects you're experiencing.
I'm not sure this has any to do with fact that it's a mobile application, however the authorization phase ir prior to the processing of the request. Verify that your are not using any other form of authorization in your project.
You should implement an authorization filter by inheriting AuthorizeAttribute and overriding IsAuthorized(HttpActionContext actionContext) method:
public class CustAuthAsync : AuthorizeAttribute
{
public CustAuthAsync()
{
///Some initialization if required. Otherwise, not necessary to declare the constructor..
}
protected override bool IsAuthorized(HttpActionContext actionContext)
{
var authValue = actionContext.Request.Headers;
if (authValue.Contains("CustomAccessToken"))
{
string token = authValue.GetValues("CustomAccessToken").First();
var result = // doing some decription
if (result != null)
{
return //database validation
}
else
{
return false;
//No need to create special unauthorized response. You should not hint the reason at this point. You can do this in the HandleUnauthorizedRequest method.
}
}
else
{
return false;//No need to create special unauthorized response.
}
}
}
You can use this attribute to decorate your controllers. You can even pass parameter in the constructor for more granular control on access management, like a required role to access de controller.

How to get user context during Web Api calls?

I have an web front end calling an ASP Web Api 2 backend. Authentication is managed with ASP Identity. For some of the controllers I'm creating I need to know the user making the call. I don't want to have to create some weird model to pass in including the user's identity (which I don't even store in the client).
All calls to the API are authorized using a bearer token, my thought is the controller should be able to determine the user context based on this but I do not know how to implement. I have searched but I don't know what I'm searching for exactly and haven't found anything relevant. I'm going for something like...
public async Task<IHttpActionResult> Post(ApplicationIdentity identity, WalkthroughModel data)
Update
I found the below which looked very promising... but the value is always null! My controller inherits from ApiController and has an Authorize header.
var userid = User.Identity.GetUserId();
Update 2
I have also tried all of the solutions in Get the current user, within an ApiController action, without passing the userID as a parameter but none work. No matter what I am getting an Identity that is valid and auth'd, but has a null UserID
Update 3
Here's where I'm at now.
[Authorize]
[Route("Email")]
public async Task<IHttpActionResult> Get()
{
var testa = User.Identity.GetType();
var testb = User.Identity.GetUserId();
var testc = User.Identity.AuthenticationType;
var testd = User.Identity.IsAuthenticated;
return Ok();
}
testa = Name: ClaimsIdentity,
testb = null,
testc = Bearer,
testd = true
The user is obviously authenticated but I am unable to retrieve their userID.
Update 4
I found an answer, but I'm really unhappy with it...
ClaimsIdentity identity = (ClaimsIdentity)User.Identity;
string username = identity.Claims.First().Value;
That gets me the username without any db calls but it seems very janky and a pain to support in the future. Would love if anyone had a better answer.
What if I need to change what claims are issued down the road? Plus any time I actually need the user's id I have to make a db call to convert username to ID
A common approach is to create a base class for your ApiControllers and take advantage of the ApplicationUserManager to retrieve the information you need. With this approach, you can keep the logic for accessing the user's information in one location and reuse it across your controllers.
public class BaseApiController : ApiController
{
private ApplicationUser _member;
public ApplicationUserManager UserManager
{
get { return HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>(); }
}
public string UserIdentityId
{
get
{
var user = UserManager.FindByName(User.Identity.Name);
return user.Id;
}
}
public ApplicationUser UserRecord
{
get
{
if (_member != null)
{
return _member ;
}
_member = UserManager.FindByEmail(Thread.CurrentPrincipal.Identity.Name);
return _member ;
}
set { _member = value; }
}
}
I use a custom user authentication (I dont use AspIdentity because my existing user table fields was far different from IdentityUser properties) and create ClaimsIdentity passing my table UserID and UserName to validate my bearer token on API calls.
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
User user;
try
{
var scope = Autofac.Integration.Owin.OwinContextExtensions.GetAutofacLifetimeScope(context.OwinContext);
_service = scope.Resolve<IUserService>();
user = await _service.FindUserAsync(context.UserName);
if (user?.HashedPassword != Helpers.CustomPasswordHasher.GetHashedPassword(context.Password, user?.Salt))
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
}
catch (Exception ex)
{
context.SetError("invalid_grant", ex.Message);
return;
}
var properties = new Dictionary<string, string>()
{
{ ClaimTypes.NameIdentifier, user.UserID.ToString() },
{ ClaimTypes.Name, context.UserName }
};
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
properties.ToList().ForEach(c => identity.AddClaim(new Claim(c.Key, c.Value)));
var ticket = new AuthenticationTicket(identity, new AuthenticationProperties(properties));
context.Validated(ticket);
context.Request.Context.Authentication.SignIn(identity);
}
And how I use the ClaimsIdentity to retrieve my User table details on User ApiController Details call.
[HostAuthentication(DefaultAuthenticationTypes.ExternalBearer)]
[Route("Details")]
public async Task<IHttpActionResult> Details()
{
var user = await _service.GetAsync(RequestContext.Principal.Identity.GetUserId<int>());
var basicDetails = Mapper.Map<User, BasicUserModel>(user);
return Ok(basicDetails);
}
Notice the
ClaimTypes.NameIdentifier = GetUserId() and ClaimTypes.Name = GetUserName()

Resources