@atlasauth/angular 0.16.1 → 0.16.3

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.
@@ -4,7 +4,7 @@ import { isPlatformBrowser, DOCUMENT } from '@angular/common';
4
4
  import { toSignal } from '@angular/core/rxjs-interop';
5
5
  import { BehaviorSubject, Subject, combineLatest, firstValueFrom } from 'rxjs';
6
6
  import { map, distinctUntilChanged, filter, take } from 'rxjs/operators';
7
- import { createOrganizationAuditLog, POLICY_STRINGS, TokenCache, SignOutNotifier, resolveFrontendApi, withDevBrowser, catalogLocale, configureReferralCapture, FapiClient, SignOutGuard, localizedErrorMessage, reauthenticate, getTemplateToken, getProviderToken, signOutReasonFromClient, readRedirectResult, completeOAuthPopupLanding, mapOAuthPopupPayload, stripRedirectParams, closeOAuthPopupAfterSignIn, SDK_HEADER, sdkHeaderValue, reportSdkAdvisory, readExpiry, msUntilRefresh, policyTranslator, injectPolicyStyles, mountPolicyClickwrap, mountPolicyConsent, controllerForPending, mountPolicyAcceptStep, joinTitles, formatPolicyDate, PolicyGateController, enrollmentOptionKey, groupSetupKey, enrollmentSubmitKey, nextStep, awaitingSignUpLink, createEmailLinkConfirmation, canDownloadRecoveryCodes, copyRecoveryCodes, downloadRecoveryCodes, createCaptchaController, atlasCaptchaLabels, PolicyAcceptanceController, flowFromPending, initialFlow, createMfaEnrollment, createPasskeySignInController, startSignInWithAtlas, mfaChoicesFrom, fetchInstanceSettings, fetchLastSignInMethod, offeredFirstFactors, preparePasswordHoldProof, prepareFactor, signInCaptchaOptions, flowNeedsCaptcha, planFirstFactor, attemptPasswordHoldProof, advanceWithCaptcha, loadPolicies, createSignUp, prepareSecondFactorCode, resendSignUpVerification, createPasskeySignUp, finishPasskeySignUp, pendingRecoveryCodes, acknowledgeRecoveryCodes, pendingExistingAccountNotice, acknowledgeExistingAccount, shouldKeepPolling, pollAttempt, emailLinkFromUrl, lastUsedMethodLabel, signUp, passkeysSupported, confirmPolicies, existingAccountResetUrl, signUpProvedByLink, signInAfterProofUrl, awaitingPasskey, listCibaRequests, getCibaPin, decideCibaRequest, setCibaPin, clearCibaPin, OrganizationProfileController, describeAuditAction, actorFromClaims } from '@atlasauth/js';
7
+ import { createOrganizationAuditLog, POLICY_STRINGS, TokenCache, SignOutNotifier, resolveFrontendApi, withDevBrowser, catalogLocale, configureReferralCapture, FapiClient, SignOutGuard, localizedErrorMessage, reauthenticate, getTemplateToken, getProviderToken, signOutReasonFromClient, readRedirectResult, completeOAuthPopupLanding, mapOAuthPopupPayload, stripRedirectParams, closeOAuthPopupAfterSignIn, SDK_HEADER, sdkHeaderValue, reportSdkAdvisory, readExpiry, msUntilRefresh, policyTranslator, injectPolicyStyles, mountPolicyClickwrap, mountPolicyConsent, controllerForPending, mountPolicyAcceptStep, joinTitles, formatPolicyDate, PolicyGateController, enrollmentOptionKey, groupSetupKey, enrollmentSubmitKey, nextStep, awaitingSignUpLink, createEmailLinkConfirmation, canDownloadRecoveryCodes, copyRecoveryCodes, downloadRecoveryCodes, createCaptchaController, atlasCaptchaLabels, PolicyAcceptanceController, flowFromPending, initialFlow, createMfaEnrollment, createPasskeySignInController, startSignInWithAtlas, mfaChoicesFrom, fetchInstanceSettings, fetchLastSignInMethod, offeredFirstFactors, preparePasswordHoldProof, prepareFactor, signInCaptchaOptions, flowNeedsCaptcha, sendOnlyWaySecondFactorCode, continueOnHostedPage, planFirstFactor, attemptPasswordHoldProof, advanceWithCaptcha, loadPolicies, createSignUp, prepareSecondFactorCode, resendSignUpVerification, createPasskeySignUp, finishPasskeySignUp, pendingRecoveryCodes, acknowledgeRecoveryCodes, pendingExistingAccountNotice, acknowledgeExistingAccount, shouldKeepPolling, pollAttempt, emailLinkFromUrl, lastUsedMethodLabel, signUp, passkeysSupported, confirmPolicies, existingAccountResetUrl, signUpProvedByLink, signInAfterProofUrl, awaitingPasskey, listCibaRequests, getCibaPin, decideCibaRequest, setCibaPin, clearCibaPin, OrganizationProfileController, describeAuditAction, actorFromClaims } from '@atlasauth/js';
8
8
  export { FapiClient, SIGN_OUT_EVENT, SignOutError, advance, flowFromPending, getProviderToken, initialFlow, onAtlasSignOut, pollAttempt, prepareFactor, requestForStep, shouldKeepPolling } from '@atlasauth/js';
9
9
  import { evaluate } from '@atlasauth/authz';
10
10
  export { evaluate, hasFromClaims } from '@atlasauth/authz';
@@ -421,7 +421,7 @@ function withFallback(locale) {
421
421
  }
422
422
 
423
423
  /** This package's version, sent as `X-Atlas-SDK` (kept in step with package.json by a test). */
424
- const ATLAS_ANGULAR_VERSION = '0.16.1';
424
+ const ATLAS_ANGULAR_VERSION = '0.16.3';
425
425
  const ATLAS_ANGULAR_SDK = { name: '@atlasauth/angular', version: ATLAS_ANGULAR_VERSION };
426
426
 
427
427
  /**
@@ -1534,6 +1534,9 @@ class AttemptFlowComponent {
1534
1534
  /** `AtlasFlowController.emailSecondFactor()`: in flight, where it went, or why not. */
1535
1535
  emailedSecondFactor = null;
1536
1536
  emailSecondFactor = new EventEmitter();
1537
+ /** "Use a passkey" at the second factor (`mfaChoices().passkey`): leaves for the hosted page. */
1538
+ offerPasskeySecondFactor = false;
1539
+ passkeySecondFactor = new EventEmitter();
1537
1540
  /**
1538
1541
  * Offer "Text me a code instead" at the second factor (where the app offers
1539
1542
  * texted codes, `AtlasFlowController.mfaChoices().sms`); `smsSecondFactor`
@@ -1633,7 +1636,7 @@ class AttemptFlowComponent {
1633
1636
  return this.errors.filter((error) => !error.param);
1634
1637
  }
1635
1638
  signInLink(error) {
1636
- return this.step().kind === 'collect_email_code' && error.code === 'IDENTIFIER_EXISTS' ? this.signInHref : null;
1639
+ return this.step().kind === 'collect_email_code' && error.code === 'IDENTIFIER_EXISTS' && error.meta?.['identifier'] !== 'username' ? this.signInHref : null;
1637
1640
  }
1638
1641
  fieldErrorsFor(name) {
1639
1642
  return this.errors.filter((error) => error.param === name);
@@ -1761,7 +1764,7 @@ class AttemptFlowComponent {
1761
1764
  }
1762
1765
  }
1763
1766
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: AttemptFlowComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1764
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: AttemptFlowComponent, isStandalone: true, selector: "atlas-attempt-flow", inputs: { attempt: "attempt", busy: "busy", errors: "errors", sessionKept: "sessionKept", titleKey: "titleKey", signInHref: "signInHref", captcha: "captcha", firstFactors: "firstFactors", preparedFactor: "preparedFactor", passwordHold: "passwordHold", holdProof: "holdProof", offerEmailSecondFactor: "offerEmailSecondFactor", emailedSecondFactor: "emailedSecondFactor", offerSmsSecondFactor: "offerSmsSecondFactor", textedSecondFactor: "textedSecondFactor", enrollment: "enrollment", enrollmentState: "enrollmentState", emailVerification: "emailVerification", matchCode: "matchCode" }, outputs: { submitFlow: "submitFlow", chooseFactor: "chooseFactor", usePassword: "usePassword", emailSecondFactor: "emailSecondFactor", smsSecondFactor: "smsSecondFactor", resend: "resend", policyDeclined: "policyDeclined" }, ngImport: i0, template: `
1767
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: AttemptFlowComponent, isStandalone: true, selector: "atlas-attempt-flow", inputs: { attempt: "attempt", busy: "busy", errors: "errors", sessionKept: "sessionKept", titleKey: "titleKey", signInHref: "signInHref", captcha: "captcha", firstFactors: "firstFactors", preparedFactor: "preparedFactor", passwordHold: "passwordHold", holdProof: "holdProof", offerEmailSecondFactor: "offerEmailSecondFactor", emailedSecondFactor: "emailedSecondFactor", offerPasskeySecondFactor: "offerPasskeySecondFactor", offerSmsSecondFactor: "offerSmsSecondFactor", textedSecondFactor: "textedSecondFactor", enrollment: "enrollment", enrollmentState: "enrollmentState", emailVerification: "emailVerification", matchCode: "matchCode" }, outputs: { submitFlow: "submitFlow", chooseFactor: "chooseFactor", usePassword: "usePassword", emailSecondFactor: "emailSecondFactor", passkeySecondFactor: "passkeySecondFactor", smsSecondFactor: "smsSecondFactor", resend: "resend", policyDeclined: "policyDeclined" }, ngImport: i0, template: `
1765
1768
  <div [class]="cls('card', 'atlas-card')">
1766
1769
  <h1 [class]="cls('headerTitle', 'atlas-title')">{{ t(titleKey) }}</h1>
1767
1770
 
@@ -1984,6 +1987,18 @@ class AttemptFlowComponent {
1984
1987
  }
1985
1988
  }
1986
1989
 
1990
+ @if (offerPasskeySecondFactor && step().kind === 'collect_second_factor') {
1991
+ <button
1992
+ [class]="cls('formButtonSecondary', 'atlas-button-secondary')"
1993
+ type="button"
1994
+ data-atlas="passkey-second-factor"
1995
+ [disabled]="busy"
1996
+ (click)="passkeySecondFactor.emit()"
1997
+ >
1998
+ {{ t('mfa.enrollPasskeyOption') }}
1999
+ </button>
2000
+ }
2001
+
1987
2002
  @if (showEmailCodeOption()) {
1988
2003
  <button
1989
2004
  [class]="cls('formButtonSecondary', 'atlas-button-secondary')"
@@ -2255,6 +2270,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
2255
2270
  }
2256
2271
  }
2257
2272
 
2273
+ @if (offerPasskeySecondFactor && step().kind === 'collect_second_factor') {
2274
+ <button
2275
+ [class]="cls('formButtonSecondary', 'atlas-button-secondary')"
2276
+ type="button"
2277
+ data-atlas="passkey-second-factor"
2278
+ [disabled]="busy"
2279
+ (click)="passkeySecondFactor.emit()"
2280
+ >
2281
+ {{ t('mfa.enrollPasskeyOption') }}
2282
+ </button>
2283
+ }
2284
+
2258
2285
  @if (showEmailCodeOption()) {
2259
2286
  <button
2260
2287
  [class]="cls('formButtonSecondary', 'atlas-button-secondary')"
@@ -2330,6 +2357,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
2330
2357
  type: Input
2331
2358
  }], emailSecondFactor: [{
2332
2359
  type: Output
2360
+ }], offerPasskeySecondFactor: [{
2361
+ type: Input
2362
+ }], passkeySecondFactor: [{
2363
+ type: Output
2333
2364
  }], offerSmsSecondFactor: [{
2334
2365
  type: Input
2335
2366
  }], textedSecondFactor: [{
@@ -2808,8 +2839,28 @@ class AtlasFlowController {
2808
2839
  this.syncCaptcha();
2809
2840
  this.syncEnrollment();
2810
2841
  this.autoSend();
2842
+ this.autoSendOnlyWay();
2811
2843
  }
2812
2844
  autoSentFor = null;
2845
+ onlyWayFor = null;
2846
+ /** Once per attempt, on arrival at the second factor: email the code if the inbox is the only way. */
2847
+ autoSendOnlyWay() {
2848
+ const { attempt } = this.state();
2849
+ const id = attempt?.id ?? null;
2850
+ if (!id || attempt?.status !== 'needs_second_factor' || this.onlyWayFor === id || !this.mfaChoices().emailCode)
2851
+ return;
2852
+ this.onlyWayFor = id;
2853
+ void sendOnlyWaySecondFactorCode(this.client, id).then((sentTo) => {
2854
+ // A stale answer (the flow moved to another attempt) is dropped.
2855
+ if (sentTo && this.state().attempt?.id === id)
2856
+ this.emailSecondFactor.set({ sentTo });
2857
+ });
2858
+ }
2859
+ /** "Use a passkey" at the second factor: leaves for the hosted page. */
2860
+ continueWithPasskey() {
2861
+ const id = this.state().attempt?.id;
2862
+ return !!id && continueOnHostedPage(this.mfaChoices(), id, 'needs_second_factor');
2863
+ }
2813
2864
  /**
2814
2865
  * With no password on offer, email the code as soon as the server asks for a
2815
2866
  * first factor — once per attempt, so a failed send waits for the user's retry
@@ -3152,6 +3203,8 @@ class SignInComponent {
3152
3203
  [enrollmentState]="flow.enrollmentState()"
3153
3204
  (emailSecondFactor)="flow.sendEmailSecondFactor()"
3154
3205
  (smsSecondFactor)="flow.sendSmsSecondFactor()"
3206
+ [offerPasskeySecondFactor]="flow.settings() !== null && flow.mfaChoices().passkey"
3207
+ (passkeySecondFactor)="flow.continueWithPasskey()"
3155
3208
  (policyDeclined)="flow.declinePolicy($event)"
3156
3209
  ></atlas-attempt-flow>
3157
3210
  }
@@ -3172,7 +3225,7 @@ class SignInComponent {
3172
3225
  </p>
3173
3226
  }
3174
3227
  }
3175
- `, isInline: true, dependencies: [{ kind: "component", type: EmailLinkComponent, selector: "atlas-email-link", inputs: ["link"] }, { kind: "component", type: AttemptFlowComponent, selector: "atlas-attempt-flow", inputs: ["attempt", "busy", "errors", "sessionKept", "titleKey", "signInHref", "captcha", "firstFactors", "preparedFactor", "passwordHold", "holdProof", "offerEmailSecondFactor", "emailedSecondFactor", "offerSmsSecondFactor", "textedSecondFactor", "enrollment", "enrollmentState", "emailVerification", "matchCode"], outputs: ["submitFlow", "chooseFactor", "usePassword", "emailSecondFactor", "smsSecondFactor", "resend", "policyDeclined"] }, { kind: "component", type: RecoveryCodesComponent, selector: "atlas-recovery-codes", inputs: ["codes", "busy"], outputs: ["saved"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3228
+ `, isInline: true, dependencies: [{ kind: "component", type: EmailLinkComponent, selector: "atlas-email-link", inputs: ["link"] }, { kind: "component", type: AttemptFlowComponent, selector: "atlas-attempt-flow", inputs: ["attempt", "busy", "errors", "sessionKept", "titleKey", "signInHref", "captcha", "firstFactors", "preparedFactor", "passwordHold", "holdProof", "offerEmailSecondFactor", "emailedSecondFactor", "offerPasskeySecondFactor", "offerSmsSecondFactor", "textedSecondFactor", "enrollment", "enrollmentState", "emailVerification", "matchCode"], outputs: ["submitFlow", "chooseFactor", "usePassword", "emailSecondFactor", "passkeySecondFactor", "smsSecondFactor", "resend", "policyDeclined"] }, { kind: "component", type: RecoveryCodesComponent, selector: "atlas-recovery-codes", inputs: ["codes", "busy"], outputs: ["saved"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3176
3229
  }
3177
3230
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: SignInComponent, decorators: [{
3178
3231
  type: Component,
@@ -3224,6 +3277,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
3224
3277
  [enrollmentState]="flow.enrollmentState()"
3225
3278
  (emailSecondFactor)="flow.sendEmailSecondFactor()"
3226
3279
  (smsSecondFactor)="flow.sendSmsSecondFactor()"
3280
+ [offerPasskeySecondFactor]="flow.settings() !== null && flow.mfaChoices().passkey"
3281
+ (passkeySecondFactor)="flow.continueWithPasskey()"
3227
3282
  (policyDeclined)="flow.declinePolicy($event)"
3228
3283
  ></atlas-attempt-flow>
3229
3284
  }
@@ -3947,6 +4002,8 @@ class SignUpComponent {
3947
4002
  [enrollmentState]="flow.enrollmentState()"
3948
4003
  (emailSecondFactor)="flow.sendEmailSecondFactor()"
3949
4004
  (smsSecondFactor)="flow.sendSmsSecondFactor()"
4005
+ [offerPasskeySecondFactor]="flow.settings() !== null && flow.mfaChoices().passkey"
4006
+ (passkeySecondFactor)="flow.continueWithPasskey()"
3950
4007
  (policyDeclined)="flow.declinePolicy($event)"
3951
4008
  [signInHref]="signInHref()"
3952
4009
  ></atlas-attempt-flow>
@@ -3966,7 +4023,7 @@ class SignUpComponent {
3966
4023
  ></atlas-sign-up-form>
3967
4024
  }
3968
4025
  }
3969
- `, isInline: true, dependencies: [{ kind: "component", type: EmailLinkComponent, selector: "atlas-email-link", inputs: ["link"] }, { kind: "component", type: AttemptFlowComponent, selector: "atlas-attempt-flow", inputs: ["attempt", "busy", "errors", "sessionKept", "titleKey", "signInHref", "captcha", "firstFactors", "preparedFactor", "passwordHold", "holdProof", "offerEmailSecondFactor", "emailedSecondFactor", "offerSmsSecondFactor", "textedSecondFactor", "enrollment", "enrollmentState", "emailVerification", "matchCode"], outputs: ["submitFlow", "chooseFactor", "usePassword", "emailSecondFactor", "smsSecondFactor", "resend", "policyDeclined"] }, { kind: "component", type: RecoveryCodesComponent, selector: "atlas-recovery-codes", inputs: ["codes", "busy"], outputs: ["saved"] }, { kind: "component", type: SignUpFormComponent, selector: "atlas-sign-up-form", inputs: ["settings", "busy", "errors", "captcha", "passkey", "switchable", "policies"], outputs: ["switchMode", "submitSignUp"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
4026
+ `, isInline: true, dependencies: [{ kind: "component", type: EmailLinkComponent, selector: "atlas-email-link", inputs: ["link"] }, { kind: "component", type: AttemptFlowComponent, selector: "atlas-attempt-flow", inputs: ["attempt", "busy", "errors", "sessionKept", "titleKey", "signInHref", "captcha", "firstFactors", "preparedFactor", "passwordHold", "holdProof", "offerEmailSecondFactor", "emailedSecondFactor", "offerPasskeySecondFactor", "offerSmsSecondFactor", "textedSecondFactor", "enrollment", "enrollmentState", "emailVerification", "matchCode"], outputs: ["submitFlow", "chooseFactor", "usePassword", "emailSecondFactor", "passkeySecondFactor", "smsSecondFactor", "resend", "policyDeclined"] }, { kind: "component", type: RecoveryCodesComponent, selector: "atlas-recovery-codes", inputs: ["codes", "busy"], outputs: ["saved"] }, { kind: "component", type: SignUpFormComponent, selector: "atlas-sign-up-form", inputs: ["settings", "busy", "errors", "captcha", "passkey", "switchable", "policies"], outputs: ["switchMode", "submitSignUp"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3970
4027
  }
3971
4028
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: SignUpComponent, decorators: [{
3972
4029
  type: Component,
@@ -4040,6 +4097,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
4040
4097
  [enrollmentState]="flow.enrollmentState()"
4041
4098
  (emailSecondFactor)="flow.sendEmailSecondFactor()"
4042
4099
  (smsSecondFactor)="flow.sendSmsSecondFactor()"
4100
+ [offerPasskeySecondFactor]="flow.settings() !== null && flow.mfaChoices().passkey"
4101
+ (passkeySecondFactor)="flow.continueWithPasskey()"
4043
4102
  (policyDeclined)="flow.declinePolicy($event)"
4044
4103
  [signInHref]="signInHref()"
4045
4104
  ></atlas-attempt-flow>