asp.net web api Authorize 302 code - angularjs

When I send request from client side I received 302 code and redirect to login but
next I received:
Console log: XMLHttpRequest cannot load https://login.microsoftonline.com/........................ No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'https://localhost:' is therefore not allowed access.
Error: Response with status: 0 for URL: null
ApiController:
[Authorize]
public string Get()
{ }
I would like to return status code 401 or something like that.

It seems like you have not enabled CORS in your API or that you are using cookie authentication instead of Token based auth.
To return a 401 instead of a 302 you could write some Custom Owin Middleware that would check what your controller is returning and alter the response to make it fit your needs.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace Middleware
{
using Microsoft.Owin;
public sealed class MyCustomMiddleware : OwinMiddleware
{
public MyCustomMiddleware(OwinMiddleware next)
: base(next)
{
}
public override async System.Threading.Tasks.Task Invoke(IOwinContext context)
{
// Code here executed before reaching the controller
await Next.Invoke(context);
// Code here executed after reaching the controller, includes the response
// check response here and modify it to suit your needs
if(context.Response.StatusCode == 302) {
var headers = context.Response.Headers;
headers.Keys.ForEach(k => headers.Remove(k));
context.Response.StatusCode = 401;
context.Response.ContentType = string.Empty;
context.Response.ContentLength = null;
await context.Response.WriteAsync(string.Empty);
}
}
}
}
then in startup.cs
app.Use<Middleware.MyCustomMiddleware>();

Related

Windows Authentication gives 401

I am working in a project that uses Windows Authentication and it is my first experience with it. I have a backend ASP.NET Web Api setup with Windows Authentication, and it includes AddNegotiate in my builder. I have a basic controller which returns username of the user.Here isthe codes from backend;
[ApiController]
[Route("[controller]")]
public class UserController : ControllerBase
{
[HttpGet(Name = "GetCurrentUser")]
public string Get()
{
string name = HttpContext.User.Identity.Name;
return name;
}
}
and my request from front-end(Reactjs) is;
try {
const response = await axios('https://localhost:7000/User');
console.log(response.data);
} catch (error) {
console.log(error.response);
}
I always get 401 Unauthorized.The response header contains WWW-Authenticate: Negotiate, but no token provided.On the other hand in SwaggerUI with this endpoint I get my username successfully. What could be the root cause of this problem ?

JSESSIONID changing every time API Fires. if same then CORS POLICY issue

So here is the Problem.
Front end: React Js
Backend: Spring Boot (No .xml file only annotation and properties files)
What I did is first user have to login then only he can access other APIs
when user login:
#PostMapping("/signin")
public boolean signIn(HttpServletRequest request, #Valid #RequestBody UserCredentials loginData) {
return userCredentials.signInToAccount(request, loginData);
}
It will goes to this file and then it calls service class
#Override
public boolean signInToAccount(HttpServletRequest request, UserCredentials loginData) {
UserCredentials user;
HttpSession session;
try {
user = userDAO.findById(loginData.getAssociateId()).get();
} catch (NoSuchElementException e) {
logger.error(e.getMessage());
throw new NoSuchElementException(env.getProperty("signin.invalidId.error"));
}
if (user.getPassword().equals(loginData.getPassword())
&& user.getIsAssociateActive().equals(env.getProperty("active.yes"))) {
session = request.getSession(true);
session.setAttribute(env.getProperty("session.attribute"), loginData.getAssociateId());
return true;
} else {
return false;
}
}
Here I am setting the session with logged user id:
Now When I try to use any APIs like
#GetMapping("/my-tasks")
public List<Task> getAllTaskInfo(HttpServletRequest request) throws UserNotLoggedInException {
String id = session.getSessionAttributeValue(request);
return taskServices.getAllTasksDetail(id);
}
It called the session to get the id if session is not created then User Define Exception is thrown.
Here is my session file:
public String getSessionAttributeValue(HttpServletRequest request) throws UserNotLoggedInException {
String loginFirst = "";
try {
HttpSession session = request.getSession();
loginFirst = properties.getPropertyObject("session.notlogin");
return session.getAttribute(properties.getPropertyObject("session.attribute")).toString();
} catch (Exception e) {
throw new UserNotLoggedInException(loginFirst);
}
}
With Postman it is working fine.
But for Browser(edge,chrome) its nt working.
What I tried:
I tried to debug I found that with postman cookies stored with same id after login but with browser JSESSIONID is changed every time its called for APIs.
Please help me.
Thanking you in advance.
EDIT 1:
I used withCredentials:true in my api call like this
axios.post(BASE_URL_USER + "/signin", credentials,{withCredentials:true});
But now Facing CORS error
Access to XMLHttpRequest at 'http://localhost:9090/signin' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.
I tried to remove the #CrossOrigin("*") to #CrossOrigin(origin="http://localhost:3000") or #CrossOrigin(origin="http://localhost:9090")
but not working
I have not applied any security filter or configuration in my backend and neither any header or cors nothing in frontend .
Just a normal call from frontend to backend.
Note: it is working perfectly with POSTMAN.

ASP.NET Core Web API POST No 'Access-Control-Allow-Origin' header is present

I have a GET Web API method in another controller working fine with UseCors allowing any origin enabled in Startup.cs
When I try to call a POST method I get a No 'Access-Control-Allow-Origin' header is present error returned along with a 500 error.
What am I doing wrong?
using System.Collections.Generic;
using CrossSell.Business.Exceptions;
using CrossSell.Business.Interfaces;
using CrossSell.Entities;
using Microsoft.AspNetCore.Mvc;
namespace CrossSell.API.Controllers
{
[Produces("application/json")]
[Route("api/Product")]
public class ProductController : Controller
{
private readonly IProductManager productManager;
public ProductController(IProductManager productManager)
{
this.productManager = productManager;
}
// POST: api/Product
[HttpPost]
public IEnumerable<Opportunity> Post([FromBody]ClientIdentifiable[] clients)
{
try
{
return productManager.GetCrossSellOpportunities(clients);
}
catch (NoInForceOrHistoricalPoliciesException)
{
return new[] { new Opportunity(true, "No In Force or historical policies") };
}
}
}
}
I'm calling the Post method from my React app (which runs on localhost:3000):
getDashboardData() {
var self = this;
axios.post( 'http://localhost:5000/product/api/', this.state.clientIdentifiables).then(function (response) {
console.log(response);
});
}
I forgot to add the dependency injection entry in ConfigureServices method of Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddTransient<IClientManager, ClientManager>();
services.AddTransient<IClientRepository, ClientRepository>();
// added the following and it hit the web api method correctly
services.AddTransient<IProductManager, ProductManager>();
services.AddTransient<IProductRepository, ProductRepository>();
}

How to enable [Authorize] on TestController on IdentityServer4 for purpose of Claim CRUD Controller

Using OIDC Client from here.
And demo server from here and here.
I have the following controller on the IdentityServer itself:
[Route("api/Test")]
//[Authorize]
[Authorize(ActiveAuthenticationSchemes = "Bearer")]
public class TestController : ControllerBase
{
public IActionResult Get()
{
var claims = User.Claims.Select(c => new { c.Type, c.Value });
return new JsonResult(claims);
}
}
If I comment out both [Authorize] attributes, I reach the TestController.
If I use just [Authorize], I get the following error:
GET http://localhost:5000/api/Test dashboard:1 XMLHttpRequest cannot
load http://localhost:5000/api/Test. No 'Access-Control-Allow-Origin'
header is present on the requested resource. Origin
'http://localhost:4200' is therefore not allowed access. The response
had HTTP status code 500.
And if I just use [Authorize(ActiveAuthenticationSchemes = "Bearer")] I get:
GET http://localhost:5000/api/Test dashboard:1 XMLHttpRequest cannot
load http://localhost:5000/api/Test. No 'Access-Control-Allow-Origin'
header is present on the requested resource. Origin
'http://localhost:4200' is therefore not allowed access. The response
had HTTP status code 500. dashboard:1 XMLHttpRequest cannot load
http://localhost:5000/api/Test. Redirect from
'http://localhost:5000/api/Test' to
'http://localhost:5000/Account/Login?ReturnUrl=%2Fapi%2FTest' has been
blocked by CORS policy: Request requires preflight, which is
disallowed to follow cross-origin redirect.
The code I use to call the Endpoint from the OIDC client is:
test() {
this.authService.mgr.getUser()
.then(user => {
// this.http.get('https://api.identityserver.io/identity',
this.http.get('http://localhost:5000/api/Test',
{ headers: new Headers({ 'Authorization': `${user.token_type} ${user.access_token}`}) })
.subscribe(res => {
console.log(res.json());
});
});
}
I am able to successfully call https://api.identityserver.io/identity with this.
This is my CorsPolicyHelper:
public class DemoCorsPolicy : ICorsPolicyService
{
public Task<bool> IsOriginAllowedAsync(string origin)
{
return Task.FromResult(true);
}
}
And this is where its called form Startup:
public void ConfigureServices(IServiceCollection services)
{
...
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<AuthDbContext>()
.AddDefaultTokenProviders();
services.AddMvc();
...
services.AddIdentityServer()
.AddTemporarySigningCredential()
.AddInMemoryPersistedGrants()
.AddInMemoryIdentityResources(Resources.GetIdentityResources())
.AddInMemoryApiResources(Resources.GetApiResources())
.AddInMemoryClients(Clients.GetClients())
.AddAspNetIdentity<ApplicationUser>();
services.AddTransient<ICorsPolicyService, DemoCorsPolicy>();
}
Ultimate goal is to perform CRUD operations on permissions/claims. I am currently stuck on this seemingly trivial task of having an Authorization protected controller :/
Ultimately since I am able to to use [Authorize] successfully outside IdentityServer4, I decided to separate Authorization from Authentication and create an Authorization server which provides for separation of concerns.

SignalR authentication failed when passing "Bearer" through query string

I'd like to enable authentication in SignalR while the server was hosted in ASP.NET WebAPI which I'm using OAuth Bearer authrntication and the client is AngularJS.
On client side I originally pass the Bearer token through HTTP header and it works well with the WebAPI. But since SignalR JavsScript doesn't support adding HTTP headers in connection (it's because WebSocket doesn't support specifying HTTP headers) I need to pass the Bearer token through query string by using the code like self.connection.qs = { Bearer: 'xxxxxx' };
The problem is on the WebAPI side my SignalR always returned 401 Unauthorized.
Below is what I did on the WebAPI side.
1, I specified OAuthBearerAuthenticationOptions.Provider to QueryStringEnabledOAuthBearerAuthenticationProvider, which is a class I created inherited from OAuthBearerAuthenticationProvider that can retrieve Bearer token from query string. Code as below.
public class QueryStringEnabledOAuthBearerAuthenticationProvider : OAuthBearerAuthenticationProvider
{
private readonly string _name;
public QueryStringEnabledOAuthBearerAuthenticationProvider()
: this(OAuthDefaults.AuthenticationType)
{
}
public QueryStringEnabledOAuthBearerAuthenticationProvider(string name)
{
_name = name;
}
public override Task RequestToken(OAuthRequestTokenContext context)
{
// try to read token from base class (header) if possible
base.RequestToken(context).Wait();
if (string.IsNullOrWhiteSpace(context.Token))
{
// try to read token from query string
var token = context.Request.Query.Get(_name);
if (!string.IsNullOrWhiteSpace(token))
{
context.Token = token;
}
}
return Task.FromResult(null);
}
}
And registered it as below while WebAPI was started.
var options = new OAuthBearerAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
AuthenticationType = AuthenticationType,
Provider = new QueryStringEnabledOAuthBearerAuthenticationProvider(),
AccessTokenFormat = _accessTokenFormat,
};
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
app.UseOAuthBearerAuthentication(options);
2, In SignalR part I created an authorize attribute as below. Nothing changed just to be used to add break point.
public class BearerAuthorizeAttribute : AuthorizeAttribute
{
public override bool AuthorizeHubConnection(HubDescriptor hubDescriptor, IRequest request)
{
return base.AuthorizeHubConnection(hubDescriptor, request);
}
public override bool AuthorizeHubMethodInvocation(IHubIncomingInvokerContext hubIncomingInvokerContext, bool appliesToMethod)
{
return base.AuthorizeHubMethodInvocation(hubIncomingInvokerContext, appliesToMethod);
}
}
And registered it when WebAPI started as well.
app.Map("/signalr", map =>
{
// Setup the CORS middleware to run before SignalR.
// By default this will allow all origins. You can
// configure the set of origins and/or http verbs by
// providing a cors options with a different policy.
map.UseCors(CorsOptions.AllowAll);
var hubConfiguration = new HubConfiguration
{
// You can enable JSONP by uncommenting line below.
// JSONP requests are insecure but some older browsers (and some
// versions of IE) require JSONP to work cross domain
// EnableJSONP = true
EnableJavaScriptProxies = false
};
// Run the SignalR pipeline. We're not using MapSignalR
// since this branch already runs under the "/signalr"
// path.
map.RunSignalR(hubConfiguration);
// Require authentication for all hubs
var authorizer = new BearerAuthorizeAttribute();
var module = new AuthorizeModule(authorizer, authorizer);
GlobalHost.HubPipeline.AddModule(module);
});
I found, when SignalR connected my QueryStringEnabledOAuthBearerAuthenticationProvider.RequestToken was invoked and it retrieved Bearer token successfully. But then when SignalR BearerAuthorizeAttribute.AuthorizeHubConnection was invoked the parameter request.User still not authenticated. So it returned 401.
Can anyone give me some ideas on what's wrong I did, thanks.
I'm using headers, this is how I solved it
var authData = localStorageService.get('authorizationData');
var token = authData.token;
$.signalR.ajaxDefaults.headers = { Authorization: "Bearer " + token };
Hope it helps
I resolved this problem by unprotect the Bearer token from query string in my AuthorizeAttribute, and set the user principal into a new ServerRequest. For detailed information please check http://blog.shaunxu.me/archive/2014/05/27/set-context-user-principal-for-customized-authentication-in-signalr.aspx
This might not be the best solution but it worked.

Resources