0

I need to redirect a user with a form post after I receive a response from an http call that contains the url and a session token.

I am following this SO post

I keep getting cannot POST /login.

The action of my form is set to http://localhost:4200/login - not the updated action I receive from the http call.

My template:

<form ngNoForm 
  [formGroup]='formGroup'
  [action]='websiteUrl' 
  method="post"
  #redirectForm 
>
  <input type="hidden" name="SessionId" [value]='sessionId' />
</form>

LoginComponent

     export class LoginComponent implements OnInit, AfterViewInit {
      @ViewChild('redirectForm')formElement!: ElementRef;
      sessionId: string = '';
      websiteUrl: string = '';
      formGroup = this.formBuilder.group({
        SessionId: ''
      });
      constructor(
        private partnerDomainService: PartnerDomainService,
        private amplifyService: AmplifyService,
        private httpClient: HttpClient,
        private configurationService: ConfigurationService,
        private formBuilder: FormBuilder
      ) {}
      ngAfterViewInit(): void {
        debugger;
        this.formElement.nativeElement.submit();
      }

      async ngOnInit(): Promise<void> {
        await this.getLoginData();
      }

      private async getLoginData(): Promise<void> {
        const loginEndpoint = `${this.configurationService.loginApi}/api/authenticate/federatedlogin`;
        const partnerDomain = this.partnerDomainService.getPartnerDomain();
        const identityToken = await this.amplifyService.getIdentityToken(
          partnerDomain
        );
        const headers: HttpHeaders = new HttpHeaders({
          Authorization: `Bearer ${identityToken}`,
          DomainName: partnerDomain,
        });
        this.httpClient
          .post<LoginResponse>(loginEndpoint, null, { headers: headers })
          .pipe(
            tap((loginResponse: LoginResponse) => {
              this.websiteUrl = loginResponse.WebsiteURL;
              this.sessionId = loginResponse.SessionCookie;
            })
          )
          .subscribe();
      }
    }
4
  • After you get the response from login, you need to redirect the user to a route? in this case, http://localhost:4200/login ? Commented Dec 9, 2022 at 2:17
  • Yes. But not the login route. The response contains the URL I need to set the form action to. Commented Dec 9, 2022 at 3:53
  • 1
    I don't get it.. why do you need the form for redirecting the user to a certain route? why not use Route from @angular/router? like this.route.navigate(...) Commented Dec 9, 2022 at 4:13
  • The url is not a local component. It is an external site. I also cannot use the HttpClient to post the form data because I don't have control of the site in the form of CORS support. They do however support a form post - which is why I am trying this. I can get this to work if I hard code action and sessionId on the component template. My challenge is the angualr lifecycle hooks are being invoked before I get the http response back. Commented Dec 12, 2022 at 20:21

1 Answer 1

0

I ended up solving this problem by implementing a service that implements the Resolve interface and then wiring up as a resolver.

This is key because it allows for backend calls to be made (and completed) before the Activated Route is completed. The data returned from the resolve is then attached to the ActivatedRoute object.

Resolve<>

    @Injectable({
      providedIn: 'root',
    })
    export class LoginSessionResolverService implements Resolve<LoginResponse> {
      private loginResponseSubject$ = new Subject<LoginResponse>();
      private loginResponse$ = this.loginResponseSubject$.asObservable();
      constructor(
        private configurationService: ConfigurationService,
        private customerService: CustomerService,
        private amplifyService: AmplifyService,
        private httpClient: HttpClient
      ) {}

      resolve(route: ActivatedRouteSnapshot): Observable<LoginResponse> {
        const loginEndpoint = `${this.configurationService.loginApi}/api/endpoint`;
        const customer = this.customerService.getCustomer();
        this.amplifyService
          .getIdentityToken(customer)
          .then((token: string) => {
            const headers: HttpHeaders = new HttpHeaders({
              Authorization: `Bearer ${token}`,
              Customer: customer,
            });
            this.httpClient
              .post<LoginResponse>(loginEndpoint, null, { headers: headers })
              .pipe(
                tap((loginResponse: LoginResponse) =>
                  this.loginResponseSubject$.next(loginResponse)
                )
              )
              .subscribe();
          });
        return this.loginResponse$;
      }

Wiring up as resolver

    const routes: Routes = [
  {
    path: 'login',
    component: LoginComponent,
    resolve: { loginResponse: LoginSessionResolverService}
  },
  {
    path: 'signin/:domain',
    component: SignInComponent
  },
  {
    path: '',
    component: RedirectComponent
  },
  {
    path: '**',
    redirectTo: '',
    pathMatch: 'full'
  }
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

Use the service in a component:

    @Component({
      selector: 'cbh-login',
      templateUrl: './login.component.html',
      styleUrls: ['./login.component.scss'],
    })
    export class LoginComponent implements AfterViewInit, OnInit {
      @ViewChild('redirectForm') formElement!: ElementRef;
      loginResponse: LoginResponse | undefined;

      formGroup = this.formBuilder.group({
        SessionId: '',
      });
      constructor(
        private formBuilder: FormBuilder,
        private activatedRoute: ActivatedRoute,
        private cookieService: CookieService,
        private customerService: CustomerService
      ) {}
      ngOnInit(): void {
        this.activatedRoute.data.subscribe((response: any) => {
          this.loginResponse = response.loginResponse;
          if (this.loginResponse) {
            const customer= this.customerService.getCustomer();
            this.cookieService.set('SessionId', this.loginResponse.SessionCookie);
            this.cookieService.set('customer', customer);
          }
        });
      }
      ngAfterViewInit(): void {
        this.formElement.nativeElement.submit();
      }
    }

Template

    <form ngNoForm 
      [formGroup]='formGroup'
      [action]='loginResponse?.WebsiteURL'
      method="post"
      #redirectForm 
    >
      <input type="hidden" name="SessionId" [value]='loginResponse?.SessionCookie' />
    </form>
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.