@mintplayer/ng-spark 22.30.0 → 22.31.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 (82) hide show
  1. package/README.md +2 -2
  2. package/auth/README.md +216 -0
  3. package/fesm2022/mintplayer-ng-spark-auth-account.mjs +2023 -0
  4. package/fesm2022/mintplayer-ng-spark-auth-account.mjs.map +1 -0
  5. package/fesm2022/mintplayer-ng-spark-auth-auth-bar.mjs +96 -0
  6. package/fesm2022/mintplayer-ng-spark-auth-auth-bar.mjs.map +1 -0
  7. package/fesm2022/mintplayer-ng-spark-auth-confirm-email.mjs +200 -0
  8. package/fesm2022/mintplayer-ng-spark-auth-confirm-email.mjs.map +1 -0
  9. package/fesm2022/mintplayer-ng-spark-auth-core.mjs +452 -0
  10. package/fesm2022/mintplayer-ng-spark-auth-core.mjs.map +1 -0
  11. package/fesm2022/mintplayer-ng-spark-auth-external-login.mjs +201 -0
  12. package/fesm2022/mintplayer-ng-spark-auth-external-login.mjs.map +1 -0
  13. package/fesm2022/mintplayer-ng-spark-auth-forgot-password.mjs +196 -0
  14. package/fesm2022/mintplayer-ng-spark-auth-forgot-password.mjs.map +1 -0
  15. package/fesm2022/mintplayer-ng-spark-auth-guards.mjs +16 -0
  16. package/fesm2022/mintplayer-ng-spark-auth-guards.mjs.map +1 -0
  17. package/fesm2022/mintplayer-ng-spark-auth-interceptors.mjs +18 -0
  18. package/fesm2022/mintplayer-ng-spark-auth-interceptors.mjs.map +1 -0
  19. package/fesm2022/mintplayer-ng-spark-auth-login.mjs +282 -0
  20. package/fesm2022/mintplayer-ng-spark-auth-login.mjs.map +1 -0
  21. package/fesm2022/mintplayer-ng-spark-auth-models.mjs +114 -0
  22. package/fesm2022/mintplayer-ng-spark-auth-models.mjs.map +1 -0
  23. package/fesm2022/mintplayer-ng-spark-auth-pipes.mjs +43 -0
  24. package/fesm2022/mintplayer-ng-spark-auth-pipes.mjs.map +1 -0
  25. package/fesm2022/mintplayer-ng-spark-auth-register.mjs +216 -0
  26. package/fesm2022/mintplayer-ng-spark-auth-register.mjs.map +1 -0
  27. package/fesm2022/mintplayer-ng-spark-auth-reset-password.mjs +223 -0
  28. package/fesm2022/mintplayer-ng-spark-auth-reset-password.mjs.map +1 -0
  29. package/fesm2022/mintplayer-ng-spark-auth-routes.mjs +194 -0
  30. package/fesm2022/mintplayer-ng-spark-auth-routes.mjs.map +1 -0
  31. package/fesm2022/mintplayer-ng-spark-auth-sign-in.mjs +210 -0
  32. package/fesm2022/mintplayer-ng-spark-auth-sign-in.mjs.map +1 -0
  33. package/fesm2022/mintplayer-ng-spark-auth-two-factor.mjs +218 -0
  34. package/fesm2022/mintplayer-ng-spark-auth-two-factor.mjs.map +1 -0
  35. package/fesm2022/mintplayer-ng-spark-auth.mjs +48 -0
  36. package/fesm2022/mintplayer-ng-spark-auth.mjs.map +1 -0
  37. package/fesm2022/mintplayer-ng-spark-client-operations.mjs +294 -2
  38. package/fesm2022/mintplayer-ng-spark-client-operations.mjs.map +1 -1
  39. package/fesm2022/mintplayer-ng-spark-identity-provider-connected-applications.mjs +389 -0
  40. package/fesm2022/mintplayer-ng-spark-identity-provider-connected-applications.mjs.map +1 -0
  41. package/fesm2022/mintplayer-ng-spark-identity-provider-core.mjs +204 -0
  42. package/fesm2022/mintplayer-ng-spark-identity-provider-core.mjs.map +1 -0
  43. package/fesm2022/mintplayer-ng-spark-identity-provider-developers.mjs +486 -0
  44. package/fesm2022/mintplayer-ng-spark-identity-provider-developers.mjs.map +1 -0
  45. package/fesm2022/mintplayer-ng-spark-identity-provider-management.mjs +350 -0
  46. package/fesm2022/mintplayer-ng-spark-identity-provider-management.mjs.map +1 -0
  47. package/fesm2022/mintplayer-ng-spark-identity-provider.mjs +66 -0
  48. package/fesm2022/mintplayer-ng-spark-identity-provider.mjs.map +1 -0
  49. package/fesm2022/mintplayer-ng-spark-pwa.mjs +162 -0
  50. package/fesm2022/mintplayer-ng-spark-pwa.mjs.map +1 -0
  51. package/fesm2022/mintplayer-ng-spark-services.mjs +11 -1
  52. package/fesm2022/mintplayer-ng-spark-services.mjs.map +1 -1
  53. package/fesm2022/mintplayer-ng-spark-shell.mjs.map +1 -1
  54. package/fesm2022/mintplayer-ng-spark.mjs.map +1 -1
  55. package/package.json +99 -1
  56. package/types/mintplayer-ng-spark-auth-account.d.ts +255 -0
  57. package/types/mintplayer-ng-spark-auth-auth-bar.d.ts +22 -0
  58. package/types/mintplayer-ng-spark-auth-confirm-email.d.ts +21 -0
  59. package/types/mintplayer-ng-spark-auth-core.d.ts +197 -0
  60. package/types/mintplayer-ng-spark-auth-external-login.d.ts +132 -0
  61. package/types/mintplayer-ng-spark-auth-forgot-password.d.ts +18 -0
  62. package/types/mintplayer-ng-spark-auth-guards.d.ts +15 -0
  63. package/types/mintplayer-ng-spark-auth-interceptors.d.ts +2 -0
  64. package/types/mintplayer-ng-spark-auth-login.d.ts +46 -0
  65. package/types/mintplayer-ng-spark-auth-models.d.ts +489 -0
  66. package/types/mintplayer-ng-spark-auth-pipes.d.ts +8 -0
  67. package/types/mintplayer-ng-spark-auth-register.d.ts +21 -0
  68. package/types/mintplayer-ng-spark-auth-reset-password.d.ts +25 -0
  69. package/types/mintplayer-ng-spark-auth-routes.d.ts +154 -0
  70. package/types/mintplayer-ng-spark-auth-sign-in.d.ts +84 -0
  71. package/types/mintplayer-ng-spark-auth-two-factor.d.ts +23 -0
  72. package/types/mintplayer-ng-spark-auth.d.ts +24 -0
  73. package/types/mintplayer-ng-spark-client-operations.d.ts +78 -3
  74. package/types/mintplayer-ng-spark-identity-provider-connected-applications.d.ts +35 -0
  75. package/types/mintplayer-ng-spark-identity-provider-core.d.ts +144 -0
  76. package/types/mintplayer-ng-spark-identity-provider-developers.d.ts +55 -0
  77. package/types/mintplayer-ng-spark-identity-provider-management.d.ts +33 -0
  78. package/types/mintplayer-ng-spark-identity-provider.d.ts +64 -0
  79. package/types/mintplayer-ng-spark-pwa.d.ts +89 -0
  80. package/types/mintplayer-ng-spark-services.d.ts +10 -0
  81. package/types/mintplayer-ng-spark-shell.d.ts +2 -2
  82. package/types/mintplayer-ng-spark.d.ts +4 -4
@@ -0,0 +1,154 @@
1
+ import { SparkAccountOverviewLink, SparkAuthRouteEntries, SparkAuthRouteEntry, SparkAuthRoutePaths, SparkExternalProviderPresentation } from "@mintplayer/ng-spark/auth/models";
2
+ import { CanActivateFn } from "@angular/router";
3
+ type Loader = () => Promise<any>;
4
+ interface Child {
5
+ path: string;
6
+ loadComponent?: Loader;
7
+ canActivate?: CanActivateFn[];
8
+ children?: [];
9
+ }
10
+ /**
11
+ * The passkeys page: the generic persistent-object page of the Authorization library's virtual
12
+ * `Passkeys` type, one per signed-in user, so the id is the fixed `me` (generic passkeys page PRD D1).
13
+ */
14
+ export declare const SPARK_PASSKEYS_PAGE_URL = "/po/passkeys/me";
15
+ /**
16
+ * One opt-in group of authentication pages, passed to {@link sparkAuthRoutes}. Constructed only by
17
+ * ng-spark's entry points: `withLocalLogin()`, `withRegistration()`, `withExternalLogin()`,
18
+ * `withPasskeys()` and `withAccount()` here, and `withIdentityProvider()` from
19
+ * `@mintplayer/ng-spark/identity-provider`. That keeps the set of mountable pages a decision this
20
+ * library makes rather than a shape an application invents.
21
+ */
22
+ export interface SparkAuthRoutesFeature {
23
+ readonly children: Child[];
24
+ readonly paths: SparkAuthRoutePaths;
25
+ readonly providers?: SparkExternalProviderPresentation[];
26
+ /** Extra links for the account overview (`SPARK_ACCOUNT_OVERVIEW_LINKS`). */
27
+ readonly accountLinks?: SparkAccountOverviewLink[];
28
+ }
29
+ /** Path override for the sign-in landing page. Named to stay clear of the core package's
30
+ * `SparkExternalLoginOptions`, which is about the sign-in *call* rather than the route. */
31
+ export type SparkExternalLoginRouteOptions = Pick<SparkAuthRouteEntries, 'signIn'>;
32
+ /**
33
+ * The routes for Spark's authentication pages.
34
+ *
35
+ * **Nothing is mounted unless a feature asks for it.** Passing no features emits no pages at all —
36
+ * opt-in enforced by construction rather than by a flag, matching `provideHttpClient(withFetch())`
37
+ * and `provideRouter(withComponentInputBinding())`. Before this the pages mounted by default and an
38
+ * application had to opt *out*, so every app shipped a registration form whether or not it wanted
39
+ * one.
40
+ *
41
+ * ```ts
42
+ * sparkAuthRoutes(
43
+ * withLocalLogin(),
44
+ * withRegistration(),
45
+ * withExternalLogin(githubProvider(), facebookProvider()),
46
+ * )
47
+ * ```
48
+ *
49
+ * The `import()` expressions live *inside* each feature. That placement is the point: a bundler
50
+ * decides whether to emit a lazy chunk from whether the `import()` call site is reachable, not from
51
+ * whether the route object referencing it survives — so filtering a children array after the fact
52
+ * would still ship every page. A page nobody opted into has no reachable `import()` at all.
53
+ */
54
+ export declare function sparkAuthRoutes(...features: SparkAuthRoutesFeature[]): any[];
55
+ /**
56
+ * Mounts the email/password family: login, two-factor, forgot-password, reset-password.
57
+ *
58
+ * These four are one feature rather than four because they form a star centred on the login page and
59
+ * every template dereferences its siblings unconditionally — removing any proper subset leaves a
60
+ * dangling link. Registration is genuinely separable, and is its own feature.
61
+ *
62
+ * Two-factor belongs here because it is reachable only from the login page's `RequiresTwoFactor`
63
+ * branch and posts to the same `/login` endpoint: it is part of password sign-in, not of
64
+ * authentication in general.
65
+ */
66
+ export declare function withLocalLogin(entries?: Pick<SparkAuthRouteEntries, 'login' | 'twoFactor' | 'forgotPassword' | 'resetPassword'>): SparkAuthRoutesFeature;
67
+ /**
68
+ * Mounts the self-service registration page.
69
+ *
70
+ * Separate from {@link withLocalLogin} because the two decisions genuinely are separate: an
71
+ * application whose accounts are provisioned by an administrator still needs password sign-in and
72
+ * password reset. Mount it only alongside a server whose `SparkLocalCredentials` is `Full` — with
73
+ * `SignInOnly` the endpoint it posts to is not mapped.
74
+ */
75
+ export declare function withRegistration(entry?: SparkAuthRouteEntry): SparkAuthRoutesFeature;
76
+ /**
77
+ * Mounts the sign-in landing page — a button per external provider the *server* reports.
78
+ *
79
+ * Accepts provider declarations and, in any position, one options object for the page's own path;
80
+ * the two shapes are disjoint, so `withExternalLogin(githubProvider(), facebookProvider())` and
81
+ * `withExternalLogin({ signIn: 'landing' }, githubProvider())` both read naturally.
82
+ *
83
+ * Declaring no providers is valid and useful: the page still renders whatever
84
+ * `GET /spark/auth/capabilities` reports, using default buttons.
85
+ */
86
+ export declare function withExternalLogin(...providersOrOptions: (SparkExternalProviderPresentation | SparkExternalLoginRouteOptions)[]): SparkAuthRoutesFeature;
87
+ /**
88
+ * Presentation for one provider's button, keyed by the scheme the server reports.
89
+ *
90
+ * The generic form. `githubProvider()` and friends are this with a scheme and an icon filled in —
91
+ * they exist so the common case does not require knowing the scheme string, not because the library
92
+ * has an opinion about which providers exist.
93
+ */
94
+ /**
95
+ * Mounts the passkey management path, which forwards to the generic passkeys page (`/po/passkeys/me`).
96
+ *
97
+ * Independent of every other feature here, and of the server's `SparkLocalCredentials`: a passkey is
98
+ * a passwordless credential, so the applications most likely to want this page are exactly the ones
99
+ * that mount no password pages at all. It pairs with `SparkPasskeys.Enabled` on the server.
100
+ *
101
+ * ⚠️ The page assumes a signed-in user — enrollment adds a credential to an existing account, it
102
+ * cannot create one. Mount it inside the application's authenticated area, or behind
103
+ * `sparkAuthGuard`; the server answers the page with 404 to a visitor who is not signed in.
104
+ */
105
+ export declare function withPasskeys(entry?: SparkAuthRouteEntry): SparkAuthRoutesFeature;
106
+ /** Options for {@link withAccount}: a path/component per page, and the guard of the signed-in pages. */
107
+ export type SparkAccountRouteOptions = Pick<SparkAuthRouteEntries, 'confirmEmail' | 'account' | 'profile' | 'changePassword' | 'twoFactorSetup' | 'externalLogins' | 'passkeys' | 'personalData'> & {
108
+ /**
109
+ * Guards the signed-in pages (everything but confirm-email). Defaults to `[sparkAuthGuard]`,
110
+ * which waits for the session check so a hard reload does not bounce a signed-in user to the
111
+ * sign-in page. Pass `[]` when the pages sit under a guarded parent route already.
112
+ */
113
+ canActivate?: CanActivateFn[];
114
+ /**
115
+ * Pages to leave out, e.g. `['externalLogins']` for an app without external providers or
116
+ * `['changePassword']` under `SparkLocalCredentials.Disabled` (the endpoint is not mapped there).
117
+ */
118
+ exclude?: (keyof Omit<SparkAccountRouteOptions, 'canActivate' | 'exclude'>)[];
119
+ };
120
+ /**
121
+ * Mounts the account pages (#460, D16), each also usable as a standalone component:
122
+ *
123
+ * | page | default path | component |
124
+ * |---|---|---|
125
+ * | confirm email (public; the mail link target) | `confirm-email` | `SparkConfirmEmailComponent` |
126
+ * | overview | `account` | `SparkAccountOverviewComponent` |
127
+ * | profile (user name, email change, mail language, app fields) | `account/profile` | `SparkAccountProfileComponent` |
128
+ * | change / set password | `account/password` | `SparkChangePasswordComponent` |
129
+ * | two-factor (authenticator, recovery codes) | `account/two-factor` | `SparkTwoFactorSetupComponent` |
130
+ * | connected logins | `account/logins` | `SparkExternalLoginsComponent` |
131
+ * | passkeys (forwards to the generic page, `/po/passkeys/me`) | `account/passkeys` | — |
132
+ * | personal data + account deletion | `account/personal-data` | `SparkPersonalDataComponent` |
133
+ *
134
+ * `confirm-email` must stay at the server's `Spark:Auth:Links:ConfirmEmailPath` (default
135
+ * `/confirm-email`), which confirmation mails link to. No path has a parameterised first segment, so the
136
+ * group neither shadows `sparkRoutes()` nor is shadowed by it.
137
+ */
138
+ export declare function withAccount(options?: SparkAccountRouteOptions): SparkAuthRoutesFeature;
139
+ export declare function externalProvider(scheme: string, presentation?: Omit<SparkExternalProviderPresentation, 'scheme'>): SparkExternalProviderPresentation;
140
+ export declare function githubProvider(presentation?: Omit<SparkExternalProviderPresentation, 'scheme'>): SparkExternalProviderPresentation;
141
+ export declare function googleProvider(presentation?: Omit<SparkExternalProviderPresentation, 'scheme'>): SparkExternalProviderPresentation;
142
+ export declare function facebookProvider(presentation?: Omit<SparkExternalProviderPresentation, 'scheme'>): SparkExternalProviderPresentation;
143
+ export declare function microsoftProvider(presentation?: Omit<SparkExternalProviderPresentation, 'scheme'>): SparkExternalProviderPresentation;
144
+ /** X (formerly Twitter) — the server's `spark.AddTwitter()` preset (OAuth 2.0), scheme `Twitter`. */
145
+ export declare function twitterProvider(presentation?: Omit<SparkExternalProviderPresentation, 'scheme'>): SparkExternalProviderPresentation;
146
+ /** LinkedIn — the server's `spark.AddLinkedIn()` preset (OpenID Connect userinfo), scheme `LinkedIn`. */
147
+ export declare function linkedInProvider(presentation?: Omit<SparkExternalProviderPresentation, 'scheme'>): SparkExternalProviderPresentation;
148
+ /**
149
+ * Any OpenID Connect provider — the client half of the server's OIDC preset
150
+ * (`spark.AddOpenIdConnect(...)`, or a `Spark:Auth:Providers:OpenIdConnect:<scheme>` section). Unlike the named presets the scheme is the
151
+ * application's own choice, so it and the button text are required; `scheme` must equal the server's
152
+ * scheme name exactly.
153
+ */
154
+ export declare function oidcProvider(scheme: string, displayName: string, iconClass?: string): SparkExternalProviderPresentation;
@@ -0,0 +1,84 @@
1
+ import * as i0 from "@angular/core";
2
+ import { TemplateRef } from "@angular/core";
3
+ import { Color } from "@mintplayer/ng-bootstrap";
4
+ import { SparkExternalProviderView, SparkProviderButtonContext, SparkProviderButtonContext as SparkProviderButtonContext$1 } from "@mintplayer/ng-spark/auth/external-login";
5
+ /**
6
+ * The sign-in landing page: a button per external provider, and a link to the password form when the
7
+ * application mounted one.
8
+ *
9
+ * The provider buttons are `<spark-external-login-buttons>`, which the login page hosts as well; this
10
+ * page adds the passkey button, the link to the password form, and says so when there is no way to
11
+ * sign in at all.
12
+ */
13
+ export declare class SparkSignInComponent {
14
+ private readonly authService;
15
+ private readonly route;
16
+ private readonly router;
17
+ private readonly config;
18
+ readonly routePaths: Partial<Record<keyof import("@mintplayer/ng-spark/auth/models").SparkAuthRouteEntries, string>>;
19
+ readonly colors: typeof Color;
20
+ /**
21
+ * Where to land after a successful external sign-in.
22
+ *
23
+ * Falls back to a `?returnUrl=` query parameter, matching what the login page already does — so a
24
+ * plain `<a routerLink="/sign-in" [queryParams]="{ returnUrl: '/somewhere' }">` works with no
25
+ * wiring, which is what a routed page needs since the router passes it no inputs. The query value
26
+ * is validated as a local path before use; an off-site one is dropped rather than followed.
27
+ */
28
+ readonly returnUrl: import("@angular/core").InputSignal<string | undefined>;
29
+ private effectiveReturnUrl;
30
+ /**
31
+ * Replaces the default provider button. Rendered once per provider with a
32
+ * {@link SparkProviderButtonContext}.
33
+ *
34
+ * Reachable only when the consumer *hosts* this component — the router instantiates it with no
35
+ * projected content, so on a default `withExternalLogin()` route there is nothing to project into.
36
+ * Host it via `withExternalLogin({ signIn: { path: 'sign-in', component: MySignIn } })` and pass
37
+ * the template from there.
38
+ */
39
+ readonly providerTemplate: import("@angular/core").InputSignal<TemplateRef<SparkProviderButtonContext$1> | null>;
40
+ /** Whether the server reported any external provider; with none and no passkeys there is no way in. */
41
+ readonly hasProviders: import("@angular/core").WritableSignal<boolean>;
42
+ readonly localCredentialsAvailable: import("@angular/core").WritableSignal<boolean>;
43
+ readonly loading: import("@angular/core").WritableSignal<boolean>;
44
+ readonly failed: import("@angular/core").WritableSignal<boolean>;
45
+ /**
46
+ * Two conditions, and both are necessary. The server must have mounted the sign-in endpoint, and
47
+ * this browser must be able to run the ceremony — offering the button on either half alone
48
+ * produces a control that fails the moment it is clicked.
49
+ */
50
+ readonly passkeysAvailable: import("@angular/core").WritableSignal<boolean>;
51
+ readonly passkeyBusy: import("@angular/core").WritableSignal<boolean>;
52
+ readonly passkeyError: import("@angular/core").WritableSignal<string>;
53
+ constructor();
54
+ private load;
55
+ /**
56
+ * The routes are a build-time decision and the server's mode a deployment-time one, so they can
57
+ * disagree without anything failing loudly. Reaching this page at all means the app routed it,
58
+ * which it only does when local credentials are meant to be limited.
59
+ */
60
+ private warnOnMismatch;
61
+ /**
62
+ * Signs in with a passkey, then navigates. The navigation matters for the same reason it does
63
+ * after an external sign-in: nothing else moves the user off this page.
64
+ *
65
+ * No username is collected, and none should be: the server's request-options endpoint refuses to
66
+ * accept an identity so that its answer cannot reveal whether an account exists. The browser
67
+ * offers whatever discoverable credential it holds.
68
+ */
69
+ signInWithPasskey(): Promise<void>;
70
+ static ɵfac: i0.ɵɵFactoryDeclaration<SparkSignInComponent, never>;
71
+ static ɵcmp: i0.ɵɵComponentDeclaration<SparkSignInComponent, "spark-sign-in", never, {
72
+ "returnUrl": {
73
+ "alias": "returnUrl";
74
+ "required": false;
75
+ "isSignal": true;
76
+ };
77
+ "providerTemplate": {
78
+ "alias": "providerTemplate";
79
+ "required": false;
80
+ "isSignal": true;
81
+ };
82
+ }, {}, never, never, true, never>;
83
+ }
84
+ export type { SparkExternalProviderView, SparkProviderButtonContext };
@@ -0,0 +1,23 @@
1
+ import { Color } from "@mintplayer/ng-bootstrap";
2
+ import * as i0 from "@angular/core";
3
+ export declare class SparkTwoFactorComponent {
4
+ private readonly fb;
5
+ private readonly authService;
6
+ private readonly router;
7
+ private readonly route;
8
+ private readonly config;
9
+ private readonly translation;
10
+ readonly routePaths: Partial<Record<keyof import("@mintplayer/ng-spark/auth/models").SparkAuthRouteEntries, string>>;
11
+ colors: typeof Color;
12
+ readonly loading: import("@angular/core").WritableSignal<boolean>;
13
+ readonly errorMessage: import("@angular/core").WritableSignal<string>;
14
+ readonly useRecoveryCode: import("@angular/core").WritableSignal<boolean>;
15
+ readonly form: import("@angular/forms").FormGroup<{
16
+ code: import("@angular/forms").FormControl<string | null>;
17
+ recoveryCode: import("@angular/forms").FormControl<string | null>;
18
+ }>;
19
+ toggleRecoveryCode(): void;
20
+ onSubmit(): Promise<void>;
21
+ static ɵfac: i0.ɵɵFactoryDeclaration<SparkTwoFactorComponent, never>;
22
+ static ɵcmp: i0.ɵɵComponentDeclaration<SparkTwoFactorComponent, "spark-two-factor", never, {}, {}, never, never, true, never>;
23
+ }
@@ -0,0 +1,24 @@
1
+ import { SPARK_AUTH_CONFIG, SparkAuthConfig, SparkAuthConfig as SparkAuthConfig$1, defaultSparkAuthConfig } from "@mintplayer/ng-spark/auth/models";
2
+ import { EnvironmentProviders } from "@angular/core";
3
+ import { HttpFeature, HttpFeatureKind } from "@angular/common/http";
4
+ import { SparkClientMethodMap } from "@mintplayer/ng-spark/client-operations";
5
+ export declare function provideSparkAuth(config?: Partial<SparkAuthConfig$1>): EnvironmentProviders;
6
+ export declare function withSparkAuth(): HttpFeature<HttpFeatureKind>[];
7
+ /**
8
+ * The browser steps the Authorization library's server actions invoke (`IRetryAccessor.Invoke`),
9
+ * registered by {@link provideSparkAuth}.
10
+ *
11
+ * `webauthn.create` is the enrollment ceremony of the passkeys page's `AddPasskey` action: the
12
+ * server hands it the creation options and receives the credential back in the same action, which
13
+ * attests it against the ceremony cookie its first pass set. The server keeps the challenge; nothing
14
+ * here chooses or carries one.
15
+ *
16
+ * - The user dismissing the prompt (`cancelled`, `no_credential`) rejects, which answers the server
17
+ * with `Cancel`: not an error, so no banner.
18
+ * - Any other failure (an authenticator already holding a credential for this account, a security
19
+ * error) resolves as `{ error }`, so the server can say it did not work.
20
+ * - An unsupported browser never runs it (`supported`), and the action shows disabled with
21
+ * `auth.passkeyUnsupported` as the reason.
22
+ */
23
+ export declare const sparkAuthClientMethods: SparkClientMethodMap;
24
+ export { SPARK_AUTH_CONFIG, type SparkAuthConfig, defaultSparkAuthConfig };
@@ -1,6 +1,7 @@
1
1
  import { PersistentObject, TranslatedString } from "@mintplayer/ng-spark/models";
2
2
  import * as i0 from "@angular/core";
3
3
  import { EnvironmentProviders, InjectionToken } from "@angular/core";
4
+ import { Color } from "@mintplayer/ng-bootstrap";
4
5
  export declare enum NotificationKind {
5
6
  Info = 0,
6
7
  Success = 1,
@@ -60,12 +61,27 @@ export interface RetryOperation {
60
61
  /** Its argument, as the server serialized it. */
61
62
  arguments?: unknown;
62
63
  }
64
+ /**
65
+ * A value the server shows exactly once (a generated client secret, an API token), in a dialog with
66
+ * a copy button (`IClientAccessor.ShowSecret`, `docs/identity_provider_platform_PRD.md` D5). The server
67
+ * keeps only a hash. The value is never written to the page's state, the URL or browser storage, and
68
+ * closing the dialog discards it.
69
+ */
70
+ export interface ShowSecretOperation {
71
+ type: 'showSecret';
72
+ /** The dialog's title, in the request's language. */
73
+ title: string;
74
+ /** The explanation shown above the value, in the request's language. */
75
+ message: string;
76
+ /** The value itself. */
77
+ value: string;
78
+ }
63
79
  /**
64
80
  * Discriminated union of known operation types, plus an open shape for unknown
65
81
  * future operations. Handlers should narrow via the `type` discriminator before
66
82
  * accessing fields specific to their operation type.
67
83
  */
68
- export type ClientOperation = NavigateOperation | NotifyOperation | RefreshAttributeOperation | RefreshQueryOperation | RetryOperation | {
84
+ export type ClientOperation = NavigateOperation | NotifyOperation | RefreshAttributeOperation | RefreshQueryOperation | RetryOperation | ShowSecretOperation | {
69
85
  type: string;
70
86
  [key: string]: unknown;
71
87
  };
@@ -91,7 +107,7 @@ export interface ClientOperationEnvelope<T = unknown> {
91
107
  * The dispatcher treats handler resolution as allow-list-by-type (unknown
92
108
  * types drop). It does NOT validate the *content* of each operation. Handlers
93
109
  * that act on URL-shaped fields (navigate, redirect, openWindow) MUST run
94
- * the value through `sanitizeReturnUrl` from `@mintplayer/ng-spark-auth/models`
110
+ * the value through `sanitizeReturnUrl` from `@mintplayer/ng-spark/auth/models`
95
111
  * (or an equivalent same-origin check) before acting on it. Otherwise a
96
112
  * single attribute-echo XSS or a single mid-channel byte flip on a non-TLS
97
113
  * path lets the server drive client navigation to an attacker host. The
@@ -220,7 +236,10 @@ export declare class SparkToastContainerComponent {
220
236
  }
221
237
  /**
222
238
  * Registers the built-in client-operation handlers: `notify`, `refreshQuery`,
223
- * `refreshAttribute` and `navigate`. Apps add this once in their bootstrap providers.
239
+ * `refreshAttribute`, `navigate` and `showSecret`. Apps add this once in their bootstrap providers.
240
+ *
241
+ * `showSecret` opens {@link SparkSecretDialogService}'s dialog, which mounts itself: without it the
242
+ * value the server shows once (a client secret, a token) would be lost, since the server keeps a hash.
224
243
  *
225
244
  * Unregistered operation types are dropped SILENTLY by the dispatcher, which is why
226
245
  * `refreshQuery` did nothing at all for as long as it went unhandled — the server emitted
@@ -240,6 +259,62 @@ export declare class SparkToastContainerComponent {
240
259
  * `multi: true` providers using <see cref="SPARK_CLIENT_OPERATION_HANDLERS" />.
241
260
  */
242
261
  export declare function provideSparkClientOperations(): EnvironmentProviders;
262
+ /** A value shown once: the `showSecret` operation's payload, or a value a page received itself. */
263
+ export interface SparkSecret {
264
+ title: string;
265
+ message: string;
266
+ value: string;
267
+ }
268
+ /**
269
+ * Shows a value the server will never show again, in a modal with a copy button (`showSecret`,
270
+ * `docs/identity_provider_platform_PRD.md` D5).
271
+ *
272
+ * The dialog mounts itself on `document.body` the first time it is needed, so an application does not
273
+ * have to place a component in its root template for `IClientAccessor.ShowSecret` to work: a handler
274
+ * whose dialog nobody placed would lose the value as surely as a missing handler.
275
+ *
276
+ * The value lives only in this service's signal while the dialog is open. Closing the dialog clears
277
+ * it; it is never written to the URL, the router state or browser storage.
278
+ */
279
+ export declare class SparkSecretDialogService {
280
+ private readonly appRef;
281
+ private readonly injector;
282
+ private readonly document;
283
+ private readonly browser;
284
+ private readonly current;
285
+ private dialog;
286
+ /** The secret on display, or null when the dialog is closed. */
287
+ readonly secret: import("@angular/core").Signal<SparkSecret | null>;
288
+ constructor();
289
+ show(secret: SparkSecret): void;
290
+ /** Closes the dialog and discards the value. */
291
+ close(): void;
292
+ private mount;
293
+ static ɵfac: i0.ɵɵFactoryDeclaration<SparkSecretDialogService, never>;
294
+ static ɵprov: i0.ɵɵInjectableDeclaration<any>;
295
+ }
296
+ /** The dialog behind {@link SparkSecretDialogService}; mounted by the service, not by applications. */
297
+ export declare class SparkShowSecretModalComponent {
298
+ private readonly dialog;
299
+ private readonly document;
300
+ private copiedTimer;
301
+ protected readonly colors: typeof Color;
302
+ protected readonly secret: import("@angular/core").Signal<SparkSecret | null>;
303
+ protected readonly isOpen: import("@angular/core").Signal<boolean>;
304
+ protected readonly copyState: import("@angular/core").WritableSignal<"copied" | "idle" | "failed">;
305
+ protected readonly text: import("@angular/core").Signal<{
306
+ copy: string;
307
+ copied: string;
308
+ copyFailed: string;
309
+ close: string;
310
+ value: string;
311
+ }>;
312
+ protected selectAll(event: Event): void;
313
+ protected copy(): Promise<void>;
314
+ protected close(): void;
315
+ static ɵfac: i0.ɵɵFactoryDeclaration<SparkShowSecretModalComponent, never>;
316
+ static ɵcmp: i0.ɵɵComponentDeclaration<SparkShowSecretModalComponent, "spark-show-secret-modal", never, {}, {}, never, never, true, never>;
317
+ }
243
318
  /**
244
319
  * Carries a server-issued `refreshQuery` to whichever grids are showing that query.
245
320
  *
@@ -0,0 +1,35 @@
1
+ import { Color } from "@mintplayer/ng-bootstrap";
2
+ import { SparkConnectedApplication, SparkConnectedScope } from "@mintplayer/ng-spark/identity-provider/core";
3
+ import * as i0 from "@angular/core";
4
+ /**
5
+ * The signed-in user's connected applications (`account/applications`, PRD D6/D7): each application
6
+ * the user granted access to, with its logo, publisher, the granted scopes and the dates. The user
7
+ * withdraws the whole grant, or unticks individual scopes and withdraws only those. Required scopes
8
+ * (`openid`, and those the application or resource marks required) go only with the whole grant.
9
+ *
10
+ * The server resolves the scope texts in the request's language; the `spark-lang` cookie
11
+ * `SparkLanguageService` writes is what makes that the language the user picked here.
12
+ */
13
+ export declare class SparkConnectedApplicationsComponent {
14
+ private readonly text;
15
+ private readonly portal;
16
+ protected readonly colors: typeof Color;
17
+ readonly loading: import("@angular/core").WritableSignal<boolean>;
18
+ readonly busy: import("@angular/core").WritableSignal<boolean>;
19
+ readonly apps: import("@angular/core").WritableSignal<SparkConnectedApplication[]>;
20
+ readonly error: import("@angular/core").WritableSignal<string>;
21
+ readonly notice: import("@angular/core").WritableSignal<string>;
22
+ /** Per application, the scopes the user unticked (to withdraw). */
23
+ private readonly unticked;
24
+ constructor();
25
+ load(): Promise<void>;
26
+ protected hasOptionalScopes(app: SparkConnectedApplication): boolean;
27
+ protected isUnticked(app: SparkConnectedApplication, scope: SparkConnectedScope): boolean;
28
+ protected untickedOf(app: SparkConnectedApplication): readonly string[];
29
+ protected setTicked(app: SparkConnectedApplication, scope: SparkConnectedScope, ticked: boolean): void;
30
+ withdrawUnticked(app: SparkConnectedApplication): Promise<void>;
31
+ withdrawAll(app: SparkConnectedApplication): Promise<void>;
32
+ private withdraw;
33
+ static ɵfac: i0.ɵɵFactoryDeclaration<SparkConnectedApplicationsComponent, never>;
34
+ static ɵcmp: i0.ɵɵComponentDeclaration<SparkConnectedApplicationsComponent, "spark-connected-applications", never, {}, {}, never, never, true, never>;
35
+ }
@@ -0,0 +1,144 @@
1
+ import * as i0 from "@angular/core";
2
+ import { PipeTransform } from "@angular/core";
3
+ /** One granted scope, with its text in the request's language (`spark-lang` cookie). */
4
+ export interface SparkConnectedScope {
5
+ name: string;
6
+ displayName: string;
7
+ description?: string | null;
8
+ /** `openid`, or a scope the resource or the application marks required: only withdrawable with the whole grant. */
9
+ required: boolean;
10
+ }
11
+ /** One application the signed-in user granted access to (`GET /applications`, PRD D6). */
12
+ export interface SparkConnectedApplication {
13
+ applicationId: string;
14
+ displayName: string;
15
+ logoUrl?: string | null;
16
+ publisher?: string | null;
17
+ homepageUrl?: string | null;
18
+ scopes: SparkConnectedScope[];
19
+ firstGrantedAt: string;
20
+ lastUsedAt?: string | null;
21
+ remembered: boolean;
22
+ rememberedUntil?: string | null;
23
+ }
24
+ /** The body of `POST /applications/withdraw`. No `scopes` withdraws the whole grant. */
25
+ export interface SparkWithdrawAccessRequest {
26
+ applicationId: string;
27
+ scopes?: string[];
28
+ }
29
+ /** The developer statuses the server stores (`OidcDeveloperStatuses`). */
30
+ export type SparkDeveloperStatusName = 'Requested' | 'Approved' | 'Rejected' | 'Revoked';
31
+ /** `GET /developer` and `POST /developer` (`OidcDeveloperStatusResponse`). */
32
+ export interface SparkDeveloperStatus {
33
+ /** Null when the user never asked. */
34
+ status: SparkDeveloperStatusName | string | null;
35
+ acceptedTermsVersion: number | null;
36
+ currentTermsVersion: number;
37
+ termsUrl: string | null;
38
+ requireApproval: boolean;
39
+ isActive: boolean;
40
+ requestedAt: string | null;
41
+ decidedAt: string | null;
42
+ note: string | null;
43
+ }
44
+ /** `POST /developer/registration-token`: an RFC 7591 initial access token, shown once. */
45
+ export interface SparkRegistrationToken {
46
+ initialAccessToken: string;
47
+ /** Seconds. */
48
+ expiresIn: number;
49
+ }
50
+ /** `POST /invitations/accept` (`OidcAcceptInvitationResponse`). */
51
+ export interface SparkInvitationOutcome {
52
+ accepted: boolean;
53
+ application: string | null;
54
+ problem: string | null;
55
+ }
56
+ /** One signing key on the management page (`OidcKeySummary`); never its private material. */
57
+ export interface SparkSigningKey {
58
+ kid: string;
59
+ algorithm: string;
60
+ state: string;
61
+ createdAt: string;
62
+ activatedAt: string | null;
63
+ retiredAt: string | null;
64
+ }
65
+ /** `POST /admin/keys/rotate`. */
66
+ export interface SparkKeyRotation {
67
+ changes: string[];
68
+ }
69
+ /** The outcome of a portal call: the value, or the status and the server's problem text. */
70
+ export type SparkPortalResult<T> = {
71
+ ok: true;
72
+ value: T;
73
+ } | {
74
+ ok: false;
75
+ status: number;
76
+ problem: string | null;
77
+ };
78
+ /** The portal API's prefix (`IdentityProviderPortalGroup.Prefix` on the server). */
79
+ export declare const SPARK_IDENTITY_PROVIDER_API = "/spark/identity-provider";
80
+ /**
81
+ * The identity provider's portal API for the SPA pages (PRD D7). Every call is for a signed-in user;
82
+ * the POSTs require the antiforgery token, which Angular's XSRF support adds from the `XSRF-TOKEN`
83
+ * cookie (`withSparkAuth()` configures it), so the calls are plain `HttpClient` requests.
84
+ *
85
+ * Calls never throw: each resolves to a {@link SparkPortalResult}, so a page branches on `ok` and on
86
+ * the status (403 for a page the user may not see) instead of catching.
87
+ */
88
+ export declare class SparkIdentityProviderPortalService {
89
+ private readonly http;
90
+ connectedApplications(): Promise<SparkPortalResult<SparkConnectedApplication[]>>;
91
+ /** Withdraws the whole grant, or only `scopes` when given. Answers 204, or 409 when it could not. */
92
+ withdraw(request: SparkWithdrawAccessRequest): Promise<SparkPortalResult<null>>;
93
+ developerStatus(): Promise<SparkPortalResult<SparkDeveloperStatus>>;
94
+ /** Accepts the terms version the page showed; the server refuses (409) a version that is no longer current. */
95
+ requestDeveloperStatus(termsVersion: number): Promise<SparkPortalResult<SparkDeveloperStatus>>;
96
+ issueRegistrationToken(): Promise<SparkPortalResult<SparkRegistrationToken>>;
97
+ acceptInvitation(applicationId: string, token: string): Promise<SparkPortalResult<SparkInvitationOutcome>>;
98
+ signingKeys(): Promise<SparkPortalResult<SparkSigningKey[]>>;
99
+ rotateSigningKeys(): Promise<SparkPortalResult<SparkKeyRotation>>;
100
+ private call;
101
+ static ɵfac: i0.ɵɵFactoryDeclaration<SparkIdentityProviderPortalService, never>;
102
+ static ɵprov: i0.ɵɵInjectableDeclaration<any>;
103
+ }
104
+ /**
105
+ * A text function for component code: `private readonly text = injectIdentityProviderText();` then
106
+ * `this.text('apps.withdrawn')`. The texts come from the server's translations in the app's current
107
+ * language, like every other ng-spark page; `{0}`, `{1}` are replaced by the arguments.
108
+ */
109
+ export declare function injectIdentityProviderText(): (key: string, ...args: unknown[]) => string;
110
+ /** `{{ 'apps.title' | idp }}`, `{{ 'apps.by' | idp: publisher }}`. Impure: follows the language and the loaded translations. */
111
+ export declare class SparkIdentityProviderTextPipe implements PipeTransform {
112
+ private readonly translations;
113
+ transform(key: string, ...args: unknown[]): string;
114
+ static ɵfac: i0.ɵɵFactoryDeclaration<SparkIdentityProviderTextPipe, never>;
115
+ static ɵpipe: i0.ɵɵPipeDeclaration<SparkIdentityProviderTextPipe, "idp", true>;
116
+ }
117
+ /**
118
+ * A server date (ISO string) as a date and time in the app's current language; empty for null.
119
+ * `Intl` rather than Angular's `DatePipe`, which needs locale data registered for every language.
120
+ */
121
+ export declare function identityProviderDate(value: string | null | undefined): string;
122
+ /** `{{ app.firstGrantedAt | idpDate }}`. Impure: follows the language signal. */
123
+ export declare class SparkIdentityProviderDatePipe implements PipeTransform {
124
+ transform(value: string | null | undefined): string;
125
+ static ɵfac: i0.ɵɵFactoryDeclaration<SparkIdentityProviderDatePipe, never>;
126
+ static ɵpipe: i0.ɵɵPipeDeclaration<SparkIdentityProviderDatePipe, "idpDate", true>;
127
+ }
128
+ /**
129
+ * The identity-provider library's queries, by the aliases in its `App_Data/Model/*.json`
130
+ * (`queries[].alias`). They open in the generic query page of `sparkRoutes()`, `/query/:queryId`,
131
+ * which resolves an alias as well as an id. Each lists only what the caller may see.
132
+ */
133
+ export declare const SPARK_IDENTITY_PROVIDER_QUERIES: {
134
+ /** `OidcApplication`: the caller's applications (members-only filter), or all of them for an administrator. */
135
+ readonly applications: "oidc-applications";
136
+ readonly resources: "oidc-resources";
137
+ readonly developerRequests: "oidc-developer-requests";
138
+ readonly scopeApprovals: "oidc-scope-approvals";
139
+ readonly goLiveReviews: "oidc-golive-reviews";
140
+ readonly grants: "oidc-grants";
141
+ readonly auditEvents: "oidc-audit-events";
142
+ };
143
+ /** The router commands that open one of {@link SPARK_IDENTITY_PROVIDER_QUERIES}. */
144
+ export declare function identityProviderQueryLink(alias: string): string[];
@@ -0,0 +1,55 @@
1
+ import { Color } from "@mintplayer/ng-bootstrap";
2
+ import { SparkDeveloperStatus } from "@mintplayer/ng-spark/identity-provider/core";
3
+ import * as i0 from "@angular/core";
4
+ /**
5
+ * The developer portal's landing page (`/developers`, PRD D2/D7): the user's developer status, the
6
+ * terms to accept to request it (again, when they were raised), and for an active developer a link to
7
+ * their applications (the generic query page, members-only filtered) and a one-time registration
8
+ * token for dynamic client registration (RFC 7591), shown in the same dialog as `showSecret`.
9
+ */
10
+ export declare class SparkDevelopersComponent {
11
+ private readonly text;
12
+ private readonly portal;
13
+ private readonly secrets;
14
+ protected readonly colors: typeof Color;
15
+ protected readonly applicationsLink: string[];
16
+ readonly loading: import("@angular/core").WritableSignal<boolean>;
17
+ readonly busy: import("@angular/core").WritableSignal<boolean>;
18
+ readonly status: import("@angular/core").WritableSignal<SparkDeveloperStatus | null>;
19
+ readonly accepted: import("@angular/core").WritableSignal<boolean>;
20
+ readonly error: import("@angular/core").WritableSignal<string>;
21
+ readonly notice: import("@angular/core").WritableSignal<string>;
22
+ protected readonly statusKey: import("@angular/core").Signal<string>;
23
+ /** A developer (or a pending request) whose accepted terms are older than the current ones. */
24
+ protected readonly termsChanged: import("@angular/core").Signal<boolean>;
25
+ /** Asking (again) is possible unless an administrator revoked the status; the server refuses that too. */
26
+ protected readonly canRequest: import("@angular/core").Signal<boolean>;
27
+ constructor();
28
+ load(): Promise<void>;
29
+ request(): Promise<void>;
30
+ issueToken(): Promise<void>;
31
+ static ɵfac: i0.ɵɵFactoryDeclaration<SparkDevelopersComponent, never>;
32
+ static ɵcmp: i0.ɵɵComponentDeclaration<SparkDevelopersComponent, "spark-developers", never, {}, {}, never, never, true, never>;
33
+ }
34
+ /**
35
+ * The target of a mailed team invitation (`/developers/invitations/:token?app=<applicationId>`, PRD
36
+ * D3). Accepting is an explicit click, not a side effect of opening the link, and needs the signed-in
37
+ * account the invitation was sent to; every failure reads the same, as the server answers it.
38
+ */
39
+ export declare class SparkAcceptInvitationComponent {
40
+ private readonly portal;
41
+ private readonly route;
42
+ protected readonly colors: typeof Color;
43
+ protected readonly applicationsLink: string[];
44
+ private readonly token;
45
+ private readonly applicationId;
46
+ /** A link without both values cannot be an invitation. */
47
+ protected readonly valid: boolean;
48
+ readonly busy: import("@angular/core").WritableSignal<boolean>;
49
+ readonly failed: import("@angular/core").WritableSignal<boolean>;
50
+ /** The application joined, once accepted. */
51
+ readonly joined: import("@angular/core").WritableSignal<string | null>;
52
+ accept(): Promise<void>;
53
+ static ɵfac: i0.ɵɵFactoryDeclaration<SparkAcceptInvitationComponent, never>;
54
+ static ɵcmp: i0.ɵɵComponentDeclaration<SparkAcceptInvitationComponent, "spark-accept-invitation", never, {}, {}, never, never, true, never>;
55
+ }