2

I'm using Angular2 with ASP.NET Core MVC and managing manual URL navigation works fine, the server is loading my Home view with Angular2 successfully.

On user authentication, I'm setting up a session variable like this :

HttpHelper.HttpContext.Session.SetString("isLoggedIn", true.ToString() );

What I want is that after the user is in the application, if somewhat he wants to load a specific route by manually navigating to it, I want my service to call my ASP Controller to check if the user already got authenticated so that my guard allows the routing. Instead, the guard is by default set to false and I obviously get redirected to my login page.

Here is my ASP Controller method I want to call to update my IsLoggedIn value in my auth.service :

[HttpGet]
public IActionResult IsConnectedState()
{
    if (!String.IsNullOrWhiteSpace(HttpHelper.HttpContext.Session.GetString("isLoggedIn")))
        return Ok(true);
    else
        return Ok(false);
}

so that my AuthenticationGuard can call the AuthenticationService to update the boolean managing the authenticated state : EDIT : pasting the whole auth.guard.ts code for clearer explanation

@Injectable()
export class AuthGuard implements CanActivate {
constructor(private authService: AuthService, private router: Router) {
    if (!this.authService.isLoggedIn) {
        this.authService.setupLoggedInState().subscribe(() => {
            alert("guard check : " + this.authService.isLoggedIn);
            });
        }
    }

    canActivate()
    {
        if (this.authService.isLoggedIn) {
            return true;
        }

        this.router.navigate(['/login']);
        return false;
    }
}

with the following code updating the boolean in my auth.service :

setupLoggedInState() {
    alert("Setting Up");

    // Setting up the Http call 
    let lControllerAction: string = "/IsConnectedState";
    let lControllerFullURL: string = this.controllerURL + lControllerAction;
    let headers = new Headers({ 'Content-Type': 'application/json' });
    let options = new RequestOptions({ headers: headers });

    // Call my ASP Controller IsConnectedState() method
    return this.http.get(lControllerFullURL, options)
        .map((res: any) => {
            // Réponse reçue du WebService
            let data = res.json();
            alert(data);
            if (data == true) {
                this.isLoggedIn = true;
            }
        }
        ).catch(this.handleError);
}

The problem comes when I try to manually load a guarded route, I get redirected to the login page before the boolean in my service is updated. I don't know what kind of solution I can setup to manage this problem...

I tried calling

if (!this.authService.isLoggedIn) {
    this.authService.setupLoggedInState().subscribe(() => { });
}
  • in my guard constructor
  • in my service constructor

But it's still not doing it at the right moment. My best guess would be to manage this when I load my first AppModule, but even if this might be the right moment, I have no guarantee my boolean will be updated by the time Angular2 finished bootstrapping, and doing a synchronous Http call seems to be a really bad solution, because it would block the browser... Any idea would be really helpful.

PS : I did a similar post today, but it was for a different problem, so I'll link it here to avoid confusion : Managing user authenticated state on manual URL navigation

Community
  • 1
  • 1
Alex Beugnet
  • 4,003
  • 4
  • 23
  • 40
  • You need to return a boolean value or observable that emits a boolean value from the guard. Where are you doing this? – Günter Zöchbauer Aug 26 '16 at 12:28
  • If I understand correctly what you are asking, I do this in the AuthGuard's cosntructor to call the SetupLoggedInState() method which will do a Http.Get call to my ASP Controller and then update my AuthService boolean value. I will edit the post accordingly – Alex Beugnet Aug 26 '16 at 12:49
  • Or maybe I didn't understand at all what you asked, but updating the boolean value of my service works correctly, it's just that it's done too late since the value is updated after I get redirected to the login page, which is not the correct behavior. When the MVC fallback route loads my view bootstrapping Angular2, I want there to update the service boolean so that my authGuard lets me access the wanted route before the router takes over and tries to load the guarded route. – Alex Beugnet Aug 26 '16 at 13:10

1 Answers1

1

Calling subscribe() doesn't automatically give the value. If this invokes a request to the server, it takes a loooong time until the response arrives. The browser continues to execute your code and when eventually the response from the server arrives, the callback passed to subscribe(...) is called by the observable. You need to make your AuthGuard wait for the observable or actually the router by returning the observable from canActivate() so the router can wait for the observable to emit a response.

@Injectable()
export class AuthGuard implements CanActivate {
constructor(private authService: AuthService, private router: Router) {
  canActivate() {
    return this.authService.checkLoggedIn
    .map(loggedIn => {
      if(loggedIn) {
        return true;
      } else {
        this.router.navigate(['/login']); // not tried myself to navigate here
        return false;
      }
    });
  }
}


@Injectable()
class LoginService {
  constructor(private http:Http) {}

  checkLoggeIn() {
    return.http.get(someUrl).map(val => val.json());
  }
}

See What is the correct way to share the result of an Angular 2 Http network call in RxJs 5? for more details about how to cache results from HTTP requests.

Community
  • 1
  • 1
Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • 1
    I'll look into it, thanks for your answer, I'll try that asap and give a feedback – Alex Beugnet Aug 26 '16 at 15:12
  • Hum, I tried to setup your solution, but I get this error with the map operators I imported correctly... import { Observable } from 'rxjs/Observable'; import 'rxjs/add/operator/map' "Property map does not exist on type Observable The weird thing is that my the .map operator works in my other files... ? – Alex Beugnet Aug 29 '16 at 08:17
  • Sorry, can't tell from this information. The error doesn't seem to be related to my answer but rather to your local project setup. – Günter Zöchbauer Aug 29 '16 at 08:19
  • 1
    This was just because I forgot the () after the method call... return this.authService.checkLoggedIn() .map(loggedIn => { – Alex Beugnet Aug 31 '16 at 12:44
  • Now I see it as well ;-) Glad to hear you figured it out. – Günter Zöchbauer Aug 31 '16 at 12:49
  • Still trying to make it work, but in the middle of it I broke my TypeScript configuration with VS2015 and had to reinstall it and so on... I was going mad T.T – Alex Beugnet Aug 31 '16 at 13:07
  • I was struggling on why I had my service being called twice, but in the end it was just the guard being called twice on the same route, so it was kinda obvious. Anyway it works great, thanks for helping me ! I'm really grateful. – Alex Beugnet Aug 31 '16 at 13:42