OktaAuthModule Not Resolving Correctly When Using Factory for Config (okta-angular and okta-auth.js)

Hi,

The OktaAuthModule is not correctly resolving when using it in the context of a DI design pattern:

Context:
@okta/okta-angular: 5.1.1
@okta/okta-auth-js: 6.0.0
Angular: 11.2.15

Error:
TypeError: Cannot read properties of undefined (reading ‘_oktaUserAgent’)

Implementation:

app.module.ts

import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { OKTA_CONFIG, OktaAuthModule }  from '@okta/okta-angular';
import { OktaAuth } from '@okta/okta-auth-js';

// Service that contains the configuration
import { OktaConfigService } from './services/okta-config/okta-config.service';

export function OktaConfigFactory(oktaconfigService: OktaConfigService) {
  const oktaConfig = oktaconfigService.GetOktaConfig();
  const oktaAuth = new OktaAuth(oktaConfig);
  return oktaAuth;
}

@NgModule({
  declarations: [ AppComponent, HeaderComponent ],
  bootstrap: [ AppComponent ],
  imports: [ OktaAuthModule ],
  providers: [
    OktaConfigService,
    {
		provide: OKTA_CONFIG,
		useFactory: OktaConfigFactory,
		deps:[OktaConfigService],
		multi: false
	},
  ]  
})
export class AppModule { }

okta-config-service.ts

import { Injectable } from '@angular/core';
import { appConfig, oktaTenantConfig} from 'src/app/app.config';

@Injectable({
  providedIn: 'root'
})

export class OktaConfigService {

  constructor() {
  }

  public host = window.location.hostname.toLowerCase();

  public GetOktaConfig() {    
    
    let oktaClaimsProvider = oktaTenantConfig.lower;

    switch (this.host) {
      case appConfig.hostName.mo:
        oktaClaimsProvider = oktaTenantConfig.mo;
        break;

      case appConfig.hostName.prod:
        oktaClaimsProvider = oktaTenantConfig.prod;
        break;
    }
    
    return {
      issuer: oktaClaimsProvider.issuer,
      redirectUri: window.location.origin + '/login/callback',
      clientId: oktaClaimsProvider.clientId,
      pkce: true,
      scopes: ['openid', 'profile'],
      tokenManager: {
        storage: 'sessionStorage',
        autoRenew: true
      }
    }
  }
}

The factory method should return {oktaAuth: new OktaAuth(oktaconfigService.getOktaConfig()} and not a direct instance of OktaAuth.

Try updating your factory method to

export function OktaConfigFactory(oktaconfigService: OktaConfigService) {
  const oktaConfig = oktaconfigService.GetOktaConfig();
  const oktaAuth = new OktaAuth(oktaConfig);
  return {oktaAuth};
}

Ok, I appreciate the quick reply, but now that I’ve done that, there’s a new error:

“ERROR AuthSdkError: No issuer passed to constructor. Required usage: new OktaAuth({issuer:”

Checking this in GIT:

I believe I’m passing the config object correctly. Testing it yields the issuer like so:

console.log(config.issuer);

And it works just fine. Looks like that’s how you all are detecting the error. What am I missing here?

Hey there.

I just tried copying the code you posted here, defined some of the values/filled in, and the app loads for me. Here’s what I have in the service so you can compare. Oh, and just as a gut check, did you console.log(oktaConfig.issuer) in the factory method?

import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class OktaConfigService {
  private oktaTenantConfig = {
    mo: {
      issuer: 'https://{yourOktaDomain}/oauth2/default',
      clientId: '{yourClientId}',
    },
    lower: {
      issuer: 'https://{yourOktaDomain}/oauth2/default',
      clientId: '{yourClientId}',
    },
    prod: {
      issuer: 'https://{yourOktaDomain}/oauth2/default',
      clientId: '{yourClientId}',
    }
  };

  private appConfig = {
    hostName: {
      mo: 'localhost', // hardcoded to local serve
      prod: 'prod'
    }
  };

  public host = window.location.hostname.toLowerCase();

  public GetOktaConfig() {    
    
    let oktaClaimsProvider = this.oktaTenantConfig.lower;

    switch (this.host) {
      case this.appConfig.hostName.mo:
        oktaClaimsProvider = this.oktaTenantConfig.mo;
        break;

      case this.appConfig.hostName.prod:
        oktaClaimsProvider = this.oktaTenantConfig.prod;
        break;
    }
    
    return {
      issuer: oktaClaimsProvider.issuer,
      redirectUri: window.location.origin + '/login/callback',
      clientId: oktaClaimsProvider.clientId,
      pkce: true,
      scopes: ['openid', 'profile'],
      tokenManager: {
        storage: 'sessionStorage',
        autoRenew: true
      }
    }
  }
}

After shutting down VS code and reinstalling the OKTA libraries, it just works now. I’ll chock it up to NPM chicanery, haha, thanks for the assist. It’s appreciated.

Awesome! Glad to hear it!

Hi @alisaduncan, I need help please. I have been following your documentation to set up OKTA, really great stuff. Thank you!

Currently I have setup my asp.net server to retrieve my clientId and issuer.

I keep getting this error:

I have my main.ts set up as the following:
image

And my app.module:

I am getting the response as the following (I am hiding the clientId and the issuer):

I have been following this: GitHub - oktadev/okta-angular-async-load-example: Loading Okta configuration from an external API in an Angular app

I am not sure how to resolve this… please reach out if you can, thank you so much

Hey there! Thanks for your kind words.

If I’m reading your console error correctly, it looks like you might not be passing in the issuer correctly when instantiating the OktaAuth object.

The config to pass into OktaAuth should include the properties for issuer and clientId.

Based on your console.log where you logged out authConfig, it looks like you might have mapped the issuer to oktaDomain. If you can change the OktaAuthConfig class to use the same interface as this file

it would be the easiest. Otherwise, if you can’t then I think you’d need to update useFactory to something like this

useFactory: (oktaConfig: OktaAuthConfig) => ({oktaAuth: new OktaAuth({...oktaConfig.config, issuer: oktaConfig.oktaDomain, redirectUri: window.location.origin + '/login/callback'})})

Apologies for any typos, I didn’t try running this to double-check. Feel free to shout out via a new question on the original post if you run into any more issues!

Thank you so much Alisa, this was very much helpful! I got it working!!! I am curious, what is the benefit of using useFactory? Is useClass something we can use instead somehow? @alisaduncan

Glad to hear it!

Great question! We are using useFactory here because we have run time dependencies. In order to provide the OKTA_CONFIG, we depend on the information in the instance ofOktaAuthConfig we created earlier.

DI is a complex topic. If you’d like to dive deeper, I recommend checking out the following documentation at angular.io

Feel free to shout out via a new question on the original blog post if you run into any more issues or questions!