Sumit
Sumit

Reputation: 71

Angular deep link navigation with OIDC

I have an application https://app.example.com (home) and I have deep link working https://app.example.com/function/123 (direct_link) and navigating directly to direct_link works if the user is already authenticated.

We are using angular-oauth2-oidc and I can't find a way to initiate authentication and bring the user back to direct_link post authentication, it always returns to the home and I have paste the direct_link again in the address bar.

import { AuthConfig } from 'angular-oauth2-oidc';

export const authConfig: AuthConfig = {

  // Url of the Identity Provider
  issuer: 'https://cognito-idp.<region>.amazonaws.com/<id>',

  // URL of the SPA to redirect the user to after login
  redirectUri: window.location.origin,

  // The SPA's id. The SPA is registerd with this id at the auth-server
  clientId: '<id>',

  // set the scope for the permissions the client should request
  // The first three are defined by OIDC. The 4th is a usecase-specific one
  scope: 'openid',

  strictDiscoveryDocumentValidation: false,
  responseType:'token',
  oidc: true
}
export class AuthGuardService implements CanActivate{

  constructor(private oauthService: OAuthService, private router: Router) { }

  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
    if (this.oauthService.hasValidIdToken()) {
      return true;
    }

    this.router.navigate(['home'], { queryParams: { returnUrl: state.url }});
    return false;
  }
}
export class HomeComponent implements OnInit {
  returnUrl:string;

  constructor(
    private oauthService: OAuthService,
    private router: Router) { }

  login() {
    this.oauthService.redirectUri = window.location.origin + this.returnUrl;
    this.oauthService.initImplicitFlow();
  }

  logout() {
    this.oauthService.logOut();
  }


  ngOnInit() {
  }

}

Upvotes: 2

Views: 4376

Answers (1)

RMD
RMD

Reputation: 3018

We're using the angular-oauth2-oidc library with Azure AD B2C as well, and had a similar requirement.

Our deep linking requirements prevented us from using the redirectUri as the URL was dynamic (ie: product IDs included in the URL), and Azure AD B2C doesn't support wildcard redirectUris.

Our solution was to capture the current URL in session storage prior to invoking the oauthService's login flow, and then using that stored URL after the login is complete to redirect to the original URL, so for example:

export class AuthenticationService {

    constructor(private storageService: SessionStorageService, private oauthService: OAuthService) {    }

...

    isLoggedIn(): boolean {
        return this.oauthService.hasValidAccessToken();
    }

...

    login(): void {
        this.oauthService.tryLoginImplicitFlow().then(success => {
            if (!success) {
                this.storageService.set('requestedUrl', location.pathname + location.search);
                this.oauthService.initLoginFlow();

            } else {
                let requestedUrl = this.storageService.get('requestedUrl');
                if (requestedUrl) {
                    sessionStorage.removeItem('requestedUrl');
                    location.replace( location.origin + requestedUrl);
                }
            }

This login method is part of our own auth service which mostly just delegates over to the OAuthService provided in the angular-oauth2-oidc package.

In our login method, we first attempt the tryLoginImplicitFlow() to see if the user has been authenticated.

If the tryLoginImplicitFlow() returns false, it means they aren't logged in, and we capture their current URL and shove it into session storage.

If it returns true, means they are authenticated, so we check to see if there is a stored URL, and if so, we redirect to it.

From a flow point of view, it works like this:

  1. User attempts to access a deep link: /site/products/1234
  2. App Component (not shown) checks the isLoggedIn() method of the auth service, and if not logged in, invokes the login() method
  3. Login method tries the tryLoginImplicitFlow() (which does things like checking for a state hash in the URL), and it fails, so the method calls initLoginFlow()
  4. User is redirected to some xxxx.b2clogin.com domain and logs in; B2C redirects the user to the root of our web app
  5. App Component kicks in again and checks isLoggedIn(), which is still false, so calls the login() method
  6. Login method tries the tryLoginImplicitFlow() (which picks up the fact that the user was just redirected from the B2C, and grabs the tokens) and it succeeds.
  7. Login method checks session storage for the originally requested URL, sees it there, and redirects the user to that original page.

I know what you are thinking: "WOW! That's a whole lot of re-directs" ...and you are right - but it actually is surprisingly quick.

Upvotes: 2

Related Questions