@sinequa/atomic-angular 1.6.0 → 1.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -10881,6 +10881,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
10881
10881
  }]
10882
10882
  }] });
10883
10883
 
10884
+ /**
10885
+ * Mirrors @sinequa/atomic's `AUTH_REDIRECT_ATTEMPT_KEY` (src/authentication/constants.ts): the
10886
+ * sessionStorage flag set right before a provider full-page redirect (`window.location.href`).
10887
+ * Reading it lets us tell "an IdP redirect is under way" from "no session", without a blind timer.
10888
+ */
10889
+ const AUTH_REDIRECT_ATTEMPT_KEY = "sinequa-auth-redirect-attempt";
10884
10890
  /**
10885
10891
  * Represents the LoginComponent class, which is responsible for handling the login functionality.
10886
10892
  * This component is used to authenticate users and manage the user's authentication status.
@@ -10922,6 +10928,13 @@ class SignInComponent {
10922
10928
  authenticated = signal(isAuthenticated(), ...(ngDevMode ? [{ debugName: "authenticated" }] : []));
10923
10929
  user = signal(getState(this.principalStore), ...(ngDevMode ? [{ debugName: "user" }] : []));
10924
10930
  expiresSoonNotified = signal(false, ...(ngDevMode ? [{ debugName: "expiresSoonNotified" }] : []));
10931
+ /**
10932
+ * Forces the credentials form to show even while `externalAuth` is true. Set when a silent
10933
+ * SSO/browser auth attempt fails and no OAuth/SAML provider is configured: in that case
10934
+ * `useSSO` only meant "unknown — try SSO, then credentials", so we fall back to the form
10935
+ * instead of routing to /error.
10936
+ */
10937
+ showCredentialsFallback = signal(false, ...(ngDevMode ? [{ debugName: "showCredentialsFallback" }] : []));
10925
10938
  constructor(destroyRef) {
10926
10939
  this.destroyRef = destroyRef;
10927
10940
  // If the user is already authenticated when landing here (e.g. page refresh on
@@ -10931,21 +10944,45 @@ class SignInComponent {
10931
10944
  const url = this.route.snapshot.queryParams["returnUrl"] || "/";
10932
10945
  this.router.navigateByUrl(url);
10933
10946
  }
10934
- // When authentication is delegated to the browser/proxy (SSO) or an OAuth/SAML
10935
- // provider, no credentials form is shown: this screen shows a loader and initiates
10936
- // the handshake automatically by calling `handleLogin()`. If the handshake never
10937
- // completes, fall back to /error after 5s; the fallback is cancelled as soon as
10938
- // the login succeeds (the `authenticated` event then drives navigation).
10947
+ // When authentication is delegated to the browser/proxy (SSO) or an OAuth/SAML provider, no
10948
+ // credentials form is shown: this screen shows a loader and starts the handshake automatically.
10949
+ // Drive what happens next from the OUTCOME of `login()`, never a blind timer racing the network
10950
+ // call (the old 5s timer fired while a slow but legitimate provider call — up to its own 10s
10951
+ // network timeout was still in flight, flashing /error just before the IdP redirect):
10952
+ // • rejected → real failure (network timeout, 5xx, callback loop) → /error WITH the reason;
10953
+ // • resolved true → authenticated → the `authenticated` event / returnUrl drives navigation;
10954
+ // • resolved false → either an OAuth/SAML full-page redirect is under way (the redirect flag was
10955
+ // set just before `window.location.href`; the page is about to unload, so do
10956
+ // nothing and let it leave for the IdP), or no provider is configured
10957
+ // (ambiguous SSO → show the credentials form), or the provider returned no
10958
+ // redirect at all (→ /error).
10939
10959
  if (this.externalAuth && !this.authenticated()) {
10940
- const timeout = setTimeout(() => {
10960
+ login()
10961
+ .then(result => {
10962
+ if (result) {
10963
+ this.auditService.notifyLogin();
10964
+ return;
10965
+ }
10966
+ const hasAutoProvider = !!(globalConfig.autoOAuthProvider || globalConfig.autoSAMLProvider);
10967
+ if (!hasAutoProvider) {
10968
+ this.showCredentialsFallback.set(true);
10969
+ return;
10970
+ }
10971
+ // A full-page redirect to the IdP was initiated → the page is about to unload. Routing to
10972
+ // /error here is exactly what caused the transient "error" flash, so skip it.
10973
+ if (sessionStorage.getItem(AUTH_REDIRECT_ATTEMPT_KEY))
10974
+ return;
10975
+ // Provider configured but the server returned no redirectUrl and there is no session → error.
10941
10976
  this.router.navigate(["/error"], {
10942
10977
  queryParams: { returnUrl: this.route.snapshot.queryParams["returnUrl"] }
10943
10978
  });
10944
- }, 5000);
10945
- destroyRef.onDestroy(() => clearTimeout(timeout));
10946
- this.handleLogin().then(result => {
10947
- if (result)
10948
- clearTimeout(timeout);
10979
+ })
10980
+ .catch((err) => {
10981
+ warn("An error occurred while logging in", err);
10982
+ this.auditService.notify({ type: "Login_Denied" });
10983
+ // Surface the failure reason on the error page (e.g. "OAuth provider not found: identity-dev").
10984
+ const message = (err?.errorMessage ?? err?.message) || undefined;
10985
+ this.router.navigate(["error"], { queryParams: { message } });
10949
10986
  });
10950
10987
  }
10951
10988
  effect(() => {
@@ -11057,7 +11094,7 @@ class SignInComponent {
11057
11094
  }
11058
11095
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SignInComponent, deps: [{ token: i0.DestroyRef }], target: i0.ɵɵFactoryTarget.Component });
11059
11096
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: SignInComponent, isStandalone: true, selector: "signIn, signin, sign-in", inputs: { class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, username: { classPropertyName: "username", publicName: "username", isSignal: true, isRequired: false, transformFunction: null }, password: { classPropertyName: "password", publicName: "password", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { forgotPassword: "forgotPassword", username: "usernameChange", password: "passwordChange" }, host: { properties: { "class": "cn('grid h-dvh w-full place-content-center', class())" } }, providers: [provideTranslocoScope("login")], ngImport: i0, template: `
11060
- @if (!authenticated() && !externalAuth) {
11097
+ @if (!authenticated() && (!externalAuth || showCredentialsFallback())) {
11061
11098
  <Card
11062
11099
  hover="no"
11063
11100
  cdkTrapFocus
@@ -11130,7 +11167,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
11130
11167
  CardHeaderComponent,
11131
11168
  CardContentComponent
11132
11169
  ], providers: [provideTranslocoScope("login")], template: `
11133
- @if (!authenticated() && !externalAuth) {
11170
+ @if (!authenticated() && (!externalAuth || showCredentialsFallback())) {
11134
11171
  <Card
11135
11172
  hover="no"
11136
11173
  cdkTrapFocus