Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Getting "AuthSdkError: The app should not attempt to call authorize API on callback. Authorize flow is already in process. Use parseFromUrl() to receive tokens." error when trying to get access token and claims. #6

Closed
ilgiznurgaliev opened this issue Oct 12, 2020 · 14 comments

Comments

@ilgiznurgaliev
Copy link

Hi.

When I'm trying to get access token and claims as described here to pass it to PHP back end I'm getting the error above.

Part of app.module.ts:

import { OKTA_CONFIG, OktaAuthModule, OktaAuthGuard, OktaCallbackComponent } from '@okta/okta-angular'; 

const oktaConfig = {
  issuer: environment.OKTA_ISSUER,
  clientId: environment.OKTA_CLIENTID,
  redirectUri: 'http://localhost:4200/login',
  scopes: ['openid', 'profile', 'email'],
  pkce: true
}

const routes: Routes = [
  {
    path: '',
    component: AppComponent,
    canActivate: [ OktaAuthGuard ]
  },
  {
    path: 'login',
    component: OktaCallbackComponent,
  }
];

@NgModule({
  ...
  imports: [
    ...
    OktaAuthModule,
    RouterModule.forRoot(routes),
  ],
  providers: [
    ...
    { provide: OKTA_CONFIG, useValue: oktaConfig }
  ],
  ...
})

Part of app.component.ts:

import { OktaAuthService } from '@okta/okta-angular';
export class AppComponent {
  isAuthenticated: boolean = false
  ...

  constructor(private httpService: HttpService, public oktaAuth: OktaAuthService) {
    this.oktaAuth.$authenticationState.subscribe(isAuthenticated => this.isAuthenticated = isAuthenticated);
  }

  async ngOnInit() {
    this.isAuthenticated = await this.oktaAuth.isAuthenticated();
    const accessToken = await this.oktaAuth.getAccessToken();
    const user = await this.oktaAuth.getUser();
    console.log('Access Token: ', accessToken)
    console.log('User: ', user)
    ...

There is no other components and routes, all content is in root (/) path.
Authentication works fine, but I'm unable to get access token to pass it to back end written in php.
Could someone please assist to solve the issue?

@ilgiznurgaliev ilgiznurgaliev changed the title "AuthSdkError: The app should not attempt to call authorize API on callback. Authorize flow is already in process. Use parseFromUrl() to receive tokens." error when trying to get accessToken and claims. Getting "AuthSdkError: The app should not attempt to call authorize API on callback. Authorize flow is already in process. Use parseFromUrl() to receive tokens." error when trying to get access token and claims. Oct 12, 2020
@swiftone
Copy link
Contributor

@ilgiznurgaliev - Thanks for the report, we will take a look and get back to you as soon as we can.

@aarongranick-okta
Copy link
Contributor

See comments left on same issue here: okta/okta-oidc-js#863 (comment)

We can continue conversation here, this is the new repository for the okta-angular SDK.

@ilgiznurgaliev
Copy link
Author

Hi and thank you for your responses.

@aarongranick-okta, replying to your comment... As first solution you suggested:
>> "Use routing and have dedicated route for the login callback. The logic on this route should call handleAuthentication and nothing else. It should not attempt to determine authentication status while processing the callback."
But I already have a dedicated route for callback, it is '/login':

const routes: Routes = [
  {
    path: '',
    component: AppComponent,
    canActivate: [ OktaAuthGuard ]
  },
  {
    path: 'login',
    component: OktaCallbackComponent,
  }
];

I'm trying to achieve the following logic.
I have a single route (/) angular application. No any other routes and components, only app.
Initially the application after Angular has initialized fetches data from API and renders html table using fetched data.

ngOnInit() {
    this.getData();
}

That's it, very simple.

Now I want to add authentication through Okta to my application. You can see above what was changed in my code.
Following this guide I:

  • added a callback route (see app.module.ts above)
  • created an okta application in Okta Developer Console
  • installed and configured '@okta/okta-angular' sdk (see app.module.ts above)
  • didn't add any 'Login' button, because entire my application (root path) will be protected
  {
    path: '',
    component: AppComponent,
    canActivate: [ OktaAuthGuard ]
  },

In this step everything works as expected. When I try to open my application url it redirects me to okta and after authentication Okta redirects me back to my application. I can see that url contains code and state GET parameters.

So now within onInit function before API call I need to get access token and pass it to my PHP API, which will verify it before sending data to application.
When I'm trying to print access token to browser console I'm getting the subj error.

async ngOnInit() {
    this.isAuthenticated = await this.oktaAuth.isAuthenticated();
    const accessToken = await this.oktaAuth.getAccessToken();
    const user = await this.oktaAuth.getUser();
    console.log('Access Token: ', accessToken)
    console.log('User: ', user)
    this.getData()
    ...

Sorry if this is something simple but I'm new in angular don't really understand where is my mistake?

@aarongranick-okta
Copy link
Contributor

"didn't add any 'Login' button, because entire my application (root path) will be protected"

This may be the problem. The login callback route must be an unprotected route. Since the logic is occurring in the middle of an auth flow, the user is neither authenticated nor authenticated. Try to navigate to /login directly. Does it redirect you to Okta for signin? If so, then this is definitely the source of the problem. It may work to set canActivateChild and return true: https://angular.io/api/router/CanActivateChild

@ilgiznurgaliev
Copy link
Author

When I try to navigate directly to /login (http://localhost:4200/login) it doesn't redirect me to to Okta, it just opens the application. Also checked in chrome's network tab of debug console. Only when I try to open root path (http://localhost:4200/) it redirects me to Okta (https://dev-xxxxx.okta.com/oauth2/default/v1/authorize...) and then redirects back (http://localhost:4200/login...).
Sorry, what do you mean by "set canActivateChild and return true"? As per link you provided this interface is to protect child routes, but I don't have them.

@ilgiznurgaliev
Copy link
Author

It is a bit strange because according to this guide:
To require authentication for all routes, use a loop to add OktaAuthGuard to every item in the Routes collection.

const appRoutes: Routes = [
  // Your routes...
];

// Require authentication on every route
appRoutes.forEach(route => {
  router.canActivate = router.canActivate || [];
  route.canActivate.push(OktaAuthGuard);
});

There is nothing about that the login callback route must be an unprotected route.

@aarongranick-okta
Copy link
Contributor

@ilgiznurgaliev Thank you for bringing this issue with the documentation to our attention, we will correct this and be sure to mention that the callback must be an unprotected route.

Since navigating to /login does not perform the redirect, it sounds like your route config is not the issue. Are you able to debug the application to see what is calling the code which results in this error being thrown? The likely cause is some code calling isAuthenticated (or any other Okta API method which attempts to read tokens) which runs on the callback route. It may be located in a service which is instantiated outside of any component. This type code will work fine in your app component, or on any other routes, but will cause this error if run on the callback route. Our SDKs include source maps, if you webpack with the source-map-loader plugin you should be able to consume these sourcemaps and debug through the original sources.

@ilgiznurgaliev
Copy link
Author

@aarongranick-okta I use builtin OktaCallbackComponent in callback route with no any customization, so not sure how something else can be called on the callback route.

import { OKTA_CONFIG, OktaAuthModule, OktaAuthGuard, OktaCallbackComponent } from '@okta/okta-angular'; 
...
const routes: Routes = [
  {
    path: '',
    component: AppComponent,
    canActivate: [ OktaAuthGuard ]
  },
  {
    path: 'login',
    component: OktaCallbackComponent,
  }
];

Currently ngOnInit() function in app.component.ts looks like below:

async ngOnInit() {
    this.isAuthenticated = await this.oktaAuth.isAuthenticated();
    const accessToken = await this.oktaAuth.getAccessToken();
    const user = await this.oktaAuth.getUser();
    console.log('Access Token: ', accessToken)
    console.log('User: ', user)
    this.getData();
  }

getData() function simple makes API call and that's it.
So when ngOnInit looks like below:

async ngOnInit() {
    this.isAuthenticated = await this.oktaAuth.isAuthenticated();
    this.getData();
  }

no any errors and authentication flow works as expected, but when I'm trying to get access token and/or user:

async ngOnInit() {
    this.isAuthenticated = await this.oktaAuth.isAuthenticated();
    const accessToken = await this.oktaAuth.getAccessToken();
    const user = await this.oktaAuth.getUser();
    console.log('Access Token: ', accessToken)
    console.log('User: ', user)
    this.getData();
  }

authentication still works fine but getting the error in browser console.

@aarongranick-okta
Copy link
Contributor

@ilgiznurgaliev Are you receiving the error on the callback route ('/login') or on the app route ('/') ? If the error is happening on the callback, make sure that none of the logic in app component runs (maybe add a debugger; line to app component). If the logic on app component is running on the callback route, that would cause the error.

If you are receiving the error on the app route (after authentication), check to see if there is a query parameter code in the URL. This should normally be cleaned up by the callback component, but maybe something went wrong with this step.

@ilgiznurgaliev
Copy link
Author

Hi, @aarongranick-okta. I'm receiving the error on the app route after authentication. ngOnInit() function I mentioned above is part of my app component. This function called once angular has initialized.
After authentication the url looks like below:
http://localhost:4200/login?code=akNlsm...&state=pQwnP...
I didn't know that this should be cleaned up by the callback component. There were no any errors in browser console.
Any idea why it was not cleaned up if it should?
Could you please advice where should I add debugger?

Today I have found that when I reload the URL above that contains code and state the application doesn't detect that token has alredy been expired and doesn't redirect to Okta. After manual cleaning up of query string to just http://localhost:4200 and reload page it redirected me to Okta and asked for login and password, and then redirected back to:
http://localhost:4200/login?code=akNlsm...&state=pQwnP...
This behavior also looks strange.

@aarongranick-okta
Copy link
Contributor

@ilgiznurgaliev You say you are "receiving the error on the app route", "yet the url looks like" http://localhost:4200/login?code=akNlsm...&state=pQwnP....

Based on router config, the URL (with path /login) should be loading the OktaCallbackComponent, so why would the error be received "on the app route"? It looks the problem may be with route config. According to the docs: https://angular.io/guide/router#route-order

The order of routes is important because the Router uses a first-match wins strategy when matching routes, so more specific routes should be placed above less specific routes.

Try moving the route definition for `login' before the definition of ''

@ilgiznurgaliev
Copy link
Author

Just tried to move /login route, so routes now looks like below:

const routes: Routes = [
  {
    path: 'login',
    component: OktaCallbackComponent,
  },
  {
    path: '',
    component: AppComponent,
    canActivate: [ OktaAuthGuard ]
  }
];

But still getting the same error. Sorry for the misunderstanding, I said that I'm received the error in the app route because I'm trying to get access token within app component.
Could you please clarify should OktaCallbackComponent normaly redirect me to root path ('' route)? I don't really understand why I'm still on /login?code... url after successfull authentication.
Probably the issue with that component, but I don't know how to debug it...

@ilgiznurgaliev
Copy link
Author

@aarongranick-okta Found in documentation:
OktaCallbackComponent - Handles the callback after the redirect. By default, it parses the tokens from the uri, stores them, then redirects to /.
The component looks very simple:

@Component({
  template: `<div>{{error}}</div>`
})
export class OktaCallbackComponent implements OnInit {
  error: string;

  constructor(private okta: OktaAuthService) {}

  ngOnInit(): void {
    /**
     * Handles the response from Okta and parses tokens.
     */
    this.okta.handleAuthentication()
      .then(() => {
        /**
         * Navigate back to the saved uri, or root of application.
         */
        const fromUri = this.okta.getFromUri();
        window.location.replace(fromUri);
      })
      .catch(e => {
        this.error = e.toString();
      });
  }
}

Tried console.log('From Uri: ', this.oktaAuth.getFromUri()); and got From Uri: http://localhost:4200/.

Probably I will need to copy the component to my own source and try to debug it to understand why window.location.replace(fromUri); does nothing.

@ilgiznurgaliev
Copy link
Author

@aarongranick-okta, I found finally where was my mistake. Angular didn't load OktaCallbackComponent because there was no <router-outlet></router-outlet> tag in app component. After adding it everything works as expected. OktaCallbackComponent now redirects me back to root path and I'm able to get access token and pass it to back end.

Thank you very much for your help and your patience.
With best regards, Casper.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants