Return ActionResult OR Task from function - c#

The following code is used to check a user's authentication status. If the user is authenticated, it redirects to front end endpoint, which is a synchronous RedirectResult. If the user is not, it needs to call the HttpContext.ChallengeAsync function, which returns a Task.
public async Task<dynamic> Authorize()
{
if (HttpContext.User.Identity.IsAuthenticated)
{
return Redirect("/authorize");
}
return await ChallengeAsync();
}
public async Task<dynamic> ChallengeAsync()
{
var authenticationProperties = new AuthenticationProperties
{
RedirectUri = "/authorize",
Items =
{
{ "scheme", "oidc" },
{ "returnUrl", "/authorize" }
}
};
return HttpContext.ChallengeAsync(OpenIdConnectDefaults.AuthenticationScheme, authenticationProperties);
}
I've been in TypeScript Land the last few years, so I would like to return something like IActionResult | Task, or perhaps Task<IActionResult> | Task, but I don't think this is possible in C#.
The code I'm using is a hack. It usually works, but sometimes (usually the first time), it throws an error
{"stateMachine":{"<>1__state":3,"<>t__builder":{
This SO question says it is likely happening because I'm not awaiting something that should be awaited. I can await the ChallengeAsync and return a Task, but that messes up the types in the Authorize function in a way I can't untangle.

I've just bumped into this issue but I resolved it using the base controllers' Challenge action helpers.
public IActionResult Authorize()
{
if (HttpContext.User.Identity.IsAuthenticated)
{
return Redirect("/authorize");
}
return Challenge(new AuthenticationProperties
{
RedirectUri = "/authorize",
Items =
{
{ "scheme", "oidc" },
{ "returnUrl", "/authorize" }
}
}, OpenIdConnectDefaults.AuthenticationScheme)
}

Related

How to capture ActionResult Api Response in Angular?

I have the below method in my user controller:
[HttpPost]
public async Task<ActionResult<bool>> Create(User user)
{
var userCreated = userService.register(user); // returns true or false
if (userCreated)
{
return Ok();
}
else
{
return BadRequest("Could not create user.");
}
}
This method should then be captured in my angular calling the service:
onSubmit(user: User): void {
this.userService.registerUser(user).subscribe((response) => {
console.warn(response);
});
}
The register URL method:
registerUser(user: User): Observable <boolean> {
const httpOptions = { headers: new HttpHeaders({ 'Content-Type': 'application/json' }) };
return this.http.post<boolean>(environment.userUrl, user, httpOptions);
}
Unfortunately, the console writes null. Am I missing out anything? I want to capture whether the status is OK or BadRequest.
In your controller you are missing to return the value of your response:
[HttpPost]
public async Task<ActionResult<bool>> Create(User user)
{
var userCreated = userService.register(user); // returns true or false
if (userCreated)
{
return Ok(userCreated); // <= HERE
}
else
{
return BadRequest("Could not create user.");
}
}
After changing the above, you should be OK.
A small tip is that you don't need to add HttpOptions on every request in Angular.
The HttpClient is doing that for you:
registerUser(user: User): Observable<boolean> {
return this.http.post<boolean>(environment.userUrl, user);
}
PS: Ok() in C# means that you are returning a response with code 200.
On the other hand, a BadRequest() will result a 400 error code and will be caught as error inside subscription. In your case I thing that the code in the back end should be like this:
[HttpPost]
public async Task<ActionResult<bool>> Create(User user)
{
var userCreated = userService.register(user); // returns true or false
return Ok(userCreated);
}

Handling session timeout with Ajax in .NET Core MVC

I have a regular application using cookie based authentication. This is how it's configured:
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication("Login")
.AddCookie("Login", c => {
c.ClaimsIssuer = "Myself";
c.LoginPath = new PathString("/Home/Login");
c.AccessDeniedPath = new PathString("/Home/Denied");
});
}
This works for my regular actions:
[Authorize]
public IActionResult Users()
{
return View();
}
But doesn't work well for my ajax requests:
[Authorize, HttpPost("Api/UpdateUserInfo"), ValidateAntiForgeryToken, Produces("application/json")]
public IActionResult UpdateUserInfo([FromBody] Request<User> request)
{
Response<User> response = request.DoWhatYouNeed();
return Json(response);
}
The problem is that when the session expires, the MVC engine will redirect the action to the login page, and my ajax call will receive that.
I'd like it to return the status code of 401 so I can redirect the user back to the login page when it's an ajax request.
I tried writing a policy, but I can't figure how to unset or make it ignore the default redirect to login page from the authentication service.
public class AuthorizeAjax : AuthorizationHandler<AuthorizeAjax>, IAuthorizationRequirement
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, AuthorizeAjax requirement)
{
if (context.User.Identity.IsAuthenticated)
{
context.Succeed(requirement);
}
else
{
context.Fail();
if (context.Resource is AuthorizationFilterContext redirectContext)
{
// - This is null already, and the redirect to login will still happen after this.
redirectContext.Result = null;
}
}
return Task.CompletedTask;
}
}
How can I do this?
Edit: After a lot of googling, I found this new way of handling it in version 2.0:
services.AddAuthentication("Login")
.AddCookie("Login", c => {
c.ClaimsIssuer = "Myself";
c.LoginPath = new PathString("/Home/Login");
c.Events.OnRedirectToLogin = (context) =>
{
// - Or a better way to detect it's an ajax request
if (context.Request.Headers["Content-Type"] == "application/json")
{
context.HttpContext.Response.StatusCode = 401;
}
else
{
context.Response.Redirect(context.RedirectUri);
}
return Task.CompletedTask;
};
});
And it works for now!
What you need can be achieved by extending AuthorizeAttribute class.
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class AjaxAuthorizeAttribute : AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
if (filterContext.HttpContext.Request.IsAjaxRequest())
{
filterContext.HttpContext.Response.StatusCode = 401;
filterContext.Result = new JsonResult
{
Data = new { Success = false, Data = "Unauthorized" },
ContentEncoding = System.Text.Encoding.UTF8,
ContentType = "application/json",
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
else
{
base.HandleUnauthorizedRequest(filterContext);
}
}
}
You can then specify this attribute on Ajax methods.
Hope this helps.
Reference: http://benedict-chan.github.io/blog/2014/02/11/asp-dot-net-mvc-how-to-handle-unauthorized-response-in-json-for-your-api/

Return status code with an authorization filter

I want to check if the access token is in the blacklist, and then return Unauthorized.
public class CheckBannedTokenAttribute : Attribute, IAsyncAuthorizationFilter
{
public Task OnAuthorizationAsync(AuthorizationFilterContext context)
{
if (TokenInBlackList("232322323"))
{
//context.Result = new HttpStatusCodeResult(HttpStatusCode.Unauthorized);
}
}
}
You are right that you need to fill context.Result. Cause you want to return 401 Unauthorized as response, use built-in UnauthorizedResult class:
if (TokenInBlackList("232322323"))
{
context.Result = new UnauthorizedResult();
return Task.CompletedTask;
}
In general, this is the same as new StatusCodeResult(401)
This will give you the classic "401 Unauthorized" that you expect.
async Task IAsyncAuthorizationFilter.OnAuthorizationAsync(AuthorizationFilterContext context)
{
ClaimsPrincipal user = context.HttpContext.User;
if (!user.Identity.IsAuthenticated)
{
context.Result = new ChallengeResult();
}
}
your code seems all good, simply initialize the Response and return Task
if (TokenInBlackList("232322323")){
context.Response = context.Request.CreateResponse(HttpStatusCode.Unauthorized);
return Task.FromResult<object>(null);
}

CreatedAtRoute with OK(200) code

Whether it's acceptable RESTful design or not, I'd like to give a result as like below code doing but with 200 OK StatusCode.
return CreatedAtRoute("DefaultApi", new { id = model.Id }, model);
Above one provides Location header which utilize given id route variable and json serialized model content.
return Ok(); // how to make it with this?
Note that I'm using ASP.NET WebApi2 (.Net 4.6) template.
What about this, create a custom IHttpActionResult that decorates CreatedAtRouteNegotiatedContentResult and updates the status code:
public class OkWithLocation<T> : IHttpActionResult
{
private readonly CreatedAtRouteNegotiatedContentResult<T> _result;
public OkWithLocation(CreatedAtRouteNegotiatedContentResult<T> result)
{
_result = result;
}
public async Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
var response = await _result.ExecuteAsync(cancellationToken).ConfigureAwait(false);
response.StatusCode = HttpStatusCode.OK;
return response;
}
}
And then use this in your controller action:
return new OkWithLocation<Model>(CreatedAtRoute("DefaultApi", new { id = model.Id }, model));
Maybe not the prettiest, but it gets the job done.

How do I return the TaskStatus when making a synchronous call asynchronous?

I have a old data access library that I need to use in my ASP.NET MVC application but I'm having trouble bringing it into the MVC world where many server-side operations are expected to be asynchronous. My data access library looks a like this:
public class MyOldDAL
{
public TaskResult CreateUser(string userName)
{
try
{
// Do user creation
return new TaskResult { Success = true, Message = "success" };
}
catch (Exception ex)
{
return new TaskResult { Success = false, Message = ex.Message };
}
}
}
And is called by my MVC application like this:
public class MyUserStore : IUserStore<ApplicationUser>
{
private readonly MyOldDAL _dal = new MyOldDAL();
public async Task CreateAsync(ApplicationUser user)
{
await Task.Run(() => _dal.CreateUser(user.UserName));
}
}
This is fine until the method in Task.Run() fails for some reason. I'd like to be able to return TaskStatus.Faulted if TaskResult.Success is false but I can't find an example online on how to do it properly - all my Google searches turn up is how to use Task<T> which the IUserStore interface doesn't permit.
For the record, much as I'd like to I can't change MyOldDAL - it's targeting .NET 3.5 so no async or await there!
The normal way to report errors from tasks is via exceptions, so you'll just need to do that transformation yourself:
public class MyUserStore : IUserStore<ApplicationUser>
{
private readonly MyOldDAL _dal = new MyOldDAL();
public Task CreateAsync(ApplicationUser user)
{
var result = _dal.CreateUser(user.UserName);
if (result.Success)
return Task.CompletedTask;
return Task.FromException(new InvalidOperationException(result.Message));
}
}
Note that Task.Run should not be used on ASP.NET.
Note: As Stephen Cleary noticed in his answer, Task.Run should not be used on ASP.NET.
Original answer (before comments):
Your CreateAsync method should normally be like this:
public async Task<TaskResult> CreateAsync(ApplicationUser user)
{
return await Task.Run(() => _dal.CreateUser(user.UserName));
}
But if you can't return Task<TaskResult> from CreateAsync method... well, than you can't obtain TaskResult from CreateAsync by definition. In that case you can store result locally:
private TaskResult taskResult;
public async Task CreateAsync(ApplicationUser user)
{
var result = await Task.Run(() => _dal.CreateUser(user.UserName));
this.taskResult = result;
// process taskResult wherether you need
}
Or raise event with TaskResult payload, allowing client of MyUserStore to subscribe to this event:
public event EventHandler<TaskResult> TaskCompleted;
public async Task CreateAsync(ApplicationUser user)
{
var result = await Task.Run(() => _dal.CreateUser(user.UserName));
this.OnTaskCompleted(result);
}
private void OnTaskCompleted(TaskResult result)
{
this.TaskCompleted?.Invoke(this, result);
}

Categories