isAuthenticated is always false, and CallbackComponent is not called

Hi,
this is my firts time using Okta :smiley:
my problem is 'isAuthenticated ’ is always false after succes login of user . I think that callback service is not called.
the base url of my application is : http://localhost:4200/#/
In okta site, the ‘#’ is not accepted but after login i have this:
http://localhost:4200/callback?code=83NZbmgEQu4sNEagXcqzQxh-g2K75C9-BT5ZP9k_25o&state=wbCkk9N8kot2xn0VFd5fh5GsGFuy4H1y6657KSFO9bioazDF2L2uDNGkv39dh5R0
after that 'isAuthenticated ’ is also false:
Code :
app.component.html

  <a class="item" *ngIf="!isAuthenticated" (click)="oktaAuth.login('/')">Login</a>
 {{isAuthenticated}}
<div *ngIf="isAuthenticated" >
<router-outlet></router-outlet>
</div>

app.component.ts

@Component({
    selector: 'app-root',
    templateUrl: './app.component.html'
}) 
export class AppComponent implements OnInit {

    isAuthenticated: boolean ;
    constructor(public oktaAuth: OktaAuthService,private authService: AuthService,private socleService: SocleService,private accesService:AccessService,private commonService: CommonService,public router: Router) {}
    ngOnInit(): void {
     
     this.oktaAuth.$isAuthenticated.subscribe(val => {
        this.isAuthenticated = val
        console.log('xxx',  this.isAuthenticated );
      });
    }

OktaAuthService.ts


import { BehaviorSubject, Observable, Observer } from 'rxjs';
import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
import { OktaAuth, IDToken, AccessToken, OktaAuthOptions } from '@okta/okta-auth-js';
import store from 'store2';
@Injectable({providedIn: 'root'})
export class OktaAuthService {


  CLIENT_ID = '***********************************************';
  ISSUER = 'https://dev-***********.okta.com/oauth2/default'
  LOGIN_REDIRECT_URI =  'http://localhost:4200/callback';
    config: OktaAuthOptions = {
    issuer:  this.ISSUER,
    clientId: this.CLIENT_ID,
    redirectUri: this.LOGIN_REDIRECT_URI,
    pkce: true,
    storageManager: {
      token: {
        storageTypes: ['localStorage', 'sessionStorage', 'cookie']
      },
      cache: {
        storageTypes: ['localStorage', 'sessionStorage', 'cookie']
      },
      transaction: {
        storageTypes: ['cookie']
      }
    }
  };
  oktaAuth = new OktaAuth(this.config);
  $isAuthenticated: Observable<boolean>;
 
  private observer?: Observer<boolean>;
  constructor(private router: Router) {
    this.$isAuthenticated = new Observable((observer: Observer<boolean>) => {
      this.observer = observer;
      this.isAuthenticated().then(val => {
        observer.next(val);
      });
    });

  
  }
 
  async isAuthenticated() {
    // Checks if there is a current accessToken in the TokenManger.
    return !!(await this.oktaAuth.tokenManager.get('accessToken'));
  }

  login(originalUrl: string) {
    store('okta-config', this.config);
   
    // Save current URL before redirect
    sessionStorage.setItem('okta-app-url', originalUrl || this.router.url);
    console.log('  this.oktaAuth.token',  this.oktaAuth.token);
    // Launches the login redirect.
    
    this.oktaAuth.token.getWithRedirect({
      scopes: ['openid', 'profile', 'email'],
    responseType: ['token', 'id_token'],
    prompt: 'consent login',
    });
  }

  async handleAuthentication() {
    const tokenContainer = await this.oktaAuth.token.parseFromUrl();

    this.oktaAuth.tokenManager.add('idToken', tokenContainer.tokens.idToken as IDToken);
    this.oktaAuth.tokenManager.add('accessToken', tokenContainer.tokens.accessToken as AccessToken);

    if (await this.isAuthenticated()) {
      this.observer?.next(true);
    }

    // Retrieve the saved URL and navigate back
    const url = sessionStorage.getItem('okta-app-url') as string;
    this.router.navigateByUrl(url);
  }

  async logout() {
    await this.oktaAuth.signOut({
      postLogoutRedirectUri: this.LOGOUT_REDIRECT_URI
    });
  }
}

callback.component.ts

@Component({ template: `
{{error}}
`, })
export class CallbackComponent implements OnInit {
  error?: string;
  constructor(private okta: OktaAuthService) {}
  ngOnInit(): void {
    // Handles the response from Okta and parses tokens
    this.okta.handleAuthentication();
  }
}

app.module.ts

const config: OktaAuthOptions = {
    issuer: 'https://dev-************.okta.com',
    clientId: '**********************',
    redirectUri: window.location.origin +'/callback'
  };
  export const authClient = new OktaAuth(config);
@NgModule({
    declarations: [
        AppComponent, NotfoundComponent,
        CallbackComponent,
        ProtectedComponent 
    ],
    imports: [
        AppRoutingModule,
        GeneralModule,
        BrowserModule,
        OktaAuthModule
    ],
    providers: [
        OktaAuthGuard ,
        {
            provide: OKTA_CONFIG,
            useValue: { authClient }
          },
        { provide: LocationStrategy, useClass: HashLocationStrategy },
    ],
    bootstrap: [AppComponent]
})
export class AppModule { }

app-routin-module.ts


@NgModule({
    imports: [
        RouterModule.forRoot([
          {
            path: window.location.origin +'callback',
            component: CallbackComponent
          },
          {
            path: 'protected',
            component: ProtectedComponent,
            canActivate: [OktaAuthGuard]
          },
         ......................
  ],
    exports: [RouterModule]
})
export class AppRoutingModule {
}

Can you help me please , how to correct my code to obtain isAuthenticated =true after succes login and to get token user :frowning:

Ps. Sign-in redirect URIs on okta site is: http://localhost:4200/callback
Thank you

Hi there @sabrinaBinaa89 !

Let’s see if we can get this figured out. :slight_smile:

I have a few questions to help me get acquainted with your project needs:

  1. Are you importing okta-auth-js library only? Or are you also using okta-angular library? The okta-angular library does have an isAuthenticated$ stream you can subscribe to already set up.
  2. What versions of the okta libraries are you using? What version of Angular are you using?
  3. I noticed you have HashLocationStrategy. The OAuth spec doesn’t support hashes, so we have specific instructions for working around this. You’ll want to follow the instructions on okta-auth-js project’s README.

You may have already seen this, but here’s the Angular Quickstart to use as a reference. It does not have HashLocationStrategy enabled on it, though.

Let us know how it goes and we’ll work through the next steps together!

1 Like

Hi,
to answer your questions:
1 & 2. I aleardy imported:
@okta/okta-angular”: “^6.3.2”,
@okta/okta-auth-js”: “^7.5.0”,
I can’t use oktaService with this versions (service not found)…for that i create my own service.
isAuthenticated is extracted from OktaAuth from ‘@okta/okta-auth-js’ ( oktaAuth = new OktaAuth(this.config); with config represent issuer,clientId,…)
** I used angular 17 /node 18.13.0
3. thanks for information about using HashLocationStrategy => in okta site i can’t define a redirect uri with a fragment (#)

Hi @sabrinaBinaa89 ,

Thanks for the clarification!

It’s possible you may not be able to use this in your project, but I will post the info for other devs who run into this thread. The OktaAuthStateService in the @okta/okta-angular library is where you can access the authenticated state observable stream. So the example usage is

import { OktaAuthStateService, OKTA_AUTH } from '@okta/okta-angular';
import { AuthState } from '@okta/okta-auth-js';

@Component({ /* component metadata */ })
export class AppComponent {
  title = 'okta-angular-standalone-example';
  public isAuthenticated$!: Observable<boolean>;
  private oktaStateService = inject(OktaAuthStateService);
  private oktaAuth = inject(OKTA_AUTH);

  public ngOnInit(): void {
    this.isAuthenticated$ = this.oktaStateService.authState$.pipe(
      filter((s: AuthState) => !!s),
      map((s: AuthState) => s.isAuthenticated ?? false)
    );
  }

  public async signIn() : Promise<void> {
    await this.oktaAuth.signInWithRedirect();
  }

  public async signOut(): Promise<void> {
    await this.oktaAuth.signOut();
  }
}

You can see an example I put together using Angular v17, @okta/okta-angular v^6.3.0, and @okta/okta-auth-js v^7.4.3 here (it is standalone and doesn’t use HashLocationStrategy)

Back to your project setup :slight_smile:
Does your authenticated stream emit false after you sign in, or only upon app initialization before signing in, or both? Have you tried isolating your OktaAuthService wrapper in an Angular app not using HashLocationStrategy? @okta/okta-auth-js library requires specific calls such as starting and stopping the service and setting up a callback subscriber. You can see how the @okta/okta-angular handles this by looking at the code for OktaAuthStateService.

As for HashLocationStrategy, I’m not aware of any examples with Angular, so following the okta-auth-js library’s README is the best option.

Let us know how it goes!

Hi!

  1. authenticated stream emit false after sign in, and upon app initialization before signing , so in both .
  2. In my project, it’s required to use HashLocationStrategy. i will try to integrate it in redirection uri
  3. I will try to use AuthState and will inform you whether it works.
    Thank you for your effort to help me :slight_smile:

Hello,

<a class="item" *ngIf="(isAuthenticated$ | async) === false" (click)="signIn()">Login</a>
<div *ngIf="(isAuthenticated$ | async) === true" >
<router-outlet></router-outlet>
</div>

import { Component, inject, OnInit } from '@angular/core';
import { Observable,filter, map } from 'rxjs';
import { Router } from '@angular/router';
import { OktaAuthStateService, OKTA_AUTH } from '@okta/okta-angular';
import { AuthState } from '@okta/okta-auth-js';
@Component({
    selector: 'app-root',
    templateUrl: './app.component.html'
})
export class AppComponent implements OnInit {
  public isAuthenticated$!: Observable<boolean>;
  private oktaAuth = inject(OKTA_AUTH);   
  private oktaStateService = inject(OktaAuthStateService);

  constructor(public router: Router) {}

    ngOnInit() {

      this.isAuthenticated$ = this.oktaStateService.authState$.pipe(
        filter((s: AuthState) => !!s),
        map((s: AuthState) => s.isAuthenticated ?? false)
      );

    }
    public async signIn() : Promise<void> {
      await this.oktaAuth.signInWithRedirect();
    }
  
    public async signOut(): Promise<void> {
      await this.oktaAuth.signOut();
    }
}


const oktaConfig: OktaAuthOptions = {
    issuer: 'https://dev-xxxxxxokta.com',
    clientId: 'xxxxxx',
    redirectUri: window.location.origin + '/callback'
  };

@NgModule({
    declarations: [
        AppComponent, NotfoundComponent
    ],
    imports: [
        AppRoutingModule,
        GeneralModule,
        BrowserModule,
        BrowserAnimationsModule,
        AdministrationModule,
        OktaAuthModule, 
    ],
    providers: [
        { provide: OKTA_CONFIG,useValue: {oktaAuth: new OktaAuth(oktaConfig)}},
        { provide: LocationStrategy, useClass: HashLocationStrategy },
    ],
    bootstrap: [AppComponent]
})
export class AppModule { }

===> I tried to use your soltion , but isAuthenticated is always=false!
can you help me please

Hey there @sabrinaBinaa89,

Sorry to hear you’re still having problems! Thanks for your patience, as I know you’ve been testing things out.

The code you posted here looks correct for projects not using the hash location strategy. I’m wondering if this is why you are not seeing the authenticated state properly.

Based on the instructions in the ReadMe (I’ll link below), it looks like you’ll need to save the original URI and parse the URL in the callback for the tokens. Can you post the code you have handling this? We can take a look together.

I set the login method:

  public async signIn() : Promise<void> { 
      try {
        const originalUri = this.router.url;
        await this.oktaAuth.signInWithRedirect();
        this.oktaAuth.handleRedirect(originalUri);
        await this.oktaAuth.token.parseFromUrl();
        const accessToken = this.oktaAuth.tokenManager.get('accessToken');
        const idToken = this.oktaAuth.tokenManager.get('idToken');
        // localStorage.setItem('accessToken', accessToken);
        // localStorage.setItem('idToken', idToken);
        window.location.href = originalUri;
      } catch (error) {
        console.error('Error during sign-in:', error);
      }
  }

but isAuthenticated still false.
Ps. The use of HashLocationStrategy is obligatory in my project

Hey @sabrinaBinaa89 ,

Apologies, I sent you the correct link, but I quoted the instructions from the section below, which talks about a dedicated route. In the hash routing strategy, you cannot have a dedicated callbackroute per the correct section of the README

When using a hash/fragment routing strategy and OAuth 2.0, the redirect callback will be the main / default route. The redirect callback flow will be very similar to handling the callback without routing. We recommend defining the logic that will parse redirect url at the very beginning of your app, before any other authorization checks.

You can follow the link in this snippet to read more about handling the callback without routing.

Please note, because you are redirecting the user to another URL, any code you define after the signInWithRedirect() won’t run - you’ve navigated away from the application. After the callback, you won’t reach the code within the signIn() method, so you’ll need to add steps that run after the redirect completes elsewhere.

I put together a quick and inelegant sample project demonstrating a working HashLocation strategy sample. It was thrown together quickly so it’s not vetted nor tested.

In my sample, I was able to leave the signIn method code the same and watch for router events in the app.component:

    this.router.events.pipe(
      filter((e: Event): e is RouterEvent => e instanceof RouterEvent && e instanceof NavigationStart),
      filter(() => oktaAuth.isLoginRedirect()),
      switchMap(() => defer(() => oktaAuth.handleLoginRedirect())),
      takeUntil(this.destroySub$)
    ).subscribe(_ => {
      console.log('Login redirect handled');
    });

Please note I do not make any claims about viability of this code for your production needs. It’s an example so you can verify the setup works for you and then you should test it very well for all different circumstances, including token expiration, refreshing, etc etc.

Please read this section, as my sample is tiny and not full-featured, so you’ll want to ensure you have all your bases covered.