@sinequa/atomic-angular 1.0.18 → 1.0.19

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.
@@ -10930,6 +10930,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
10930
10930
  }]
10931
10931
  }], propDecorators: { cancel: [{ type: i0.Output, args: ["cancel"] }], success: [{ type: i0.Output, args: ["success"] }], userName: [{ type: i0.Input, args: [{ isSignal: true, alias: "userName", required: false }] }, { type: i0.Output, args: ["userNameChange"] }] } });
10932
10932
 
10933
+ /**
10934
+ * Mirrors @sinequa/atomic's `AUTH_REDIRECT_ATTEMPT_KEY` (src/authentication/constants.ts): the
10935
+ * sessionStorage flag set right before a provider full-page redirect (`window.location.href`).
10936
+ * Reading it lets us tell "an IdP redirect is under way" from "no session", without a blind timer.
10937
+ */
10938
+ const AUTH_REDIRECT_ATTEMPT_KEY = "sinequa-auth-redirect-attempt";
10933
10939
  /**
10934
10940
  * Represents the LoginComponent class, which is responsible for handling the login functionality.
10935
10941
  * This component is used to authenticate users and manage the user's authentication status.
@@ -10971,6 +10977,13 @@ class SignInComponent {
10971
10977
  authenticated = signal(isAuthenticated(), ...(ngDevMode ? [{ debugName: "authenticated" }] : []));
10972
10978
  user = signal(getState(this.principalStore), ...(ngDevMode ? [{ debugName: "user" }] : []));
10973
10979
  expiresSoonNotified = signal(false, ...(ngDevMode ? [{ debugName: "expiresSoonNotified" }] : []));
10980
+ /**
10981
+ * Forces the credentials form to show even while `externalAuth` is true. Set when a silent
10982
+ * SSO/browser auth attempt fails and no OAuth/SAML provider is configured: in that case
10983
+ * `useSSO` only meant "unknown — try SSO, then credentials", so we fall back to the form
10984
+ * instead of routing to /error.
10985
+ */
10986
+ showCredentialsFallback = signal(false, ...(ngDevMode ? [{ debugName: "showCredentialsFallback" }] : []));
10974
10987
  constructor(destroyRef) {
10975
10988
  this.destroyRef = destroyRef;
10976
10989
  // If the user is already authenticated when landing here (e.g. page refresh on
@@ -10980,21 +10993,45 @@ class SignInComponent {
10980
10993
  const url = this.route.snapshot.queryParams["returnUrl"] || "/";
10981
10994
  this.router.navigateByUrl(url);
10982
10995
  }
10983
- // When authentication is delegated to the browser/proxy (SSO) or an OAuth/SAML
10984
- // provider, no credentials form is shown: this screen shows a loader and initiates
10985
- // the handshake automatically by calling `handleLogin()`. If the handshake never
10986
- // completes, fall back to /error after 5s; the fallback is cancelled as soon as
10987
- // the login succeeds (the `authenticated` event then drives navigation).
10996
+ // When authentication is delegated to the browser/proxy (SSO) or an OAuth/SAML provider, no
10997
+ // credentials form is shown: this screen shows a loader and starts the handshake automatically.
10998
+ // Drive what happens next from the OUTCOME of `login()`, never a blind timer racing the network
10999
+ // call (the old 5s timer fired while a slow but legitimate provider call — up to its own 10s
11000
+ // network timeout was still in flight, flashing /error just before the IdP redirect):
11001
+ // • rejected → real failure (network timeout, 5xx, callback loop) → /error WITH the reason;
11002
+ // • resolved true → authenticated → the `authenticated` event / returnUrl drives navigation;
11003
+ // • resolved false → either an OAuth/SAML full-page redirect is under way (the redirect flag was
11004
+ // set just before `window.location.href`; the page is about to unload, so do
11005
+ // nothing and let it leave for the IdP), or no provider is configured
11006
+ // (ambiguous SSO → show the credentials form), or the provider returned no
11007
+ // redirect at all (→ /error).
10988
11008
  if (this.externalAuth && !this.authenticated()) {
10989
- const timeout = setTimeout(() => {
11009
+ login()
11010
+ .then(result => {
11011
+ if (result) {
11012
+ this.auditService.notifyLogin();
11013
+ return;
11014
+ }
11015
+ const hasAutoProvider = !!(globalConfig.autoOAuthProvider || globalConfig.autoSAMLProvider);
11016
+ if (!hasAutoProvider) {
11017
+ this.showCredentialsFallback.set(true);
11018
+ return;
11019
+ }
11020
+ // A full-page redirect to the IdP was initiated → the page is about to unload. Routing to
11021
+ // /error here is exactly what caused the transient "error" flash, so skip it.
11022
+ if (sessionStorage.getItem(AUTH_REDIRECT_ATTEMPT_KEY))
11023
+ return;
11024
+ // Provider configured but the server returned no redirectUrl and there is no session → error.
10990
11025
  this.router.navigate(["/error"], {
10991
11026
  queryParams: { returnUrl: this.route.snapshot.queryParams["returnUrl"] }
10992
11027
  });
10993
- }, 5000);
10994
- destroyRef.onDestroy(() => clearTimeout(timeout));
10995
- this.handleLogin().then(result => {
10996
- if (result)
10997
- clearTimeout(timeout);
11028
+ })
11029
+ .catch((err) => {
11030
+ warn("An error occurred while logging in", err);
11031
+ this.auditService.notify({ type: "Login_Denied" });
11032
+ // Surface the failure reason on the error page (e.g. "OAuth provider not found: identity-dev").
11033
+ const message = (err?.errorMessage ?? err?.message) || undefined;
11034
+ this.router.navigate(["error"], { queryParams: { message } });
10998
11035
  });
10999
11036
  }
11000
11037
  effect(() => {
@@ -11106,7 +11143,7 @@ class SignInComponent {
11106
11143
  }
11107
11144
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SignInComponent, deps: [{ token: i0.DestroyRef }], target: i0.ɵɵFactoryTarget.Component });
11108
11145
  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: `
11109
- @if (!authenticated() && !externalAuth) {
11146
+ @if (!authenticated() && (!externalAuth || showCredentialsFallback())) {
11110
11147
  <Card
11111
11148
  hover="no"
11112
11149
  cdkTrapFocus
@@ -11179,7 +11216,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
11179
11216
  CardHeaderComponent,
11180
11217
  CardContentComponent
11181
11218
  ], providers: [provideTranslocoScope("login")], template: `
11182
- @if (!authenticated() && !externalAuth) {
11219
+ @if (!authenticated() && (!externalAuth || showCredentialsFallback())) {
11183
11220
  <Card
11184
11221
  hover="no"
11185
11222
  cdkTrapFocus