2
votes

I'm using angular 8 with oidc-client-js. I'm connected to IdentityServer4 (Code Flow + PKCE). After I open the app (inside main component) I want to check if user is authenticated. That's why I call signinRedirect(). Instead of manually clicking the button I just call it inside the constructor (the whole flow worked when I was just clicking the button to call signinRedirect()). The issue is that I'm stuck inside infinite loop. Angular keeps calling IdenityServer and refreshing login page. The api call to the server (and redirect to login page as a result) works fine but it doesn't stop. Please help.

export class AuthService {
  private userManager: UserManager;
  private user: User;

  constructor(private client: HttpClient) {
    this.userManager = new UserManager(AuthSettings.settings);
    this.userManager.getUser().then(user => {
      this.user = user;
    });
  }

  checkCredentials() {
    if (!this.isUserLoggedIn()) {
      this.redirectToLogin();
    }
  }

  redirectToLogin() {
    return this.userManager.signinRedirect();
  }

  isUserLoggedIn(): boolean {
    return this.user != null && !this.user.expired;
  }
}
export class AppComponent {
  title = "app";

  constructor(private authService: AuthService) {
    this.authService.checkCredentials();
  }
}

The user enter the angular app. Then I call authorize endpoint (signinRedirect, sending server things that are required in code flow) - server checks for the cookie if user is logged in. If not it redirects me to the login page. The issue is that scenario works if I click a button that calls (signinRedirect) but not when I execute it when a component is open. The loops ends with log - Showing login: User is not authenticated. The loop begins with Request starting HTTP/1.1 GET http://localhost:5555/.well-known/openid-configuration. Then - Request starting HTTP/1.1 GET http://localhost:5555/connect/authorize?response_type=code&client_id=ng&state=kYkvO3CO4SW3asopth-dmZW8SYkuyz79Npfn0K4MPAMCT&redirect_uri=http%3A%2F%2Flocalhost%3A4200%2Fcallback&scope=openid%20API&code_challenge=2iGwqANCfZGshjmhDmmwm4Eh4Q8SowgPcImf1-CsDzs&code_challenge_method=S256&nonce=kYkvO3CO4SW3asopth-dmZW8SYkuyz79Npfn0K4MPAMCT. Then it repeats.

1
isn't the oidc client really old and depricated? I think theres a new more angular way to do this - johnny 5
You're probably checking credentials on the login page... Don't do that - johnny 5
based on your comment on my answer. You are mixing 2 concepts. The signing redirect, is NOT redirecting to the login. Your signing redirect handles completing the challenge, and redirecting after signing, additionally your user is being set in a promise, you need to wait for that to complete before checking checkCredentials or else you may be redirecting before the user is set. - johnny 5
Yes I know that. But that method is calling an endpoint and a user might be redirected to login page. I can implement a normal redirect in the other way. I apologize, because the title is confusing. I will change that. - dawzaw
basically what you need to do is 2 things. 1. Create a way to redirect to login, for credential validation. e.g if the user is trying to access a protected resource redirect them to login. 2. Then you need to handle the redirect, configure your redirect url in IdentityServer4 to be another page that allows anonymous. e.g login/access and there you attempt to read the access token infromation from the url using the oidc client, you can even redirect to the login, but you need to attempt to read the access token information first. - johnny 5

1 Answers

2
votes

maybe I'm late to respond. Here are my observations.

this below code in your auth service constructor works asynchronously, there is a very good reason you will not have the user object populated by the time check credentials called in-app component, also fixing this by making the constructor is an antipattern, you really need to have an async and await usage here.

 this.userManager.getUser().then(user => {
      this.user = user;
    });