@atlasauth/angular 0.9.0 → 0.11.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.
@@ -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, TokenCache, SignOutNotifier, resolveFrontendApi, withDevBrowser, catalogLocale, configureReferralCapture, FapiClient, SignOutGuard, localizedErrorMessage, reauthenticate, getTemplateToken, getProviderToken, signOutReasonFromClient, readRedirectResult, completeOAuthPopupLanding, mapOAuthPopupPayload, stripRedirectParams, closeOAuthPopupAfterSignIn, SDK_HEADER, sdkHeaderValue, reportSdkAdvisory, readExpiry, msUntilRefresh, groupSetupKey, enrollmentSubmitKey, nextStep, awaitingSignUpLink, canDownloadRecoveryCodes, copyRecoveryCodes, downloadRecoveryCodes, createCaptchaController, atlasCaptchaLabels, flowFromPending, initialFlow, createMfaEnrollment, mfaChoicesFrom, fetchInstanceSettings, fetchLastSignInMethod, offeredFirstFactors, prepareFactor, signInCaptchaOptions, flowNeedsCaptcha, planFirstFactor, advanceWithCaptcha, createSignUp, prepareSecondFactorCode, resendSignUpVerification, createPasskeySignUp, finishPasskeySignUp, pendingRecoveryCodes, acknowledgeRecoveryCodes, shouldKeepPolling, pollAttempt, lastUsedMethodLabel, signUp, passkeysSupported, awaitingPasskey, 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, groupSetupKey, enrollmentSubmitKey, nextStep, awaitingSignUpLink, canDownloadRecoveryCodes, copyRecoveryCodes, downloadRecoveryCodes, createCaptchaController, atlasCaptchaLabels, PolicyAcceptanceController, flowFromPending, initialFlow, createMfaEnrollment, mfaChoicesFrom, fetchInstanceSettings, fetchLastSignInMethod, offeredFirstFactors, prepareFactor, signInCaptchaOptions, flowNeedsCaptcha, planFirstFactor, advanceWithCaptcha, loadPolicies, createSignUp, prepareSecondFactorCode, resendSignUpVerification, createPasskeySignUp, finishPasskeySignUp, pendingRecoveryCodes, acknowledgeRecoveryCodes, shouldKeepPolling, pollAttempt, lastUsedMethodLabel, signUp, confirmPolicies, passkeysSupported, awaitingPasskey, 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';
@@ -79,7 +79,7 @@ const DEFAULT_TOKENS = {
79
79
  colorPrimary: '#5b5bd6',
80
80
  colorText: '#1c1c1f',
81
81
  colorBackground: '#ffffff',
82
- colorDanger: '#d64545',
82
+ colorDanger: '#c53030',
83
83
  colorBorder: '#e4e4e9',
84
84
  borderRadius: '8px',
85
85
  fontFamily: 'ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif',
@@ -327,6 +327,8 @@ const EN_US = {
327
327
  'impersonation.banner': 'You are signed in as {name} on someone else\'s behalf.',
328
328
  'impersonation.returnToAdmin': 'Return to admin',
329
329
  'impersonation.end': 'End impersonation',
330
+ // Policy acceptance (terms, privacy, any policy).
331
+ ...POLICY_STRINGS,
330
332
  'error.generic': 'Something went wrong. Please try again.',
331
333
  'error.network': 'We could not reach the server. Check your connection.',
332
334
  // Server errors, by the stable `reason` the Frontend API sends. Shown instead
@@ -804,7 +806,7 @@ class AtlasService {
804
806
  readPendingAttempt() {
805
807
  const result = readRedirectResult(window.location.search);
806
808
  return result?.status && !result.ticket
807
- ? { id: result.attemptId, status: result.status }
809
+ ? { id: result.attemptId, status: result.status, ...(result.policyToken ? { policyToken: result.policyToken } : {}) }
808
810
  : null;
809
811
  }
810
812
  async call(path, init) {
@@ -1199,6 +1201,254 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
1199
1201
  args: ['atlasCaptchaSlot']
1200
1202
  }] } });
1201
1203
 
1204
+ /** The policy UI's translator (the localization bundle, else the English policy strings) for the current Atlas service. */
1205
+ function policyUi(atlas) {
1206
+ return {
1207
+ t: policyTranslator((key, vars) => translate(atlas.localization, key, vars)),
1208
+ classes: {
1209
+ link: 'atlas-link',
1210
+ text: 'atlas-text',
1211
+ primaryButton: classFor('formButtonPrimary', atlas.appearance, 'atlas-button'),
1212
+ error: classFor('formFieldError', atlas.appearance, 'atlas-error'),
1213
+ },
1214
+ };
1215
+ }
1216
+ /**
1217
+ * `[atlasPolicies]="controller"` — draws the instance's policies (terms, privacy,
1218
+ * …) into the host element: `kind="consent"` (default) is the checkbox block with
1219
+ * its in-page viewer, `kind="clickwrap"` the "By continuing you agree to …" line.
1220
+ * The state is the controller's (`AtlasFlowController.policies`); the DOM comes
1221
+ * from `@atlasauth/js`, shared with every SDK.
1222
+ */
1223
+ class AtlasPoliciesDirective {
1224
+ host = inject(ElementRef);
1225
+ atlas = inject(AtlasService);
1226
+ mounted = null;
1227
+ ready = false;
1228
+ controller = null;
1229
+ atlasPolicyKind = 'consent';
1230
+ ngOnInit() {
1231
+ this.ready = true;
1232
+ this.draw();
1233
+ }
1234
+ ngOnChanges(changes) {
1235
+ if (this.ready && (changes['controller'] || changes['atlasPolicyKind']))
1236
+ this.draw();
1237
+ }
1238
+ ngOnDestroy() {
1239
+ this.mounted?.destroy();
1240
+ }
1241
+ draw() {
1242
+ this.mounted?.destroy();
1243
+ this.mounted = null;
1244
+ if (!this.controller)
1245
+ return;
1246
+ const el = this.host.nativeElement;
1247
+ injectPolicyStyles(el.ownerDocument);
1248
+ const ui = policyUi(this.atlas);
1249
+ this.mounted =
1250
+ this.atlasPolicyKind === 'clickwrap'
1251
+ ? mountPolicyClickwrap(el, this.controller, ui)
1252
+ : mountPolicyConsent(el, this.controller, ui);
1253
+ }
1254
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: AtlasPoliciesDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1255
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.3.12", type: AtlasPoliciesDirective, isStandalone: true, selector: "[atlasPolicies]", inputs: { controller: ["atlasPolicies", "controller"], atlasPolicyKind: "atlasPolicyKind" }, usesOnChanges: true, ngImport: i0 });
1256
+ }
1257
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: AtlasPoliciesDirective, decorators: [{
1258
+ type: Directive,
1259
+ args: [{ selector: '[atlasPolicies]', standalone: true }]
1260
+ }], propDecorators: { controller: [{
1261
+ type: Input,
1262
+ args: ['atlasPolicies']
1263
+ }], atlasPolicyKind: [{
1264
+ type: Input
1265
+ }] } });
1266
+ /**
1267
+ * `<atlas-policy-accept-step>` — the sign-in step for a `needs_policy_acceptance`
1268
+ * attempt: scroll-mode policies in their unskippable dialog, the rest as
1269
+ * checkboxes, then "Accept and continue". Emits `submitFlow` with the
1270
+ * `policy_acceptances` JSON for the flow, and `declined` with the policy title
1271
+ * when the user declines (cancel the attempt).
1272
+ */
1273
+ class PolicyAcceptStepComponent {
1274
+ host = inject(ElementRef);
1275
+ atlas = inject(AtlasService);
1276
+ step = null;
1277
+ alive = true;
1278
+ attempt = null;
1279
+ busy = false;
1280
+ submitFlow = new EventEmitter();
1281
+ declined = new EventEmitter();
1282
+ loading = signal(true);
1283
+ loadingText = policyUi(this.atlas).t('policy.loading');
1284
+ ngOnInit() {
1285
+ const client = this.atlas.createFapiClient();
1286
+ void controllerForPending(client, this.attempt, { locale: this.atlas.locale ?? undefined }).then((controller) => {
1287
+ if (!this.alive)
1288
+ return;
1289
+ this.loading.set(false);
1290
+ const el = this.host.nativeElement.querySelector('.atlas-policy-step-host');
1291
+ if (!el)
1292
+ return;
1293
+ injectPolicyStyles(el.ownerDocument);
1294
+ this.step = mountPolicyAcceptStep(el, controller, {
1295
+ ...policyUi(this.atlas),
1296
+ onSubmit: (acceptances) => this.submitFlow.emit({ policy_acceptances: JSON.stringify(acceptances) }),
1297
+ onDecline: (policy) => this.declined.emit(policy.title),
1298
+ });
1299
+ this.step.setBusy(this.busy);
1300
+ });
1301
+ }
1302
+ ngOnChanges(changes) {
1303
+ if (changes['busy'])
1304
+ this.step?.setBusy(this.busy);
1305
+ }
1306
+ ngOnDestroy() {
1307
+ this.alive = false;
1308
+ this.step?.destroy();
1309
+ }
1310
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: PolicyAcceptStepComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1311
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: PolicyAcceptStepComponent, isStandalone: true, selector: "atlas-policy-accept-step", inputs: { attempt: "attempt", busy: "busy" }, outputs: { submitFlow: "submitFlow", declined: "declined" }, usesOnChanges: true, ngImport: i0, template: `@if (loading()) {<p class="atlas-text">{{ loadingText }}</p>}<div class="atlas-policy-step-host"></div>`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
1312
+ }
1313
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: PolicyAcceptStepComponent, decorators: [{
1314
+ type: Component,
1315
+ args: [{
1316
+ selector: 'atlas-policy-accept-step',
1317
+ standalone: true,
1318
+ changeDetection: ChangeDetectionStrategy.OnPush,
1319
+ template: `@if (loading()) {<p class="atlas-text">{{ loadingText }}</p>}<div class="atlas-policy-step-host"></div>`,
1320
+ }]
1321
+ }], propDecorators: { attempt: [{
1322
+ type: Input
1323
+ }], busy: [{
1324
+ type: Input
1325
+ }], submitFlow: [{
1326
+ type: Output
1327
+ }], declined: [{
1328
+ type: Output
1329
+ }] } });
1330
+ /**
1331
+ * `<atlas-policy-gate>` — in-app re-acceptance. For a signed-in user, a banner
1332
+ * when a policy needs re-acceptance; "Review and accept" opens the unskippable
1333
+ * dialog / checkboxes and posts `/v1/client/me/policies/accept`. Projects its
1334
+ * content always.
1335
+ */
1336
+ class PolicyGateComponent {
1337
+ host = inject(ElementRef);
1338
+ atlas = inject(AtlasService);
1339
+ ui = policyUi(this.atlas);
1340
+ gate;
1341
+ off = null;
1342
+ step = null;
1343
+ version = signal(0);
1344
+ accepted = new EventEmitter();
1345
+ reviewing = signal(false);
1346
+ error = signal('');
1347
+ title = this.ui.t('policy.bannerTitle');
1348
+ reviewText = this.ui.t('policy.bannerReview');
1349
+ linkClass = this.ui.classes.link ?? '';
1350
+ errorClass = this.ui.classes.error ?? '';
1351
+ bannerClass = 'atlas-policy-banner';
1352
+ cardClass = classFor('card', this.atlas.appearance, 'atlas-card');
1353
+ bannerText() {
1354
+ this.version();
1355
+ if (!this.gate || this.gate.pending.length === 0 || this.reviewing())
1356
+ return null;
1357
+ const t = this.ui.t;
1358
+ const grace = this.gate.graceEndsAt;
1359
+ return (t('policy.bannerBody', { policies: joinTitles(this.gate.pending.map((p) => p.title), t('policy.and')) }) +
1360
+ (grace ? ` ${t('policy.bannerGrace', { date: formatPolicyDate(grace) })}` : ''));
1361
+ }
1362
+ ngOnInit() {
1363
+ this.gate = new PolicyGateController(this.atlas.createFapiClient(), this.atlas.locale ?? undefined);
1364
+ this.off = this.gate.subscribe(() => this.version.update((n) => n + 1));
1365
+ if (this.atlas.status() === 'signed_in')
1366
+ void this.gate.load();
1367
+ }
1368
+ async begin() {
1369
+ const controller = await this.gate.begin();
1370
+ this.reviewing.set(true);
1371
+ await Promise.resolve();
1372
+ const el = this.host.nativeElement.querySelector('.atlas-policy-gate-host');
1373
+ if (!el)
1374
+ return;
1375
+ injectPolicyStyles(el.ownerDocument);
1376
+ this.step = mountPolicyAcceptStep(el, controller, {
1377
+ ...this.ui,
1378
+ onSubmit: async () => {
1379
+ this.step?.setBusy(true);
1380
+ const res = await this.gate.accept(controller);
1381
+ this.step?.setBusy(false);
1382
+ if (!res.ok) {
1383
+ this.error.set(res.errors[0]?.message ?? this.ui.t('policy.requiredGeneric'));
1384
+ return;
1385
+ }
1386
+ this.close();
1387
+ this.accepted.emit();
1388
+ },
1389
+ onDecline: () => this.close(),
1390
+ });
1391
+ }
1392
+ close() {
1393
+ this.step?.destroy();
1394
+ this.step = null;
1395
+ this.reviewing.set(false);
1396
+ }
1397
+ ngOnDestroy() {
1398
+ this.off?.();
1399
+ this.step?.destroy();
1400
+ }
1401
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: PolicyGateComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1402
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: PolicyGateComponent, isStandalone: true, selector: "atlas-policy-gate", outputs: { accepted: "accepted" }, ngImport: i0, template: `
1403
+ @if (bannerText(); as text) {
1404
+ <div [class]="bannerClass" role="status" data-atlas-policy-banner="">
1405
+ <span
1406
+ ><strong>{{ title }}. </strong>{{ text }}</span
1407
+ >
1408
+ <button type="button" [class]="linkClass" (click)="begin()">{{ reviewText }}</button>
1409
+ </div>
1410
+ }
1411
+ @if (reviewing()) {
1412
+ <div [class]="cardClass" data-atlas-policy-gate="">
1413
+ <div class="atlas-policy-gate-host"></div>
1414
+ @if (error()) {
1415
+ <p [class]="errorClass" role="alert">{{ error() }}</p>
1416
+ }
1417
+ </div>
1418
+ }
1419
+ <ng-content></ng-content>
1420
+ `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
1421
+ }
1422
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: PolicyGateComponent, decorators: [{
1423
+ type: Component,
1424
+ args: [{
1425
+ selector: 'atlas-policy-gate',
1426
+ standalone: true,
1427
+ changeDetection: ChangeDetectionStrategy.OnPush,
1428
+ template: `
1429
+ @if (bannerText(); as text) {
1430
+ <div [class]="bannerClass" role="status" data-atlas-policy-banner="">
1431
+ <span
1432
+ ><strong>{{ title }}. </strong>{{ text }}</span
1433
+ >
1434
+ <button type="button" [class]="linkClass" (click)="begin()">{{ reviewText }}</button>
1435
+ </div>
1436
+ }
1437
+ @if (reviewing()) {
1438
+ <div [class]="cardClass" data-atlas-policy-gate="">
1439
+ <div class="atlas-policy-gate-host"></div>
1440
+ @if (error()) {
1441
+ <p [class]="errorClass" role="alert">{{ error() }}</p>
1442
+ }
1443
+ </div>
1444
+ }
1445
+ <ng-content></ng-content>
1446
+ `,
1447
+ }]
1448
+ }], propDecorators: { accepted: [{
1449
+ type: Output
1450
+ }] } });
1451
+
1202
1452
  /**
1203
1453
  * The server-driven flow shell, shared by `<atlas-sign-in>` and
1204
1454
  * `<atlas-sign-up>`. The Angular peer of React's internal `AttemptFlow`.
@@ -1267,6 +1517,8 @@ class AttemptFlowComponent {
1267
1517
  */
1268
1518
  emailVerification = 'code';
1269
1519
  resend = new EventEmitter();
1520
+ /** The user declined a policy the sign-in was waiting on (`needs_policy_acceptance`): cancel the attempt. */
1521
+ policyDeclined = new EventEmitter();
1270
1522
  values = {};
1271
1523
  t(key, vars) {
1272
1524
  return translate(this.atlas.localization, key, vars);
@@ -1366,6 +1618,10 @@ class AttemptFlowComponent {
1366
1618
  return null;
1367
1619
  return this.t('signIn.codeSentTo', { email: this.attempt?.identifier ?? '' });
1368
1620
  }
1621
+ /** The attempt is `needs_policy_acceptance`. */
1622
+ awaitingPolicies() {
1623
+ return this.step().kind === 'accept_policies';
1624
+ }
1369
1625
  showSubmit() {
1370
1626
  const kind = this.step().kind;
1371
1627
  // Nothing to submit on a first-factor step that only offers an emailed code
@@ -1377,7 +1633,7 @@ class AttemptFlowComponent {
1377
1633
  // Enrolment: nothing to submit while choosing a method or fetching the setup.
1378
1634
  if (this.enrolling() && this.enrollmentState && !enrollmentSubmitKey(this.enrollmentState))
1379
1635
  return false;
1380
- return kind !== 'done' && kind !== 'await_oauth';
1636
+ return kind !== 'done' && kind !== 'await_oauth' && kind !== 'accept_policies';
1381
1637
  }
1382
1638
  fields() {
1383
1639
  const step = this.step();
@@ -1411,6 +1667,9 @@ class AttemptFlowComponent {
1411
1667
  return this.linkOnly() ? [] : [{ kind: 'input', name: 'code', labelKey: 'signIn.emailCodeLabel', type: 'text' }];
1412
1668
  case 'collect_new_password':
1413
1669
  return [{ kind: 'input', name: 'password', labelKey: 'reset.newPasswordLabel', type: 'password' }];
1670
+ case 'accept_policies':
1671
+ // Drawn by <atlas-policy-accept-step> above.
1672
+ return [];
1414
1673
  case 'collect_captcha':
1415
1674
  // §5 the server withheld the attempt until a captcha clears. Solving the
1416
1675
  // widget resubmits the identifier on its own; the button retries.
@@ -1434,7 +1693,7 @@ class AttemptFlowComponent {
1434
1693
  }
1435
1694
  }
1436
1695
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: AttemptFlowComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1437
- 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", captcha: "captcha", firstFactors: "firstFactors", preparedFactor: "preparedFactor", offerEmailSecondFactor: "offerEmailSecondFactor", emailedSecondFactor: "emailedSecondFactor", offerSmsSecondFactor: "offerSmsSecondFactor", textedSecondFactor: "textedSecondFactor", enrollment: "enrollment", enrollmentState: "enrollmentState", emailVerification: "emailVerification" }, outputs: { submitFlow: "submitFlow", chooseFactor: "chooseFactor", emailSecondFactor: "emailSecondFactor", smsSecondFactor: "smsSecondFactor", resend: "resend" }, ngImport: i0, template: `
1696
+ 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", captcha: "captcha", firstFactors: "firstFactors", preparedFactor: "preparedFactor", offerEmailSecondFactor: "offerEmailSecondFactor", emailedSecondFactor: "emailedSecondFactor", offerSmsSecondFactor: "offerSmsSecondFactor", textedSecondFactor: "textedSecondFactor", enrollment: "enrollment", enrollmentState: "enrollmentState", emailVerification: "emailVerification" }, outputs: { submitFlow: "submitFlow", chooseFactor: "chooseFactor", emailSecondFactor: "emailSecondFactor", smsSecondFactor: "smsSecondFactor", resend: "resend", policyDeclined: "policyDeclined" }, ngImport: i0, template: `
1438
1697
  <div [class]="cls('card', 'atlas-card')">
1439
1698
  <h1 [class]="cls('headerTitle', 'atlas-title')">{{ t(titleKey) }}</h1>
1440
1699
 
@@ -1560,6 +1819,16 @@ class AttemptFlowComponent {
1560
1819
  }
1561
1820
  }
1562
1821
 
1822
+ @if (awaitingPolicies()) {
1823
+ <!-- A required policy changed: scroll-mode ones in their dialog, the rest as checkboxes. -->
1824
+ <atlas-policy-accept-step
1825
+ [attempt]="attempt"
1826
+ [busy]="busy"
1827
+ (submitFlow)="submitFlow.emit($event)"
1828
+ (declined)="policyDeclined.emit($event)"
1829
+ ></atlas-policy-accept-step>
1830
+ }
1831
+
1563
1832
  @if (showCaptcha()) {
1564
1833
  <div class="atlas-captcha" data-atlas-captcha="" [atlasCaptchaSlot]="captcha"></div>
1565
1834
  }
@@ -1626,7 +1895,7 @@ class AttemptFlowComponent {
1626
1895
  }
1627
1896
  </form>
1628
1897
  </div>
1629
- `, isInline: true, dependencies: [{ kind: "directive", type: AtlasCaptchaSlotDirective, selector: "[atlasCaptchaSlot]", inputs: ["atlasCaptchaSlot"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1898
+ `, isInline: true, dependencies: [{ kind: "directive", type: AtlasCaptchaSlotDirective, selector: "[atlasCaptchaSlot]", inputs: ["atlasCaptchaSlot"] }, { kind: "component", type: PolicyAcceptStepComponent, selector: "atlas-policy-accept-step", inputs: ["attempt", "busy"], outputs: ["submitFlow", "declined"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1630
1899
  }
1631
1900
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: AttemptFlowComponent, decorators: [{
1632
1901
  type: Component,
@@ -1634,7 +1903,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
1634
1903
  selector: 'atlas-attempt-flow',
1635
1904
  standalone: true,
1636
1905
  changeDetection: ChangeDetectionStrategy.OnPush,
1637
- imports: [AtlasCaptchaSlotDirective],
1906
+ imports: [AtlasCaptchaSlotDirective, PolicyAcceptStepComponent],
1638
1907
  template: `
1639
1908
  <div [class]="cls('card', 'atlas-card')">
1640
1909
  <h1 [class]="cls('headerTitle', 'atlas-title')">{{ t(titleKey) }}</h1>
@@ -1761,6 +2030,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
1761
2030
  }
1762
2031
  }
1763
2032
 
2033
+ @if (awaitingPolicies()) {
2034
+ <!-- A required policy changed: scroll-mode ones in their dialog, the rest as checkboxes. -->
2035
+ <atlas-policy-accept-step
2036
+ [attempt]="attempt"
2037
+ [busy]="busy"
2038
+ (submitFlow)="submitFlow.emit($event)"
2039
+ (declined)="policyDeclined.emit($event)"
2040
+ ></atlas-policy-accept-step>
2041
+ }
2042
+
1764
2043
  @if (showCaptcha()) {
1765
2044
  <div class="atlas-captcha" data-atlas-captcha="" [atlasCaptchaSlot]="captcha"></div>
1766
2045
  }
@@ -1869,6 +2148,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
1869
2148
  type: Input
1870
2149
  }], resend: [{
1871
2150
  type: Output
2151
+ }], policyDeclined: [{
2152
+ type: Output
1872
2153
  }] } });
1873
2154
  /** A read-only message row: `labelKey` holds the i18n key of the text to show. */
1874
2155
  function msg(labelKey) {
@@ -2055,6 +2336,11 @@ class AtlasFlowController {
2055
2336
  enrollment;
2056
2337
  /** {@link enrollment}'s state, for templates. */
2057
2338
  enrollmentState;
2339
+ /**
2340
+ * The instance's policies (terms, privacy, …) for the create-account form:
2341
+ * {@link create} sends their acceptances and reloads them after a refusal.
2342
+ */
2343
+ policies = new PolicyAcceptanceController([]);
2058
2344
  pollTimer = null;
2059
2345
  identifier = '';
2060
2346
  constructor(client, reload, pending,
@@ -2191,6 +2477,17 @@ class AtlasFlowController {
2191
2477
  this.identifier = out.identifier;
2192
2478
  await this.finish(out.state);
2193
2479
  }
2480
+ /** Load the sign-up's policies into {@link policies} (`<atlas-sign-up>` calls it on init). */
2481
+ async loadPolicies(locale) {
2482
+ this.policies.setPolicies(await loadPolicies(this.client, { locale }));
2483
+ }
2484
+ /** The user declined a policy a sign-in was waiting on: cancel the attempt and say why. */
2485
+ declinePolicy(title) {
2486
+ this.state.set({
2487
+ ...initialFlow,
2488
+ errors: [{ code: 'POLICY_DECLINED', message: this.t('policy.declined').replace('{policy}', title) }],
2489
+ });
2490
+ }
2194
2491
  /**
2195
2492
  * Create an account (`POST /v1/client/sign_ups`) from a create-account form's
2196
2493
  * values — email, password and any configured sign-up fields — carrying the
@@ -2203,6 +2500,7 @@ class AtlasFlowController {
2203
2500
  consent,
2204
2501
  captcha: this.signUpCaptcha,
2205
2502
  t: this.t,
2503
+ policies: this.policies,
2206
2504
  });
2207
2505
  await this.finish(next);
2208
2506
  }
@@ -2247,7 +2545,7 @@ class AtlasFlowController {
2247
2545
  */
2248
2546
  async createWithPasskey(values, consent = true) {
2249
2547
  this.state.update((current) => ({ ...current, busy: true, errors: [] }));
2250
- await this.finish(await createPasskeySignUp(this.client, values, { consent, captcha: this.signUpCaptcha, t: this.t }));
2548
+ await this.finish(await createPasskeySignUp(this.client, values, { consent, captcha: this.signUpCaptcha, t: this.t, policies: this.policies }));
2251
2549
  }
2252
2550
  /** The passkey-only sign-up's last step: create the passkey, and with it the account. */
2253
2551
  async createPasskey() {
@@ -2429,6 +2727,7 @@ class SignInComponent {
2429
2727
  [enrollmentState]="flow.enrollmentState()"
2430
2728
  (emailSecondFactor)="flow.sendEmailSecondFactor()"
2431
2729
  (smsSecondFactor)="flow.sendSmsSecondFactor()"
2730
+ (policyDeclined)="flow.declinePolicy($event)"
2432
2731
  ></atlas-attempt-flow>
2433
2732
  }
2434
2733
  @if (flow.state().attempt?.status !== 'complete' && signUpHref(); as href) {
@@ -2436,7 +2735,7 @@ class SignInComponent {
2436
2735
  {{ t('signIn.noAccount') }} <a class="atlas-link" [href]="href">{{ t('signIn.signUpLink') }}</a>
2437
2736
  </p>
2438
2737
  }
2439
- `, isInline: true, dependencies: [{ kind: "component", type: AttemptFlowComponent, selector: "atlas-attempt-flow", inputs: ["attempt", "busy", "errors", "sessionKept", "titleKey", "captcha", "firstFactors", "preparedFactor", "offerEmailSecondFactor", "emailedSecondFactor", "offerSmsSecondFactor", "textedSecondFactor", "enrollment", "enrollmentState", "emailVerification"], outputs: ["submitFlow", "chooseFactor", "emailSecondFactor", "smsSecondFactor", "resend"] }, { kind: "component", type: RecoveryCodesComponent, selector: "atlas-recovery-codes", inputs: ["codes", "busy"], outputs: ["saved"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2738
+ `, isInline: true, dependencies: [{ kind: "component", type: AttemptFlowComponent, selector: "atlas-attempt-flow", inputs: ["attempt", "busy", "errors", "sessionKept", "titleKey", "captcha", "firstFactors", "preparedFactor", "offerEmailSecondFactor", "emailedSecondFactor", "offerSmsSecondFactor", "textedSecondFactor", "enrollment", "enrollmentState", "emailVerification"], outputs: ["submitFlow", "chooseFactor", "emailSecondFactor", "smsSecondFactor", "resend", "policyDeclined"] }, { kind: "component", type: RecoveryCodesComponent, selector: "atlas-recovery-codes", inputs: ["codes", "busy"], outputs: ["saved"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2440
2739
  }
2441
2740
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: SignInComponent, decorators: [{
2442
2741
  type: Component,
@@ -2473,6 +2772,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
2473
2772
  [enrollmentState]="flow.enrollmentState()"
2474
2773
  (emailSecondFactor)="flow.sendEmailSecondFactor()"
2475
2774
  (smsSecondFactor)="flow.sendSmsSecondFactor()"
2775
+ (policyDeclined)="flow.declinePolicy($event)"
2476
2776
  ></atlas-attempt-flow>
2477
2777
  }
2478
2778
  @if (flow.state().attempt?.status !== 'complete' && signUpHref(); as href) {
@@ -2498,6 +2798,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
2498
2798
  */
2499
2799
  class SignUpFormComponent {
2500
2800
  atlas = inject(AtlasService);
2801
+ cdr = inject(ChangeDetectorRef);
2802
+ unwatch = null;
2501
2803
  settings = null;
2502
2804
  busy = false;
2503
2805
  errors = [];
@@ -2506,12 +2808,27 @@ class SignUpFormComponent {
2506
2808
  passkey = null;
2507
2809
  /** Whether the password / passkey toggle is shown (both are offered). */
2508
2810
  switchable = false;
2811
+ /** The instance's policies (terms, privacy, …); they replace the legacy consent row. */
2812
+ policies = null;
2509
2813
  /** The user asked for the other sign-up method. */
2510
2814
  switchMode = new EventEmitter();
2511
2815
  /** Emits the typed values and the consent tick when the form is submitted. */
2512
2816
  submitSignUp = new EventEmitter();
2513
2817
  values = {};
2514
2818
  agreed = false;
2819
+ declined = null;
2820
+ ngOnChanges(changes) {
2821
+ if (!changes['policies'])
2822
+ return;
2823
+ this.unwatch?.();
2824
+ this.unwatch = this.policies?.subscribe(() => this.cdr.markForCheck()) ?? null;
2825
+ }
2826
+ ngOnDestroy() {
2827
+ this.unwatch?.();
2828
+ }
2829
+ hasPolicies() {
2830
+ return !!this.policies && this.policies.policies.length > 0;
2831
+ }
2515
2832
  t(key, vars) {
2516
2833
  return translate(this.atlas.localization, key, vars);
2517
2834
  }
@@ -2564,10 +2881,15 @@ class SignUpFormComponent {
2564
2881
  this.setValue(f.key, (on ? [...current, option] : current.filter((p) => p !== option)).join(', '));
2565
2882
  }
2566
2883
  showLegal() {
2884
+ // Instance policies replace the legacy single "I agree to the Terms" row.
2885
+ if (this.hasPolicies())
2886
+ return false;
2567
2887
  const legal = this.settings?.legal;
2568
2888
  return !!legal && (!!legal.termsUrl || !!legal.privacyUrl);
2569
2889
  }
2570
2890
  consentOk() {
2891
+ if (this.hasPolicies())
2892
+ return this.policies.canSubmit();
2571
2893
  return !this.settings?.legal?.required || this.agreed;
2572
2894
  }
2573
2895
  formErrors() {
@@ -2580,13 +2902,27 @@ class SignUpFormComponent {
2580
2902
  event.preventDefault();
2581
2903
  if (this.emailUnavailable() || !this.consentOk())
2582
2904
  return;
2905
+ this.declined = null;
2906
+ if (this.hasPolicies()) {
2907
+ // Scroll-mode policies are read in their unskippable dialog first.
2908
+ const values = { ...this.values };
2909
+ void confirmPolicies(this.policies, { t: policyUi(this.atlas).t }).then((res) => {
2910
+ if (res.ok)
2911
+ this.submitSignUp.emit({ values, consent: true });
2912
+ else {
2913
+ this.declined = res.declined;
2914
+ this.cdr.markForCheck();
2915
+ }
2916
+ });
2917
+ return;
2918
+ }
2583
2919
  this.submitSignUp.emit({
2584
2920
  values: { ...this.values },
2585
2921
  consent: this.settings?.legal?.required ? this.agreed : true,
2586
2922
  });
2587
2923
  }
2588
2924
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: SignUpFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2589
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: SignUpFormComponent, isStandalone: true, selector: "atlas-sign-up-form", inputs: { settings: "settings", busy: "busy", errors: "errors", captcha: "captcha", passkey: "passkey", switchable: "switchable" }, outputs: { switchMode: "switchMode", submitSignUp: "submitSignUp" }, ngImport: i0, template: `
2925
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: SignUpFormComponent, isStandalone: true, selector: "atlas-sign-up-form", inputs: { settings: "settings", busy: "busy", errors: "errors", captcha: "captcha", passkey: "passkey", switchable: "switchable", policies: "policies" }, outputs: { switchMode: "switchMode", submitSignUp: "submitSignUp" }, usesOnChanges: true, ngImport: i0, template: `
2590
2926
  <div [class]="cls('card', 'atlas-card')">
2591
2927
  <h1 [class]="cls('headerTitle', 'atlas-title')">{{ t('signUp.title') }}</h1>
2592
2928
 
@@ -2715,6 +3051,15 @@ class SignUpFormComponent {
2715
3051
  </label>
2716
3052
  }
2717
3053
 
3054
+ @if (hasPolicies()) {
3055
+ <div [atlasPolicies]="policies"></div>
3056
+ @if (declined) {
3057
+ <span [class]="cls('formFieldError', 'atlas-error')" role="alert" data-atlas-policy-declined="">
3058
+ {{ t('policy.declined', { policy: declined }) }}
3059
+ </span>
3060
+ }
3061
+ }
3062
+
2718
3063
  @if (showLegal()) {
2719
3064
  <label [class]="cls('formField', 'atlas-consent')">
2720
3065
  @if (settings?.legal?.required) {
@@ -2754,6 +3099,9 @@ class SignUpFormComponent {
2754
3099
  >
2755
3100
  {{ t('signUp.submit') }}
2756
3101
  </button>
3102
+ @if (hasPolicies()) {
3103
+ <p atlasPolicyKind="clickwrap" [atlasPolicies]="policies"></p>
3104
+ }
2757
3105
  @if (switchable) {
2758
3106
  <div class="atlas-alternatives">
2759
3107
  <button
@@ -2770,7 +3118,7 @@ class SignUpFormComponent {
2770
3118
  </form>
2771
3119
  }
2772
3120
  </div>
2773
- `, isInline: true, dependencies: [{ kind: "directive", type: AtlasCaptchaSlotDirective, selector: "[atlasCaptchaSlot]", inputs: ["atlasCaptchaSlot"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3121
+ `, isInline: true, dependencies: [{ kind: "directive", type: AtlasCaptchaSlotDirective, selector: "[atlasCaptchaSlot]", inputs: ["atlasCaptchaSlot"] }, { kind: "directive", type: AtlasPoliciesDirective, selector: "[atlasPolicies]", inputs: ["atlasPolicies", "atlasPolicyKind"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2774
3122
  }
2775
3123
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: SignUpFormComponent, decorators: [{
2776
3124
  type: Component,
@@ -2778,7 +3126,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
2778
3126
  selector: 'atlas-sign-up-form',
2779
3127
  standalone: true,
2780
3128
  changeDetection: ChangeDetectionStrategy.OnPush,
2781
- imports: [AtlasCaptchaSlotDirective],
3129
+ imports: [AtlasCaptchaSlotDirective, AtlasPoliciesDirective],
2782
3130
  template: `
2783
3131
  <div [class]="cls('card', 'atlas-card')">
2784
3132
  <h1 [class]="cls('headerTitle', 'atlas-title')">{{ t('signUp.title') }}</h1>
@@ -2908,6 +3256,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
2908
3256
  </label>
2909
3257
  }
2910
3258
 
3259
+ @if (hasPolicies()) {
3260
+ <div [atlasPolicies]="policies"></div>
3261
+ @if (declined) {
3262
+ <span [class]="cls('formFieldError', 'atlas-error')" role="alert" data-atlas-policy-declined="">
3263
+ {{ t('policy.declined', { policy: declined }) }}
3264
+ </span>
3265
+ }
3266
+ }
3267
+
2911
3268
  @if (showLegal()) {
2912
3269
  <label [class]="cls('formField', 'atlas-consent')">
2913
3270
  @if (settings?.legal?.required) {
@@ -2947,6 +3304,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
2947
3304
  >
2948
3305
  {{ t('signUp.submit') }}
2949
3306
  </button>
3307
+ @if (hasPolicies()) {
3308
+ <p atlasPolicyKind="clickwrap" [atlasPolicies]="policies"></p>
3309
+ }
2950
3310
  @if (switchable) {
2951
3311
  <div class="atlas-alternatives">
2952
3312
  <button
@@ -2977,6 +3337,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
2977
3337
  type: Input
2978
3338
  }], switchable: [{
2979
3339
  type: Input
3340
+ }], policies: [{
3341
+ type: Input
2980
3342
  }], switchMode: [{
2981
3343
  type: Output
2982
3344
  }], submitSignUp: [{
@@ -3028,8 +3390,10 @@ class SignUpComponent {
3028
3390
  }
3029
3391
  ngOnInit() {
3030
3392
  // §5 the instance's sign-up fields, consent policy and captcha settings.
3031
- if (typeof window !== 'undefined')
3393
+ if (typeof window !== 'undefined') {
3032
3394
  void this.flow.loadSettings();
3395
+ void this.flow.loadPolicies(this.atlas.locale ?? undefined);
3396
+ }
3033
3397
  }
3034
3398
  ngOnDestroy() {
3035
3399
  this.flow.destroy();
@@ -3069,6 +3433,7 @@ class SignUpComponent {
3069
3433
  [busy]="flow.state().busy"
3070
3434
  [errors]="flow.state().errors"
3071
3435
  [captcha]="flow.signUpCaptchaDrawn() ? flow.signUpCaptcha : null"
3436
+ [policies]="flow.policies"
3072
3437
  [passkey]="passkeyForm()"
3073
3438
  [switchable]="canPasskey()"
3074
3439
  (switchMode)="toggleMode()"
@@ -3077,7 +3442,7 @@ class SignUpComponent {
3077
3442
  "
3078
3443
  ></atlas-sign-up-form>
3079
3444
  }
3080
- `, isInline: true, dependencies: [{ kind: "component", type: AttemptFlowComponent, selector: "atlas-attempt-flow", inputs: ["attempt", "busy", "errors", "sessionKept", "titleKey", "captcha", "firstFactors", "preparedFactor", "offerEmailSecondFactor", "emailedSecondFactor", "offerSmsSecondFactor", "textedSecondFactor", "enrollment", "enrollmentState", "emailVerification"], outputs: ["submitFlow", "chooseFactor", "emailSecondFactor", "smsSecondFactor", "resend"] }, { kind: "component", type: SignUpFormComponent, selector: "atlas-sign-up-form", inputs: ["settings", "busy", "errors", "captcha", "passkey", "switchable"], outputs: ["switchMode", "submitSignUp"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3445
+ `, isInline: true, dependencies: [{ kind: "component", type: AttemptFlowComponent, selector: "atlas-attempt-flow", inputs: ["attempt", "busy", "errors", "sessionKept", "titleKey", "captcha", "firstFactors", "preparedFactor", "offerEmailSecondFactor", "emailedSecondFactor", "offerSmsSecondFactor", "textedSecondFactor", "enrollment", "enrollmentState", "emailVerification"], outputs: ["submitFlow", "chooseFactor", "emailSecondFactor", "smsSecondFactor", "resend", "policyDeclined"] }, { kind: "component", type: SignUpFormComponent, selector: "atlas-sign-up-form", inputs: ["settings", "busy", "errors", "captcha", "passkey", "switchable", "policies"], outputs: ["switchMode", "submitSignUp"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3081
3446
  }
3082
3447
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: SignUpComponent, decorators: [{
3083
3448
  type: Component,
@@ -3120,6 +3485,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
3120
3485
  [busy]="flow.state().busy"
3121
3486
  [errors]="flow.state().errors"
3122
3487
  [captcha]="flow.signUpCaptchaDrawn() ? flow.signUpCaptcha : null"
3488
+ [policies]="flow.policies"
3123
3489
  [passkey]="passkeyForm()"
3124
3490
  [switchable]="canPasskey()"
3125
3491
  (switchMode)="toggleMode()"
@@ -4216,6 +4582,9 @@ const ATLAS_UI = [
4216
4582
  UserProfileComponent,
4217
4583
  OrganizationProfileComponent,
4218
4584
  OrganizationAuditLogComponent,
4585
+ AtlasPoliciesDirective,
4586
+ PolicyAcceptStepComponent,
4587
+ PolicyGateComponent,
4219
4588
  ];
4220
4589
 
4221
4590
  /**
@@ -4273,7 +4642,7 @@ class AtlasModule {
4273
4642
  return { ngModule: AtlasModule, providers };
4274
4643
  }
4275
4644
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: AtlasModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
4276
- static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "17.3.12", ngImport: i0, type: AtlasModule, imports: [AtlasSignedInDirective, AtlasSignedOutDirective, AtlasLoadingDirective, AtlasLoadedDirective, AtlasProtectDirective, AtlasRootDirective, SignInComponent, SignUpComponent, UserButtonComponent, OrganizationSwitcherComponent, UserProfileComponent, OrganizationProfileComponent, OrganizationAuditLogComponent], exports: [AtlasSignedInDirective, AtlasSignedOutDirective, AtlasLoadingDirective, AtlasLoadedDirective, AtlasProtectDirective, AtlasRootDirective, SignInComponent, SignUpComponent, UserButtonComponent, OrganizationSwitcherComponent, UserProfileComponent, OrganizationProfileComponent, OrganizationAuditLogComponent] });
4645
+ static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "17.3.12", ngImport: i0, type: AtlasModule, imports: [AtlasSignedInDirective, AtlasSignedOutDirective, AtlasLoadingDirective, AtlasLoadedDirective, AtlasProtectDirective, AtlasRootDirective, SignInComponent, SignUpComponent, UserButtonComponent, OrganizationSwitcherComponent, UserProfileComponent, OrganizationProfileComponent, OrganizationAuditLogComponent, AtlasPoliciesDirective, PolicyAcceptStepComponent, PolicyGateComponent], exports: [AtlasSignedInDirective, AtlasSignedOutDirective, AtlasLoadingDirective, AtlasLoadedDirective, AtlasProtectDirective, AtlasRootDirective, SignInComponent, SignUpComponent, UserButtonComponent, OrganizationSwitcherComponent, UserProfileComponent, OrganizationProfileComponent, OrganizationAuditLogComponent, AtlasPoliciesDirective, PolicyAcceptStepComponent, PolicyGateComponent] });
4277
4646
  static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: AtlasModule });
4278
4647
  }
4279
4648
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: AtlasModule, decorators: [{
@@ -4434,5 +4803,5 @@ function atlasChildGuard(options = {}) {
4434
4803
  * Generated bundle index. Do not edit.
4435
4804
  */
4436
4805
 
4437
- export { ATLAS_ANGULAR_SDK, ATLAS_ANGULAR_VERSION, ATLAS_OPTIONS, ATLAS_UI, AtlasCaptchaSlotDirective, AtlasFlowController, AtlasLoadedDirective, AtlasLoadingDirective, AtlasModule, AtlasOrganizationAuditLog, AtlasProtectDirective, AtlasRootDirective, AtlasService, AtlasSignedInDirective, AtlasSignedOutDirective, AttemptFlowComponent, DEFAULT_TOKENS, EN_US, ImpersonationBannerComponent, OrganizationAuditLogComponent, OrganizationProfileComponent, OrganizationSwitcherComponent, SignInComponent, SignUpComponent, SignUpFormComponent, THEMES, UserButtonComponent, UserProfileComponent, atlasChildGuard, atlasGuard, classFor, cssVariables, explicitTokens, provideAtlas, renderAppearance, resolveTokens, translate, withFallback };
4806
+ export { ATLAS_ANGULAR_SDK, ATLAS_ANGULAR_VERSION, ATLAS_OPTIONS, ATLAS_UI, AtlasCaptchaSlotDirective, AtlasFlowController, AtlasLoadedDirective, AtlasLoadingDirective, AtlasModule, AtlasOrganizationAuditLog, AtlasPoliciesDirective, AtlasProtectDirective, AtlasRootDirective, AtlasService, AtlasSignedInDirective, AtlasSignedOutDirective, AttemptFlowComponent, DEFAULT_TOKENS, EN_US, ImpersonationBannerComponent, OrganizationAuditLogComponent, OrganizationProfileComponent, OrganizationSwitcherComponent, PolicyAcceptStepComponent, PolicyGateComponent, SignInComponent, SignUpComponent, SignUpFormComponent, THEMES, UserButtonComponent, UserProfileComponent, atlasChildGuard, atlasGuard, classFor, cssVariables, explicitTokens, provideAtlas, renderAppearance, resolveTokens, translate, withFallback };
4438
4807
  //# sourceMappingURL=atlasauth-angular.mjs.map