Base solution for your next web application

Activities of "hra"

I am testing authentication failure to ensure our smart client (a mobile application) is resilient.

During this testing, I will obtain an authentication token from one environment, and then use that token against another environment (yes, a crazy use-case, but this is just a byproduct of trying to debug a collateral issue).

The token is successfully decoded by the different environment, however, the user id does not exist on that environment, so it throws an exception with the relevant information. I found that, instead of returning useful information, such as "invalid refresh token", the server just returns an unhelpful HTTP 500.

I dug a little deeper, an I found that any time the server receives an invalid token, it will 500 instead of giving an appropriate response.

The reason for this is:

https://github.com/aspnetzero/aspnet-zero-core/blob/c3941a4248476419e91071448956468e7e985f16/aspnet-core/src/MyCompanyName.AbpZeroTemplate.Web.Core/Controllers/TokenAuthController.cs#L249 When an invalid token is received, either because the token payload is invalid (line 256) or because the user id within the payload is not found (264) the method throws an ValidationError. (note: line 268 checks user for null, but this code never executes because UserManager.GetUserAsync explicitly throws an Exception when no user is found - hence line 268 is redundant (this in itself is a bug)

The AbpExceptionFilter then wraps this exception, and because the type "ValidationException" does not match any of the mappable types in the abp DefaultErrorInfoConverter, the error is simply mapped to HTTP 500.

So, there are 2 bugs here. The following line is redundant because GetUserAsync cannot return null: https://github.com/aspnetzero/aspnet-zero-core/blob/c3941a4248476419e91071448956468e7e985f16/aspnet-core/src/MyCompanyName.AbpZeroTemplate.Web.Core/Controllers/TokenAuthController.cs#L268

And "ValidationException" is probably the wrong exception to throw - I think it should be something like AbpAuthorizationException - allowing the client to explain to the user, or at least figure out that token refresh is not going to work, and tell the user that they need to provide their credentials again - as opposed to retrying the same operation.

Of course, I dont know how this change will affect the rest of the application - so it would need to be assessed.

I have a similar issue to here: https://github.com/aspnetboilerplate/aspnetboilerplate/issues/5164

However, I need to return IActionResult, because my method will return a FileStreamResult in the case of success, but ObjectResults for detailed errors:

    var result = new ObjectResult(new AjaxResponse(
        ErrorInfoBuilder.BuildForException(vex),
        false
    ))
{
    StatusCode = (int)HttpStatusCode.BadRequest
};
return result;

When the customers mobile application makes a request with an expired token, they get a 302 instead of a 401 - not what the app is expecting.

How do I resolve this?

UPDATE: I have noticed that this is happening on the Azure hosted application, but when running on local machine, it correctly produces a 302. Why does it behave differently in Azure?

**Note: ** Yes I am setting the ajax-request header

      options.headers = {
        ...options.headers, 
        'Authorization': 'Bearer $_token',
        'X-Requested-With': 'XMLHttpRequest'};
        

Another note: If I call methods in the Application services layer, instead of my custom Controllers, then the server produces the correct 401. It's just this 1 method in a Controller which will not return 401 :(

Another Update I have found the cause of the difference between Azure and running locally... The default ANZ code for turning on redirects... of course, I dont want to just go blindly removing ANZ default template code...

if (env.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}
else
{
    app.UseStatusCodePagesWithRedirects("~/Error?statusCode={0}");
    app.UseExceptionHandler("/Error");
}

I've also remote debugged app in Azure and stepped into CookieAuthenticationEvents.IsAjaxRequest - and it definitely returns True

public Func<RedirectContext<CookieAuthenticationOptions>, Task> OnRedirectToLogin { get; set; } = context =>
{
    if (IsAjaxRequest(context.Request))
    {
        context.Response.Headers[HeaderNames.Location] = context.RedirectUri;
        context.Response.StatusCode = 401;
    }
    else
    {
        context.Response.Redirect(context.RedirectUri);
    }
    return Task.CompletedTask;
};

Tracking this through to the StatusCodePagesMiddleware, I can see that the response code is 401 (as I want), but then the StatusCodePagesMiddleware overwrites it, returning the 302.

I also debugged an ajax call to a method that resides in the application service layer (not a pure controller) and that looks like during the authentication, the response gets closed early, before the StatusCodePagesMiddleware has a chance to execute (i.e, the client has already received the 401 before the server middleware finishes evaluating, hence the "bad" behavior of the StatusCodePagesMiddleware doesnt matter - it's too late to mess things up).

I've simplified my code all the way back to a very basic method in the controller, declared to return a POCO instead of IActionResult - and it still returns 302 instead of 401. It looks like controllers just have this problem - only the application services correctly return 401 when an auth error occurs. Where do I go from here?

Hi,

Probably my biggest loss of productivity is when I am writing new angular HTML template code, and I accidentally save some malformed HTML, or syntactically incorrect template binding code in the html. The angular hot reload attempts to compile, and fails (yes it does identify the bad code), bit it also dumps a lot of "No pipe found with name 'localize'" errors.

The problem, is that even if I fix my incorrect syntax, the hot reload is now completely broken, the "No pipe found with name 'localize'" errors remain, I have to kill my angular server, and re-run it - meaning a full recompile which wastes a couple of minutes.

As you can imagine, this is quite a productivity killer.

This has always happened in my AspNetZero code base, so I dont believe I introduced anything to cause this brittle behavior.

Is this just me? Can this be made more resilient?

Thanks,

We have customers who have existing user accounts, and they have now decided to enable Microsoft Authentication on their tenant.

What is the recommended procedure for converting their user accounts to be associated with an Azure A/D login with the same email address?

Note: If the user clicks "sign in with Microsoft", they receive an error similar to "cannot create user account, email already exists". This makes sense - but is clearly not desirable.

Thanks,

Hi there,

I'm using AspNetZero core 10 with angular.

I wish to add ip restricted authentication to the platform. I have tracked the necessary code location to the LogInManager.LoginAsyncInternal overloads. These methods allow me to capture the identify, then lookup the IP whitelist for that tenant, before making a decision as to whether login should continue or not.

However, I can see a hurdle to extending this functionality is the AbpLoginResultType enum. There is no generic failure mode (i.e) FailedForOtherReason - and I really do not want to return an 'invalid password' failure - I want the user to know that the service is not available from their IP.

What's the most suitable way to go about this which doesn't involve forking entire chunks of AspNetBoilerplate?

Thanks,

Hi,

I have scanned the documentation, but dont see an indepth explanation.

I am curious what the purpose of the "User" role is - given that it has no permissions out of the box? Is it purely demonstrative?

Thank you

We are working on a suite of mobile applications, and a (potentially) 3rd party accessible API.

The current error mechanism in AspNetZero, is fine - but a little opaque. Making client-side decisions based on the error information provided is not reliable - for several reasons.

  1. returned errors are often only differentiated by their message, which is fine for providing an explanation to the user, but no good for making application flow decisions. e.g, AbpAuthorizationException can be returned for several reasons - tenant disabled, user account inactive, incorrect username/password, etc etc. I don't want to be parsing localized error messages in a client, to make application flow decisions.
  2. HTTP result codes are course in their granularity, again making it impossible to handle specific scenarios to improve user experience

the ErrorInfo object returned in the AjaxResponse does have a "code" - but I have never seen it used/populated by ANZ. I can see that I only need to throw an exception that implements IHasErrorCode, to populate the "code" field - such as with "UserFriendlyException".

To my questions:

  1. I figured all the above out myself, because there is no documentation under AspNetZero or AspNet Boilerplate. The "abp.io" product does document, what I can only assume is something similar, however, as ANZ/Boilerplate customers, we generally avoid ABP.io documentation as the product is different, and it can lead us astray.
  2. Can we see the ANZ/ABP products better adopt this "code" field? I would like to see a const list of known exception cases and codes in the application server code - which would enable client applications to move to toward a functional error-handling practice, and avoid missing exception scenarios.

Of course - ANZ getting ABP.IO business-exception subsystem would be fantastic... https://docs.abp.io/en/abp/latest/Exception-Handling

Thanks,

We have enabled "Microsoft" authentication, in our multi-tenant AspNetZero application to allow our customers to create accounts, and sign-in using their Microsoft organisational account.

From what I can tell, the default implementation will allow anyone from any Microsoft organisational to sign up.

Our customers will want to be able to "restrict" sign-ups to users from specific organisations.

For example, our customer "Acme" has also purchased business services from Microsoft under the organisation "Acme". They want to allow their users to sign up to our site, under the "Acme" tenant, using ONLY their "Acme" Microsoft credential.

  1. Is there a mechanism in ANZ to allow for this today?
  2. If not, is there a reason? Perhaps what I am wanting to do is achieve through other means?
  3. If I need to roll this myself in ANZ - what's the high-level guidance?

Reading this: https://learn.microsoft.com/en-us/azure/active-directory/develop/id-tokens, apparently the "iss" component of the claims, identifies "the Azure AD tenant for which the user was authenticated". It also says "Your app should use the GUID portion of the claim to restrict the set of tenants that can sign in to the app, if applicable." - so I guess that's what we should be restricting.

Thanks!

ANZ v10, AspNetCore + Angular

We are building in Azure Pipelines, using the following command

gulp build && ng build --prod

Our build has been stable for a couple of years. The last successful build was on October 17, then I tried to build today and build failed.

My suspicion is that a dependency been published, and our template uses it - and is causing the build error.

I have proven this is not due to a code change, by queuing a build for a previously successful branch - it now fails with the exact same error.

Here are my logs build logs:

...trimmed for brevity...

2022-11-02T09:08:50.7712455Z Compiling primeng : es2015 as esm2015
2022-11-02T09:11:32.6360136Z 
2022-11-02T09:11:32.6362877Z ERROR in node_modules/@types/lodash/common/object.d.ts:1025:21 - error TS1110: Type expected.
2022-11-02T09:11:32.6363522Z 
2022-11-02T09:11:32.6364013Z 1025         : K extends `${number}`
2022-11-02T09:11:32.6364899Z                          ~~~
2022-11-02T09:11:32.6365743Z node_modules/@types/lodash/common/object.d.ts:1026:19 - error TS1005: ':' expected.
2022-11-02T09:11:32.6366246Z 
2022-11-02T09:11:32.6366974Z 1026             ? '0' extends keyof T
2022-11-02T09:11:32.6367513Z                        ~~~~~~~
2022-11-02T09:11:32.6368336Z node_modules/@types/lodash/common/object.d.ts:1026:33 - error TS1005: ';' expected.
2022-11-02T09:11:32.6368830Z 
2022-11-02T09:11:32.6369674Z 1026             ? '0' extends keyof T
2022-11-02T09:11:32.6370392Z                                      ~
2022-11-02T09:11:32.6371194Z node_modules/@types/lodash/common/object.d.ts:1028:22 - error TS1005: ';' expected.
2022-11-02T09:11:32.6371895Z 
2022-11-02T09:11:32.6372364Z 1028             : number extends keyof T
2022-11-02T09:11:32.6373028Z                           ~~~~~~~
2022-11-02T09:11:32.6374281Z node_modules/@types/lodash/common/object.d.ts:1028:36 - error TS1005: ';' expected.
2022-11-02T09:11:32.6374800Z 
2022-11-02T09:11:32.6375325Z 1028             : number extends keyof T
2022-11-02T09:11:32.6376348Z                                         ~
2022-11-02T09:11:32.6377703Z node_modules/@types/lodash/common/object.d.ts:1031:13 - error TS1128: Declaration or statement expected.
2022-11-02T09:11:32.6379458Z 
2022-11-02T09:11:32.6380388Z 1031             : undefined;
2022-11-02T09:11:32.6381201Z                  ~

...trimmed for brevity... (just more similar errors as above)...

2022-11-02T09:11:32.6567947Z      
2022-11-02T09:11:32.6568021Z 
2022-11-02T09:11:32.7143043Z npm ERR! code ELIFECYCLE
2022-11-02T09:11:32.7143478Z npm ERR! errno 1
2022-11-02T09:11:32.7185919Z npm ERR! [email protected] publish: `gulp build && ng build --prod`
2022-11-02T09:11:32.7188823Z npm ERR! Exit status 1
2022-11-02T09:11:32.7189483Z npm ERR! 
2022-11-02T09:11:32.7190590Z npm ERR! Failed at the [email protected] publish script.
2022-11-02T09:11:32.7191504Z npm ERR! This is probably not a problem with npm. There is likely additional logging output above.
2022-11-02T09:11:32.7273029Z 
2022-11-02T09:11:32.7273658Z npm ERR! A complete log of this run can be found in:
2022-11-02T09:11:32.7275074Z npm ERR!     /home/vsts/.npm/_logs/2022-11-02T09_11_32_719Z-debug.log
2022-11-02T09:11:32.7354536Z cp: cannot create regular file './dist/': Not a directory
2022-11-02T09:11:32.7453705Z ##[error]Bash exited with code '1'.
2022-11-02T09:11:32.7523473Z ##[section]Finishing: npm install and build

I'm on v10, AspNetCore + Angular.

I have noticed that an account is put into "password reset" state immediately upon submitting the "forgot password" form.

This does not seem like a very user friendy flow - as once in this state, the user cannot get into their account until they have satisfied the "reset password" form (i.e, set a new password).

Given that anybody can access the "forgot password" page - it allows anonymous people to mess with anyone's account, by forcing them to change their password. This could be pivoted into a denial of service, or used in combination with social engineering to maliciously access protected systems.

Here is the specific scenario.

  1. Anonymous users fills out "forgot password" form, for any valid user account
  2. The victim user attempts to log into their account, and is forced to the "set a new password" page

Suggestion: Just like most websites I have ever used - if I successfully log in, even when a valid password-reset request is underway, the password reset is canceled, and the user is allowed into the account.

Note: This should obvously not allow bypassing of forced password resets, i.e, when a breach of an account password has been suspected. It's a password-reset use-case specific to the 'forgotten password' scenario.

Showing 1 to 10 of 27 entries