@idea-ionic/auth 8.4.1 → 8.5.0

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.
@@ -5,10 +5,10 @@ import * as i1 from '@angular/forms';
5
5
  import { FormsModule } from '@angular/forms';
6
6
  import { NavController, IonButton, IonText, IonCol, IonRow, IonIcon, IonLabel, IonCardSubtitle, IonCardTitle, IonCardHeader, IonImg, IonCardContent, IonCard, IonContent, IonItem, IonInput, IonCheckbox, PopoverController, ModalController, IonListHeader, IonList, IonTitle, IonButtons, IonToolbar, IonHeader } from '@ionic/angular/standalone';
7
7
  import { Browser } from '@capacitor/browser';
8
+ import { isEmpty } from 'idea-toolbox';
8
9
  import { IDEAEnvironment, IDEATranslationsService, IDEAStorageService, IDEAMessageService, IDEALoadingService, IDEATranslatePipe } from '@idea-ionic/common';
9
10
  import { CognitoUserPool, AuthenticationDetails, CognitoUser, CognitoUserAttribute, CognitoRefreshToken } from 'amazon-cognito-identity-js';
10
- import { isEmpty } from 'idea-toolbox';
11
- import { ActivatedRoute } from '@angular/router';
11
+ import { ActivatedRoute, Router } from '@angular/router';
12
12
  import { toCanvas } from 'qrcode';
13
13
 
14
14
  const COMMON_PASSWORDS_TOP_1000 = [
@@ -1538,6 +1538,7 @@ class IDEASignInPage {
1538
1538
  this._loading = inject(IDEALoadingService);
1539
1539
  this._translate = inject(IDEATranslationsService);
1540
1540
  this._auth = inject(IDEAAuthService);
1541
+ this.doneExternalProviderCheck = false;
1541
1542
  this.agreementsCheck = true;
1542
1543
  this.newAccountRegistered = false;
1543
1544
  this.darkMode = window.matchMedia('(prefers-color-scheme: dark)').matches;
@@ -1580,6 +1581,15 @@ class IDEASignInPage {
1580
1581
  this._loading.hide();
1581
1582
  }
1582
1583
  }
1584
+ checkForExternalProviderEmail() {
1585
+ if (isEmpty(this.email, 'email'))
1586
+ return;
1587
+ const emailDomain = this.email.split('@')[1];
1588
+ this.doneExternalProviderCheck = true;
1589
+ const provider = this.externalProviders.find(p => p.emailDomains.includes(emailDomain));
1590
+ if (provider)
1591
+ this._nav.navigateForward(['auth', provider.type], { queryParams: { provider: provider.name, go: true } });
1592
+ }
1583
1593
  goToIntro() {
1584
1594
  this._nav.navigateBack(['intro']);
1585
1595
  }
@@ -1638,103 +1648,130 @@ class IDEASignInPage {
1638
1648
  autocomplete="email"
1639
1649
  [placeholder]="'IDEA_AUTH.EMAIL' | translate"
1640
1650
  [title]="'IDEA_AUTH.EMAIL_HINT' | translate"
1651
+ [disabled]="externalProviders.length && doneExternalProviderCheck"
1641
1652
  [ngModelOptions]="{ standalone: true }"
1642
1653
  [(ngModel)]="email"
1643
- (keyup.enter)="login()"
1644
- />
1645
- </ion-item>
1646
- <ion-item>
1647
- <ion-label position="inline">
1648
- <ion-icon name="key" color="primary" />
1649
- </ion-label>
1650
- <ion-input
1651
- testId="signin.password"
1652
- id="current-password"
1653
- type="password"
1654
- spellcheck="false"
1655
- autocorrect="off"
1656
- autocomplete="current-password"
1657
- [clearOnEdit]="false"
1658
- [placeholder]="'IDEA_AUTH.PASSWORD' | translate"
1659
- [title]="'IDEA_AUTH.PASSWORD_HINT' | translate"
1660
- [ngModelOptions]="{ standalone: true }"
1661
- [(ngModel)]="password"
1662
- (keyup.enter)="login()"
1654
+ (keyup.enter)="externalProviders.length ? checkForExternalProviderEmail() : login()"
1663
1655
  />
1656
+ @if (externalProviders.length && doneExternalProviderCheck) {
1657
+ <ion-button
1658
+ slot="end"
1659
+ fill="clear"
1660
+ [title]="'IDEA_AUTH.EDIT_EMAIL' | translate"
1661
+ (click)="doneExternalProviderCheck = false"
1662
+ >
1663
+ <ion-icon icon="pencil" slot="icon-only" />
1664
+ </ion-button>
1665
+ }
1664
1666
  </ion-item>
1665
- @if (
1666
- !registrationPossible &&
1667
- (translationExists('IDEA_VARIABLES.TERMS_AND_CONDITIONS_URL') ||
1668
- translationExists('IDEA_VARIABLES.PRIVACY_POLICY_URL'))
1669
- ) {
1670
- <ion-row class="agreementsAcceptanceRow">
1671
- <ion-col>
1672
- <ion-checkbox size="small" [ngModelOptions]="{ standalone: true }" [(ngModel)]="agreementsCheck" />
1673
- {{ 'IDEA_AUTH.AGREEMENTS_CHECK_LOGIN' | translate }}:
1674
- @if (translationExists('IDEA_VARIABLES.TERMS_AND_CONDITIONS_URL')) {
1675
- <a
1676
- ion-text
1677
- testId="openTermsAndConditionsButton"
1678
- color="primary"
1679
- target="_blank"
1680
- [title]="'IDEA_AUTH.TERMS_AND_CONDITIONS_HINT' | translate"
1681
- [href]="'IDEA_VARIABLES.TERMS_AND_CONDITIONS_URL' | translate"
1682
- >
1683
- {{ 'IDEA_AUTH.TERMS_AND_CONDITIONS' | translate }}
1684
- </a>
1685
- }
1686
- @if (
1687
- translationExists('IDEA_VARIABLES.PRIVACY_POLICY_URL') &&
1688
- translationExists('IDEA_VARIABLES.TERMS_AND_CONDITIONS_URL')
1689
- ) {
1690
- <ion-text> & </ion-text>
1691
- }
1692
- @if (translationExists('IDEA_VARIABLES.PRIVACY_POLICY_URL')) {
1693
- <a
1694
- ion-text
1695
- testId="openPrivacyPolicyButton"
1696
- color="primary"
1697
- target="_blank"
1698
- [title]="'IDEA_AUTH.PRIVACY_POLICY_HINT' | translate"
1699
- [href]="'IDEA_VARIABLES.PRIVACY_POLICY_URL' | translate"
1700
- >
1701
- {{ 'IDEA_AUTH.PRIVACY_POLICY' | translate }}
1702
- </a>
1703
- }
1704
- </ion-col>
1705
- </ion-row>
1706
- }
1707
- <ion-button
1708
- testId="signInButton"
1709
- expand="block"
1710
- [disabled]="!agreementsCheck"
1711
- [title]="'IDEA_AUTH.SIGN_IN_HINT' | translate"
1712
- (click)="login()"
1713
- >
1714
- {{ 'IDEA_AUTH.SIGN_IN' | translate }}
1715
- </ion-button>
1716
- @if (registrationPossible) {
1667
+ @if (externalProviders.length && !doneExternalProviderCheck) {
1668
+ <ion-button
1669
+ testId="continueButton"
1670
+ expand="block"
1671
+ [disabled]="!agreementsCheck"
1672
+ [title]="'IDEA_AUTH.CONTINUE_HINT' | translate"
1673
+ (click)="checkExternalProviderEmail()"
1674
+ >
1675
+ {{ 'IDEA_AUTH.CONTINUE' | translate }}
1676
+ </ion-button>
1677
+ } @else {
1678
+ <ion-item>
1679
+ <ion-label position="inline">
1680
+ <ion-icon name="key" color="primary" />
1681
+ </ion-label>
1682
+ <ion-input
1683
+ testId="signin.password"
1684
+ id="current-password"
1685
+ type="password"
1686
+ spellcheck="false"
1687
+ autocorrect="off"
1688
+ autocomplete="current-password"
1689
+ [clearOnEdit]="false"
1690
+ [placeholder]="'IDEA_AUTH.PASSWORD' | translate"
1691
+ [title]="'IDEA_AUTH.PASSWORD_HINT' | translate"
1692
+ [ngModelOptions]="{ standalone: true }"
1693
+ [(ngModel)]="password"
1694
+ (keyup.enter)="login()"
1695
+ />
1696
+ </ion-item>
1697
+ @if (
1698
+ !registrationPossible &&
1699
+ (translationExists('IDEA_VARIABLES.TERMS_AND_CONDITIONS_URL') ||
1700
+ translationExists('IDEA_VARIABLES.PRIVACY_POLICY_URL'))
1701
+ ) {
1702
+ <ion-row class="agreementsAcceptanceRow">
1703
+ <ion-col>
1704
+ <ion-checkbox
1705
+ size="small"
1706
+ [ngModelOptions]="{ standalone: true }"
1707
+ [(ngModel)]="agreementsCheck"
1708
+ />
1709
+ {{ 'IDEA_AUTH.AGREEMENTS_CHECK_LOGIN' | translate }}:
1710
+ @if (translationExists('IDEA_VARIABLES.TERMS_AND_CONDITIONS_URL')) {
1711
+ <a
1712
+ ion-text
1713
+ testId="openTermsAndConditionsButton"
1714
+ color="primary"
1715
+ target="_blank"
1716
+ [title]="'IDEA_AUTH.TERMS_AND_CONDITIONS_HINT' | translate"
1717
+ [href]="'IDEA_VARIABLES.TERMS_AND_CONDITIONS_URL' | translate"
1718
+ >
1719
+ {{ 'IDEA_AUTH.TERMS_AND_CONDITIONS' | translate }}
1720
+ </a>
1721
+ }
1722
+ @if (
1723
+ translationExists('IDEA_VARIABLES.PRIVACY_POLICY_URL') &&
1724
+ translationExists('IDEA_VARIABLES.TERMS_AND_CONDITIONS_URL')
1725
+ ) {
1726
+ <ion-text> & </ion-text>
1727
+ }
1728
+ @if (translationExists('IDEA_VARIABLES.PRIVACY_POLICY_URL')) {
1729
+ <a
1730
+ ion-text
1731
+ testId="openPrivacyPolicyButton"
1732
+ color="primary"
1733
+ target="_blank"
1734
+ [title]="'IDEA_AUTH.PRIVACY_POLICY_HINT' | translate"
1735
+ [href]="'IDEA_VARIABLES.PRIVACY_POLICY_URL' | translate"
1736
+ >
1737
+ {{ 'IDEA_AUTH.PRIVACY_POLICY' | translate }}
1738
+ </a>
1739
+ }
1740
+ </ion-col>
1741
+ </ion-row>
1742
+ }
1743
+ <ion-button
1744
+ testId="signInButton"
1745
+ expand="block"
1746
+ [disabled]="!agreementsCheck"
1747
+ [title]="'IDEA_AUTH.SIGN_IN_HINT' | translate"
1748
+ (click)="login()"
1749
+ >
1750
+ {{ 'IDEA_AUTH.SIGN_IN' | translate }}
1751
+ </ion-button>
1752
+ @if (registrationPossible) {
1753
+ <ion-button
1754
+ testId="createAnAccountButton"
1755
+ fill="clear"
1756
+ expand="block"
1757
+ class="smallCaseButton"
1758
+ [title]="'IDEA_AUTH.CREATE_AN_ACCOUNT_HINT' | translate"
1759
+ (click)="goToRegistration()"
1760
+ >
1761
+ {{ 'IDEA_AUTH.CREATE_AN_ACCOUNT' | translate }}
1762
+ </ion-button>
1763
+ }
1717
1764
  <ion-button
1718
- testId="createAnAccountButton"
1765
+ testId="forgotPasswordButton"
1719
1766
  fill="clear"
1720
1767
  expand="block"
1721
1768
  class="smallCaseButton"
1722
- [title]="'IDEA_AUTH.CREATE_AN_ACCOUNT_HINT' | translate"
1723
- (click)="goToRegistration()"
1769
+ [title]="'IDEA_AUTH.I_FORGOT_MY_PASSWORD_HINT' | translate"
1770
+ (click)="goToForgotPassword()"
1724
1771
  >
1725
- {{ 'IDEA_AUTH.CREATE_AN_ACCOUNT' | translate }}
1772
+ {{ 'IDEA_AUTH.I_FORGOT_MY_PASSWORD' | translate }}
1726
1773
  </ion-button>
1727
1774
  }
1728
- <ion-button
1729
- testId="forgotPasswordButton"
1730
- fill="clear"
1731
- expand="block"
1732
- class="smallCaseButton"
1733
- [title]="'IDEA_AUTH.I_FORGOT_MY_PASSWORD_HINT' | translate"
1734
- (click)="goToForgotPassword()"
1735
- >
1736
- {{ 'IDEA_AUTH.I_FORGOT_MY_PASSWORD' | translate }}
1737
- </ion-button>
1738
1775
  @if (hasIntroPage) {
1739
1776
  <ion-button
1740
1777
  testId="backToIntroButton"
@@ -1827,103 +1864,130 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.9", ngImpor
1827
1864
  autocomplete="email"
1828
1865
  [placeholder]="'IDEA_AUTH.EMAIL' | translate"
1829
1866
  [title]="'IDEA_AUTH.EMAIL_HINT' | translate"
1867
+ [disabled]="externalProviders.length && doneExternalProviderCheck"
1830
1868
  [ngModelOptions]="{ standalone: true }"
1831
1869
  [(ngModel)]="email"
1832
- (keyup.enter)="login()"
1833
- />
1834
- </ion-item>
1835
- <ion-item>
1836
- <ion-label position="inline">
1837
- <ion-icon name="key" color="primary" />
1838
- </ion-label>
1839
- <ion-input
1840
- testId="signin.password"
1841
- id="current-password"
1842
- type="password"
1843
- spellcheck="false"
1844
- autocorrect="off"
1845
- autocomplete="current-password"
1846
- [clearOnEdit]="false"
1847
- [placeholder]="'IDEA_AUTH.PASSWORD' | translate"
1848
- [title]="'IDEA_AUTH.PASSWORD_HINT' | translate"
1849
- [ngModelOptions]="{ standalone: true }"
1850
- [(ngModel)]="password"
1851
- (keyup.enter)="login()"
1870
+ (keyup.enter)="externalProviders.length ? checkForExternalProviderEmail() : login()"
1852
1871
  />
1872
+ @if (externalProviders.length && doneExternalProviderCheck) {
1873
+ <ion-button
1874
+ slot="end"
1875
+ fill="clear"
1876
+ [title]="'IDEA_AUTH.EDIT_EMAIL' | translate"
1877
+ (click)="doneExternalProviderCheck = false"
1878
+ >
1879
+ <ion-icon icon="pencil" slot="icon-only" />
1880
+ </ion-button>
1881
+ }
1853
1882
  </ion-item>
1854
- @if (
1855
- !registrationPossible &&
1856
- (translationExists('IDEA_VARIABLES.TERMS_AND_CONDITIONS_URL') ||
1857
- translationExists('IDEA_VARIABLES.PRIVACY_POLICY_URL'))
1858
- ) {
1859
- <ion-row class="agreementsAcceptanceRow">
1860
- <ion-col>
1861
- <ion-checkbox size="small" [ngModelOptions]="{ standalone: true }" [(ngModel)]="agreementsCheck" />
1862
- {{ 'IDEA_AUTH.AGREEMENTS_CHECK_LOGIN' | translate }}:
1863
- @if (translationExists('IDEA_VARIABLES.TERMS_AND_CONDITIONS_URL')) {
1864
- <a
1865
- ion-text
1866
- testId="openTermsAndConditionsButton"
1867
- color="primary"
1868
- target="_blank"
1869
- [title]="'IDEA_AUTH.TERMS_AND_CONDITIONS_HINT' | translate"
1870
- [href]="'IDEA_VARIABLES.TERMS_AND_CONDITIONS_URL' | translate"
1871
- >
1872
- {{ 'IDEA_AUTH.TERMS_AND_CONDITIONS' | translate }}
1873
- </a>
1874
- }
1875
- @if (
1876
- translationExists('IDEA_VARIABLES.PRIVACY_POLICY_URL') &&
1877
- translationExists('IDEA_VARIABLES.TERMS_AND_CONDITIONS_URL')
1878
- ) {
1879
- <ion-text> & </ion-text>
1880
- }
1881
- @if (translationExists('IDEA_VARIABLES.PRIVACY_POLICY_URL')) {
1882
- <a
1883
- ion-text
1884
- testId="openPrivacyPolicyButton"
1885
- color="primary"
1886
- target="_blank"
1887
- [title]="'IDEA_AUTH.PRIVACY_POLICY_HINT' | translate"
1888
- [href]="'IDEA_VARIABLES.PRIVACY_POLICY_URL' | translate"
1889
- >
1890
- {{ 'IDEA_AUTH.PRIVACY_POLICY' | translate }}
1891
- </a>
1892
- }
1893
- </ion-col>
1894
- </ion-row>
1895
- }
1896
- <ion-button
1897
- testId="signInButton"
1898
- expand="block"
1899
- [disabled]="!agreementsCheck"
1900
- [title]="'IDEA_AUTH.SIGN_IN_HINT' | translate"
1901
- (click)="login()"
1902
- >
1903
- {{ 'IDEA_AUTH.SIGN_IN' | translate }}
1904
- </ion-button>
1905
- @if (registrationPossible) {
1883
+ @if (externalProviders.length && !doneExternalProviderCheck) {
1906
1884
  <ion-button
1907
- testId="createAnAccountButton"
1885
+ testId="continueButton"
1886
+ expand="block"
1887
+ [disabled]="!agreementsCheck"
1888
+ [title]="'IDEA_AUTH.CONTINUE_HINT' | translate"
1889
+ (click)="checkExternalProviderEmail()"
1890
+ >
1891
+ {{ 'IDEA_AUTH.CONTINUE' | translate }}
1892
+ </ion-button>
1893
+ } @else {
1894
+ <ion-item>
1895
+ <ion-label position="inline">
1896
+ <ion-icon name="key" color="primary" />
1897
+ </ion-label>
1898
+ <ion-input
1899
+ testId="signin.password"
1900
+ id="current-password"
1901
+ type="password"
1902
+ spellcheck="false"
1903
+ autocorrect="off"
1904
+ autocomplete="current-password"
1905
+ [clearOnEdit]="false"
1906
+ [placeholder]="'IDEA_AUTH.PASSWORD' | translate"
1907
+ [title]="'IDEA_AUTH.PASSWORD_HINT' | translate"
1908
+ [ngModelOptions]="{ standalone: true }"
1909
+ [(ngModel)]="password"
1910
+ (keyup.enter)="login()"
1911
+ />
1912
+ </ion-item>
1913
+ @if (
1914
+ !registrationPossible &&
1915
+ (translationExists('IDEA_VARIABLES.TERMS_AND_CONDITIONS_URL') ||
1916
+ translationExists('IDEA_VARIABLES.PRIVACY_POLICY_URL'))
1917
+ ) {
1918
+ <ion-row class="agreementsAcceptanceRow">
1919
+ <ion-col>
1920
+ <ion-checkbox
1921
+ size="small"
1922
+ [ngModelOptions]="{ standalone: true }"
1923
+ [(ngModel)]="agreementsCheck"
1924
+ />
1925
+ {{ 'IDEA_AUTH.AGREEMENTS_CHECK_LOGIN' | translate }}:
1926
+ @if (translationExists('IDEA_VARIABLES.TERMS_AND_CONDITIONS_URL')) {
1927
+ <a
1928
+ ion-text
1929
+ testId="openTermsAndConditionsButton"
1930
+ color="primary"
1931
+ target="_blank"
1932
+ [title]="'IDEA_AUTH.TERMS_AND_CONDITIONS_HINT' | translate"
1933
+ [href]="'IDEA_VARIABLES.TERMS_AND_CONDITIONS_URL' | translate"
1934
+ >
1935
+ {{ 'IDEA_AUTH.TERMS_AND_CONDITIONS' | translate }}
1936
+ </a>
1937
+ }
1938
+ @if (
1939
+ translationExists('IDEA_VARIABLES.PRIVACY_POLICY_URL') &&
1940
+ translationExists('IDEA_VARIABLES.TERMS_AND_CONDITIONS_URL')
1941
+ ) {
1942
+ <ion-text> & </ion-text>
1943
+ }
1944
+ @if (translationExists('IDEA_VARIABLES.PRIVACY_POLICY_URL')) {
1945
+ <a
1946
+ ion-text
1947
+ testId="openPrivacyPolicyButton"
1948
+ color="primary"
1949
+ target="_blank"
1950
+ [title]="'IDEA_AUTH.PRIVACY_POLICY_HINT' | translate"
1951
+ [href]="'IDEA_VARIABLES.PRIVACY_POLICY_URL' | translate"
1952
+ >
1953
+ {{ 'IDEA_AUTH.PRIVACY_POLICY' | translate }}
1954
+ </a>
1955
+ }
1956
+ </ion-col>
1957
+ </ion-row>
1958
+ }
1959
+ <ion-button
1960
+ testId="signInButton"
1961
+ expand="block"
1962
+ [disabled]="!agreementsCheck"
1963
+ [title]="'IDEA_AUTH.SIGN_IN_HINT' | translate"
1964
+ (click)="login()"
1965
+ >
1966
+ {{ 'IDEA_AUTH.SIGN_IN' | translate }}
1967
+ </ion-button>
1968
+ @if (registrationPossible) {
1969
+ <ion-button
1970
+ testId="createAnAccountButton"
1971
+ fill="clear"
1972
+ expand="block"
1973
+ class="smallCaseButton"
1974
+ [title]="'IDEA_AUTH.CREATE_AN_ACCOUNT_HINT' | translate"
1975
+ (click)="goToRegistration()"
1976
+ >
1977
+ {{ 'IDEA_AUTH.CREATE_AN_ACCOUNT' | translate }}
1978
+ </ion-button>
1979
+ }
1980
+ <ion-button
1981
+ testId="forgotPasswordButton"
1908
1982
  fill="clear"
1909
1983
  expand="block"
1910
1984
  class="smallCaseButton"
1911
- [title]="'IDEA_AUTH.CREATE_AN_ACCOUNT_HINT' | translate"
1912
- (click)="goToRegistration()"
1985
+ [title]="'IDEA_AUTH.I_FORGOT_MY_PASSWORD_HINT' | translate"
1986
+ (click)="goToForgotPassword()"
1913
1987
  >
1914
- {{ 'IDEA_AUTH.CREATE_AN_ACCOUNT' | translate }}
1988
+ {{ 'IDEA_AUTH.I_FORGOT_MY_PASSWORD' | translate }}
1915
1989
  </ion-button>
1916
1990
  }
1917
- <ion-button
1918
- testId="forgotPasswordButton"
1919
- fill="clear"
1920
- expand="block"
1921
- class="smallCaseButton"
1922
- [title]="'IDEA_AUTH.I_FORGOT_MY_PASSWORD_HINT' | translate"
1923
- (click)="goToForgotPassword()"
1924
- >
1925
- {{ 'IDEA_AUTH.I_FORGOT_MY_PASSWORD' | translate }}
1926
- </ion-button>
1927
1991
  @if (hasIntroPage) {
1928
1992
  <ion-button
1929
1993
  testId="backToIntroButton"
@@ -3684,6 +3748,156 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.9", ngImpor
3684
3748
  `, styles: [".flexBox{background:var(--idea-auth-background-color, var(--ion-color-white-shade))}.authCard{min-width:400px;max-width:550px;margin:0 10px;padding:20px;background-color:var(--idea-auth-card-background-color, var(--ion-color-white));border:1px solid var(--idea-auth-card-border-color, var(--ion-color-light));border-radius:10px;box-shadow:none;text-align:center}.authCard ion-img{width:150px;display:block;margin:0 auto}.authCard ion-card-title{font-family:auth-title;font-size:3em}.authCard ion-card-subtitle{font-family:auth-title;font-size:1.2em}.authCard ion-card-content{padding-top:16px}.authCard ion-card-content .errorBox{padding-left:10px;margin-bottom:20px;text-align:left;border-left:2px solid var(--ion-color-danger)}.authCard ion-card-content ion-item{--ion-item-background: var(--idea-auth-card-background-color, var(--ion-color-white));--border-color: var(--idea-auth-card-border-color, var(--ion-color-light))}.authCard ion-card-content ion-label[position=inline] ion-icon{padding-top:4px;padding-right:5px;font-size:1.3em}.authCard ion-card-content .agreementsAcceptanceRow{margin-top:15px;font-size:.9em}.authCard ion-card-content .agreementsAcceptanceRow ion-checkbox{vertical-align:bottom;margin-right:3px}.authCard ion-card-content ion-button:not([slot=end]){margin-top:15px}.authCard ion-card-content .smallCaseButton{text-transform:none;letter-spacing:normal;font-size:1em}@media only screen and (max-width: 500px){.authCard{background-color:transparent;border:none}}.ideaLogo{width:65px;margin:40px auto 0;cursor:pointer}.passwordPolicyPopover{--width: 350px}\n"] }]
3685
3749
  }] });
3686
3750
 
3751
+ class IDEAOktaPage {
3752
+ constructor() {
3753
+ this._route = inject(ActivatedRoute);
3754
+ this._router = inject(Router);
3755
+ this._env = inject(IDEAEnvironment);
3756
+ this.go = false;
3757
+ this.domain = this._env.aws.cognito.domain;
3758
+ this.clientId = this._env.aws.cognito.userPoolClientId;
3759
+ this.redirectURI = `${window.location.origin}${window.location.pathname}`; // Cognito client must include this callback
3760
+ this.scope = 'email openid profile aws.cognito.signin.user.admin'; // Cognito client config must match
3761
+ this.title = this._env.idea.auth.title;
3762
+ this.darkMode = window.matchMedia('(prefers-color-scheme: dark)').matches;
3763
+ }
3764
+ ionViewWillEnter() {
3765
+ this.identityProvider = this._route.snapshot.queryParamMap.get('provider');
3766
+ this.code = this._route.snapshot.queryParamMap.get('code');
3767
+ this.go = !!this._route.snapshot.queryParamMap.get('go');
3768
+ this._router.navigate([], { queryParams: {}, replaceUrl: true });
3769
+ if (this.code)
3770
+ this.endLogin();
3771
+ else if (this.identityProvider && this.go)
3772
+ this.startLogin();
3773
+ }
3774
+ startLogin() {
3775
+ const url = `https://${this.domain}/oauth2/authorize`;
3776
+ const params = new URLSearchParams();
3777
+ params.append('identity_provider', this.identityProvider);
3778
+ params.append('redirect_uri', this.redirectURI);
3779
+ params.append('response_type', 'code');
3780
+ params.append('client_id', this.clientId);
3781
+ params.append('scope', this.scope);
3782
+ window.location.assign(`${url}?${params.toString()}`);
3783
+ }
3784
+ async endLogin() {
3785
+ const url = `https://${this.domain}/oauth2/token`;
3786
+ const params = new URLSearchParams();
3787
+ params.append('code', this.code);
3788
+ params.append('grant_type', 'authorization_code');
3789
+ params.append('redirect_uri', this.redirectURI);
3790
+ params.append('client_id', this.clientId);
3791
+ const headers = { 'Content-Type': 'application/x-www-form-urlencoded' };
3792
+ const response = await fetch(url, { method: 'POST', headers, body: params.toString() });
3793
+ if (!response.ok)
3794
+ throw new Error(`HTTP error! Status: ${response.status}`);
3795
+ const { id_token, access_token, refresh_token } = await response.json();
3796
+ storeCognitoSession(id_token, access_token, refresh_token);
3797
+ window.location.assign('');
3798
+ }
3799
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: IDEAOktaPage, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3800
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.9", type: IDEAOktaPage, isStandalone: true, selector: "okta", ngImport: i0, template: `
3801
+ @if (!code && !go) {
3802
+ <ion-content>
3803
+ <div class="flexBox">
3804
+ <ion-card class="authCard">
3805
+ <ion-img
3806
+ [title]="'IDEA_AUTH.SERVICE_LOGO' | translate"
3807
+ [src]="darkMode ? 'assets/icons/icon-auth-alt.svg' : 'assets/icons/icon-auth.svg'"
3808
+ />
3809
+ @if (title) {
3810
+ <ion-card-header>
3811
+ <ion-card-title color="primary">{{ title }}</ion-card-title>
3812
+ </ion-card-header>
3813
+ }
3814
+ <ion-card-content>
3815
+ @if (identityProvider) {
3816
+ <ion-button expand="block" [title]="'IDEA_AUTH.SIGN_IN_HINT' | translate" (click)="startLogin()">
3817
+ {{ 'IDEA_AUTH.SIGN_IN' | translate }}
3818
+ </ion-button>
3819
+ } @else {
3820
+ <p testId="signin.error" class="errorBox">
3821
+ <b>{{ 'IDEA_AUTH.UNKNOWN_PROVIDER' | translate }}.</b>
3822
+ </p>
3823
+ }
3824
+ </ion-card-content>
3825
+ </ion-card>
3826
+ </div>
3827
+ </ion-content>
3828
+ }
3829
+ `, isInline: true, dependencies: [{ kind: "pipe", type: IDEATranslatePipe, name: "translate" }, { kind: "component", type: IonContent, selector: "ion-content", inputs: ["color", "fixedSlotPlacement", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: IonCard, selector: "ion-card", inputs: ["button", "color", "disabled", "download", "href", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: IonImg, selector: "ion-img", inputs: ["alt", "src"] }, { kind: "component", type: IonCardHeader, selector: "ion-card-header", inputs: ["color", "mode", "translucent"] }, { kind: "component", type: IonCardTitle, selector: "ion-card-title", inputs: ["color", "mode"] }, { kind: "component", type: IonCardContent, selector: "ion-card-content", inputs: ["mode"] }, { kind: "component", type: IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }] }); }
3830
+ }
3831
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: IDEAOktaPage, decorators: [{
3832
+ type: Component,
3833
+ args: [{
3834
+ selector: 'okta',
3835
+ standalone: true,
3836
+ imports: [IDEATranslatePipe, IonContent, IonCard, IonImg, IonCardHeader, IonCardTitle, IonCardContent, IonButton],
3837
+ template: `
3838
+ @if (!code && !go) {
3839
+ <ion-content>
3840
+ <div class="flexBox">
3841
+ <ion-card class="authCard">
3842
+ <ion-img
3843
+ [title]="'IDEA_AUTH.SERVICE_LOGO' | translate"
3844
+ [src]="darkMode ? 'assets/icons/icon-auth-alt.svg' : 'assets/icons/icon-auth.svg'"
3845
+ />
3846
+ @if (title) {
3847
+ <ion-card-header>
3848
+ <ion-card-title color="primary">{{ title }}</ion-card-title>
3849
+ </ion-card-header>
3850
+ }
3851
+ <ion-card-content>
3852
+ @if (identityProvider) {
3853
+ <ion-button expand="block" [title]="'IDEA_AUTH.SIGN_IN_HINT' | translate" (click)="startLogin()">
3854
+ {{ 'IDEA_AUTH.SIGN_IN' | translate }}
3855
+ </ion-button>
3856
+ } @else {
3857
+ <p testId="signin.error" class="errorBox">
3858
+ <b>{{ 'IDEA_AUTH.UNKNOWN_PROVIDER' | translate }}.</b>
3859
+ </p>
3860
+ }
3861
+ </ion-card-content>
3862
+ </ion-card>
3863
+ </div>
3864
+ </ion-content>
3865
+ }
3866
+ `
3867
+ }]
3868
+ }] });
3869
+ //
3870
+ // HELPERS
3871
+ //
3872
+ /**
3873
+ * Store the tokens and the info needed by Cognito's front-end lib to authenticate.
3874
+ */
3875
+ const storeCognitoSession = (idToken, accessToken, refreshToken) => {
3876
+ const { sub } = decodeJWTToken(idToken);
3877
+ const { client_id } = decodeJWTToken(accessToken);
3878
+ const prefix = `CognitoIdentityServiceProvider.${client_id}`;
3879
+ window.localStorage.setItem(`${prefix}.LastAuthUser`, sub);
3880
+ window.localStorage.setItem(`${prefix}.${sub}.accessToken`, accessToken);
3881
+ window.localStorage.setItem(`${prefix}.${sub}.idToken`, idToken);
3882
+ window.localStorage.setItem(`${prefix}.${sub}.refreshToken`, refreshToken);
3883
+ };
3884
+ /**
3885
+ * Decode a JWT token and return its payload as a JSON object.
3886
+ */
3887
+ const decodeJWTToken = (token) => {
3888
+ if (!token)
3889
+ return null;
3890
+ try {
3891
+ const base64Url = token.split('.')[1];
3892
+ const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
3893
+ return JSON.parse(window.atob(base64));
3894
+ }
3895
+ catch (error) {
3896
+ console.error('Error decoding token:', error);
3897
+ return null;
3898
+ }
3899
+ };
3900
+
3687
3901
  const routes = [
3688
3902
  { path: '', redirectTo: 'sign-in', pathMatch: 'full' },
3689
3903
  { path: 'sign-in', component: IDEASignInPage },
@@ -3693,7 +3907,8 @@ const routes = [
3693
3907
  { path: 'setup-mfa', component: IDEASetupMFAPage },
3694
3908
  { path: 'resend-link', component: IDEAResendLinkPage },
3695
3909
  { path: 'forgot-password', component: IDEAForgotPasswordPage },
3696
- { path: 'confirm-password', component: IDEAConfirmPasswordPage }
3910
+ { path: 'confirm-password', component: IDEAConfirmPasswordPage },
3911
+ { path: 'okta', component: IDEAOktaPage }
3697
3912
  ];
3698
3913
 
3699
3914
  /**