2

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() {
  }

}
Sumit
  • 71
  • 1
  • 5
  • which auth-flow du you use? Implicit? – nologin Jan 18 '20 at 17:55
  • Have you set the redirect_uri in your request header? Can you post it? Should look like: GET /authorize? response_type=id_token%20token &client_id=s6BhdRkqt3 &redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb &scope=openid%20profile &state=af0ifjsldkj &nonce=n-0S6_WzA2Mj HTTP/1.1 – nologin Jan 18 '20 at 18:01
  • I am using Implicit auth-flow – Sumit Jan 18 '20 at 18:11
  • Some code would be nice ;) – nologin Jan 18 '20 at 18:15
  • Added code to the original post – Sumit Jan 18 '20 at 18:39
  • Can you show the code to generate the direct_link. Is there somewhere an oidc redirect_url set? – nologin Jan 18 '20 at 18:56
  • The http request, which is send from the direct_link is helpful too. – nologin Jan 18 '20 at 19:03
  • Using cognito and redirecturi would need me to add all deep links manually as wildcards aren’t supported. This means the solution is to pass it as state and then read the state after authentication. I have recreated here: stackblitz.com/edit/angular-tit33s and based on https://github.com/manfredsteyer/angular-oauth2-oidc/issues/424 it doesn't look simple to read state with convenience function loadDiscoveryDocumentAndLogin(). A bit over my noob head! – Sumit Jan 20 '20 at 03:09

1 Answers1

2

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.

RMD
  • 2,907
  • 30
  • 47