@idea-ionic/auth 8.4.2 → 8.5.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.
@@ -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;
@@ -1545,6 +1546,7 @@ class IDEASignInPage {
1545
1546
  this.registrationPossible = this._env.idea.auth.registrationIsPossible;
1546
1547
  this.hasIntroPage = this._env.idea.auth.hasIntroPage;
1547
1548
  this.website = this._env.idea.auth.website;
1549
+ this.externalProviders = this._env.aws.cognito?.externalProviders || [];
1548
1550
  }
1549
1551
  ionViewDidEnter() {
1550
1552
  // manage the scenario in which we just created a new account (show a explanatory message: email must be confirmed)
@@ -1580,6 +1582,15 @@ class IDEASignInPage {
1580
1582
  this._loading.hide();
1581
1583
  }
1582
1584
  }
1585
+ checkForExternalProviderEmail() {
1586
+ if (isEmpty(this.email, 'email'))
1587
+ return;
1588
+ const emailDomain = this.email.split('@')[1];
1589
+ this.doneExternalProviderCheck = true;
1590
+ const provider = this.externalProviders.find(p => p.emailDomains.includes(emailDomain));
1591
+ if (provider)
1592
+ this._nav.navigateForward(['auth', provider.type], { queryParams: { provider: provider.name, go: true } });
1593
+ }
1583
1594
  goToIntro() {
1584
1595
  this._nav.navigateBack(['intro']);
1585
1596
  }
@@ -1638,103 +1649,130 @@ class IDEASignInPage {
1638
1649
  autocomplete="email"
1639
1650
  [placeholder]="'IDEA_AUTH.EMAIL' | translate"
1640
1651
  [title]="'IDEA_AUTH.EMAIL_HINT' | translate"
1652
+ [disabled]="externalProviders.length && doneExternalProviderCheck"
1641
1653
  [ngModelOptions]="{ standalone: true }"
1642
1654
  [(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()"
1655
+ (keyup.enter)="externalProviders.length ? checkForExternalProviderEmail() : login()"
1663
1656
  />
1657
+ @if (externalProviders.length && doneExternalProviderCheck) {
1658
+ <ion-button
1659
+ slot="end"
1660
+ fill="clear"
1661
+ [title]="'IDEA_AUTH.EDIT_EMAIL' | translate"
1662
+ (click)="doneExternalProviderCheck = false"
1663
+ >
1664
+ <ion-icon icon="pencil" slot="icon-only" />
1665
+ </ion-button>
1666
+ }
1664
1667
  </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) {
1668
+ @if (externalProviders.length && !doneExternalProviderCheck) {
1669
+ <ion-button
1670
+ testId="continueButton"
1671
+ expand="block"
1672
+ [disabled]="!agreementsCheck"
1673
+ [title]="'IDEA_AUTH.CONTINUE_HINT' | translate"
1674
+ (click)="checkExternalProviderEmail()"
1675
+ >
1676
+ {{ 'IDEA_AUTH.CONTINUE' | translate }}
1677
+ </ion-button>
1678
+ } @else {
1679
+ <ion-item>
1680
+ <ion-label position="inline">
1681
+ <ion-icon name="key" color="primary" />
1682
+ </ion-label>
1683
+ <ion-input
1684
+ testId="signin.password"
1685
+ id="current-password"
1686
+ type="password"
1687
+ spellcheck="false"
1688
+ autocorrect="off"
1689
+ autocomplete="current-password"
1690
+ [clearOnEdit]="false"
1691
+ [placeholder]="'IDEA_AUTH.PASSWORD' | translate"
1692
+ [title]="'IDEA_AUTH.PASSWORD_HINT' | translate"
1693
+ [ngModelOptions]="{ standalone: true }"
1694
+ [(ngModel)]="password"
1695
+ (keyup.enter)="login()"
1696
+ />
1697
+ </ion-item>
1698
+ @if (
1699
+ !registrationPossible &&
1700
+ (translationExists('IDEA_VARIABLES.TERMS_AND_CONDITIONS_URL') ||
1701
+ translationExists('IDEA_VARIABLES.PRIVACY_POLICY_URL'))
1702
+ ) {
1703
+ <ion-row class="agreementsAcceptanceRow">
1704
+ <ion-col>
1705
+ <ion-checkbox
1706
+ size="small"
1707
+ [ngModelOptions]="{ standalone: true }"
1708
+ [(ngModel)]="agreementsCheck"
1709
+ />
1710
+ {{ 'IDEA_AUTH.AGREEMENTS_CHECK_LOGIN' | translate }}:
1711
+ @if (translationExists('IDEA_VARIABLES.TERMS_AND_CONDITIONS_URL')) {
1712
+ <a
1713
+ ion-text
1714
+ testId="openTermsAndConditionsButton"
1715
+ color="primary"
1716
+ target="_blank"
1717
+ [title]="'IDEA_AUTH.TERMS_AND_CONDITIONS_HINT' | translate"
1718
+ [href]="'IDEA_VARIABLES.TERMS_AND_CONDITIONS_URL' | translate"
1719
+ >
1720
+ {{ 'IDEA_AUTH.TERMS_AND_CONDITIONS' | translate }}
1721
+ </a>
1722
+ }
1723
+ @if (
1724
+ translationExists('IDEA_VARIABLES.PRIVACY_POLICY_URL') &&
1725
+ translationExists('IDEA_VARIABLES.TERMS_AND_CONDITIONS_URL')
1726
+ ) {
1727
+ <ion-text> & </ion-text>
1728
+ }
1729
+ @if (translationExists('IDEA_VARIABLES.PRIVACY_POLICY_URL')) {
1730
+ <a
1731
+ ion-text
1732
+ testId="openPrivacyPolicyButton"
1733
+ color="primary"
1734
+ target="_blank"
1735
+ [title]="'IDEA_AUTH.PRIVACY_POLICY_HINT' | translate"
1736
+ [href]="'IDEA_VARIABLES.PRIVACY_POLICY_URL' | translate"
1737
+ >
1738
+ {{ 'IDEA_AUTH.PRIVACY_POLICY' | translate }}
1739
+ </a>
1740
+ }
1741
+ </ion-col>
1742
+ </ion-row>
1743
+ }
1744
+ <ion-button
1745
+ testId="signInButton"
1746
+ expand="block"
1747
+ [disabled]="!agreementsCheck"
1748
+ [title]="'IDEA_AUTH.SIGN_IN_HINT' | translate"
1749
+ (click)="login()"
1750
+ >
1751
+ {{ 'IDEA_AUTH.SIGN_IN' | translate }}
1752
+ </ion-button>
1753
+ @if (registrationPossible) {
1754
+ <ion-button
1755
+ testId="createAnAccountButton"
1756
+ fill="clear"
1757
+ expand="block"
1758
+ class="smallCaseButton"
1759
+ [title]="'IDEA_AUTH.CREATE_AN_ACCOUNT_HINT' | translate"
1760
+ (click)="goToRegistration()"
1761
+ >
1762
+ {{ 'IDEA_AUTH.CREATE_AN_ACCOUNT' | translate }}
1763
+ </ion-button>
1764
+ }
1717
1765
  <ion-button
1718
- testId="createAnAccountButton"
1766
+ testId="forgotPasswordButton"
1719
1767
  fill="clear"
1720
1768
  expand="block"
1721
1769
  class="smallCaseButton"
1722
- [title]="'IDEA_AUTH.CREATE_AN_ACCOUNT_HINT' | translate"
1723
- (click)="goToRegistration()"
1770
+ [title]="'IDEA_AUTH.I_FORGOT_MY_PASSWORD_HINT' | translate"
1771
+ (click)="goToForgotPassword()"
1724
1772
  >
1725
- {{ 'IDEA_AUTH.CREATE_AN_ACCOUNT' | translate }}
1773
+ {{ 'IDEA_AUTH.I_FORGOT_MY_PASSWORD' | translate }}
1726
1774
  </ion-button>
1727
1775
  }
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
1776
  @if (hasIntroPage) {
1739
1777
  <ion-button
1740
1778
  testId="backToIntroButton"
@@ -1827,103 +1865,130 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.9", ngImpor
1827
1865
  autocomplete="email"
1828
1866
  [placeholder]="'IDEA_AUTH.EMAIL' | translate"
1829
1867
  [title]="'IDEA_AUTH.EMAIL_HINT' | translate"
1868
+ [disabled]="externalProviders.length && doneExternalProviderCheck"
1830
1869
  [ngModelOptions]="{ standalone: true }"
1831
1870
  [(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()"
1871
+ (keyup.enter)="externalProviders.length ? checkForExternalProviderEmail() : login()"
1852
1872
  />
1873
+ @if (externalProviders.length && doneExternalProviderCheck) {
1874
+ <ion-button
1875
+ slot="end"
1876
+ fill="clear"
1877
+ [title]="'IDEA_AUTH.EDIT_EMAIL' | translate"
1878
+ (click)="doneExternalProviderCheck = false"
1879
+ >
1880
+ <ion-icon icon="pencil" slot="icon-only" />
1881
+ </ion-button>
1882
+ }
1853
1883
  </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) {
1884
+ @if (externalProviders.length && !doneExternalProviderCheck) {
1906
1885
  <ion-button
1907
- testId="createAnAccountButton"
1886
+ testId="continueButton"
1887
+ expand="block"
1888
+ [disabled]="!agreementsCheck"
1889
+ [title]="'IDEA_AUTH.CONTINUE_HINT' | translate"
1890
+ (click)="checkExternalProviderEmail()"
1891
+ >
1892
+ {{ 'IDEA_AUTH.CONTINUE' | translate }}
1893
+ </ion-button>
1894
+ } @else {
1895
+ <ion-item>
1896
+ <ion-label position="inline">
1897
+ <ion-icon name="key" color="primary" />
1898
+ </ion-label>
1899
+ <ion-input
1900
+ testId="signin.password"
1901
+ id="current-password"
1902
+ type="password"
1903
+ spellcheck="false"
1904
+ autocorrect="off"
1905
+ autocomplete="current-password"
1906
+ [clearOnEdit]="false"
1907
+ [placeholder]="'IDEA_AUTH.PASSWORD' | translate"
1908
+ [title]="'IDEA_AUTH.PASSWORD_HINT' | translate"
1909
+ [ngModelOptions]="{ standalone: true }"
1910
+ [(ngModel)]="password"
1911
+ (keyup.enter)="login()"
1912
+ />
1913
+ </ion-item>
1914
+ @if (
1915
+ !registrationPossible &&
1916
+ (translationExists('IDEA_VARIABLES.TERMS_AND_CONDITIONS_URL') ||
1917
+ translationExists('IDEA_VARIABLES.PRIVACY_POLICY_URL'))
1918
+ ) {
1919
+ <ion-row class="agreementsAcceptanceRow">
1920
+ <ion-col>
1921
+ <ion-checkbox
1922
+ size="small"
1923
+ [ngModelOptions]="{ standalone: true }"
1924
+ [(ngModel)]="agreementsCheck"
1925
+ />
1926
+ {{ 'IDEA_AUTH.AGREEMENTS_CHECK_LOGIN' | translate }}:
1927
+ @if (translationExists('IDEA_VARIABLES.TERMS_AND_CONDITIONS_URL')) {
1928
+ <a
1929
+ ion-text
1930
+ testId="openTermsAndConditionsButton"
1931
+ color="primary"
1932
+ target="_blank"
1933
+ [title]="'IDEA_AUTH.TERMS_AND_CONDITIONS_HINT' | translate"
1934
+ [href]="'IDEA_VARIABLES.TERMS_AND_CONDITIONS_URL' | translate"
1935
+ >
1936
+ {{ 'IDEA_AUTH.TERMS_AND_CONDITIONS' | translate }}
1937
+ </a>
1938
+ }
1939
+ @if (
1940
+ translationExists('IDEA_VARIABLES.PRIVACY_POLICY_URL') &&
1941
+ translationExists('IDEA_VARIABLES.TERMS_AND_CONDITIONS_URL')
1942
+ ) {
1943
+ <ion-text> & </ion-text>
1944
+ }
1945
+ @if (translationExists('IDEA_VARIABLES.PRIVACY_POLICY_URL')) {
1946
+ <a
1947
+ ion-text
1948
+ testId="openPrivacyPolicyButton"
1949
+ color="primary"
1950
+ target="_blank"
1951
+ [title]="'IDEA_AUTH.PRIVACY_POLICY_HINT' | translate"
1952
+ [href]="'IDEA_VARIABLES.PRIVACY_POLICY_URL' | translate"
1953
+ >
1954
+ {{ 'IDEA_AUTH.PRIVACY_POLICY' | translate }}
1955
+ </a>
1956
+ }
1957
+ </ion-col>
1958
+ </ion-row>
1959
+ }
1960
+ <ion-button
1961
+ testId="signInButton"
1962
+ expand="block"
1963
+ [disabled]="!agreementsCheck"
1964
+ [title]="'IDEA_AUTH.SIGN_IN_HINT' | translate"
1965
+ (click)="login()"
1966
+ >
1967
+ {{ 'IDEA_AUTH.SIGN_IN' | translate }}
1968
+ </ion-button>
1969
+ @if (registrationPossible) {
1970
+ <ion-button
1971
+ testId="createAnAccountButton"
1972
+ fill="clear"
1973
+ expand="block"
1974
+ class="smallCaseButton"
1975
+ [title]="'IDEA_AUTH.CREATE_AN_ACCOUNT_HINT' | translate"
1976
+ (click)="goToRegistration()"
1977
+ >
1978
+ {{ 'IDEA_AUTH.CREATE_AN_ACCOUNT' | translate }}
1979
+ </ion-button>
1980
+ }
1981
+ <ion-button
1982
+ testId="forgotPasswordButton"
1908
1983
  fill="clear"
1909
1984
  expand="block"
1910
1985
  class="smallCaseButton"
1911
- [title]="'IDEA_AUTH.CREATE_AN_ACCOUNT_HINT' | translate"
1912
- (click)="goToRegistration()"
1986
+ [title]="'IDEA_AUTH.I_FORGOT_MY_PASSWORD_HINT' | translate"
1987
+ (click)="goToForgotPassword()"
1913
1988
  >
1914
- {{ 'IDEA_AUTH.CREATE_AN_ACCOUNT' | translate }}
1989
+ {{ 'IDEA_AUTH.I_FORGOT_MY_PASSWORD' | translate }}
1915
1990
  </ion-button>
1916
1991
  }
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
1992
  @if (hasIntroPage) {
1928
1993
  <ion-button
1929
1994
  testId="backToIntroButton"
@@ -3684,6 +3749,156 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.9", ngImpor
3684
3749
  `, 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
3750
  }] });
3686
3751
 
3752
+ class IDEAOktaPage {
3753
+ constructor() {
3754
+ this._route = inject(ActivatedRoute);
3755
+ this._router = inject(Router);
3756
+ this._env = inject(IDEAEnvironment);
3757
+ this.go = false;
3758
+ this.domain = this._env.aws.cognito.domain;
3759
+ this.clientId = this._env.aws.cognito.userPoolClientId;
3760
+ this.redirectURI = `${window.location.origin}${window.location.pathname}`; // Cognito client must include this callback
3761
+ this.scope = 'email openid profile aws.cognito.signin.user.admin'; // Cognito client config must match
3762
+ this.title = this._env.idea.auth.title;
3763
+ this.darkMode = window.matchMedia('(prefers-color-scheme: dark)').matches;
3764
+ }
3765
+ ionViewWillEnter() {
3766
+ this.identityProvider = this._route.snapshot.queryParamMap.get('provider');
3767
+ this.code = this._route.snapshot.queryParamMap.get('code');
3768
+ this.go = !!this._route.snapshot.queryParamMap.get('go');
3769
+ this._router.navigate([], { queryParams: {}, replaceUrl: true });
3770
+ if (this.code)
3771
+ this.endLogin();
3772
+ else if (this.identityProvider && this.go)
3773
+ this.startLogin();
3774
+ }
3775
+ startLogin() {
3776
+ const url = `https://${this.domain}/oauth2/authorize`;
3777
+ const params = new URLSearchParams();
3778
+ params.append('identity_provider', this.identityProvider);
3779
+ params.append('redirect_uri', this.redirectURI);
3780
+ params.append('response_type', 'code');
3781
+ params.append('client_id', this.clientId);
3782
+ params.append('scope', this.scope);
3783
+ window.location.assign(`${url}?${params.toString()}`);
3784
+ }
3785
+ async endLogin() {
3786
+ const url = `https://${this.domain}/oauth2/token`;
3787
+ const params = new URLSearchParams();
3788
+ params.append('code', this.code);
3789
+ params.append('grant_type', 'authorization_code');
3790
+ params.append('redirect_uri', this.redirectURI);
3791
+ params.append('client_id', this.clientId);
3792
+ const headers = { 'Content-Type': 'application/x-www-form-urlencoded' };
3793
+ const response = await fetch(url, { method: 'POST', headers, body: params.toString() });
3794
+ if (!response.ok)
3795
+ throw new Error(`HTTP error! Status: ${response.status}`);
3796
+ const { id_token, access_token, refresh_token } = await response.json();
3797
+ storeCognitoSession(id_token, access_token, refresh_token);
3798
+ window.location.assign('');
3799
+ }
3800
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: IDEAOktaPage, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3801
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.9", type: IDEAOktaPage, isStandalone: true, selector: "okta", ngImport: i0, template: `
3802
+ @if (!code && !go) {
3803
+ <ion-content>
3804
+ <div class="flexBox">
3805
+ <ion-card class="authCard">
3806
+ <ion-img
3807
+ [title]="'IDEA_AUTH.SERVICE_LOGO' | translate"
3808
+ [src]="darkMode ? 'assets/icons/icon-auth-alt.svg' : 'assets/icons/icon-auth.svg'"
3809
+ />
3810
+ @if (title) {
3811
+ <ion-card-header>
3812
+ <ion-card-title color="primary">{{ title }}</ion-card-title>
3813
+ </ion-card-header>
3814
+ }
3815
+ <ion-card-content>
3816
+ @if (identityProvider) {
3817
+ <ion-button expand="block" [title]="'IDEA_AUTH.SIGN_IN_HINT' | translate" (click)="startLogin()">
3818
+ {{ 'IDEA_AUTH.SIGN_IN' | translate }}
3819
+ </ion-button>
3820
+ } @else {
3821
+ <p testId="signin.error" class="errorBox">
3822
+ <b>{{ 'IDEA_AUTH.UNKNOWN_PROVIDER' | translate }}.</b>
3823
+ </p>
3824
+ }
3825
+ </ion-card-content>
3826
+ </ion-card>
3827
+ </div>
3828
+ </ion-content>
3829
+ }
3830
+ `, 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"] }] }); }
3831
+ }
3832
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: IDEAOktaPage, decorators: [{
3833
+ type: Component,
3834
+ args: [{
3835
+ selector: 'okta',
3836
+ standalone: true,
3837
+ imports: [IDEATranslatePipe, IonContent, IonCard, IonImg, IonCardHeader, IonCardTitle, IonCardContent, IonButton],
3838
+ template: `
3839
+ @if (!code && !go) {
3840
+ <ion-content>
3841
+ <div class="flexBox">
3842
+ <ion-card class="authCard">
3843
+ <ion-img
3844
+ [title]="'IDEA_AUTH.SERVICE_LOGO' | translate"
3845
+ [src]="darkMode ? 'assets/icons/icon-auth-alt.svg' : 'assets/icons/icon-auth.svg'"
3846
+ />
3847
+ @if (title) {
3848
+ <ion-card-header>
3849
+ <ion-card-title color="primary">{{ title }}</ion-card-title>
3850
+ </ion-card-header>
3851
+ }
3852
+ <ion-card-content>
3853
+ @if (identityProvider) {
3854
+ <ion-button expand="block" [title]="'IDEA_AUTH.SIGN_IN_HINT' | translate" (click)="startLogin()">
3855
+ {{ 'IDEA_AUTH.SIGN_IN' | translate }}
3856
+ </ion-button>
3857
+ } @else {
3858
+ <p testId="signin.error" class="errorBox">
3859
+ <b>{{ 'IDEA_AUTH.UNKNOWN_PROVIDER' | translate }}.</b>
3860
+ </p>
3861
+ }
3862
+ </ion-card-content>
3863
+ </ion-card>
3864
+ </div>
3865
+ </ion-content>
3866
+ }
3867
+ `
3868
+ }]
3869
+ }] });
3870
+ //
3871
+ // HELPERS
3872
+ //
3873
+ /**
3874
+ * Store the tokens and the info needed by Cognito's front-end lib to authenticate.
3875
+ */
3876
+ const storeCognitoSession = (idToken, accessToken, refreshToken) => {
3877
+ const { sub } = decodeJWTToken(idToken);
3878
+ const { client_id } = decodeJWTToken(accessToken);
3879
+ const prefix = `CognitoIdentityServiceProvider.${client_id}`;
3880
+ window.localStorage.setItem(`${prefix}.LastAuthUser`, sub);
3881
+ window.localStorage.setItem(`${prefix}.${sub}.accessToken`, accessToken);
3882
+ window.localStorage.setItem(`${prefix}.${sub}.idToken`, idToken);
3883
+ window.localStorage.setItem(`${prefix}.${sub}.refreshToken`, refreshToken);
3884
+ };
3885
+ /**
3886
+ * Decode a JWT token and return its payload as a JSON object.
3887
+ */
3888
+ const decodeJWTToken = (token) => {
3889
+ if (!token)
3890
+ return null;
3891
+ try {
3892
+ const base64Url = token.split('.')[1];
3893
+ const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
3894
+ return JSON.parse(window.atob(base64));
3895
+ }
3896
+ catch (error) {
3897
+ console.error('Error decoding token:', error);
3898
+ return null;
3899
+ }
3900
+ };
3901
+
3687
3902
  const routes = [
3688
3903
  { path: '', redirectTo: 'sign-in', pathMatch: 'full' },
3689
3904
  { path: 'sign-in', component: IDEASignInPage },
@@ -3693,7 +3908,8 @@ const routes = [
3693
3908
  { path: 'setup-mfa', component: IDEASetupMFAPage },
3694
3909
  { path: 'resend-link', component: IDEAResendLinkPage },
3695
3910
  { path: 'forgot-password', component: IDEAForgotPasswordPage },
3696
- { path: 'confirm-password', component: IDEAConfirmPasswordPage }
3911
+ { path: 'confirm-password', component: IDEAConfirmPasswordPage },
3912
+ { path: 'okta', component: IDEAOktaPage }
3697
3913
  ];
3698
3914
 
3699
3915
  /**