@atlasauth/angular 0.8.0 → 0.9.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.
Files changed (39) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/esm2022/lib/appearance.mjs +1 -1
  3. package/dist/esm2022/lib/atlas.service.mjs +39 -2
  4. package/dist/esm2022/lib/atlas.ui.mjs +3 -1
  5. package/dist/esm2022/lib/components/attempt-flow.component.mjs +384 -8
  6. package/dist/esm2022/lib/components/flow-controller.mjs +115 -2
  7. package/dist/esm2022/lib/components/impersonation-banner.component.mjs +75 -0
  8. package/dist/esm2022/lib/components/organization-audit-log.component.mjs +115 -0
  9. package/dist/esm2022/lib/components/organization-audit-log.mjs +40 -0
  10. package/dist/esm2022/lib/components/organization-profile.component.mjs +602 -27
  11. package/dist/esm2022/lib/components/recovery-codes.component.mjs +122 -0
  12. package/dist/esm2022/lib/components/sign-in.component.mjs +45 -4
  13. package/dist/esm2022/lib/components/sign-up-form.component.mjs +69 -6
  14. package/dist/esm2022/lib/components/sign-up.component.mjs +81 -7
  15. package/dist/esm2022/lib/i18n.mjs +58 -1
  16. package/dist/esm2022/lib/provide-atlas.mjs +3 -2
  17. package/dist/esm2022/lib/types.mjs +1 -1
  18. package/dist/esm2022/lib/version.mjs +2 -2
  19. package/dist/esm2022/public-api.mjs +4 -1
  20. package/dist/fesm2022/atlasauth-angular.mjs +1711 -46
  21. package/dist/fesm2022/atlasauth-angular.mjs.map +1 -1
  22. package/dist/lib/appearance.d.ts +1 -1
  23. package/dist/lib/atlas.service.d.ts +22 -1
  24. package/dist/lib/atlas.ui.d.ts +2 -1
  25. package/dist/lib/components/attempt-flow.component.d.ts +57 -2
  26. package/dist/lib/components/flow-controller.d.ts +64 -1
  27. package/dist/lib/components/impersonation-banner.component.d.ts +27 -0
  28. package/dist/lib/components/organization-audit-log.component.d.ts +34 -0
  29. package/dist/lib/components/organization-audit-log.d.ts +25 -0
  30. package/dist/lib/components/organization-profile.component.d.ts +55 -6
  31. package/dist/lib/components/recovery-codes.component.d.ts +26 -0
  32. package/dist/lib/components/sign-in.component.d.ts +2 -0
  33. package/dist/lib/components/sign-up-form.component.d.ts +11 -1
  34. package/dist/lib/components/sign-up.component.d.ts +13 -0
  35. package/dist/lib/provide-atlas.d.ts +2 -1
  36. package/dist/lib/types.d.ts +6 -0
  37. package/dist/lib/version.d.ts +1 -1
  38. package/dist/public-api.d.ts +4 -0
  39. package/package.json +3 -3
@@ -31,7 +31,7 @@ export interface DesignTokens {
31
31
  spacingUnit?: string;
32
32
  }
33
33
  /** Every element a customer may target. Named for what it IS, not where it sits. */
34
- export type ElementKey = 'root' | 'card' | 'headerTitle' | 'headerSubtitle' | 'formField' | 'formFieldLabel' | 'formFieldInput' | 'formFieldError' | 'formButtonPrimary' | 'formButtonSecondary' | 'socialButton' | 'dividerText' | 'footerAction' | 'avatar' | 'menu' | 'menuItem' | 'badge';
34
+ export type ElementKey = 'root' | 'card' | 'headerTitle' | 'headerSubtitle' | 'formField' | 'formFieldLabel' | 'formFieldInput' | 'formFieldError' | 'formButtonPrimary' | 'formButtonSecondary' | 'socialButton' | 'dividerText' | 'footerAction' | 'avatar' | 'menu' | 'menuItem' | 'list' | 'listItem' | 'badge';
35
35
  export interface Appearance extends DesignAppearance {
36
36
  /** A prebuilt token theme to start from (pre-design-system themes). */
37
37
  baseTheme?: keyof typeof THEMES;
@@ -1,6 +1,7 @@
1
1
  import { OnDestroy, Signal } from '@angular/core';
2
2
  import { Observable } from 'rxjs';
3
- import { FapiClient, type ErrorLike, type ProviderToken, type SignOutOptions, type SignOutResult, type SignOutEvent, type SignOutListener } from '@atlasauth/js';
3
+ import { AtlasOrganizationAuditLog } from './components/organization-audit-log';
4
+ import { FapiClient, type ErrorLike, type OrganizationAuditLogOptions, type ProviderToken, type SignOutOptions, type SignOutResult, type EndImpersonationResult, type SignOutEvent, type SignOutListener } from '@atlasauth/js';
4
5
  import { type ProtectCondition } from '@atlasauth/authz';
5
6
  import { type Appearance, type AppearanceRender, type InstanceDesign } from './appearance';
6
7
  import { type Catalog } from './i18n';
@@ -124,6 +125,19 @@ export declare class AtlasService implements OnDestroy {
124
125
  * `appearance.instanceDesign: false`.
125
126
  */
126
127
  loadInstanceDesign(): Promise<InstanceDesign | null>;
128
+ /**
129
+ * The active organization's audit trail, for its admins: `state` is a signal
130
+ * of `{ entries, loading, forbidden, hasMore }`, one page at a time with
131
+ * `loadMore()`. `action` narrows it (exact, or a prefix ending in `.`). It
132
+ * follows the active organization; call `destroy()` when done. The peer of
133
+ * React's `useOrganizationAuditLog`.
134
+ */
135
+ organizationAuditLog(options?: OrganizationAuditLogOptions): AtlasOrganizationAuditLog;
136
+ /**
137
+ * Re-authenticate the signed-in user with their password (a step-up), for an
138
+ * action the server answered with STEP_UP_REQUIRED. Rejects when refused.
139
+ */
140
+ reauthenticate(password: string): Promise<void>;
127
141
  /** A fresh {@link FapiClient} for driving a sign-in/sign-up flow. */
128
142
  createFapiClient(): FapiClient;
129
143
  /**
@@ -159,6 +173,13 @@ export declare class AtlasService implements OnDestroy {
159
173
  * unless `{ frontchannel: false }`.
160
174
  */
161
175
  signOut(options?: SignOutOptions): Promise<SignOutResult>;
176
+ /**
177
+ * End the active impersonation ("return to admin"): the impersonator's own
178
+ * session on this browser becomes active again with no sign-in
179
+ * (`resumed: true`); with none, another account signed in here is adopted,
180
+ * or this browser is signed out.
181
+ */
182
+ endImpersonation(): Promise<EndImpersonationResult>;
162
183
  /**
163
184
  * Switch the active organization (or `null` for the personal account). The
164
185
  * new token carries the new org claims, so the app's permission checks update
@@ -10,6 +10,7 @@ import { UserButtonComponent } from './components/user-button.component';
10
10
  import { OrganizationSwitcherComponent } from './components/organization-switcher.component';
11
11
  import { UserProfileComponent } from './components/user-profile.component';
12
12
  import { OrganizationProfileComponent } from './components/organization-profile.component';
13
+ import { OrganizationAuditLogComponent } from './components/organization-audit-log.component';
13
14
  /**
14
15
  * Every standalone directive and component the SDK ships. Import the array in a
15
16
  * standalone component's `imports`, or rely on `AtlasModule` re-exporting it for
@@ -19,4 +20,4 @@ import { OrganizationProfileComponent } from './components/organization-profile.
19
20
  * @Component({ standalone: true, imports: [...ATLAS_UI], template: '…' })
20
21
  * ```
21
22
  */
22
- export declare const ATLAS_UI: readonly [typeof AtlasSignedInDirective, typeof AtlasSignedOutDirective, typeof AtlasLoadingDirective, typeof AtlasLoadedDirective, typeof AtlasProtectDirective, typeof AtlasRootDirective, typeof SignInComponent, typeof SignUpComponent, typeof UserButtonComponent, typeof OrganizationSwitcherComponent, typeof UserProfileComponent, typeof OrganizationProfileComponent];
23
+ export declare const ATLAS_UI: readonly [typeof AtlasSignedInDirective, typeof AtlasSignedOutDirective, typeof AtlasLoadingDirective, typeof AtlasLoadedDirective, typeof AtlasProtectDirective, typeof AtlasRootDirective, typeof SignInComponent, typeof SignUpComponent, typeof UserButtonComponent, typeof OrganizationSwitcherComponent, typeof UserProfileComponent, typeof OrganizationProfileComponent, typeof OrganizationAuditLogComponent];
@@ -1,5 +1,5 @@
1
1
  import { EventEmitter } from '@angular/core';
2
- import { type AttemptView, type CaptchaController, type FieldError } from '@atlasauth/js';
2
+ import { type AttemptView, type CaptchaController, type FieldError, type MfaEnrollmentController, type MfaEnrollmentState, type MfaEnrollmentStrategy } from '@atlasauth/js';
3
3
  import { type ElementKey } from '../appearance';
4
4
  import * as i0 from "@angular/core";
5
5
  /**
@@ -50,6 +50,46 @@ export declare class AttemptFlowComponent {
50
50
  submitFlow: EventEmitter<Record<string, string>>;
51
51
  /** Emits when the user picks a first factor the server must send first (an emailed code). */
52
52
  chooseFactor: EventEmitter<"email_code">;
53
+ /**
54
+ * Offer "Email me a code instead" at the second factor (the instance's
55
+ * `account.emailSecondFactor`); `emailSecondFactor` asks for it and
56
+ * `emailedSecondFactor` reports how it went.
57
+ */
58
+ offerEmailSecondFactor: boolean;
59
+ /** `AtlasFlowController.emailSecondFactor()`: in flight, where it went, or why not. */
60
+ emailedSecondFactor: {
61
+ busy?: boolean;
62
+ sentTo?: string;
63
+ error?: string;
64
+ } | null;
65
+ emailSecondFactor: EventEmitter<void>;
66
+ /**
67
+ * Offer "Text me a code instead" at the second factor (where the app offers
68
+ * texted codes, `AtlasFlowController.mfaChoices().sms`); `smsSecondFactor`
69
+ * asks for it and `textedSecondFactor` reports how it went.
70
+ */
71
+ offerSmsSecondFactor: boolean;
72
+ /** `AtlasFlowController.smsSecondFactor()`: in flight, where it went, or why not. */
73
+ textedSecondFactor: {
74
+ busy?: boolean;
75
+ sentTo?: string;
76
+ error?: string;
77
+ } | null;
78
+ smsSecondFactor: EventEmitter<void>;
79
+ /**
80
+ * The MFA enrolment step (`AtlasFlowController.enrollment` and
81
+ * `enrollmentState()`): the authenticator app's QR code and setup key, or a
82
+ * texted code. Null: the step asks for two authenticator codes only.
83
+ */
84
+ enrollment: MfaEnrollmentController | null;
85
+ enrollmentState: MfaEnrollmentState | null;
86
+ /**
87
+ * The instance's `account.emailVerification`. On `link` a sign-up's
88
+ * verification step has nothing to type: it says to open the emailed link
89
+ * while the flow polls, and offers a resend (`resend`).
90
+ */
91
+ emailVerification: string;
92
+ resend: EventEmitter<void>;
53
93
  protected values: Record<string, string>;
54
94
  protected t(key: string, vars?: Record<string, string>): string;
55
95
  /** A server error in the app's language (`error.<reason>`, else the server's message). */
@@ -57,18 +97,33 @@ export declare class AttemptFlowComponent {
57
97
  protected cls(key: ElementKey, base: string): string;
58
98
  protected setValue(name: string, value: string): void;
59
99
  protected onSubmit(event: Event): void;
100
+ /** The shared enrolment step is drawn (a controller was passed and the flow is enrolling). */
101
+ protected enrolling(): boolean;
102
+ protected chooseMethod(method: MfaEnrollmentStrategy): void;
103
+ protected resendEnrollment(): void;
104
+ protected otherMethods(state: MfaEnrollmentState): MfaEnrollmentStrategy[];
105
+ protected setupKey(secret: string): string;
106
+ /** The submit button's label key (enrolment: "Text me a code" before the number is sent). */
107
+ protected submitKey(): string;
108
+ protected showSmsSecondFactor(): boolean;
109
+ /** Where the texted second-factor code went (masked), once it was sent. */
110
+ protected smsSecondFactorSentTo(): string | null;
60
111
  protected formErrors(): FieldError[];
61
112
  protected fieldErrorsFor(name: string): FieldError[];
62
113
  private step;
63
114
  protected showCaptcha(): boolean;
64
115
  /** The first factors on offer at a `needs_first_factor` step (empty on any other). */
65
116
  private offered;
117
+ protected linkOnly(): boolean;
118
+ protected showEmailSecondFactor(): boolean;
119
+ /** Where the emailed second-factor code went (masked), once it was sent. */
120
+ protected emailSecondFactorSentTo(): string | null;
66
121
  protected showEmailCodeOption(): boolean;
67
122
  /** The "we sent a code to …" line on the code step. */
68
123
  protected codeSentText(): string | null;
69
124
  protected showSubmit(): boolean;
70
125
  protected fields(): StepField[];
71
126
  static ɵfac: i0.ɵɵFactoryDeclaration<AttemptFlowComponent, never>;
72
- static ɵcmp: i0.ɵɵComponentDeclaration<AttemptFlowComponent, "atlas-attempt-flow", never, { "attempt": { "alias": "attempt"; "required": false; }; "busy": { "alias": "busy"; "required": false; }; "errors": { "alias": "errors"; "required": false; }; "sessionKept": { "alias": "sessionKept"; "required": false; }; "titleKey": { "alias": "titleKey"; "required": false; }; "captcha": { "alias": "captcha"; "required": false; }; "firstFactors": { "alias": "firstFactors"; "required": false; }; "preparedFactor": { "alias": "preparedFactor"; "required": false; }; }, { "submitFlow": "submitFlow"; "chooseFactor": "chooseFactor"; }, never, never, true, never>;
127
+ static ɵcmp: i0.ɵɵComponentDeclaration<AttemptFlowComponent, "atlas-attempt-flow", never, { "attempt": { "alias": "attempt"; "required": false; }; "busy": { "alias": "busy"; "required": false; }; "errors": { "alias": "errors"; "required": false; }; "sessionKept": { "alias": "sessionKept"; "required": false; }; "titleKey": { "alias": "titleKey"; "required": false; }; "captcha": { "alias": "captcha"; "required": false; }; "firstFactors": { "alias": "firstFactors"; "required": false; }; "preparedFactor": { "alias": "preparedFactor"; "required": false; }; "offerEmailSecondFactor": { "alias": "offerEmailSecondFactor"; "required": false; }; "emailedSecondFactor": { "alias": "emailedSecondFactor"; "required": false; }; "offerSmsSecondFactor": { "alias": "offerSmsSecondFactor"; "required": false; }; "textedSecondFactor": { "alias": "textedSecondFactor"; "required": false; }; "enrollment": { "alias": "enrollment"; "required": false; }; "enrollmentState": { "alias": "enrollmentState"; "required": false; }; "emailVerification": { "alias": "emailVerification"; "required": false; }; }, { "submitFlow": "submitFlow"; "chooseFactor": "chooseFactor"; "emailSecondFactor": "emailSecondFactor"; "smsSecondFactor": "smsSecondFactor"; "resend": "resend"; }, never, never, true, never>;
73
128
  }
74
129
  export {};
@@ -1,5 +1,5 @@
1
1
  import { WritableSignal } from '@angular/core';
2
- import { FapiClient, type CaptchaController, type FlowState, type InstanceSettings } from '@atlasauth/js';
2
+ import { FapiClient, type CaptchaController, type FlowState, type InstanceSettings, type MfaChoices, type MfaEnrollmentController, type MfaEnrollmentState } from '@atlasauth/js';
3
3
  /**
4
4
  * The server-driven sign-in / sign-up engine, shared by `<atlas-sign-in>` and
5
5
  * `<atlas-sign-up>` — the Angular counterpart of React's `useFlow` hook.
@@ -26,6 +26,11 @@ export declare class AtlasFlowController {
26
26
  * resolves, or when the server could not be reached.
27
27
  */
28
28
  readonly settings: WritableSignal<InstanceSettings>;
29
+ /**
30
+ * The method this browser last signed in with (`password`, `oauth_github`, …)
31
+ * for the "Last used" hint; null when none, or the instance turned it off.
32
+ */
33
+ readonly lastMethod: WritableSignal<string>;
29
34
  /** The sign-in captcha (identifier step / `needs_captcha` challenge). */
30
35
  readonly captcha: CaptchaController;
31
36
  /** The sign-up captcha (the create-account step). */
@@ -40,6 +45,29 @@ export declare class AtlasFlowController {
40
45
  * otherwise.
41
46
  */
42
47
  readonly preparedFactor: WritableSignal<"email_code">;
48
+ /**
49
+ * "Email me a code instead" at the second factor ({@link sendEmailSecondFactor}):
50
+ * in flight, where the code went, or why it was not sent. Null until asked,
51
+ * and again once the flow leaves that step.
52
+ */
53
+ readonly emailSecondFactor: WritableSignal<{
54
+ busy?: boolean;
55
+ sentTo?: string;
56
+ error?: string;
57
+ }>;
58
+ /** "Text me a code instead" at the second factor ({@link sendSmsSecondFactor}): as {@link emailSecondFactor}. */
59
+ readonly smsSecondFactor: WritableSignal<{
60
+ busy?: boolean;
61
+ sentTo?: string;
62
+ error?: string;
63
+ }>;
64
+ /**
65
+ * The MFA enrolment step (`needs_mfa_enrollment`): the authenticator app's
66
+ * setup (QR code, setup key) or a texted code. Started and reset with the flow.
67
+ */
68
+ readonly enrollment: MfaEnrollmentController;
69
+ /** {@link enrollment}'s state, for templates. */
70
+ readonly enrollmentState: WritableSignal<MfaEnrollmentState>;
43
71
  private pollTimer;
44
72
  private identifier;
45
73
  constructor(client: FapiClient, reload: () => Promise<boolean>, pending: {
@@ -48,6 +76,10 @@ export declare class AtlasFlowController {
48
76
  } | null,
49
77
  /** Resolves the captcha messages' catalog keys. Defaults to en-US. */
50
78
  t?: (key: string) => string);
79
+ /** What the app lets the MFA steps offer (from {@link settings}). */
80
+ mfaChoices(): MfaChoices;
81
+ /** Start the enrolment step for its attempt, or reset it once the flow leaves. */
82
+ private syncEnrollment;
51
83
  /** Fetch the instance's public settings and draw whatever captcha they require. */
52
84
  loadSettings(): Promise<void>;
53
85
  /**
@@ -94,6 +126,37 @@ export declare class AtlasFlowController {
94
126
  * then posted with {@link submit}.
95
127
  */
96
128
  create(values: Record<string, string>, consent?: boolean): Promise<void>;
129
+ /**
130
+ * "Email me a code instead" at the second factor (the instance's
131
+ * `account.emailSecondFactor`): mails a code to the account's verified email;
132
+ * it is then posted with {@link submit} like any 2FA code.
133
+ */
134
+ sendEmailSecondFactor(): Promise<void>;
135
+ /**
136
+ * "Text me a code instead" at the second factor (where the app offers texted
137
+ * codes, {@link mfaChoices}`().sms`): texts a code to the account's enrolled
138
+ * phone; it is then posted with {@link submit} like any 2FA code.
139
+ */
140
+ sendSmsSecondFactor(): Promise<void>;
141
+ private sendSecondFactorCode;
142
+ /** Resend a sign-up's emailed code / link (the earlier one stops working). */
143
+ resendSignUp(): Promise<void>;
144
+ /**
145
+ * Start a passkey-only sign-up (`POST /v1/client/sign_ups/passkey`) from the
146
+ * create-account form's values — no password. Offered when the instance's
147
+ * `account.passkeySignUp` is set.
148
+ */
149
+ createWithPasskey(values: Record<string, string>, consent?: boolean): Promise<void>;
150
+ /** The passkey-only sign-up's last step: create the passkey, and with it the account. */
151
+ createPasskey(): Promise<void>;
152
+ /**
153
+ * The recovery codes an MFA enrolment just completed the sign-in with, shown
154
+ * once before it finishes ({@link saveRecoveryCodes}); null otherwise. They
155
+ * live only in this controller's memory.
156
+ */
157
+ recoveryCodes(): string[] | null;
158
+ /** "I've saved my codes": forget the recovery codes and finish the sign-in. */
159
+ saveRecoveryCodes(): Promise<void>;
97
160
  private finish;
98
161
  /** Start/stop the §5.3 magic-link poll to match the current state. */
99
162
  syncPolling(): void;
@@ -0,0 +1,27 @@
1
+ import { EventEmitter } from '@angular/core';
2
+ import { type EndImpersonationResult } from '@atlasauth/js';
3
+ import { AtlasService } from '../atlas.service';
4
+ import { type ElementKey } from '../appearance';
5
+ import * as i0 from "@angular/core";
6
+ /**
7
+ * `<atlas-impersonation-banner>` — shown only while the active session is an
8
+ * impersonation, naming the user acted as, with "Return to admin" (or "End
9
+ * impersonation" when there is no session of the impersonator's to resume).
10
+ * Emits `ended` with the outcome. The Angular peer of React's
11
+ * `<ImpersonationBanner/>`.
12
+ */
13
+ export declare class ImpersonationBannerComponent {
14
+ protected readonly atlas: AtlasService;
15
+ readonly ended: EventEmitter<EndImpersonationResult>;
16
+ protected readonly busy: import("@angular/core").WritableSignal<boolean>;
17
+ protected readonly actor: import("@angular/core").Signal<{
18
+ sub: string;
19
+ }>;
20
+ protected readonly canReturn: import("@angular/core").Signal<boolean>;
21
+ protected readonly name: import("@angular/core").Signal<string>;
22
+ protected t(key: string, vars?: Record<string, string>): string;
23
+ protected cls(key: ElementKey, base: string): string;
24
+ protected end(): Promise<void>;
25
+ static ɵfac: i0.ɵɵFactoryDeclaration<ImpersonationBannerComponent, never>;
26
+ static ɵcmp: i0.ɵɵComponentDeclaration<ImpersonationBannerComponent, "atlas-impersonation-banner", never, {}, { "ended": "ended"; }, never, never, true, never>;
27
+ }
@@ -0,0 +1,34 @@
1
+ import { type OnDestroy, type OnInit } from '@angular/core';
2
+ import { AtlasService } from '../atlas.service';
3
+ import { type ElementKey } from '../appearance';
4
+ import type { AtlasOrganizationAuditLog } from './organization-audit-log';
5
+ import * as i0 from "@angular/core";
6
+ /**
7
+ * `<atlas-organization-audit-log>` — the active organization's audit trail for
8
+ * its admins, next to `<atlas-organization-profile>`. The Angular peer of
9
+ * React's `<OrganizationAuditLog/>`. Renders nothing for a member who may not
10
+ * read it (the server is the authority; the component just follows it). Labels
11
+ * are inputs so an app can localise them.
12
+ */
13
+ export declare class OrganizationAuditLogComponent implements OnInit, OnDestroy {
14
+ /** Entries per page (the server caps it). */
15
+ pageSize?: number;
16
+ /** Narrow to one action, or a prefix ending in `.`. */
17
+ action?: string;
18
+ labels: {
19
+ title?: string;
20
+ empty?: string;
21
+ loadMore?: string;
22
+ actor?: string;
23
+ };
24
+ formatDate: (ms: number) => string;
25
+ protected readonly atlas: AtlasService;
26
+ /** Created in ngOnInit, once the inputs are set (templates render after it). */
27
+ protected log: AtlasOrganizationAuditLog;
28
+ ngOnInit(): void;
29
+ ngOnDestroy(): void;
30
+ protected describe(action: string): string;
31
+ protected cls(key: ElementKey, base: string): string;
32
+ static ɵfac: i0.ɵɵFactoryDeclaration<OrganizationAuditLogComponent, never>;
33
+ static ɵcmp: i0.ɵɵComponentDeclaration<OrganizationAuditLogComponent, "atlas-organization-audit-log", never, { "pageSize": { "alias": "pageSize"; "required": false; }; "action": { "alias": "action"; "required": false; }; "labels": { "alias": "labels"; "required": false; }; "formatDate": { "alias": "formatDate"; "required": false; }; }, {}, never, never, true, never>;
34
+ }
@@ -0,0 +1,25 @@
1
+ import { type Signal } from '@angular/core';
2
+ import type { Observable } from 'rxjs';
3
+ import { type FapiClient, type OrganizationAuditLogOptions, type OrganizationAuditLogState } from '@atlasauth/js';
4
+ import type { AtlasOrganizationMembership } from '../types';
5
+ /**
6
+ * The active organization's audit trail, for its admins — the engine behind
7
+ * `<atlas-organization-audit-log>` and `AtlasService.organizationAuditLog()`
8
+ * (the Angular peer of React's `useOrganizationAuditLog`). `state` is a signal
9
+ * of `{ entries, loading, forbidden, hasMore }`, one page at a time, newest
10
+ * first. It follows the active organization until {@link destroy}.
11
+ */
12
+ export declare class AtlasOrganizationAuditLog {
13
+ private readonly _state;
14
+ private readonly log;
15
+ private readonly unsubscribe;
16
+ private readonly organizationSub;
17
+ readonly state: Signal<OrganizationAuditLogState>;
18
+ constructor(client: Pick<FapiClient, 'get'>, organization$: Observable<AtlasOrganizationMembership | null>, options?: OrganizationAuditLogOptions);
19
+ /** Append the next page, when there is one. */
20
+ loadMore(): Promise<void>;
21
+ /** Re-read from the newest entry. */
22
+ reload(): Promise<void>;
23
+ /** Stop following the active organization. */
24
+ destroy(): void;
25
+ }
@@ -1,16 +1,65 @@
1
+ import { EventEmitter } from '@angular/core';
2
+ import { type OrganizationProfileState } from '@atlasauth/js';
1
3
  import { AtlasService } from '../atlas.service';
2
4
  import { type ElementKey } from '../appearance';
3
5
  import * as i0 from "@angular/core";
6
+ type Tab = 'members' | 'invitations' | 'roles' | 'settings';
7
+ type ProfileRole = OrganizationProfileState['roles'][number];
4
8
  /**
5
- * `<atlas-organization-profile>` — the active organization's surface. The
6
- * Angular peer of React's `<OrganizationProfile/>`. Management controls are
7
- * gated on the ACTIVE org role via `*atlasProtect`, matching the server's rule
8
- * rather than duplicating a looser one. Renders nothing with no active org.
9
+ * `<atlas-organization-profile>` — the active organization's members (admins
10
+ * change roles and remove), invitations (admins invite, revoke and resend) and
11
+ * settings (leave; an admin deletes after confirming their password). The
12
+ * Angular peer of React's `<OrganizationProfile/>`, on the same headless
13
+ * controller. Every control is gated on the ACTIVE org role, matching the
14
+ * server's rule. Renders nothing with no active org. Emits `exit` with `left`
15
+ * or `deleted`.
9
16
  */
10
17
  export declare class OrganizationProfileComponent {
11
18
  protected readonly atlas: AtlasService;
12
- protected t(key: string): string;
19
+ readonly exit: EventEmitter<"left" | "deleted">;
20
+ private readonly client;
21
+ private controller;
22
+ private unsubscribe;
23
+ protected readonly state: import("@angular/core").WritableSignal<OrganizationProfileState>;
24
+ protected readonly tab: import("@angular/core").WritableSignal<Tab>;
25
+ protected readonly email: import("@angular/core").WritableSignal<string>;
26
+ protected readonly inviteRole: import("@angular/core").WritableSignal<string>;
27
+ protected readonly confirm: import("@angular/core").WritableSignal<"delete" | "leave">;
28
+ protected readonly needsPassword: import("@angular/core").WritableSignal<boolean>;
29
+ protected readonly password: import("@angular/core").WritableSignal<string>;
30
+ protected readonly editingRole: import("@angular/core").WritableSignal<string>;
31
+ protected readonly roleKey: import("@angular/core").WritableSignal<string>;
32
+ protected readonly roleName: import("@angular/core").WritableSignal<string>;
33
+ protected readonly rolePerms: import("@angular/core").WritableSignal<string[]>;
34
+ protected readonly customRoles: import("@angular/core").Signal<import("@atlasauth/js").OrganizationRole[]>;
35
+ protected readonly userId: import("@angular/core").Signal<string>;
36
+ protected readonly canManage: import("@angular/core").Signal<boolean>;
37
+ protected readonly isAdmin: import("@angular/core").Signal<boolean>;
38
+ protected readonly tabs: import("@angular/core").Signal<Tab[]>;
39
+ protected readonly roles: import("@angular/core").Signal<import("@atlasauth/js").OrganizationRole[] | {
40
+ key: string;
41
+ name: string;
42
+ }[]>;
43
+ protected readonly errorMessage: import("@angular/core").Signal<string>;
44
+ constructor();
45
+ private bind;
46
+ protected t(key: string, vars?: Record<string, string>): string;
13
47
  protected cls(key: ElementKey, base: string): string;
48
+ protected memberName(m: OrganizationProfileState['members'][number]): string;
49
+ protected changeRole(userId: string, role: string): void;
50
+ protected removeMember(userId: string): void;
51
+ protected sendInvite(): Promise<void>;
52
+ protected resend(id: string): void;
53
+ protected revoke(id: string): void;
54
+ protected leave(): Promise<void>;
55
+ protected remove(): Promise<void>;
56
+ protected editRole(role: ProfileRole): void;
57
+ protected resetRoleForm(): void;
58
+ protected togglePermission(key: string, on: boolean): void;
59
+ protected saveRole(): Promise<void>;
60
+ protected deleteRole(role: ProfileRole): void;
61
+ protected cancel(): void;
14
62
  static ɵfac: i0.ɵɵFactoryDeclaration<OrganizationProfileComponent, never>;
15
- static ɵcmp: i0.ɵɵComponentDeclaration<OrganizationProfileComponent, "atlas-organization-profile", never, {}, {}, never, never, true, never>;
63
+ static ɵcmp: i0.ɵɵComponentDeclaration<OrganizationProfileComponent, "atlas-organization-profile", never, {}, { "exit": "exit"; }, never, never, true, never>;
16
64
  }
65
+ export {};
@@ -0,0 +1,26 @@
1
+ import { EventEmitter, OnChanges } from '@angular/core';
2
+ import { type ElementKey } from '../appearance';
3
+ import * as i0 from "@angular/core";
4
+ /**
5
+ * The recovery codes a mid-sign-in MFA enrolment came with, shown once before
6
+ * the sign-in finishes: copy them, download them as a .txt file, then
7
+ * "I've saved my codes" (`saved`) exchanges the ticket. The codes live only in
8
+ * the flow controller's memory and are forgotten once saved.
9
+ */
10
+ export declare class RecoveryCodesComponent implements OnChanges {
11
+ private readonly atlas;
12
+ private readonly cdr;
13
+ codes: string[];
14
+ busy: boolean;
15
+ saved: EventEmitter<void>;
16
+ protected readonly canCopy: boolean;
17
+ protected readonly canDownload: boolean;
18
+ protected copied: boolean;
19
+ ngOnChanges(): void;
20
+ protected t(key: string): string;
21
+ protected cls(key: ElementKey, base: string): string;
22
+ protected copy(): void;
23
+ protected download(): void;
24
+ static ɵfac: i0.ɵɵFactoryDeclaration<RecoveryCodesComponent, never>;
25
+ static ɵcmp: i0.ɵɵComponentDeclaration<RecoveryCodesComponent, "atlas-recovery-codes", never, { "codes": { "alias": "codes"; "required": false; }; "busy": { "alias": "busy"; "required": false; }; }, { "saved": "saved"; }, never, never, true, never>;
26
+ }
@@ -22,6 +22,8 @@ export declare class SignInComponent implements OnInit, OnDestroy {
22
22
  */
23
23
  signUpUrl?: string | null;
24
24
  protected t(key: string): string;
25
+ /** "Last used: Password" on the identifier step, when the instance shows the hint. */
26
+ protected lastUsedLabel(): string | null;
25
27
  protected signUpHref(): string | null;
26
28
  protected readonly flow: AtlasFlowController;
27
29
  ngOnInit(): void;
@@ -21,6 +21,14 @@ export declare class SignUpFormComponent {
21
21
  busy: boolean;
22
22
  errors: FieldError[];
23
23
  captcha: CaptchaController | null;
24
+ /** Passkey-only sign-up: no password field; the email per the instance's mode. */
25
+ passkey: {
26
+ email: 'off' | 'optional' | 'required';
27
+ } | null;
28
+ /** Whether the password / passkey toggle is shown (both are offered). */
29
+ switchable: boolean;
30
+ /** The user asked for the other sign-up method. */
31
+ switchMode: EventEmitter<void>;
24
32
  /** Emits the typed values and the consent tick when the form is submitted. */
25
33
  submitSignUp: EventEmitter<SignUpFormSubmit>;
26
34
  protected values: Record<string, string>;
@@ -32,6 +40,8 @@ export declare class SignUpFormComponent {
32
40
  protected setValue(name: string, value: string): void;
33
41
  /** Email + password sign-up cannot complete: the app cannot send the verification email. */
34
42
  protected emailUnavailable(): boolean;
43
+ protected showEmail(): boolean;
44
+ protected emailRequired(): boolean;
35
45
  protected fields(): PublicSignUpField[];
36
46
  protected label(f: PublicSignUpField): string;
37
47
  protected inputType(f: PublicSignUpField): string;
@@ -45,5 +55,5 @@ export declare class SignUpFormComponent {
45
55
  protected fieldErrorsFor(name: string): FieldError[];
46
56
  protected onSubmit(event: Event): void;
47
57
  static ɵfac: i0.ɵɵFactoryDeclaration<SignUpFormComponent, never>;
48
- static ɵcmp: i0.ɵɵComponentDeclaration<SignUpFormComponent, "atlas-sign-up-form", never, { "settings": { "alias": "settings"; "required": false; }; "busy": { "alias": "busy"; "required": false; }; "errors": { "alias": "errors"; "required": false; }; "captcha": { "alias": "captcha"; "required": false; }; }, { "submitSignUp": "submitSignUp"; }, never, never, true, never>;
58
+ static ɵcmp: i0.ɵɵComponentDeclaration<SignUpFormComponent, "atlas-sign-up-form", never, { "settings": { "alias": "settings"; "required": false; }; "busy": { "alias": "busy"; "required": false; }; "errors": { "alias": "errors"; "required": false; }; "captcha": { "alias": "captcha"; "required": false; }; "passkey": { "alias": "passkey"; "required": false; }; "switchable": { "alias": "switchable"; "required": false; }; }, { "switchMode": "switchMode"; "submitSignUp": "submitSignUp"; }, never, never, true, never>;
49
59
  }
@@ -1,4 +1,6 @@
1
1
  import { OnDestroy, OnInit } from '@angular/core';
2
+ import { type FieldError } from '@atlasauth/js';
3
+ import { type ElementKey } from '../appearance';
2
4
  import { AtlasFlowController } from './flow-controller';
3
5
  import * as i0 from "@angular/core";
4
6
  /**
@@ -18,6 +20,17 @@ export declare class SignUpComponent implements OnInit, OnDestroy {
18
20
  /** Where to send the user once the flow completes. Reserved for host routing. */
19
21
  afterUrl?: string;
20
22
  protected readonly flow: AtlasFlowController;
23
+ /** Password or passkey-only sign-up (the latter when the instance offers it). */
24
+ protected readonly mode: import("@angular/core").WritableSignal<"password" | "passkey">;
25
+ protected canPasskey(): boolean;
26
+ protected passkeyForm(): {
27
+ email: 'off' | 'optional' | 'required';
28
+ } | null;
29
+ protected toggleMode(): void;
30
+ protected awaitingPasskey(): boolean;
31
+ protected t(key: string): string;
32
+ protected cls(key: ElementKey, base: string): string;
33
+ protected errorText(error: FieldError): string;
21
34
  ngOnInit(): void;
22
35
  ngOnDestroy(): void;
23
36
  static ɵfac: i0.ɵɵFactoryDeclaration<SignUpComponent, never>;
@@ -13,6 +13,7 @@ import * as i9 from "./components/user-button.component";
13
13
  import * as i10 from "./components/organization-switcher.component";
14
14
  import * as i11 from "./components/user-profile.component";
15
15
  import * as i12 from "./components/organization-profile.component";
16
+ import * as i13 from "./components/organization-audit-log.component";
16
17
  /**
17
18
  * Standalone-provider entry point (Angular 17+). Register Atlas once at the
18
19
  * application root — the direct analogue of wrapping the tree in React's
@@ -41,6 +42,6 @@ export declare function provideAtlas(options: AtlasOptions): EnvironmentProvider
41
42
  export declare class AtlasModule {
42
43
  static forRoot(options: AtlasOptions): ModuleWithProviders<AtlasModule>;
43
44
  static ɵfac: i0.ɵɵFactoryDeclaration<AtlasModule, never>;
44
- static ɵmod: i0.ɵɵNgModuleDeclaration<AtlasModule, never, [typeof i1.AtlasSignedInDirective, typeof i2.AtlasSignedOutDirective, typeof i3.AtlasLoadingDirective, typeof i4.AtlasLoadedDirective, typeof i5.AtlasProtectDirective, typeof i6.AtlasRootDirective, typeof i7.SignInComponent, typeof i8.SignUpComponent, typeof i9.UserButtonComponent, typeof i10.OrganizationSwitcherComponent, typeof i11.UserProfileComponent, typeof i12.OrganizationProfileComponent], [typeof i1.AtlasSignedInDirective, typeof i2.AtlasSignedOutDirective, typeof i3.AtlasLoadingDirective, typeof i4.AtlasLoadedDirective, typeof i5.AtlasProtectDirective, typeof i6.AtlasRootDirective, typeof i7.SignInComponent, typeof i8.SignUpComponent, typeof i9.UserButtonComponent, typeof i10.OrganizationSwitcherComponent, typeof i11.UserProfileComponent, typeof i12.OrganizationProfileComponent]>;
45
+ static ɵmod: i0.ɵɵNgModuleDeclaration<AtlasModule, never, [typeof i1.AtlasSignedInDirective, typeof i2.AtlasSignedOutDirective, typeof i3.AtlasLoadingDirective, typeof i4.AtlasLoadedDirective, typeof i5.AtlasProtectDirective, typeof i6.AtlasRootDirective, typeof i7.SignInComponent, typeof i8.SignUpComponent, typeof i9.UserButtonComponent, typeof i10.OrganizationSwitcherComponent, typeof i11.UserProfileComponent, typeof i12.OrganizationProfileComponent, typeof i13.OrganizationAuditLogComponent], [typeof i1.AtlasSignedInDirective, typeof i2.AtlasSignedOutDirective, typeof i3.AtlasLoadingDirective, typeof i4.AtlasLoadedDirective, typeof i5.AtlasProtectDirective, typeof i6.AtlasRootDirective, typeof i7.SignInComponent, typeof i8.SignUpComponent, typeof i9.UserButtonComponent, typeof i10.OrganizationSwitcherComponent, typeof i11.UserProfileComponent, typeof i12.OrganizationProfileComponent, typeof i13.OrganizationAuditLogComponent]>;
45
46
  static ɵinj: i0.ɵɵInjectorDeclaration<AtlasModule>;
46
47
  }
@@ -43,6 +43,12 @@ export interface AtlasSession {
43
43
  id: string;
44
44
  status: string;
45
45
  last_active_organization_id: string | null;
46
+ /** On an impersonation session: who is acting (the token's `act` claim). */
47
+ actor?: {
48
+ sub: string;
49
+ } | null;
50
+ /** On an impersonation session: the impersonator's own session that "return to admin" resumes, or null. */
51
+ actor_session_id?: string | null;
46
52
  }
47
53
  /** The three-state auth lifecycle, identical to the React provider's. */
48
54
  export type AuthStatus = 'loading' | 'signed_in' | 'signed_out';
@@ -1,4 +1,4 @@
1
1
  import type { AtlasSdkInfo } from '@atlasauth/js';
2
2
  /** This package's version, sent as `X-Atlas-SDK` (kept in step with package.json by a test). */
3
- export declare const ATLAS_ANGULAR_VERSION = "0.8.0";
3
+ export declare const ATLAS_ANGULAR_VERSION = "0.9.0";
4
4
  export declare const ATLAS_ANGULAR_SDK: AtlasSdkInfo;
@@ -17,6 +17,10 @@ export { UserButtonComponent } from './lib/components/user-button.component';
17
17
  export { OrganizationSwitcherComponent } from './lib/components/organization-switcher.component';
18
18
  export { UserProfileComponent } from './lib/components/user-profile.component';
19
19
  export { OrganizationProfileComponent } from './lib/components/organization-profile.component';
20
+ export { OrganizationAuditLogComponent } from './lib/components/organization-audit-log.component';
21
+ export { AtlasOrganizationAuditLog } from './lib/components/organization-audit-log';
22
+ export type { OrganizationAuditLogEntry, OrganizationAuditLogState, OrganizationAuditLogOptions } from '@atlasauth/js';
23
+ export { ImpersonationBannerComponent } from './lib/components/impersonation-banner.component';
20
24
  export { AttemptFlowComponent } from './lib/components/attempt-flow.component';
21
25
  export { SignUpFormComponent, type SignUpFormSubmit, } from './lib/components/sign-up-form.component';
22
26
  export { AtlasCaptchaSlotDirective } from './lib/directives/captcha-slot.directive';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlasauth/angular",
3
- "version": "0.8.0",
3
+ "version": "0.9.0",
4
4
  "description": "Official Angular SDK for Atlas — the Angular peer of @atlasauth/react.",
5
5
  "license": "MIT",
6
6
  "module": "./dist/fesm2022/atlas-angular.mjs",
@@ -20,8 +20,8 @@
20
20
  "dependencies": {
21
21
  "tslib": "^2.6.3",
22
22
  "@atlasauth/design": "0.2.0",
23
- "@atlasauth/js": "0.15.0",
24
- "@atlasauth/authz": "0.1.1"
23
+ "@atlasauth/js": "0.16.0",
24
+ "@atlasauth/authz": "0.2.0"
25
25
  },
26
26
  "peerDependencies": {
27
27
  "@angular/core": ">=17",