@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,194 @@
1
+ import { SPARK_ACCOUNT_OVERVIEW_LINKS, SPARK_AUTH_ROUTE_PATHS, SPARK_EXTERNAL_PROVIDERS } from "@mintplayer/ng-spark/auth/models";
2
+ import { inject } from "@angular/core";
3
+ import { Router } from "@angular/router";
4
+ import { sparkAuthGuard } from "@mintplayer/ng-spark/auth/guards";
5
+ const SPARK_PASSKEYS_PAGE_URL = "/po/passkeys/me";
6
+ const toPasskeysPage = () => inject(Router).parseUrl(SPARK_PASSKEYS_PAGE_URL);
7
+ function passkeysChild(entry, path, guard) {
8
+ if (typeof entry === "object" && entry.component) {
9
+ const component = entry.component;
10
+ return {
11
+ path,
12
+ loadComponent: () => Promise.resolve(component),
13
+ ...guard.length ? { canActivate: guard } : {}
14
+ };
15
+ }
16
+ return {
17
+ path,
18
+ canActivate: [...guard, toPasskeysPage],
19
+ children: []
20
+ };
21
+ }
22
+ function entryPath(entry, defaultPath) {
23
+ if (entry === void 0) return defaultPath;
24
+ return typeof entry === "string" ? entry : entry.path;
25
+ }
26
+ function child(entry, path, defaultLoader) {
27
+ const component = typeof entry === "object" && entry.component ? entry.component : void 0;
28
+ return {
29
+ path,
30
+ loadComponent: component ? () => Promise.resolve(component) : defaultLoader
31
+ };
32
+ }
33
+ function sparkAuthRoutes(...features) {
34
+ const children = [];
35
+ const paths = {};
36
+ const externalProviders = [];
37
+ const accountLinks = [];
38
+ for (const feature of features) {
39
+ children.push(...feature.children);
40
+ Object.assign(paths, feature.paths);
41
+ if (feature.providers) externalProviders.push(...feature.providers);
42
+ if (feature.accountLinks) accountLinks.push(...feature.accountLinks);
43
+ }
44
+ return [{
45
+ path: "",
46
+ providers: [
47
+ {
48
+ provide: SPARK_AUTH_ROUTE_PATHS,
49
+ useValue: paths
50
+ },
51
+ {
52
+ provide: SPARK_EXTERNAL_PROVIDERS,
53
+ useValue: externalProviders
54
+ },
55
+ {
56
+ provide: SPARK_ACCOUNT_OVERVIEW_LINKS,
57
+ useValue: accountLinks
58
+ }
59
+ ],
60
+ children
61
+ }];
62
+ }
63
+ function withLocalLogin(entries) {
64
+ const login = entryPath(entries?.login, "login");
65
+ const twoFactor = entryPath(entries?.twoFactor, "login/two-factor");
66
+ const forgotPassword = entryPath(entries?.forgotPassword, "forgot-password");
67
+ const resetPassword = entryPath(entries?.resetPassword, "reset-password");
68
+ return {
69
+ paths: {
70
+ login: "/" + login,
71
+ twoFactor: "/" + twoFactor,
72
+ forgotPassword: "/" + forgotPassword,
73
+ resetPassword: "/" + resetPassword
74
+ },
75
+ children: [
76
+ child(entries?.login, login, () => import("@mintplayer/ng-spark/auth/login").then((m) => m.SparkLoginComponent)),
77
+ child(entries?.twoFactor, twoFactor, () => import("@mintplayer/ng-spark/auth/two-factor").then((m) => m.SparkTwoFactorComponent)),
78
+ child(entries?.forgotPassword, forgotPassword, () => import("@mintplayer/ng-spark/auth/forgot-password").then((m) => m.SparkForgotPasswordComponent)),
79
+ child(entries?.resetPassword, resetPassword, () => import("@mintplayer/ng-spark/auth/reset-password").then((m) => m.SparkResetPasswordComponent))
80
+ ]
81
+ };
82
+ }
83
+ function withRegistration(entry) {
84
+ const register = entryPath(entry, "register");
85
+ return {
86
+ paths: { register: "/" + register },
87
+ children: [child(entry, register, () => import("@mintplayer/ng-spark/auth/register").then((m) => m.SparkRegisterComponent))]
88
+ };
89
+ }
90
+ function withExternalLogin(...providersOrOptions) {
91
+ const providers = providersOrOptions.filter(isProvider);
92
+ const options = providersOrOptions.find((p) => !isProvider(p));
93
+ const signIn = entryPath(options?.signIn, "sign-in");
94
+ return {
95
+ paths: { signIn: "/" + signIn },
96
+ providers,
97
+ children: [child(options?.signIn, signIn, () => import("@mintplayer/ng-spark/auth/sign-in").then((m) => m.SparkSignInComponent))]
98
+ };
99
+ }
100
+ function isProvider(value) {
101
+ return typeof value.scheme === "string";
102
+ }
103
+ function withPasskeys(entry) {
104
+ const passkeys = entryPath(entry, "passkeys");
105
+ return {
106
+ paths: { passkeys: "/" + passkeys },
107
+ children: [passkeysChild(entry, passkeys, [])]
108
+ };
109
+ }
110
+ function withAccount(options) {
111
+ const guard = options?.canActivate ?? [sparkAuthGuard];
112
+ const excluded = new Set(options?.exclude ?? []);
113
+ const paths = {};
114
+ const children = [];
115
+ const add = (key, defaultPath, loader, guarded = true) => {
116
+ if (excluded.has(key)) return;
117
+ const entry = options?.[key];
118
+ const path = entryPath(entry, defaultPath);
119
+ paths[key] = "/" + path;
120
+ const route = child(entry, path, loader);
121
+ children.push(guarded && guard.length ? {
122
+ ...route,
123
+ canActivate: guard
124
+ } : route);
125
+ };
126
+ add("confirmEmail", "confirm-email", () => import("@mintplayer/ng-spark/auth/confirm-email").then((m) => m.SparkConfirmEmailComponent), false);
127
+ add("profile", "account/profile", () => import("@mintplayer/ng-spark/auth/account").then((m) => m.SparkAccountProfileComponent));
128
+ add("changePassword", "account/password", () => import("@mintplayer/ng-spark/auth/account").then((m) => m.SparkChangePasswordComponent));
129
+ add("twoFactorSetup", "account/two-factor", () => import("@mintplayer/ng-spark/auth/account").then((m) => m.SparkTwoFactorSetupComponent));
130
+ add("externalLogins", "account/logins", () => import("@mintplayer/ng-spark/auth/account").then((m) => m.SparkExternalLoginsComponent));
131
+ if (!excluded.has("passkeys")) {
132
+ const path = entryPath(options?.passkeys, "account/passkeys");
133
+ paths.passkeys = "/" + path;
134
+ children.push(passkeysChild(options?.passkeys, path, guard));
135
+ }
136
+ add("personalData", "account/personal-data", () => import("@mintplayer/ng-spark/auth/account").then((m) => m.SparkPersonalDataComponent));
137
+ add("account", "account", () => import("@mintplayer/ng-spark/auth/account").then((m) => m.SparkAccountOverviewComponent));
138
+ return {
139
+ paths,
140
+ children
141
+ };
142
+ }
143
+ function externalProvider(scheme, presentation) {
144
+ return {
145
+ scheme,
146
+ ...presentation
147
+ };
148
+ }
149
+ function githubProvider(presentation) {
150
+ return externalProvider("GitHub", {
151
+ iconClass: "bi bi-github",
152
+ ...presentation
153
+ });
154
+ }
155
+ function googleProvider(presentation) {
156
+ return externalProvider("Google", {
157
+ iconClass: "bi bi-google",
158
+ ...presentation
159
+ });
160
+ }
161
+ function facebookProvider(presentation) {
162
+ return externalProvider("Facebook", {
163
+ iconClass: "bi bi-facebook",
164
+ ...presentation
165
+ });
166
+ }
167
+ function microsoftProvider(presentation) {
168
+ return externalProvider("Microsoft", {
169
+ iconClass: "bi bi-microsoft",
170
+ ...presentation
171
+ });
172
+ }
173
+ function twitterProvider(presentation) {
174
+ return externalProvider("Twitter", {
175
+ displayName: "X",
176
+ iconClass: "bi bi-twitter-x",
177
+ ...presentation
178
+ });
179
+ }
180
+ function linkedInProvider(presentation) {
181
+ return externalProvider("LinkedIn", {
182
+ iconClass: "bi bi-linkedin",
183
+ ...presentation
184
+ });
185
+ }
186
+ function oidcProvider(scheme, displayName, iconClass = "bi bi-person-badge") {
187
+ return externalProvider(scheme, {
188
+ displayName,
189
+ iconClass
190
+ });
191
+ }
192
+ export { SPARK_PASSKEYS_PAGE_URL, externalProvider, facebookProvider, githubProvider, googleProvider, linkedInProvider, microsoftProvider, oidcProvider, sparkAuthRoutes, twitterProvider, withAccount, withExternalLogin, withLocalLogin, withPasskeys, withRegistration };
193
+
194
+ //# sourceMappingURL=mintplayer-ng-spark-auth-routes.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mintplayer-ng-spark-auth-routes.mjs","names":[],"sources":["../../auth/routes/src/spark-auth-routes.ts"],"sourcesContent":["import {\n SPARK_ACCOUNT_OVERVIEW_LINKS,\n SPARK_AUTH_ROUTE_PATHS,\n SPARK_EXTERNAL_PROVIDERS,\n SparkAccountOverviewLink,\n SparkAuthRouteEntries,\n SparkAuthRouteEntry,\n SparkAuthRoutePaths,\n SparkExternalProviderPresentation,\n} from '@mintplayer/ng-spark/auth/models';\n\nimport { inject } from '@angular/core';\nimport { Router, type CanActivateFn } from '@angular/router';\nimport { sparkAuthGuard } from '@mintplayer/ng-spark/auth/guards';\n\ntype Loader = () => Promise<any>;\n\ninterface Child {\n path: string;\n loadComponent?: Loader;\n canActivate?: CanActivateFn[];\n children?: [];\n}\n\n/**\n * The passkeys page: the generic persistent-object page of the Authorization library's virtual\n * `Passkeys` type, one per signed-in user, so the id is the fixed `me` (generic passkeys page PRD D1).\n */\nexport const SPARK_PASSKEYS_PAGE_URL = '/po/passkeys/me';\n\n/** Forwards to {@link SPARK_PASSKEYS_PAGE_URL}. */\nconst toPasskeysPage: CanActivateFn = () => inject(Router).parseUrl(SPARK_PASSKEYS_PAGE_URL);\n\n/**\n * The passkeys path: a redirect into the generic page, kept for bookmarks and the account overview's\n * link (PRD D8). A guard rather than `redirectTo`, because Angular refuses `canActivate` beside\n * `redirectTo` and the sign-in guard must still run first: the router acts on the first guard in\n * array order that does not pass, so an anonymous visitor is sent to sign in, not to a 404. An\n * application that supplies its own component for the entry keeps it.\n */\nfunction passkeysChild(entry: SparkAuthRouteEntry | undefined, path: string, guard: CanActivateFn[]): Child {\n if (typeof entry === 'object' && entry.component) {\n const component = entry.component;\n return { path, loadComponent: () => Promise.resolve(component), ...(guard.length ? { canActivate: guard } : {}) };\n }\n return { path, canActivate: [...guard, toPasskeysPage], children: [] };\n}\n\n/** The routed path for an entry, independent of how its component is loaded. */\nfunction entryPath(entry: SparkAuthRouteEntry | undefined, defaultPath: string): string {\n if (entry === undefined) return defaultPath;\n return typeof entry === 'string' ? entry : entry.path;\n}\n\n/** An explicitly supplied component wins over the library's lazy import. */\nfunction child(entry: SparkAuthRouteEntry | undefined, path: string, defaultLoader: Loader): Child {\n const component = typeof entry === 'object' && entry.component ? entry.component : undefined;\n return { path, loadComponent: component ? () => Promise.resolve(component) : defaultLoader };\n}\n\n/**\n * One opt-in group of authentication pages, passed to {@link sparkAuthRoutes}. Constructed only by\n * ng-spark's entry points: `withLocalLogin()`, `withRegistration()`, `withExternalLogin()`,\n * `withPasskeys()` and `withAccount()` here, and `withIdentityProvider()` from\n * `@mintplayer/ng-spark/identity-provider`. That keeps the set of mountable pages a decision this\n * library makes rather than a shape an application invents.\n */\nexport interface SparkAuthRoutesFeature {\n readonly children: Child[];\n readonly paths: SparkAuthRoutePaths;\n readonly providers?: SparkExternalProviderPresentation[];\n /** Extra links for the account overview (`SPARK_ACCOUNT_OVERVIEW_LINKS`). */\n readonly accountLinks?: SparkAccountOverviewLink[];\n}\n\n/** Path override for the sign-in landing page. Named to stay clear of the core package's\n * `SparkExternalLoginOptions`, which is about the sign-in *call* rather than the route. */\nexport type SparkExternalLoginRouteOptions = Pick<SparkAuthRouteEntries, 'signIn'>;\n\n/**\n * The routes for Spark's authentication pages.\n *\n * **Nothing is mounted unless a feature asks for it.** Passing no features emits no pages at all —\n * opt-in enforced by construction rather than by a flag, matching `provideHttpClient(withFetch())`\n * and `provideRouter(withComponentInputBinding())`. Before this the pages mounted by default and an\n * application had to opt *out*, so every app shipped a registration form whether or not it wanted\n * one.\n *\n * ```ts\n * sparkAuthRoutes(\n * withLocalLogin(),\n * withRegistration(),\n * withExternalLogin(githubProvider(), facebookProvider()),\n * )\n * ```\n *\n * The `import()` expressions live *inside* each feature. That placement is the point: a bundler\n * decides whether to emit a lazy chunk from whether the `import()` call site is reachable, not from\n * whether the route object referencing it survives — so filtering a children array after the fact\n * would still ship every page. A page nobody opted into has no reachable `import()` at all.\n */\nexport function sparkAuthRoutes(...features: SparkAuthRoutesFeature[]): any[] {\n const children: Child[] = [];\n const paths: SparkAuthRoutePaths = {};\n const externalProviders: SparkExternalProviderPresentation[] = [];\n const accountLinks: SparkAccountOverviewLink[] = [];\n\n for (const feature of features) {\n children.push(...feature.children);\n Object.assign(paths, feature.paths);\n if (feature.providers) externalProviders.push(...feature.providers);\n if (feature.accountLinks) accountLinks.push(...feature.accountLinks);\n }\n\n return [\n {\n path: '',\n providers: [\n { provide: SPARK_AUTH_ROUTE_PATHS, useValue: paths },\n { provide: SPARK_EXTERNAL_PROVIDERS, useValue: externalProviders },\n { provide: SPARK_ACCOUNT_OVERVIEW_LINKS, useValue: accountLinks },\n ],\n children,\n },\n ];\n}\n\n/**\n * Mounts the email/password family: login, two-factor, forgot-password, reset-password.\n *\n * These four are one feature rather than four because they form a star centred on the login page and\n * every template dereferences its siblings unconditionally — removing any proper subset leaves a\n * dangling link. Registration is genuinely separable, and is its own feature.\n *\n * Two-factor belongs here because it is reachable only from the login page's `RequiresTwoFactor`\n * branch and posts to the same `/login` endpoint: it is part of password sign-in, not of\n * authentication in general.\n */\nexport function withLocalLogin(\n entries?: Pick<SparkAuthRouteEntries, 'login' | 'twoFactor' | 'forgotPassword' | 'resetPassword'>,\n): SparkAuthRoutesFeature {\n const login = entryPath(entries?.login, 'login');\n const twoFactor = entryPath(entries?.twoFactor, 'login/two-factor');\n const forgotPassword = entryPath(entries?.forgotPassword, 'forgot-password');\n const resetPassword = entryPath(entries?.resetPassword, 'reset-password');\n\n return {\n paths: {\n login: '/' + login,\n twoFactor: '/' + twoFactor,\n forgotPassword: '/' + forgotPassword,\n resetPassword: '/' + resetPassword,\n },\n children: [\n child(entries?.login, login,\n () => import('@mintplayer/ng-spark/auth/login').then(m => m.SparkLoginComponent)),\n child(entries?.twoFactor, twoFactor,\n () => import('@mintplayer/ng-spark/auth/two-factor').then(m => m.SparkTwoFactorComponent)),\n child(entries?.forgotPassword, forgotPassword,\n () => import('@mintplayer/ng-spark/auth/forgot-password').then(m => m.SparkForgotPasswordComponent)),\n child(entries?.resetPassword, resetPassword,\n () => import('@mintplayer/ng-spark/auth/reset-password').then(m => m.SparkResetPasswordComponent)),\n ],\n };\n}\n\n/**\n * Mounts the self-service registration page.\n *\n * Separate from {@link withLocalLogin} because the two decisions genuinely are separate: an\n * application whose accounts are provisioned by an administrator still needs password sign-in and\n * password reset. Mount it only alongside a server whose `SparkLocalCredentials` is `Full` — with\n * `SignInOnly` the endpoint it posts to is not mapped.\n */\nexport function withRegistration(entry?: SparkAuthRouteEntry): SparkAuthRoutesFeature {\n const register = entryPath(entry, 'register');\n\n return {\n paths: { register: '/' + register },\n children: [\n child(entry, register,\n () => import('@mintplayer/ng-spark/auth/register').then(m => m.SparkRegisterComponent)),\n ],\n };\n}\n\n/**\n * Mounts the sign-in landing page — a button per external provider the *server* reports.\n *\n * Accepts provider declarations and, in any position, one options object for the page's own path;\n * the two shapes are disjoint, so `withExternalLogin(githubProvider(), facebookProvider())` and\n * `withExternalLogin({ signIn: 'landing' }, githubProvider())` both read naturally.\n *\n * Declaring no providers is valid and useful: the page still renders whatever\n * `GET /spark/auth/capabilities` reports, using default buttons.\n */\nexport function withExternalLogin(\n ...providersOrOptions: (SparkExternalProviderPresentation | SparkExternalLoginRouteOptions)[]\n): SparkAuthRoutesFeature {\n const providers = providersOrOptions.filter(isProvider);\n const options = providersOrOptions.find((p): p is SparkExternalLoginRouteOptions => !isProvider(p));\n const signIn = entryPath(options?.signIn, 'sign-in');\n\n return {\n paths: { signIn: '/' + signIn },\n providers,\n children: [\n child(options?.signIn, signIn,\n () => import('@mintplayer/ng-spark/auth/sign-in').then(m => m.SparkSignInComponent)),\n ],\n };\n}\n\nfunction isProvider(\n value: SparkExternalProviderPresentation | SparkExternalLoginRouteOptions,\n): value is SparkExternalProviderPresentation {\n return typeof (value as SparkExternalProviderPresentation).scheme === 'string';\n}\n\n/**\n * Presentation for one provider's button, keyed by the scheme the server reports.\n *\n * The generic form. `githubProvider()` and friends are this with a scheme and an icon filled in —\n * they exist so the common case does not require knowing the scheme string, not because the library\n * has an opinion about which providers exist.\n */\n/**\n * Mounts the passkey management path, which forwards to the generic passkeys page (`/po/passkeys/me`).\n *\n * Independent of every other feature here, and of the server's `SparkLocalCredentials`: a passkey is\n * a passwordless credential, so the applications most likely to want this page are exactly the ones\n * that mount no password pages at all. It pairs with `SparkPasskeys.Enabled` on the server.\n *\n * ⚠️ The page assumes a signed-in user — enrollment adds a credential to an existing account, it\n * cannot create one. Mount it inside the application's authenticated area, or behind\n * `sparkAuthGuard`; the server answers the page with 404 to a visitor who is not signed in.\n */\nexport function withPasskeys(entry?: SparkAuthRouteEntry): SparkAuthRoutesFeature {\n const passkeys = entryPath(entry, 'passkeys');\n\n return {\n paths: { passkeys: '/' + passkeys },\n children: [passkeysChild(entry, passkeys, [])],\n };\n}\n\n/** Options for {@link withAccount}: a path/component per page, and the guard of the signed-in pages. */\nexport type SparkAccountRouteOptions =\n Pick<SparkAuthRouteEntries, 'confirmEmail' | 'account' | 'profile' | 'changePassword' | 'twoFactorSetup' | 'externalLogins' | 'passkeys' | 'personalData'>\n & {\n /**\n * Guards the signed-in pages (everything but confirm-email). Defaults to `[sparkAuthGuard]`,\n * which waits for the session check so a hard reload does not bounce a signed-in user to the\n * sign-in page. Pass `[]` when the pages sit under a guarded parent route already.\n */\n canActivate?: CanActivateFn[];\n /**\n * Pages to leave out, e.g. `['externalLogins']` for an app without external providers or\n * `['changePassword']` under `SparkLocalCredentials.Disabled` (the endpoint is not mapped there).\n */\n exclude?: (keyof Omit<SparkAccountRouteOptions, 'canActivate' | 'exclude'>)[];\n };\n\n/**\n * Mounts the account pages (#460, D16), each also usable as a standalone component:\n *\n * | page | default path | component |\n * |---|---|---|\n * | confirm email (public; the mail link target) | `confirm-email` | `SparkConfirmEmailComponent` |\n * | overview | `account` | `SparkAccountOverviewComponent` |\n * | profile (user name, email change, mail language, app fields) | `account/profile` | `SparkAccountProfileComponent` |\n * | change / set password | `account/password` | `SparkChangePasswordComponent` |\n * | two-factor (authenticator, recovery codes) | `account/two-factor` | `SparkTwoFactorSetupComponent` |\n * | connected logins | `account/logins` | `SparkExternalLoginsComponent` |\n * | passkeys (forwards to the generic page, `/po/passkeys/me`) | `account/passkeys` | — |\n * | personal data + account deletion | `account/personal-data` | `SparkPersonalDataComponent` |\n *\n * `confirm-email` must stay at the server's `Spark:Auth:Links:ConfirmEmailPath` (default\n * `/confirm-email`), which confirmation mails link to. No path has a parameterised first segment, so the\n * group neither shadows `sparkRoutes()` nor is shadowed by it.\n */\nexport function withAccount(options?: SparkAccountRouteOptions): SparkAuthRoutesFeature {\n const guard = options?.canActivate ?? [sparkAuthGuard];\n const excluded = new Set(options?.exclude ?? []);\n const paths: SparkAuthRoutePaths = {};\n const children: Child[] = [];\n\n const add = (key: keyof SparkAccountRouteOptions & keyof SparkAuthRouteEntries, defaultPath: string, loader: Loader, guarded = true) => {\n if (excluded.has(key as never)) return;\n const entry = options?.[key] as SparkAuthRouteEntry | undefined;\n const path = entryPath(entry, defaultPath);\n paths[key] = '/' + path;\n const route = child(entry, path, loader);\n children.push(guarded && guard.length ? { ...route, canActivate: guard } : route);\n };\n\n add('confirmEmail', 'confirm-email',\n () => import('@mintplayer/ng-spark/auth/confirm-email').then(m => m.SparkConfirmEmailComponent), false);\n add('profile', 'account/profile',\n () => import('@mintplayer/ng-spark/auth/account').then(m => m.SparkAccountProfileComponent));\n add('changePassword', 'account/password',\n () => import('@mintplayer/ng-spark/auth/account').then(m => m.SparkChangePasswordComponent));\n add('twoFactorSetup', 'account/two-factor',\n () => import('@mintplayer/ng-spark/auth/account').then(m => m.SparkTwoFactorSetupComponent));\n add('externalLogins', 'account/logins',\n () => import('@mintplayer/ng-spark/auth/account').then(m => m.SparkExternalLoginsComponent));\n if (!excluded.has('passkeys')) {\n const path = entryPath(options?.passkeys, 'account/passkeys');\n paths.passkeys = '/' + path;\n children.push(passkeysChild(options?.passkeys, path, guard));\n }\n add('personalData', 'account/personal-data',\n () => import('@mintplayer/ng-spark/auth/account').then(m => m.SparkPersonalDataComponent));\n add('account', 'account',\n () => import('@mintplayer/ng-spark/auth/account').then(m => m.SparkAccountOverviewComponent));\n\n return { paths, children };\n}\n\nexport function externalProvider(\n scheme: string,\n presentation?: Omit<SparkExternalProviderPresentation, 'scheme'>,\n): SparkExternalProviderPresentation {\n return { scheme, ...presentation };\n}\n\nexport function githubProvider(\n presentation?: Omit<SparkExternalProviderPresentation, 'scheme'>,\n): SparkExternalProviderPresentation {\n return externalProvider('GitHub', { iconClass: 'bi bi-github', ...presentation });\n}\n\nexport function googleProvider(\n presentation?: Omit<SparkExternalProviderPresentation, 'scheme'>,\n): SparkExternalProviderPresentation {\n return externalProvider('Google', { iconClass: 'bi bi-google', ...presentation });\n}\n\nexport function facebookProvider(\n presentation?: Omit<SparkExternalProviderPresentation, 'scheme'>,\n): SparkExternalProviderPresentation {\n return externalProvider('Facebook', { iconClass: 'bi bi-facebook', ...presentation });\n}\n\nexport function microsoftProvider(\n presentation?: Omit<SparkExternalProviderPresentation, 'scheme'>,\n): SparkExternalProviderPresentation {\n return externalProvider('Microsoft', { iconClass: 'bi bi-microsoft', ...presentation });\n}\n\n/** X (formerly Twitter) — the server's `spark.AddTwitter()` preset (OAuth 2.0), scheme `Twitter`. */\nexport function twitterProvider(\n presentation?: Omit<SparkExternalProviderPresentation, 'scheme'>,\n): SparkExternalProviderPresentation {\n return externalProvider('Twitter', { displayName: 'X', iconClass: 'bi bi-twitter-x', ...presentation });\n}\n\n/** LinkedIn — the server's `spark.AddLinkedIn()` preset (OpenID Connect userinfo), scheme `LinkedIn`. */\nexport function linkedInProvider(\n presentation?: Omit<SparkExternalProviderPresentation, 'scheme'>,\n): SparkExternalProviderPresentation {\n return externalProvider('LinkedIn', { iconClass: 'bi bi-linkedin', ...presentation });\n}\n\n/**\n * Any OpenID Connect provider — the client half of the server's OIDC preset\n * (`spark.AddOpenIdConnect(...)`, or a `Spark:Auth:Providers:OpenIdConnect:<scheme>` section). Unlike the named presets the scheme is the\n * application's own choice, so it and the button text are required; `scheme` must equal the server's\n * scheme name exactly.\n */\nexport function oidcProvider(\n scheme: string,\n displayName: string,\n iconClass = 'bi bi-person-badge',\n): SparkExternalProviderPresentation {\n return externalProvider(scheme, { displayName, iconClass });\n}\n"],"mappings":"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"}
@@ -0,0 +1,210 @@
1
+ import * as i0 from "@angular/core";
2
+ import { ChangeDetectionStrategy, Component, inject, input, isDevMode, signal } from "@angular/core";
3
+ import { ActivatedRoute, Router, RouterLink } from "@angular/router";
4
+ import { Color } from "@mintplayer/ng-bootstrap";
5
+ import { BsAlertComponent } from "@mintplayer/ng-bootstrap/alert";
6
+ import { BsCardComponent, BsCardHeaderComponent } from "@mintplayer/ng-bootstrap/card";
7
+ import { BsSpinnerComponent } from "@mintplayer/ng-bootstrap/spinner";
8
+ import { SPARK_AUTH_CONFIG, SPARK_AUTH_ROUTE_PATHS, isSafeReturnUrl, passkeysSupported } from "@mintplayer/ng-spark/auth/models";
9
+ import { SparkAuthService } from "@mintplayer/ng-spark/auth/core";
10
+ import { SparkExternalLoginButtonsComponent } from "@mintplayer/ng-spark/auth/external-login";
11
+ import { TranslateKeyPipe } from "@mintplayer/ng-spark/auth/pipes";
12
+ var SparkSignInComponent = class SparkSignInComponent {
13
+ authService = inject(SparkAuthService);
14
+ route = inject(ActivatedRoute, { optional: true });
15
+ router = inject(Router);
16
+ config = inject(SPARK_AUTH_CONFIG);
17
+ routePaths = inject(SPARK_AUTH_ROUTE_PATHS);
18
+ colors = Color;
19
+ returnUrl = input(void 0, ...ngDevMode ? [{ debugName: "returnUrl" }] : /* istanbul ignore next */ []);
20
+ effectiveReturnUrl() {
21
+ const explicit = this.returnUrl();
22
+ if (explicit) return explicit;
23
+ const fromQuery = this.route?.snapshot.queryParamMap.get("returnUrl");
24
+ return isSafeReturnUrl(fromQuery) ? fromQuery : void 0;
25
+ }
26
+ providerTemplate = input(null, ...ngDevMode ? [{ debugName: "providerTemplate" }] : /* istanbul ignore next */ []);
27
+ hasProviders = signal(false, ...ngDevMode ? [{ debugName: "hasProviders" }] : /* istanbul ignore next */ []);
28
+ localCredentialsAvailable = signal(false, ...ngDevMode ? [{ debugName: "localCredentialsAvailable" }] : /* istanbul ignore next */ []);
29
+ loading = signal(true, ...ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []);
30
+ failed = signal(false, ...ngDevMode ? [{ debugName: "failed" }] : /* istanbul ignore next */ []);
31
+ passkeysAvailable = signal(false, ...ngDevMode ? [{ debugName: "passkeysAvailable" }] : /* istanbul ignore next */ []);
32
+ passkeyBusy = signal(false, ...ngDevMode ? [{ debugName: "passkeyBusy" }] : /* istanbul ignore next */ []);
33
+ passkeyError = signal("", ...ngDevMode ? [{ debugName: "passkeyError" }] : /* istanbul ignore next */ []);
34
+ constructor() {
35
+ this.load();
36
+ }
37
+ async load() {
38
+ try {
39
+ const capabilities = await this.authService.capabilities();
40
+ this.hasProviders.set((capabilities.externalProviders ?? []).length > 0);
41
+ this.localCredentialsAvailable.set(capabilities.localCredentials !== "Disabled");
42
+ this.passkeysAvailable.set(capabilities.passkeys === true && passkeysSupported());
43
+ this.warnOnMismatch(capabilities);
44
+ } catch {
45
+ this.failed.set(true);
46
+ } finally {
47
+ this.loading.set(false);
48
+ }
49
+ }
50
+ warnOnMismatch(capabilities) {
51
+ if (isDevMode() && capabilities.localCredentials === "Full") console.warn("[ng-spark-auth] This app routes the sign-in landing page, but the server reports localCredentials = Full. The two are configured independently — check that the features passed to sparkAuthRoutes() match AddAuthentication's LocalCredentials mode.");
52
+ }
53
+ async signInWithPasskey() {
54
+ this.passkeyError.set("");
55
+ this.passkeyBusy.set(true);
56
+ try {
57
+ const returnUrl = this.effectiveReturnUrl() ?? this.config.defaultRedirectUrl;
58
+ const result = await this.authService.signInWithPasskey();
59
+ if (result.success) {
60
+ await this.router.navigateByUrl(returnUrl);
61
+ return;
62
+ }
63
+ if (result.error === "cancelled") return;
64
+ this.passkeyError.set(result.error === "locked_out" ? "auth.lockedOut" : "auth.passkeyFailed");
65
+ } finally {
66
+ this.passkeyBusy.set(false);
67
+ }
68
+ }
69
+ static ɵfac = i0.ɵɵngDeclareFactory({
70
+ minVersion: "12.0.0",
71
+ version: "22.2.0",
72
+ ngImport: i0,
73
+ type: SparkSignInComponent,
74
+ deps: [],
75
+ target: i0.ɵɵFactoryTarget.Component
76
+ });
77
+ static ɵcmp = i0.ɵɵngDeclareComponent({
78
+ minVersion: "17.0.0",
79
+ version: "22.2.0",
80
+ type: SparkSignInComponent,
81
+ isStandalone: true,
82
+ selector: "spark-sign-in",
83
+ inputs: {
84
+ returnUrl: {
85
+ classPropertyName: "returnUrl",
86
+ publicName: "returnUrl",
87
+ isSignal: true,
88
+ isRequired: false,
89
+ transformFunction: null
90
+ },
91
+ providerTemplate: {
92
+ classPropertyName: "providerTemplate",
93
+ publicName: "providerTemplate",
94
+ isSignal: true,
95
+ isRequired: false,
96
+ transformFunction: null
97
+ }
98
+ },
99
+ ngImport: i0,
100
+ template: "<div class=\"d-flex justify-content-center\">\n <bs-card style=\"width: 100%; max-width: 400px;\">\n <bs-card-header>\n <h3 class=\"mb-0 text-center\">{{ 'auth.login' | t }}</h3>\n </bs-card-header>\n <div class=\"p-4\">\n @if (loading()) {\n <div class=\"text-center\"><bs-spinner /></div>\n } @else if (failed()) {\n <bs-alert [type]=\"colors.danger\" class=\"mb-0\">{{ 'auth.signInUnavailable' | t }}</bs-alert>\n }\n\n <!-- Rendered from the start, not after this page's own load: it asks for the capabilities at\n the same moment this page does, which the service turns into one request. It shows nothing\n until they arrive, nor when they fail (the alert above says so). -->\n <spark-external-login-buttons [providerTemplate]=\"providerTemplate()\" [returnUrl]=\"returnUrl()\" />\n\n @if (!loading() && !failed()) {\n @if (!hasProviders() && !passkeysAvailable()) {\n <bs-alert [type]=\"colors.warning\" class=\"mb-0\">{{ 'auth.noSignInMethods' | t }}</bs-alert>\n }\n\n <!-- Guarded on the server's capability *and* browser support, both resolved into\n passkeysAvailable(). A passkey needs no provider button and no username field: the\n browser offers whatever discoverable credential it holds for this site. -->\n @if (passkeysAvailable()) {\n @if (passkeyError()) {\n <bs-alert [type]=\"colors.danger\" class=\"mb-2\">{{ passkeyError() | t }}</bs-alert>\n }\n <button type=\"button\" class=\"btn btn-outline-secondary w-100 mb-2\"\n [disabled]=\"passkeyBusy()\" (click)=\"signInWithPasskey()\">\n <i class=\"bi bi-fingerprint me-2\"></i>{{ 'auth.signInWithPasskey' | t }}\n </button>\n }\n\n <!-- Two conditions, not one. The server may still accept passwords while this app chose not\n to mount the page — sparkAuthRoutes is opt-in per feature now — and a [routerLink] bound\n to undefined silently navigates to the current route rather than failing. -->\n @if (localCredentialsAvailable() && routePaths.login) {\n <div class=\"text-center mt-3\">\n <a [routerLink]=\"routePaths.login\">{{ 'auth.login' | t }}</a>\n </div>\n }\n }\n </div>\n </bs-card>\n</div>\n",
101
+ dependencies: [
102
+ {
103
+ kind: "directive",
104
+ type: RouterLink,
105
+ selector: "[routerLink]",
106
+ inputs: [
107
+ "target",
108
+ "queryParams",
109
+ "fragment",
110
+ "queryParamsHandling",
111
+ "state",
112
+ "info",
113
+ "relativeTo",
114
+ "preserveFragment",
115
+ "skipLocationChange",
116
+ "replaceUrl",
117
+ "browserUrl",
118
+ "routerLink"
119
+ ]
120
+ },
121
+ {
122
+ kind: "component",
123
+ type: BsAlertComponent,
124
+ selector: "bs-alert",
125
+ inputs: [
126
+ "type",
127
+ "announce",
128
+ "isVisible"
129
+ ],
130
+ outputs: ["isVisibleChange", "afterOpenedOrClosed"]
131
+ },
132
+ {
133
+ kind: "component",
134
+ type: BsCardComponent,
135
+ selector: "bs-card",
136
+ inputs: ["color", "outline"]
137
+ },
138
+ {
139
+ kind: "component",
140
+ type: BsCardHeaderComponent,
141
+ selector: "bs-card-header",
142
+ inputs: ["color", "navStyle"]
143
+ },
144
+ {
145
+ kind: "component",
146
+ type: BsSpinnerComponent,
147
+ selector: "bs-spinner",
148
+ inputs: ["type", "color"]
149
+ },
150
+ {
151
+ kind: "component",
152
+ type: SparkExternalLoginButtonsComponent,
153
+ selector: "spark-external-login-buttons",
154
+ inputs: ["returnUrl", "providerTemplate"]
155
+ },
156
+ {
157
+ kind: "pipe",
158
+ type: TranslateKeyPipe,
159
+ name: "t"
160
+ }
161
+ ],
162
+ changeDetection: i0.ChangeDetectionStrategy.OnPush
163
+ });
164
+ };
165
+ i0.ɵɵngDeclareClassMetadata({
166
+ minVersion: "12.0.0",
167
+ version: "22.2.0",
168
+ ngImport: i0,
169
+ type: SparkSignInComponent,
170
+ decorators: [{
171
+ type: Component,
172
+ args: [{
173
+ selector: "spark-sign-in",
174
+ standalone: true,
175
+ changeDetection: ChangeDetectionStrategy.OnPush,
176
+ imports: [
177
+ RouterLink,
178
+ BsAlertComponent,
179
+ BsCardComponent,
180
+ BsCardHeaderComponent,
181
+ BsSpinnerComponent,
182
+ SparkExternalLoginButtonsComponent,
183
+ TranslateKeyPipe
184
+ ],
185
+ template: "<div class=\"d-flex justify-content-center\">\n <bs-card style=\"width: 100%; max-width: 400px;\">\n <bs-card-header>\n <h3 class=\"mb-0 text-center\">{{ 'auth.login' | t }}</h3>\n </bs-card-header>\n <div class=\"p-4\">\n @if (loading()) {\n <div class=\"text-center\"><bs-spinner /></div>\n } @else if (failed()) {\n <bs-alert [type]=\"colors.danger\" class=\"mb-0\">{{ 'auth.signInUnavailable' | t }}</bs-alert>\n }\n\n <!-- Rendered from the start, not after this page's own load: it asks for the capabilities at\n the same moment this page does, which the service turns into one request. It shows nothing\n until they arrive, nor when they fail (the alert above says so). -->\n <spark-external-login-buttons [providerTemplate]=\"providerTemplate()\" [returnUrl]=\"returnUrl()\" />\n\n @if (!loading() && !failed()) {\n @if (!hasProviders() && !passkeysAvailable()) {\n <bs-alert [type]=\"colors.warning\" class=\"mb-0\">{{ 'auth.noSignInMethods' | t }}</bs-alert>\n }\n\n <!-- Guarded on the server's capability *and* browser support, both resolved into\n passkeysAvailable(). A passkey needs no provider button and no username field: the\n browser offers whatever discoverable credential it holds for this site. -->\n @if (passkeysAvailable()) {\n @if (passkeyError()) {\n <bs-alert [type]=\"colors.danger\" class=\"mb-2\">{{ passkeyError() | t }}</bs-alert>\n }\n <button type=\"button\" class=\"btn btn-outline-secondary w-100 mb-2\"\n [disabled]=\"passkeyBusy()\" (click)=\"signInWithPasskey()\">\n <i class=\"bi bi-fingerprint me-2\"></i>{{ 'auth.signInWithPasskey' | t }}\n </button>\n }\n\n <!-- Two conditions, not one. The server may still accept passwords while this app chose not\n to mount the page — sparkAuthRoutes is opt-in per feature now — and a [routerLink] bound\n to undefined silently navigates to the current route rather than failing. -->\n @if (localCredentialsAvailable() && routePaths.login) {\n <div class=\"text-center mt-3\">\n <a [routerLink]=\"routePaths.login\">{{ 'auth.login' | t }}</a>\n </div>\n }\n }\n </div>\n </bs-card>\n</div>\n"
186
+ }]
187
+ }],
188
+ ctorParameters: () => [],
189
+ propDecorators: {
190
+ returnUrl: [{
191
+ type: i0.Input,
192
+ args: [{
193
+ isSignal: true,
194
+ alias: "returnUrl",
195
+ required: false
196
+ }]
197
+ }],
198
+ providerTemplate: [{
199
+ type: i0.Input,
200
+ args: [{
201
+ isSignal: true,
202
+ alias: "providerTemplate",
203
+ required: false
204
+ }]
205
+ }]
206
+ }
207
+ });
208
+ export { SparkSignInComponent };
209
+
210
+ //# sourceMappingURL=mintplayer-ng-spark-auth-sign-in.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mintplayer-ng-spark-auth-sign-in.mjs","names":[],"sources":["../../auth/sign-in/src/spark-sign-in.component.ts","../../auth/sign-in/src/spark-sign-in.component.html"],"sourcesContent":["import { ChangeDetectionStrategy, Component, TemplateRef, inject, input, isDevMode, signal } from '@angular/core';\nimport { ActivatedRoute, Router, RouterLink } from '@angular/router';\nimport { Color } from '@mintplayer/ng-bootstrap';\nimport { BsAlertComponent } from '@mintplayer/ng-bootstrap/alert';\nimport { BsCardComponent, BsCardHeaderComponent } from '@mintplayer/ng-bootstrap/card';\nimport { BsSpinnerComponent } from '@mintplayer/ng-bootstrap/spinner';\nimport {\n SPARK_AUTH_ROUTE_PATHS,\n SparkAuthCapabilities,\n SPARK_AUTH_CONFIG,\n isSafeReturnUrl,\n passkeysSupported,\n} from '@mintplayer/ng-spark/auth/models';\nimport { SparkAuthService } from '@mintplayer/ng-spark/auth/core';\nimport {\n SparkExternalLoginButtonsComponent,\n SparkProviderButtonContext,\n} from '@mintplayer/ng-spark/auth/external-login';\nimport { TranslateKeyPipe } from '@mintplayer/ng-spark/auth/pipes';\n\n// Moved to @mintplayer/ng-spark/auth/external-login with the buttons themselves; re-exported so an\n// import from this entry point keeps working.\nexport type { SparkProviderButtonContext, SparkExternalProviderView } from '@mintplayer/ng-spark/auth/external-login';\n\n/**\n * The sign-in landing page: a button per external provider, and a link to the password form when the\n * application mounted one.\n *\n * The provider buttons are `<spark-external-login-buttons>`, which the login page hosts as well; this\n * page adds the passkey button, the link to the password form, and says so when there is no way to\n * sign in at all.\n */\n@Component({\n selector: 'spark-sign-in',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [\n RouterLink, BsAlertComponent, BsCardComponent, BsCardHeaderComponent,\n BsSpinnerComponent, SparkExternalLoginButtonsComponent, TranslateKeyPipe,\n ],\n templateUrl: './spark-sign-in.component.html',\n})\nexport class SparkSignInComponent {\n private readonly authService = inject(SparkAuthService);\n private readonly route = inject(ActivatedRoute, { optional: true });\n private readonly router = inject(Router);\n private readonly config = inject(SPARK_AUTH_CONFIG);\n readonly routePaths = inject(SPARK_AUTH_ROUTE_PATHS);\n readonly colors = Color;\n\n /**\n * Where to land after a successful external sign-in.\n *\n * Falls back to a `?returnUrl=` query parameter, matching what the login page already does — so a\n * plain `<a routerLink=\"/sign-in\" [queryParams]=\"{ returnUrl: '/somewhere' }\">` works with no\n * wiring, which is what a routed page needs since the router passes it no inputs. The query value\n * is validated as a local path before use; an off-site one is dropped rather than followed.\n */\n readonly returnUrl = input<string | undefined>(undefined);\n\n private effectiveReturnUrl(): string | undefined {\n const explicit = this.returnUrl();\n if (explicit) return explicit;\n\n const fromQuery = this.route?.snapshot.queryParamMap.get('returnUrl');\n return isSafeReturnUrl(fromQuery) ? fromQuery! : undefined;\n }\n\n /**\n * Replaces the default provider button. Rendered once per provider with a\n * {@link SparkProviderButtonContext}.\n *\n * Reachable only when the consumer *hosts* this component — the router instantiates it with no\n * projected content, so on a default `withExternalLogin()` route there is nothing to project into.\n * Host it via `withExternalLogin({ signIn: { path: 'sign-in', component: MySignIn } })` and pass\n * the template from there.\n */\n readonly providerTemplate = input<TemplateRef<SparkProviderButtonContext> | null>(null);\n\n /** Whether the server reported any external provider; with none and no passkeys there is no way in. */\n readonly hasProviders = signal(false);\n readonly localCredentialsAvailable = signal(false);\n readonly loading = signal(true);\n readonly failed = signal(false);\n\n /**\n * Two conditions, and both are necessary. The server must have mounted the sign-in endpoint, and\n * this browser must be able to run the ceremony — offering the button on either half alone\n * produces a control that fails the moment it is clicked.\n */\n readonly passkeysAvailable = signal(false);\n readonly passkeyBusy = signal(false);\n readonly passkeyError = signal('');\n\n constructor() {\n void this.load();\n }\n\n private async load(): Promise<void> {\n try {\n // The provider buttons ask at the same time; the service makes that one request.\n const capabilities = await this.authService.capabilities();\n this.hasProviders.set((capabilities.externalProviders ?? []).length > 0);\n this.localCredentialsAvailable.set(capabilities.localCredentials !== 'Disabled');\n this.passkeysAvailable.set(capabilities.passkeys === true && passkeysSupported());\n this.warnOnMismatch(capabilities);\n } catch {\n // A sign-in page that renders nothing looks identical to one whose providers all failed to\n // load, so say which happened rather than showing an empty page.\n this.failed.set(true);\n } finally {\n this.loading.set(false);\n }\n }\n\n /**\n * The routes are a build-time decision and the server's mode a deployment-time one, so they can\n * disagree without anything failing loudly. Reaching this page at all means the app routed it,\n * which it only does when local credentials are meant to be limited.\n */\n private warnOnMismatch(capabilities: SparkAuthCapabilities): void {\n if (isDevMode() && capabilities.localCredentials === 'Full') {\n console.warn(\n '[ng-spark-auth] This app routes the sign-in landing page, but the server reports '\n + 'localCredentials = Full. The two are configured independently — check that the features '\n + \"passed to sparkAuthRoutes() match AddAuthentication's LocalCredentials mode.\",\n );\n }\n }\n\n /**\n * Signs in with a passkey, then navigates. The navigation matters for the same reason it does\n * after an external sign-in: nothing else moves the user off this page.\n *\n * No username is collected, and none should be: the server's request-options endpoint refuses to\n * accept an identity so that its answer cannot reveal whether an account exists. The browser\n * offers whatever discoverable credential it holds.\n */\n async signInWithPasskey(): Promise<void> {\n this.passkeyError.set('');\n this.passkeyBusy.set(true);\n try {\n const returnUrl = this.effectiveReturnUrl() ?? this.config.defaultRedirectUrl;\n const result = await this.authService.signInWithPasskey();\n\n if (result.success) {\n await this.router.navigateByUrl(returnUrl);\n return;\n }\n\n // Dismissing the authenticator prompt is \"not now\", not a failure — the same reasoning that\n // leaves popup_closed alone for the provider buttons.\n if (result.error === 'cancelled') return;\n\n this.passkeyError.set(result.error === 'locked_out' ? 'auth.lockedOut' : 'auth.passkeyFailed');\n } finally {\n this.passkeyBusy.set(false);\n }\n }\n}\n\nexport default SparkSignInComponent;\n","<div class=\"d-flex justify-content-center\">\n <bs-card style=\"width: 100%; max-width: 400px;\">\n <bs-card-header>\n <h3 class=\"mb-0 text-center\">{{ 'auth.login' | t }}</h3>\n </bs-card-header>\n <div class=\"p-4\">\n @if (loading()) {\n <div class=\"text-center\"><bs-spinner /></div>\n } @else if (failed()) {\n <bs-alert [type]=\"colors.danger\" class=\"mb-0\">{{ 'auth.signInUnavailable' | t }}</bs-alert>\n }\n\n <!-- Rendered from the start, not after this page's own load: it asks for the capabilities at\n the same moment this page does, which the service turns into one request. It shows nothing\n until they arrive, nor when they fail (the alert above says so). -->\n <spark-external-login-buttons [providerTemplate]=\"providerTemplate()\" [returnUrl]=\"returnUrl()\" />\n\n @if (!loading() && !failed()) {\n @if (!hasProviders() && !passkeysAvailable()) {\n <bs-alert [type]=\"colors.warning\" class=\"mb-0\">{{ 'auth.noSignInMethods' | t }}</bs-alert>\n }\n\n <!-- Guarded on the server's capability *and* browser support, both resolved into\n passkeysAvailable(). A passkey needs no provider button and no username field: the\n browser offers whatever discoverable credential it holds for this site. -->\n @if (passkeysAvailable()) {\n @if (passkeyError()) {\n <bs-alert [type]=\"colors.danger\" class=\"mb-2\">{{ passkeyError() | t }}</bs-alert>\n }\n <button type=\"button\" class=\"btn btn-outline-secondary w-100 mb-2\"\n [disabled]=\"passkeyBusy()\" (click)=\"signInWithPasskey()\">\n <i class=\"bi bi-fingerprint me-2\"></i>{{ 'auth.signInWithPasskey' | t }}\n </button>\n }\n\n <!-- Two conditions, not one. The server may still accept passwords while this app chose not\n to mount the page — sparkAuthRoutes is opt-in per feature now — and a [routerLink] bound\n to undefined silently navigates to the current route rather than failing. -->\n @if (localCredentialsAvailable() && routePaths.login) {\n <div class=\"text-center mt-3\">\n <a [routerLink]=\"routePaths.login\">{{ 'auth.login' | t }}</a>\n </div>\n }\n }\n </div>\n </bs-card>\n</div>\n"],"mappings":"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"}