I have a http module which redirects to a website when a user is not authorised. This website then checks for credentials and returns the user to the original page based on a query string.
The problem I have is that Request.Url.AbsoluteUri seems to omit default.aspx whenever a root directory is requested, e.g. http://example/application/
This behaviour can be observed using the test case below. When using Response.Redirect within Application_AuthenticateRequest
Please note, the VS web development server Cassini behaves normally and will correctly redirect to http://example/application/?url=http://example/application/default.aspx I presume this is related to IIS processing the request differently. (I'm running IIS6)
namespace HI.Test {
public class Authentication : IHttpModule {
private HttpRequest Request { get { return HttpContext.Current.Request; } }
private HttpResponse Response { get { return HttpContext.Current.Response; } }
private Cache Cache { get { return HttpContext.Current.Cache; } }
public void Init(HttpApplication application) {
application.AuthenticateRequest += (new EventHandler(Application_AuthenticateRequest));
}
private void Application_AuthenticateRequest(Object source, EventArgs e) {
if (Request.QueryString["url"] == null) {
Cache.Insert("URLRedirected", Request.Url.AbsoluteUri);
Response.Redirect(Request.Url.AbsoluteUri + "?url=" + Request.Url.AbsoluteUri);
}
}
public void Dispose() {
}
}
}
I'm obviously looking for a fix to the problem and also I'd like to understand why this happens.
Related
I'm working on a project to demonstrate Authorization code flow. Therefore I don't want to use any library that handles the authentication for me, but I want to make the whole process myself.
I created an Blazor Server app (SignalR).
On index page there is a single "Connect" button, that starts the whole authentication process and it is as follows.
Index.razor
//on button click
protected async Task ConnectClick()
{
await ConnectService.Connect();
}
ConnectService.cs
public void CreateSession()
{
if(!_httpContextAccessor.HttpContext.Request.Cookies.TryGetValue("userId", out string userId))
{
_httpContextAccessor.HttpContext.Response.Cookies.Append("userId", Guid.NewGuid().ToString());
}
}
public async Task Connect()
{
CreateSession();
//Generate random string as "state" parameter for ACF
var state = Guid.NewGuid().ToString();
var authorizeArgs = new Dictionary<string, string>
{
{"client_id", ...},
{"scope", ...},
{"redirect_uri", ".../Auth/ConnectCallback"},
{"response_type", "code"},
{"state", state}
};
//Save state to cookie to verify in later step
_httpContextAccessor.HttpContext.Response.Cookies.Append("state", state);
var url = ... //prepare url, not important
_navigationManager.NavigateTo(url);
}
public async Task ConnectCallback(string code, string state)
{
//Verify state
if(!_httpContextAccessor.HttpContext.Request.Cookies.TryGetValue("state", out string stateValue) || stateValue != state)
{
throw new AuthenticationException();
}
... //rest of authentication steps
_httpContextAccessor.HttpContext.Request.Cookies.TryGetValue("userId", out string userId);
_memoryCache.Set(userId, access_token);
_navigationManager.NavigateTo("/mypage");
}
ConnectCallback.razor
#page "/Auth/ConnectCallback"
...
#code {
protected override async Task OnInitializedAsync()
{
await AuthService.ConnectCallback(HttpContextAccessor.HttpContext.Request.Query["code"][0], HttpContextAccessor.HttpContext.Request.Query["state"][0]);
}
I know that a library would handle this in much more cleaner way, but the goal is to show the flow in a small demo app.
This is the latest state. I don't know if it is better to save the access token directly in the browser, but for now I keep it in the memory paired with userId.
What happens in this case is whenever I try to append a cookie I will receive:
System.InvalidOperationException: Headers are read-only, response has already started.
Now, I understand I'm doing something wrong. Does anyone know what would be the proper way to this, or what am I doing wrong here? I don't seem to find any solution to this anywhere.
You can create a CookieController that you will use for cookie management and redirect to it.
IsEssential indicates that the cookie is necessary for the website to function correctly.
[Route("[controller]")]
[ApiController]
public class CookieController : ControllerBase
{
[HttpGet("SetStateCookie")]
public async Task<ActionResult> SetStateCookie()
{
CookieOptions opt = new CookieOptions
{
IsEssential = true
};
Response.Cookies.Append("state", $"{Guid.NewGuid()}", opt);
return Redirect("/");
}
}
I have created a one custom handler for redirect to new files in asp.net application. Page redirection was working fine but post method(Ajax) redirection not hitting that method. I attached my code below. Please help me to fix this issue.
In web.config:
<add verb="GET,POST" path="*.aspx" name="CustomHandler" type="CustomHandler"/>
In customhandler.cs page :
public void ProcessRequest(HttpContext context)
{
string method = context.Request.HttpMethod;
var url = new UriBuilder(HttpContext.Current.Request.Url.AbsoluteUri);
if (url.ToString() == "https://Test.Test.com:5689/login/login.aspx")
{
context.Response.Redirect("https://Test.Test.com:5689/New/login.aspx");
}
else
{
var pageInstance = PageParser.GetCompiledPageInstance(context.Request.AppRelativeCurrentExecutionFilePath,
context.Request.PhysicalApplicationPath + context.Request.Path,
context);
pageInstance.ProcessRequest(context);
}
}
Get method(if condition) is working fine problem in else condition.
ajax url like :
https://test.test.com:5689/New/Login.aspx/CheckisTrue
webservice method in login.aspx page:
[System.Web.Services.WebMethod]
public static bool CheckisTrue(string value)
{
return true;
}
I am trying to upgrade some parts of an old project, and ran across the issue of calling the webapi from the aspx.cs.
When i call the webapi from the htmlpage/angularjs, the request is authenticated and everything works fine.
When i try to call it from the aspx page, even though this.User is Authenticated(on the aspx page), when the call is recieved in the controller, the user is not set and therefor not authenticated.
We are using owin cookie based authentication.
On framework 4.7.
Is there any way to authenticate the webapi call, by include the RVT cookie or something?
the call:
protected void Page_Load(object sender, EventArgs e)
{
var accessList = GetAccessList();
}
private List<ClientAccessEntityWithStatisticsDto> GetAccessList()
{
string baseUrl = Request.Url.GetLeftPart(UriPartial.Authority);
ApiHelper.InitializeClient(baseUrl);
string apiUrl = ApiHelper.ApiClient.BaseAddress + "/client/api/ClientAccess/ClientsUserCanAccessWithStatistics/";
var task = Task.Run(() => ApiHelper.ApiClient.GetAsync(apiUrl));
task.Wait();
return task.Result.Content.ReadAsAsync<List<ClientAccessEntityWithStatisticsDto>>().Result;
}
}
The helper:
public class ApiHelper
{
public static HttpClient ApiClient { get; set; }
public static void InitializeClient(string baseUrl)
{
ApiClient = new HttpClient()
{
BaseAddress = new Uri(baseUrl)
};
ApiClient.DefaultRequestHeaders.Accept.Clear();
ApiClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}
}
Anyone coming across this.
Just parse all the request cookies to the api call, and it works like a charm.
I am writing an website where I get some data from the database. When starting the website on my computer I get the data for 15 min. After these 15 min the files don't load anymore.
When I restart the backend (Visual Studio C#) then it happens the same.
Controller from the file:
[UnitOfWorkActionFilter]
[RoutePrefix("categories")]
public class CategoriesController : ApiController {
private ICategoriesProcessor _categoriesProcessor;
private IPagedDataRequestFactory _pagedDataRequestFactory;
public CategoriesController(ICategoriesProcessor categoriesProcessor, IPagedDataRequestFactory pagedDataRequestFactory) {
_pagedDataRequestFactory = pagedDataRequestFactory;
_categoriesProcessor = categoriesProcessor;
}
[Route()]
[HttpGet]
public PagedResponse<Category> GetCategories(HttpRequestMessage requestMessage) {
var request = _pagedDataRequestFactory.Create(requestMessage.RequestUri);
return _categoriesProcessor.GetCategories(request);
}
}
here is the code from the UnitWorkActionFilterAttribute
public class UnitOfWorkActionFilterAttribute : ActionFilterAttribute {
public virtual IActionTransactionHelper ActionTransactionHelper { get { return WebContainerManager.Get<IActionTransactionHelper>(); } }
public override bool AllowMultiple { get { return false; } }
public override void OnActionExecuting(HttpActionContext actionContext) {
ActionTransactionHelper.BeginTransaction();
}
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext) {
ActionTransactionHelper.EndTransaction(actionExecutedContext);
ActionTransactionHelper.CloseSession();
}
}
I found out that the problem is, that the Session opens but not close but I don't know how to fix it.
Does someone has an idea why it's not working?
have you try access from Fiddler ??? what the client you used to call your API...
see what the fiddler got message, and if you call the API, that is call that Method API or not...need detail information, this error have come to method or just in client stuff...
I'm developing an ASP.net WebAPI application with OAUTH 2.0 authentication with separated STS (token service) and custom JSON formatter (ServiceStack.Text).
I'm trying to customize the access denied object/message to make it homogeneous with the rest of error messages but i haven't found a way to change it.
I'm also thinking that in this case is used the default formatter.
Example:
{
"Message": "Authorization has been denied for this request."
}
Example result:
{
"message": "... insert error message here ...",
"details": null
}
Thanks in advance.
You can return a custom response for the current HttpActionContext using a class for which you can define its members.
public override void OnActionExecuting(HttpActionContext actionContext)
{
bool isAuthenticated = IsAuthenticated(actionContext);
if (!isAuthenticated)
{
actionContext.Response = actionExecutedContext.Request.CreateResponse<CustomActionResult>(HttpStatusCode.Unauthorized, new CustomActionResult
{
Message = "... insert error message here ...",
Details = null
});
}
}
}
public class CustomActionResult
{
public string Message { get; set; }
public string Details { get; set; }
}
To change all 'Access Denied' messages for your entire ASP.NET web site, you can create a HttpModule for all not authorized status returned from your site. In the HttpModule you can handle the EndRequest event and you can check the response.StatusCode if it is 401 you can change the message to anything you want. Like so:
public class AuthorizeMsgModule : IHttpModule
{
public void Init(HttpApplication context)
{
context.EndRequest += OnApplicationEndRequest;
}
// If the request was unauthorized, modify the response sent to the user
private static void OnApplicationEndRequest(object sender, EventArgs e)
{
var response = HttpContext.Current.Response;
if (response.StatusCode == 401)
{
response.ClearContent();
response.Write("{\"message\": \"... insert error message here ...\",\"details\": null}");
}
}
public void Dispose()
{
}
}
Register your module in your web.config for example:
<modules runAllManagedModulesForAllRequests="true">
<add name="AuthorizeMsgModule" type="mynamespace.AuthorizeMsgModule, myassembly" />
</modules>
This will give you the content you write any time you return a 401 status. As you can see here in fiddler.