ASP.NET Identity - making actions on behalf of someone - c#

I have few BL functions that checking who is invoking a method by reading the data from httpContext.User property. It works great when the user really SignIn to the system.
I would like to allow users to schedule an action to be run on a later time. Once the user asks to send an email later, i am writing the request to the DB and the automation service i developed will execute it on the right time by making an http call like: https://mysystem.lan/SendEmail/?Requestid=122d8f273465.
since the automation process not happens in the client side, the user is not logged in to the system so the action SendEmail will not run because the httpContext.User property will be null.
Thanks to the parameter Requestid in the url, i can resolve the UserId who created the request and i want to mock the ASP.NET Identity to be the identity of that user and that the GetUserId() extension method will work too (since mocking not affects that kind of methods).
All the answers i saw here explaining how to mock the identity by creating an instance of the controller for unit testing but this situation is different because i need to mock the identity inside inside the controller action when the server is getting the request https://mysystem.lan/SendEmail/?Requestid=122d8f273465.
Can you suggest how to do that?

Related

Properly using CAS Authentication with an Angular 11 App and .Net 4.7.2 Web API

In my first exposure to Angular, I have an existing application using an Angular frontend and a .NET Web API for the backend. The pre-existing application was utilizing a login scheme with local accounts stored in the database. I am attempting to modify this app to use our organization's CAS server for authentication, instead.
The work I have done so far was based on the guidance found here: https://www.blinkingcaret.com/2018/10/10/sign-in-with-an-external-login-provider-in-an-angular-application-served-by-asp-net-core/
Thus far, I have made a few key changes. In authentication.service.ts, I have modified the login function to point to my new action on the server
login(){
this.document.location.href = this.casUrl + "login"
}
In my newly added CAS controller, I have the following actions.
public ActionResult Login() {
return new ChallengeResult("CAS");
}
public ActionResult HandleLogin() {
var claimsId = (ClaimsIdentity)User.Identity;
//Do things with claims, check against DB, etc
}
private class ChallengeResult : HttpUnauthorizedResult {
//sets the RedirectUri to HandleLogin, fires the Challenge in the ExecuteResult function
}
Up to this point, things seem to be behaving. The User.Identity populates correctly, so I can retrieve the corresponding user from the database. I am able to construct a LoginResponse object as well, though currently am not doing anything with it. This LoginResponse includes a token generated in another piece of the application, and seems to be a JWT.
At this point, I do not know how to transfer my LoginResponse back to the Angular.
In the original implementation using local accounts, a post was made to an API endpoint, and the response was piped to where it was needed.
return this.http.post(this.apiUrl + 'auth/login',
//parameters
), headers).pipe(
map((user:LoginResponse) => {
//do stuff with LoginResponse
}));
In turn, the component that called this service is subscribed to its return value.
My thought was to have a new function called within the init of a component. The first action it would take would be to go to my CasUrl endpoint and request the LoginResponse constructed using the User.Identity and DB lookup. Unfortunately, the Identity is empty upon all subsequent calls to the server. It does not seem to persist between requests.
One more note that may be relevant: My Web API solution is running through Visual Studio on localhost:46000, whereas the Angular application is running in VSCode on localhost:4200.
What is it I am missing to get external authentication working with this app? Is it possible to have an angular pipe or subscribe wait for results from an external website?
If I understand it correctly you do a full redirect to your CAS-server with location.href which does (after successful authentification) a redirect to your route /login.
So /login should response with the full angular site or redirect to the site with HttpStatus 302. The session cookie generated by the server can then be used for further requests to the backend.
The AccountService.ts can check if the user isAuthenticated by calling the api api/home/isAuthenticated This request should automatically append the newly gathered session cookie.
see example code of the blog you mentioned:
https://github.com/ruidfigueiredo/angular-aspnetcore-external-login/blob/master/AngularWithGoogleLogin/src/app/account.service.ts#:~:text=%20updateuserauthenticationstatus
and its counter piece:
https://github.com/ruidfigueiredo/angular-aspnetcore-external-login/blob/master/GoogleSpaWeb/Controllers/HomeController.cs#:~:text=public%20IActionResult-,IsAuthenticated,-()
Stef's answer is essentially what was required. I found that the session cookie was properly being created and stored in the browser, though I was used to the backend automatically maintaining session state.
I added a call to the API to verify login status, and as stated in the question, the User.Identity value was empty.
What I chose to do was create the LoginResponse object and assign its value to a cookie. Then, on the component's init function, I would check the cookie rather than calling to the API.

HandleAuthenticateAsync called even without [Authorize] attribute

I'm using a custom AuthenticationHandler and only some of my controller methods have the [Authorize] attribute. I log on Info level and it creates a log entry for any method call. Now I can't tell apart if somebody actually tried accessing a method that requires authorization and failed or if it's a call to a method that doesn't even need authorization and it fails because it's supposed to.
Is there a way to tell them apart or preferably to keep MVC from calling HandleAuthenticateAsync when it's not needed?
It sounds like you might be misusing the AuthenticationHandler. You are not supposed to immediately reject access if the user fails to authenticate. If you do so, it doesn't even reach the MVC context in the pipeline.
Unless you intentionally want to reject any access to unauthenticated users, you should only either authenticate or pass it through as anonymous user. After that at some point AuthorizeAttribute will kick in and check whether user has access to the requested resource or not. If he is not authenticated, the authorization will reject the request.

How in ASP.NET MVC do I maintain info about whether a user is "logged in" throughout pages?

After they type in their password, should I keep a variable in server session storage like Session["loggedIn"] = true that I check when requests are made to pages that require a login, or is there a better/safer way this is traditionally done in ASP.NET?
No, do not keep this in a session variable. In fact, I would argue you should try not to use session for anything. Try to keep the HTTP communication as stateless as possible.
For authentication in ASP.NET MVC, you have 2 alternatives, but ultimately, the both end up working the same way: by writing an encrypted authentication cookie to the browser after you successfully log a user in.
The newer alternative was mentioned in a comment to the OP: Microsoft.AspNet.Identity. To use this, you should be using a newer version of ASP.NET MVC with the OWIN pipeline (though I am not sure if you have to). The older alternative is called Forms Authentication, and can be used with any version of MVC except version 6 (the new vNext / core 1.0 stuff) I believe.
When you have successfully integrated one of these 2 tools into your MVC application, after a user logs on, your Controllers will have a non-null User property of type IPrincipal. You can use this property to determine whether or not a user is authenticated:
// in a controller
bool isThereAUserLoggedIn = this.User != null && this.User.Identity.IsAuthenticated;
If you are using the newer Microsoft.AspNet.Identity, then this IPrincipal User property will be implemented as a ClaimsPrincipal with one or more ClaimsIdentitys. Forms Authentication does not do claims like this, so if you want claims, or social login, etc, use Microsoft.AspNet.Identity.
The way that this is traditionally done in ASP.NET and by my opinion also better and safer is by making use of the ASP.NET Identity package.
ASP.NET Identity handles all aspects around user accounts in a web application:
database for users, including roles and more
user registration and management, like register, email verification, log in, remember me option, forgot my password action and more.
user authentication & authorization
Just to make things more clear, authentication means that the user making the request is actually a valid application user and authorization means that the user has the authority to perform the requested action.
Practically, when a user logs in, Identity automatically keeps that information and makes it available in all controllers and views under User property. So you know at any time which user made the request. Identity also supplies each request with a cookie used for user authentication and authorization.
To check for user authentication you use the User.Identity.IsAuthenticated in a view and the Authorize attribute in a controller:
[Authorize]
public ActionResult Create( ... ){ ... }
The above use of the Authorize attribute will allow only to registered users to request this page.
It is also very common to extend the functionality of your application to include roles for the users and user authorization. Identity creates a "Users" table, a "Roles" table and a many to many relationship between them. After assigning roles to your users you can authorize their requests by using User.Identity.IsInRole("YourRoleName") in a view and in a controller:
[Authorize("YourRoleName")]
public ActionResult Create( ... ){ ... }
The above use of the Authorize attribute will allow only to registered users having the "YourRoleName" role to request this page. In any case if Identity fails to authenticate or authorize the request will prompt to the log in page.
ASP.NET Identity is simple to use, it works and it is easy to extend the membership functionality of your application both by making use the many tools supplied with it and overriding its classes to give them a more specific or complex behaviour.
You will find infinite help on the web on how to use it or a step by step guide.

WCF custom auth using session id

I need to create a custom authentication/authorization on an existing WCF service, which should be per-method: some methods should force user to log in, and other should allow anonymous usage. The service is implemented as singleton.
To do so, I want to do the following:
enable sessions with default behaviour, so each of the existing service methods will be initiating session in case it doesn't exist;
add login(userName, password) method, which will keep a successful login artefact in a wrapper around Dictionary<>, with current session id used as a key;
add logout() method which closes the session and removes the artefact from the dictionary;
add sessions monitor which will use Dictionary<SessionId, WeakReference<OperationContext.Current>> to drop sessions, for which the logout() method was not called. The monitor will be executed each time a new session is added.
There are two questions though:
Is there a simpler method to achieve per-method auth?
Are there any problems with this approach?
I haved a similar problem a few days ago and I found a solution.
You can check if you want;
WCF service and adding custom Identity claims
For custom authorization you should use Custom Authorization Manager. You should manage sessions with ASP.net which you can have access if you enable AspNet Compatibility.

C# Authentication - setting UserRole as global variable?

Our dba has an SPROC for authentication that returns the User Role only if successful validation occurs.
In my custom authentication class, the ValidateUser method hits this SPROC via an Entity Model & Domain Service, and since the SPROC returns the RoleName on successful validation, I already have an instance of it.
When it comes time to run the GetRolesForUser method in my custom RoleProvider, I suppose I could write another SPROC to grab the RoleName again, but that seems a bit redundant, since I've already retrieved the role. I'd love to be able to cache the User Role in my ValidateUser method, access it in my GetRolesForUser method, and ride off into the sunset. Not only would this save time writing an SPROC, but it would also limit the number of db calls the app is making.
Thoughts?
Scott
Thats correct. When you are running your custom RoleProvider, you already have the session state which you can use to store the value of "RoleName".. Alternately, you can extend the MembershipUser call to include a string field for "RoleName" which you can populate in the first call and then assign back to the context.user.
I would not suggest Cache object here becuase the Roles would be tied to a user.

Categories

Resources