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.