@atlasauth/angular 0.6.0 → 0.7.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 { TokenCache, SignOutNotifier, resolveFrontendApi, catalogLocale, FapiClient, SignOutGuard, localizedErrorMessage, getTemplateToken, getProviderToken, signOutReasonFromClient, readRedirectResult, completeOAuthPopupLanding, mapOAuthPopupPayload, stripRedirectParams, closeOAuthPopupAfterSignIn, SDK_HEADER, sdkHeaderValue, reportSdkAdvisory, readExpiry, msUntilRefresh, nextStep, createCaptchaController, atlasCaptchaLabels, flowFromPending, initialFlow, fetchInstanceSettings, offeredFirstFactors, prepareFactor, signInCaptchaOptions, flowNeedsCaptcha, planFirstFactor, advanceWithCaptcha, createSignUp, shouldKeepPolling, pollAttempt } from '@atlasauth/js';
7
+ import { TokenCache, SignOutNotifier, resolveFrontendApi, catalogLocale, configureReferralCapture, FapiClient, SignOutGuard, localizedErrorMessage, getTemplateToken, getProviderToken, signOutReasonFromClient, readRedirectResult, completeOAuthPopupLanding, mapOAuthPopupPayload, stripRedirectParams, closeOAuthPopupAfterSignIn, SDK_HEADER, sdkHeaderValue, reportSdkAdvisory, readExpiry, msUntilRefresh, nextStep, createCaptchaController, atlasCaptchaLabels, flowFromPending, initialFlow, fetchInstanceSettings, offeredFirstFactors, prepareFactor, signInCaptchaOptions, flowNeedsCaptcha, planFirstFactor, advanceWithCaptcha, createSignUp, shouldKeepPolling, pollAttempt } 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';
@@ -219,6 +219,11 @@ const EN_US = {
219
219
  'userProfile.addEmail': 'Add an email address',
220
220
  'userProfile.makePrimary': 'Make primary',
221
221
  'userProfile.unverified': 'Unverified',
222
+ 'userProfile.verify': 'Verify',
223
+ 'userProfile.verifyConfirm': 'Confirm',
224
+ 'userProfile.verifyCodePlaceholder': 'Code from the email',
225
+ 'userProfile.verifySendFailed': 'We could not send a code right now. Try again in a minute.',
226
+ 'userProfile.verifyCodeRejected': 'That code was not accepted.',
222
227
  'userProfile.revokeDevice': 'Sign out',
223
228
  'userProfile.deleteAccount': 'Delete account',
224
229
  'organization.switcher': 'Switch organization',
@@ -286,7 +291,7 @@ function withFallback(locale) {
286
291
  }
287
292
 
288
293
  /** This package's version, sent as `X-Atlas-SDK` (kept in step with package.json by a test). */
289
- const ATLAS_ANGULAR_VERSION = '0.6.0';
294
+ const ATLAS_ANGULAR_VERSION = '0.7.0';
290
295
  const ATLAS_ANGULAR_SDK = { name: '@atlasauth/angular', version: ATLAS_ANGULAR_VERSION };
291
296
 
292
297
  /**
@@ -397,6 +402,8 @@ class AtlasService {
397
402
  this.catalog = options.localization ? withFallback(options.localization) : EN_US;
398
403
  this.errorCatalog = options.localization ?? EN_US;
399
404
  this.requestLocale = catalogLocale(options.localization);
405
+ // Referral attribution: app-wide options every FAPI client on the page falls back to.
406
+ configureReferralCapture({ referral: options.referral, referralConsent: options.referralConsent });
400
407
  this.fapiClient = new FapiClient({
401
408
  publishableKey: this.publishableKey,
402
409
  baseUrl: this.baseUrl,
@@ -2303,6 +2310,43 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
2303
2310
  */
2304
2311
  class UserProfileComponent {
2305
2312
  atlas = inject(AtlasService);
2313
+ // An unverified address (added and never confirmed, or the sign-up address on
2314
+ // an app that did not require verification) can be proven here: send a code,
2315
+ // then enter it.
2316
+ verifying = signal(null);
2317
+ code = signal('');
2318
+ busy = signal(false);
2319
+ verifyError = signal(null);
2320
+ client = this.atlas.createFapiClient();
2321
+ async sendCode(id) {
2322
+ this.verifyError.set(null);
2323
+ this.busy.set(true);
2324
+ const res = await this.client.post(`/v1/client/me/email_addresses/${id}/prepare_verification`);
2325
+ this.busy.set(false);
2326
+ if (!res.ok) {
2327
+ this.verifyError.set(this.t('userProfile.verifySendFailed'));
2328
+ return;
2329
+ }
2330
+ this.code.set('');
2331
+ this.verifying.set(id);
2332
+ }
2333
+ async confirmCode() {
2334
+ const id = this.verifying();
2335
+ if (!id)
2336
+ return;
2337
+ this.verifyError.set(null);
2338
+ this.busy.set(true);
2339
+ const res = await this.client.post(`/v1/client/me/email_addresses/${id}/attempt_verification`, {
2340
+ code: this.code(),
2341
+ });
2342
+ this.busy.set(false);
2343
+ if (!res.ok) {
2344
+ this.verifyError.set(this.t('userProfile.verifyCodeRejected'));
2345
+ return;
2346
+ }
2347
+ this.verifying.set(null);
2348
+ await this.atlas.reload();
2349
+ }
2306
2350
  t(key) {
2307
2351
  return translate(this.atlas.localization, key);
2308
2352
  }
@@ -2323,10 +2367,42 @@ class UserProfileComponent {
2323
2367
  {{ email.email_address }}
2324
2368
  @if (!email.verified) {
2325
2369
  <em> — {{ t('userProfile.unverified') }}</em>
2370
+ @if (verifying() === email.id) {
2371
+ <span data-atlas="email-verify">
2372
+ <input
2373
+ [class]="cls('formFieldInput', 'atlas-account-input')"
2374
+ inputmode="numeric"
2375
+ autocomplete="one-time-code"
2376
+ [placeholder]="t('userProfile.verifyCodePlaceholder')"
2377
+ [value]="code()"
2378
+ (input)="code.set($any($event.target).value)"
2379
+ />
2380
+ <button
2381
+ [class]="cls('formButtonSecondary', 'atlas-button-secondary')"
2382
+ type="button"
2383
+ [disabled]="busy()"
2384
+ (click)="confirmCode()"
2385
+ >
2386
+ {{ t('userProfile.verifyConfirm') }}
2387
+ </button>
2388
+ </span>
2389
+ } @else {
2390
+ <button
2391
+ [class]="cls('formButtonSecondary', 'atlas-button-secondary')"
2392
+ type="button"
2393
+ [disabled]="busy()"
2394
+ (click)="sendCode(email.id)"
2395
+ >
2396
+ {{ t('userProfile.verify') }}
2397
+ </button>
2398
+ }
2326
2399
  }
2327
2400
  </li>
2328
2401
  }
2329
2402
  </ul>
2403
+ @if (verifyError(); as message) {
2404
+ <p role="alert">{{ message }}</p>
2405
+ }
2330
2406
  </section>
2331
2407
 
2332
2408
  <section>
@@ -2356,10 +2432,42 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
2356
2432
  {{ email.email_address }}
2357
2433
  @if (!email.verified) {
2358
2434
  <em> — {{ t('userProfile.unverified') }}</em>
2435
+ @if (verifying() === email.id) {
2436
+ <span data-atlas="email-verify">
2437
+ <input
2438
+ [class]="cls('formFieldInput', 'atlas-account-input')"
2439
+ inputmode="numeric"
2440
+ autocomplete="one-time-code"
2441
+ [placeholder]="t('userProfile.verifyCodePlaceholder')"
2442
+ [value]="code()"
2443
+ (input)="code.set($any($event.target).value)"
2444
+ />
2445
+ <button
2446
+ [class]="cls('formButtonSecondary', 'atlas-button-secondary')"
2447
+ type="button"
2448
+ [disabled]="busy()"
2449
+ (click)="confirmCode()"
2450
+ >
2451
+ {{ t('userProfile.verifyConfirm') }}
2452
+ </button>
2453
+ </span>
2454
+ } @else {
2455
+ <button
2456
+ [class]="cls('formButtonSecondary', 'atlas-button-secondary')"
2457
+ type="button"
2458
+ [disabled]="busy()"
2459
+ (click)="sendCode(email.id)"
2460
+ >
2461
+ {{ t('userProfile.verify') }}
2462
+ </button>
2463
+ }
2359
2464
  }
2360
2465
  </li>
2361
2466
  }
2362
2467
  </ul>
2468
+ @if (verifyError(); as message) {
2469
+ <p role="alert">{{ message }}</p>
2470
+ }
2363
2471
  </section>
2364
2472
 
2365
2473
  <section>