@memberjunction/ng-auth-services 6.1.0-edge.1 → 6.1.0-edge.3
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.
- package/LICENSE +180 -4
- package/README.md +58 -1
- package/dist/lib/auth-provider-catalog.d.ts +103 -0
- package/dist/lib/auth-provider-catalog.d.ts.map +1 -0
- package/dist/lib/auth-provider-catalog.js +186 -0
- package/dist/lib/auth-provider-catalog.js.map +1 -0
- package/dist/lib/auth-services.module.d.ts +33 -3
- package/dist/lib/auth-services.module.d.ts.map +1 -1
- package/dist/lib/auth-services.module.js +64 -18
- package/dist/lib/auth-services.module.js.map +1 -1
- package/dist/lib/catalog-environment.d.ts +50 -0
- package/dist/lib/catalog-environment.d.ts.map +1 -0
- package/dist/lib/catalog-environment.js +87 -0
- package/dist/lib/catalog-environment.js.map +1 -0
- package/dist/lib/login-picker.component.d.ts +72 -0
- package/dist/lib/login-picker.component.d.ts.map +1 -0
- package/dist/lib/login-picker.component.js +284 -0
- package/dist/lib/login-picker.component.js.map +1 -0
- package/dist/lib/providers/mjexplorer-cognito-provider.service.d.ts.map +1 -1
- package/dist/lib/providers/mjexplorer-cognito-provider.service.js +4 -1
- package/dist/lib/providers/mjexplorer-cognito-provider.service.js.map +1 -1
- package/dist/lib/providers/mjexplorer-msal-provider.service.d.ts +16 -0
- package/dist/lib/providers/mjexplorer-msal-provider.service.d.ts.map +1 -1
- package/dist/lib/providers/mjexplorer-msal-provider.service.js +28 -0
- package/dist/lib/providers/mjexplorer-msal-provider.service.js.map +1 -1
- package/dist/public-api.d.ts +3 -0
- package/dist/public-api.d.ts.map +1 -1
- package/dist/public-api.js +4 -0
- package/dist/public-api.js.map +1 -1
- package/package.json +7 -6
|
@@ -1,47 +1,93 @@
|
|
|
1
|
-
import { NgModule } from '@angular/core';
|
|
1
|
+
import { NgModule, InjectionToken } from '@angular/core';
|
|
2
2
|
import { CommonModule } from '@angular/common';
|
|
3
3
|
import { MJAuthBase } from './mjexplorer-auth-base.service';
|
|
4
4
|
import { AngularAuthProviderFactory } from './AngularAuthProviderFactory';
|
|
5
5
|
import { MJMagicLinkProvider } from './providers/mjexplorer-magic-link-provider.service';
|
|
6
6
|
import { MJGlobal } from '@memberjunction/global';
|
|
7
|
+
import { AuthProviderCatalog } from './auth-provider-catalog';
|
|
8
|
+
import { mergeCatalogEnvironment } from './catalog-environment';
|
|
9
|
+
import { MJLoginPickerComponent } from './login-picker.component';
|
|
7
10
|
// Import our generic redirect component
|
|
8
11
|
import { RedirectComponent } from './redirect.component';
|
|
9
12
|
import * as i0 from "@angular/core";
|
|
10
13
|
// Export the generic redirect component for backward compatibility
|
|
11
14
|
export { RedirectComponent };
|
|
15
|
+
/**
|
|
16
|
+
* Injection token carrying how authentication resolved for this page load.
|
|
17
|
+
*
|
|
18
|
+
* A login surface injects this to decide whether to render the multi-provider picker or the
|
|
19
|
+
* single sign-in button, and whether to start the login flow immediately after a picker
|
|
20
|
+
* choice caused a reload.
|
|
21
|
+
*/
|
|
22
|
+
export const MJ_AUTH_PROVIDER_RESOLUTION = new InjectionToken('MJ_AUTH_PROVIDER_RESOLUTION');
|
|
23
|
+
/** The resolution used when no catalog is supplied — i.e. the pre-existing single-provider path. */
|
|
24
|
+
const NO_CATALOG_RESOLUTION = { active: null, choices: [], showPicker: false, autoLogin: false };
|
|
12
25
|
/**
|
|
13
26
|
* Extensible authentication module that supports N providers
|
|
14
27
|
* Uses MJGlobal ClassFactory pattern for dynamic provider creation
|
|
15
28
|
*/
|
|
16
29
|
export class AuthServicesModule {
|
|
17
|
-
|
|
30
|
+
/**
|
|
31
|
+
* Wires the authentication provider into DI.
|
|
32
|
+
*
|
|
33
|
+
* @param environment The app's compiled environment. Typed loosely on purpose: every app
|
|
34
|
+
* declares its own environment shape (`MJEnvironmentConfig` and downstream equivalents
|
|
35
|
+
* are interfaces, which TypeScript will not assign to an indexed record), so narrowing
|
|
36
|
+
* this parameter would break every existing caller. It is narrowed once, below.
|
|
37
|
+
* @param catalog Optional pre-auth provider catalog. Defaults to whatever the app preloaded
|
|
38
|
+
* via {@link AuthProviderCatalog.Preload} in its bootstrap entry point — passing it
|
|
39
|
+
* explicitly is for tests and for apps that source the catalog some other way. Callers
|
|
40
|
+
* must NOT pass `AuthProviderCatalog.GetPreloaded()` themselves: Angular's compiler
|
|
41
|
+
* requires every `imports` entry to be statically analyzable, and a function call in
|
|
42
|
+
* the argument list fails AOT with "Value could not be determined statically".
|
|
43
|
+
*
|
|
44
|
+
* When the catalog is empty — an older server, a failed fetch, or a deployment that
|
|
45
|
+
* never adopted the entity — resolution falls back to `environment.AUTH_TYPE` and
|
|
46
|
+
* behaviour is byte-for-byte what it was before the catalog existed, which is what
|
|
47
|
+
* keeps every existing deployment working untouched.
|
|
48
|
+
*/
|
|
49
|
+
static forRoot(environment, catalog) {
|
|
50
|
+
const env = environment;
|
|
18
51
|
const providers = [];
|
|
19
|
-
const configuredType =
|
|
52
|
+
const configuredType = env['AUTH_TYPE']?.toLowerCase();
|
|
53
|
+
const resolvedCatalog = catalog ?? AuthProviderCatalog.GetPreloaded();
|
|
20
54
|
// Coexistence resolution: a single Explorer deployment can serve both SSO
|
|
21
55
|
// users and magic-link guests. If a magic-link session token is present for
|
|
22
56
|
// this page load (URL fragment from /redeem, or stored earlier this tab), use
|
|
23
57
|
// the magic-link provider regardless of the configured primary IdP. Otherwise
|
|
24
|
-
// use the configured AUTH_TYPE. This auto-falls-back
|
|
25
|
-
// token is gone (expired/logged out), so the normal
|
|
58
|
+
// use the catalog selection, then the configured AUTH_TYPE. This auto-falls-back
|
|
59
|
+
// to the IdP once a guest's token is gone (expired/logged out), so the normal
|
|
60
|
+
// login button works again.
|
|
26
61
|
const magicLinkActive = MJMagicLinkProvider.hasSessionToken();
|
|
27
|
-
|
|
62
|
+
// The catalog resolves which provider this load uses; it never overrides an active
|
|
63
|
+
// magic-link session, which is a property of THIS page load rather than a configured choice.
|
|
64
|
+
const resolution = resolvedCatalog.length ? AuthProviderCatalog.Resolve(resolvedCatalog) : NO_CATALOG_RESOLUTION;
|
|
65
|
+
providers.push({ provide: MJ_AUTH_PROVIDER_RESOLUTION, useValue: resolution });
|
|
66
|
+
const catalogType = resolution.active?.driverClass?.toLowerCase();
|
|
67
|
+
const authType = magicLinkActive ? MJMagicLinkProvider.PROVIDER_TYPE : (catalogType ?? configuredType);
|
|
28
68
|
if (!authType) {
|
|
29
|
-
console.error('No AUTH_TYPE
|
|
69
|
+
console.error('No authentication provider available: the server published no catalog and no AUTH_TYPE is set in the environment');
|
|
30
70
|
return {
|
|
31
71
|
ngModule: AuthServicesModule,
|
|
32
|
-
providers:
|
|
72
|
+
providers: providers
|
|
33
73
|
};
|
|
34
74
|
}
|
|
35
|
-
if (magicLinkActive && authType !== configuredType) {
|
|
36
|
-
console.log(`[Auth] Magic-link session token detected — using the magic-link provider for this load (configured primary: '${configuredType ?? 'none'}').`);
|
|
75
|
+
if (magicLinkActive && authType !== (catalogType ?? configuredType)) {
|
|
76
|
+
console.log(`[Auth] Magic-link session token detected — using the magic-link provider for this load (configured primary: '${catalogType ?? configuredType ?? 'none'}').`);
|
|
37
77
|
}
|
|
38
|
-
// Use the factory to get provider-specific Angular services
|
|
39
|
-
// This uses the static method on each provider class for extensibility
|
|
40
|
-
const angularServices = AngularAuthProviderFactory.getProviderAngularServices(authType, environment);
|
|
41
|
-
providers.push(...angularServices);
|
|
42
78
|
// Get the provider class from ClassFactory for extensibility
|
|
43
79
|
const registration = MJGlobal.Instance.ClassFactory.GetRegistration(MJAuthBase, authType);
|
|
44
80
|
const providerClass = registration?.SubClass;
|
|
81
|
+
// A catalog-selected provider takes its settings from metadata rather than from the compiled
|
|
82
|
+
// environment, so the driver receives the row's values overlaid on the app's environment.
|
|
83
|
+
// Magic-link is excluded: its session is established by the redeem flow, not by catalog config.
|
|
84
|
+
const effectiveEnvironment = resolution.active && !magicLinkActive
|
|
85
|
+
? mergeCatalogEnvironment(environment, resolution.active, providerClass)
|
|
86
|
+
: environment;
|
|
87
|
+
// Use the factory to get provider-specific Angular services
|
|
88
|
+
// This uses the static method on each provider class for extensibility
|
|
89
|
+
const angularServices = AngularAuthProviderFactory.getProviderAngularServices(authType, effectiveEnvironment);
|
|
90
|
+
providers.push(...angularServices);
|
|
45
91
|
if (providerClass) {
|
|
46
92
|
// Add the provider itself
|
|
47
93
|
providers.push({
|
|
@@ -56,7 +102,7 @@ export class AuthServicesModule {
|
|
|
56
102
|
providers.push(AngularAuthProviderFactory);
|
|
57
103
|
return {
|
|
58
104
|
ngModule: AuthServicesModule,
|
|
59
|
-
providers
|
|
105
|
+
providers: providers
|
|
60
106
|
};
|
|
61
107
|
}
|
|
62
108
|
static ɵfac = function AuthServicesModule_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || AuthServicesModule)(); };
|
|
@@ -66,10 +112,10 @@ export class AuthServicesModule {
|
|
|
66
112
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(AuthServicesModule, [{
|
|
67
113
|
type: NgModule,
|
|
68
114
|
args: [{
|
|
69
|
-
imports: [CommonModule],
|
|
115
|
+
imports: [CommonModule, MJLoginPickerComponent],
|
|
70
116
|
declarations: [RedirectComponent],
|
|
71
|
-
exports: [RedirectComponent]
|
|
117
|
+
exports: [RedirectComponent, MJLoginPickerComponent]
|
|
72
118
|
}]
|
|
73
119
|
}], null, null); })();
|
|
74
|
-
(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(AuthServicesModule, { declarations: [RedirectComponent], imports: [CommonModule], exports: [RedirectComponent] }); })();
|
|
120
|
+
(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(AuthServicesModule, { declarations: [RedirectComponent], imports: [CommonModule, MJLoginPickerComponent], exports: [RedirectComponent, MJLoginPickerComponent] }); })();
|
|
75
121
|
//# sourceMappingURL=auth-services.module.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-services.module.js","sourceRoot":"","sources":["../../src/lib/auth-services.module.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAuB,MAAM,eAAe,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAC;AAC5D,OAAO,EAAE,0BAA0B,EAAE,MAAM,8BAA8B,CAAC;AAC1E,OAAO,EAAE,mBAAmB,EAAE,MAAM,oDAAoD,CAAC;AACzF,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAElD,wCAAwC;AACxC,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;;AAEzD,mEAAmE;AACnE,OAAO,EAAE,iBAAiB,EAAE,CAAC;AAE7B;;;GAGG;AAMH,MAAM,OAAO,kBAAkB;IAC7B,MAAM,CAAC,OAAO,CAAC,WAAgB;QAE7B,MAAM,SAAS,GAAU,EAAE,CAAC;QAC5B,MAAM,cAAc,GAAG,WAAW,CAAC,SAAS,EAAE,WAAW,EAAE,CAAC;QAE5D,0EAA0E;QAC1E,4EAA4E;QAC5E,8EAA8E;QAC9E,8EAA8E;QAC9E,+EAA+E;QAC/E,8EAA8E;QAC9E,MAAM,eAAe,GAAG,mBAAmB,CAAC,eAAe,EAAE,CAAC;QAC9D,MAAM,QAAQ,GAAG,eAAe,CAAC,CAAC,CAAC,mBAAmB,CAAC,aAAa,CAAC,CAAC,CAAC,cAAc,CAAC;QAEtF,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,OAAO,CAAC,KAAK,CAAC,uCAAuC,CAAC,CAAC;YACvD,OAAO;gBACL,QAAQ,EAAE,kBAAkB;gBAC5B,SAAS,EAAE,EAAE;aACd,CAAC;QACJ,CAAC;QAED,IAAI,eAAe,IAAI,QAAQ,KAAK,cAAc,EAAE,CAAC;YACnD,OAAO,CAAC,GAAG,CAAC,gHAAgH,cAAc,IAAI,MAAM,KAAK,CAAC,CAAC;QAC7J,CAAC;QAED,4DAA4D;QAC5D,uEAAuE;QACvE,MAAM,eAAe,GAAG,0BAA0B,CAAC,0BAA0B,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;QACrG,SAAS,CAAC,IAAI,CAAC,GAAG,eAAe,CAAC,CAAC;QAEnC,6DAA6D;QAC7D,MAAM,YAAY,GAAG,QAAQ,CAAC,QAAQ,CAAC,YAAY,CAAC,eAAe,CACjE,UAAU,EACV,QAAQ,CACT,CAAC;QACF,MAAM,aAAa,GAAG,YAAY,EAAE,QAAQ,CAAC;QAE7C,IAAI,aAAa,EAAE,CAAC;YAClB,0BAA0B;YAC1B,SAAS,CAAC,IAAI,CAAC;gBACb,OAAO,EAAE,UAAU;gBACnB,QAAQ,EAAE,aAAa;aACxB,CAAC,CAAC;QACL,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,KAAK,CAAC,+CAA+C,QAAQ,EAAE,CAAC,CAAC;QAC3E,CAAC;QAED,yBAAyB;QACzB,SAAS,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;QAE3C,OAAO;YACL,QAAQ,EAAE,kBAAkB;YAC5B,SAAS;SACV,CAAC;IACJ,CAAC;4GAxDU,kBAAkB;4DAAlB,kBAAkB;gEAJnB,YAAY;;iFAIX,kBAAkB;cAL9B,QAAQ;eAAC;gBACR,OAAO,EAAE,CAAC,YAAY,CAAC;gBACvB,YAAY,EAAE,CAAC,iBAAiB,CAAC;gBACjC,OAAO,EAAE,CAAC,iBAAiB,CAAC;aAC7B;;wFACY,kBAAkB,mBAHd,iBAAiB,aADtB,YAAY,aAEZ,iBAAiB","sourcesContent":["import { NgModule, ModuleWithProviders } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { MJAuthBase } from './mjexplorer-auth-base.service';\nimport { AngularAuthProviderFactory } from './AngularAuthProviderFactory';\nimport { MJMagicLinkProvider } from './providers/mjexplorer-magic-link-provider.service';\nimport { MJGlobal } from '@memberjunction/global';\n\n// Import our generic redirect component\nimport { RedirectComponent } from './redirect.component';\n\n// Export the generic redirect component for backward compatibility\nexport { RedirectComponent };\n\n/**\n * Extensible authentication module that supports N providers\n * Uses MJGlobal ClassFactory pattern for dynamic provider creation\n */\n@NgModule({\n imports: [CommonModule],\n declarations: [RedirectComponent],\n exports: [RedirectComponent]\n})\nexport class AuthServicesModule {\n static forRoot(environment: any): ModuleWithProviders<AuthServicesModule> {\n\n const providers: any[] = [];\n const configuredType = environment.AUTH_TYPE?.toLowerCase();\n\n // Coexistence resolution: a single Explorer deployment can serve both SSO\n // users and magic-link guests. If a magic-link session token is present for\n // this page load (URL fragment from /redeem, or stored earlier this tab), use\n // the magic-link provider regardless of the configured primary IdP. Otherwise\n // use the configured AUTH_TYPE. This auto-falls-back to the IdP once a guest's\n // token is gone (expired/logged out), so the normal login button works again.\n const magicLinkActive = MJMagicLinkProvider.hasSessionToken();\n const authType = magicLinkActive ? MJMagicLinkProvider.PROVIDER_TYPE : configuredType;\n\n if (!authType) {\n console.error('No AUTH_TYPE specified in environment');\n return {\n ngModule: AuthServicesModule,\n providers: []\n };\n }\n\n if (magicLinkActive && authType !== configuredType) {\n console.log(`[Auth] Magic-link session token detected — using the magic-link provider for this load (configured primary: '${configuredType ?? 'none'}').`);\n }\n\n // Use the factory to get provider-specific Angular services\n // This uses the static method on each provider class for extensibility\n const angularServices = AngularAuthProviderFactory.getProviderAngularServices(authType, environment);\n providers.push(...angularServices);\n\n // Get the provider class from ClassFactory for extensibility\n const registration = MJGlobal.Instance.ClassFactory.GetRegistration(\n MJAuthBase,\n authType\n );\n const providerClass = registration?.SubClass;\n\n if (providerClass) {\n // Add the provider itself\n providers.push({\n provide: MJAuthBase,\n useClass: providerClass\n });\n } else {\n console.error(`No provider class registered for auth type: ${authType}`);\n }\n\n // Add the factory itself\n providers.push(AngularAuthProviderFactory);\n\n return {\n ngModule: AuthServicesModule,\n providers\n };\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"auth-services.module.js","sourceRoot":"","sources":["../../src/lib/auth-services.module.ts"],"names":[],"mappings":"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","sourcesContent":["import { NgModule, ModuleWithProviders, InjectionToken, Provider } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { MJAuthBase } from './mjexplorer-auth-base.service';\nimport { AngularAuthProviderFactory } from './AngularAuthProviderFactory';\nimport { MJMagicLinkProvider } from './providers/mjexplorer-magic-link-provider.service';\nimport { MJGlobal } from '@memberjunction/global';\nimport type { PublicAuthProviderInfo } from '@memberjunction/core';\nimport { AuthProviderCatalog, type AuthProviderResolution } from './auth-provider-catalog';\nimport { mergeCatalogEnvironment, type CatalogEnvironmentMapper } from './catalog-environment';\nimport { MJLoginPickerComponent } from './login-picker.component';\n\n// Import our generic redirect component\nimport { RedirectComponent } from './redirect.component';\n\n// Export the generic redirect component for backward compatibility\nexport { RedirectComponent };\n\n/**\n * Injection token carrying how authentication resolved for this page load.\n *\n * A login surface injects this to decide whether to render the multi-provider picker or the\n * single sign-in button, and whether to start the login flow immediately after a picker\n * choice caused a reload.\n */\nexport const MJ_AUTH_PROVIDER_RESOLUTION = new InjectionToken<AuthProviderResolution>('MJ_AUTH_PROVIDER_RESOLUTION');\n\n/** The resolution used when no catalog is supplied — i.e. the pre-existing single-provider path. */\nconst NO_CATALOG_RESOLUTION: AuthProviderResolution = { active: null, choices: [], showPicker: false, autoLogin: false };\n\n/**\n * Extensible authentication module that supports N providers\n * Uses MJGlobal ClassFactory pattern for dynamic provider creation\n */\n@NgModule({\n imports: [CommonModule, MJLoginPickerComponent],\n declarations: [RedirectComponent],\n exports: [RedirectComponent, MJLoginPickerComponent]\n})\nexport class AuthServicesModule {\n /**\n * Wires the authentication provider into DI.\n *\n * @param environment The app's compiled environment. Typed loosely on purpose: every app\n * declares its own environment shape (`MJEnvironmentConfig` and downstream equivalents\n * are interfaces, which TypeScript will not assign to an indexed record), so narrowing\n * this parameter would break every existing caller. It is narrowed once, below.\n * @param catalog Optional pre-auth provider catalog. Defaults to whatever the app preloaded\n * via {@link AuthProviderCatalog.Preload} in its bootstrap entry point — passing it\n * explicitly is for tests and for apps that source the catalog some other way. Callers\n * must NOT pass `AuthProviderCatalog.GetPreloaded()` themselves: Angular's compiler\n * requires every `imports` entry to be statically analyzable, and a function call in\n * the argument list fails AOT with \"Value could not be determined statically\".\n *\n * When the catalog is empty — an older server, a failed fetch, or a deployment that\n * never adopted the entity — resolution falls back to `environment.AUTH_TYPE` and\n * behaviour is byte-for-byte what it was before the catalog existed, which is what\n * keeps every existing deployment working untouched.\n */\n static forRoot(environment: any, catalog?: PublicAuthProviderInfo[]): ModuleWithProviders<AuthServicesModule> {\n const env = environment as Record<string, unknown>;\n const providers: Provider[] = [];\n const configuredType = (env['AUTH_TYPE'] as string | undefined)?.toLowerCase();\n const resolvedCatalog = catalog ?? AuthProviderCatalog.GetPreloaded();\n\n // Coexistence resolution: a single Explorer deployment can serve both SSO\n // users and magic-link guests. If a magic-link session token is present for\n // this page load (URL fragment from /redeem, or stored earlier this tab), use\n // the magic-link provider regardless of the configured primary IdP. Otherwise\n // use the catalog selection, then the configured AUTH_TYPE. This auto-falls-back\n // to the IdP once a guest's token is gone (expired/logged out), so the normal\n // login button works again.\n const magicLinkActive = MJMagicLinkProvider.hasSessionToken();\n\n // The catalog resolves which provider this load uses; it never overrides an active\n // magic-link session, which is a property of THIS page load rather than a configured choice.\n const resolution = resolvedCatalog.length ? AuthProviderCatalog.Resolve(resolvedCatalog) : NO_CATALOG_RESOLUTION;\n providers.push({ provide: MJ_AUTH_PROVIDER_RESOLUTION, useValue: resolution });\n\n const catalogType = resolution.active?.driverClass?.toLowerCase();\n const authType = magicLinkActive ? MJMagicLinkProvider.PROVIDER_TYPE : (catalogType ?? configuredType);\n\n if (!authType) {\n console.error('No authentication provider available: the server published no catalog and no AUTH_TYPE is set in the environment');\n return {\n ngModule: AuthServicesModule,\n providers: providers as never[]\n };\n }\n\n if (magicLinkActive && authType !== (catalogType ?? configuredType)) {\n console.log(`[Auth] Magic-link session token detected — using the magic-link provider for this load (configured primary: '${catalogType ?? configuredType ?? 'none'}').`);\n }\n\n // Get the provider class from ClassFactory for extensibility\n const registration = MJGlobal.Instance.ClassFactory.GetRegistration(\n MJAuthBase,\n authType\n );\n const providerClass = registration?.SubClass;\n\n // A catalog-selected provider takes its settings from metadata rather than from the compiled\n // environment, so the driver receives the row's values overlaid on the app's environment.\n // Magic-link is excluded: its session is established by the redeem flow, not by catalog config.\n const effectiveEnvironment =\n resolution.active && !magicLinkActive\n ? mergeCatalogEnvironment(environment, resolution.active, providerClass as CatalogEnvironmentMapper | undefined)\n : environment;\n\n // Use the factory to get provider-specific Angular services\n // This uses the static method on each provider class for extensibility\n const angularServices = AngularAuthProviderFactory.getProviderAngularServices(authType, effectiveEnvironment);\n providers.push(...angularServices);\n\n if (providerClass) {\n // Add the provider itself\n providers.push({\n provide: MJAuthBase,\n useClass: providerClass\n });\n } else {\n console.error(`No provider class registered for auth type: ${authType}`);\n }\n\n // Add the factory itself\n providers.push(AngularAuthProviderFactory);\n\n return {\n ngModule: AuthServicesModule,\n providers: providers as never[]\n };\n }\n}\n"]}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Bridges a metadata provider row to the flat `environment` record that the
|
|
3
|
+
* browser auth drivers already consume.
|
|
4
|
+
*
|
|
5
|
+
* Every `MJAuthBase` subclass exposes `static angularProviderFactory(environment)` and reads
|
|
6
|
+
* its settings from prefixed keys on that record (`AUTH0_DOMAIN`, `WORKOS_CLIENTID`, ...).
|
|
7
|
+
* Rather than rewrite each driver to accept a catalog row, the catalog is projected INTO that
|
|
8
|
+
* record — so a provider configured from metadata and one configured from `environment.ts`
|
|
9
|
+
* take an identical code path, and drivers keep working untouched.
|
|
10
|
+
*
|
|
11
|
+
* @module @memberjunction/ng-auth-services
|
|
12
|
+
*/
|
|
13
|
+
import type { PublicAuthProviderInfo } from '@memberjunction/core';
|
|
14
|
+
/**
|
|
15
|
+
* Optional hook a provider class may expose when the generic prefixed-key convention does not
|
|
16
|
+
* describe its configuration. MSAL is the built-in example: it reads unprefixed `CLIENT_ID` /
|
|
17
|
+
* `CLIENT_AUTHORITY`, so it maps the catalog row itself.
|
|
18
|
+
*/
|
|
19
|
+
export interface CatalogEnvironmentMapper {
|
|
20
|
+
EnvironmentFromCatalog?: (info: PublicAuthProviderInfo) => Record<string, unknown>;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Projects a catalog row onto the conventional prefixed environment keys.
|
|
24
|
+
*
|
|
25
|
+
* The convention — `<DRIVER>_CLIENTID`, `<DRIVER>_DOMAIN`, `<DRIVER>_SCOPES`, plus one key per
|
|
26
|
+
* `clientConfiguration` entry — is exactly what the Auth0, Okta, Cognito and WorkOS drivers
|
|
27
|
+
* already read, so those four need no per-driver mapping at all. A driver that deviates
|
|
28
|
+
* supplies `EnvironmentFromCatalog`.
|
|
29
|
+
*
|
|
30
|
+
* Only defined values are emitted, so a catalog row never blanks out a key the app's compiled
|
|
31
|
+
* environment legitimately supplies. `clientConfiguration` entries are projected FIRST so the
|
|
32
|
+
* modelled columns win a key collision — the same precedence the server enforces in
|
|
33
|
+
* `buildProviderConfig`, where a blob must not silently redefine a described, reviewable column
|
|
34
|
+
* (`{"scopes": "..."}` in the blob would otherwise clobber the parsed `Scopes` column).
|
|
35
|
+
*/
|
|
36
|
+
export declare function buildGenericEnvironmentOverlay(info: PublicAuthProviderInfo): Record<string, unknown>;
|
|
37
|
+
/**
|
|
38
|
+
* Produces the environment record to hand a driver for a catalog-selected provider.
|
|
39
|
+
*
|
|
40
|
+
* The app's compiled environment stays the base layer — it carries app-wide settings such as
|
|
41
|
+
* `GRAPHQL_URI` that have nothing to do with the identity provider — and the catalog overlays
|
|
42
|
+
* only the provider's own keys on top. Metadata therefore wins over a stale compiled value for
|
|
43
|
+
* the settings it owns, without the catalog having to restate the whole environment.
|
|
44
|
+
*
|
|
45
|
+
* @param environment The app's compiled environment object.
|
|
46
|
+
* @param info The provider selected from the catalog.
|
|
47
|
+
* @param providerClass The resolved `MJAuthBase` subclass, consulted for a custom mapper.
|
|
48
|
+
*/
|
|
49
|
+
export declare function mergeCatalogEnvironment(environment: Record<string, unknown>, info: PublicAuthProviderInfo, providerClass?: CatalogEnvironmentMapper): Record<string, unknown>;
|
|
50
|
+
//# sourceMappingURL=catalog-environment.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"catalog-environment.d.ts","sourceRoot":"","sources":["../../src/lib/catalog-environment.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAEnE;;;;GAIG;AACH,MAAM,WAAW,wBAAwB;IACvC,sBAAsB,CAAC,EAAE,CAAC,IAAI,EAAE,sBAAsB,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpF;AAmBD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,8BAA8B,CAAC,IAAI,EAAE,sBAAsB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CA6BpG;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,uBAAuB,CACrC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACpC,IAAI,EAAE,sBAAsB,EAC5B,aAAa,CAAC,EAAE,wBAAwB,GACvC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAOzB"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Bridges a metadata provider row to the flat `environment` record that the
|
|
3
|
+
* browser auth drivers already consume.
|
|
4
|
+
*
|
|
5
|
+
* Every `MJAuthBase` subclass exposes `static angularProviderFactory(environment)` and reads
|
|
6
|
+
* its settings from prefixed keys on that record (`AUTH0_DOMAIN`, `WORKOS_CLIENTID`, ...).
|
|
7
|
+
* Rather than rewrite each driver to accept a catalog row, the catalog is projected INTO that
|
|
8
|
+
* record — so a provider configured from metadata and one configured from `environment.ts`
|
|
9
|
+
* take an identical code path, and drivers keep working untouched.
|
|
10
|
+
*
|
|
11
|
+
* @module @memberjunction/ng-auth-services
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* `redirectUri` → `REDIRECT_URI`, `apiHostname` → `API_HOSTNAME`, `userPoolId` → `USER_POOL_ID`.
|
|
15
|
+
*/
|
|
16
|
+
function camelToUpperSnake(key) {
|
|
17
|
+
return key
|
|
18
|
+
.replace(/([a-z0-9])([A-Z])/g, '$1_$2')
|
|
19
|
+
.replace(/[-\s]+/g, '_')
|
|
20
|
+
.toUpperCase();
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* The environment-key prefix for a driver: `workos` → `WORKOS`, `magic-link` → `MAGIC_LINK`.
|
|
24
|
+
*/
|
|
25
|
+
function environmentPrefix(driverClass) {
|
|
26
|
+
return camelToUpperSnake(driverClass);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Projects a catalog row onto the conventional prefixed environment keys.
|
|
30
|
+
*
|
|
31
|
+
* The convention — `<DRIVER>_CLIENTID`, `<DRIVER>_DOMAIN`, `<DRIVER>_SCOPES`, plus one key per
|
|
32
|
+
* `clientConfiguration` entry — is exactly what the Auth0, Okta, Cognito and WorkOS drivers
|
|
33
|
+
* already read, so those four need no per-driver mapping at all. A driver that deviates
|
|
34
|
+
* supplies `EnvironmentFromCatalog`.
|
|
35
|
+
*
|
|
36
|
+
* Only defined values are emitted, so a catalog row never blanks out a key the app's compiled
|
|
37
|
+
* environment legitimately supplies. `clientConfiguration` entries are projected FIRST so the
|
|
38
|
+
* modelled columns win a key collision — the same precedence the server enforces in
|
|
39
|
+
* `buildProviderConfig`, where a blob must not silently redefine a described, reviewable column
|
|
40
|
+
* (`{"scopes": "..."}` in the blob would otherwise clobber the parsed `Scopes` column).
|
|
41
|
+
*/
|
|
42
|
+
export function buildGenericEnvironmentOverlay(info) {
|
|
43
|
+
const prefix = environmentPrefix(info.driverClass);
|
|
44
|
+
const overlay = {};
|
|
45
|
+
for (const [key, value] of Object.entries(info.clientConfiguration ?? {})) {
|
|
46
|
+
if (value !== null && value !== undefined) {
|
|
47
|
+
overlay[`${prefix}_${camelToUpperSnake(key)}`] = value;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
if (info.clientId) {
|
|
51
|
+
overlay[`${prefix}_CLIENTID`] = info.clientId;
|
|
52
|
+
}
|
|
53
|
+
if (info.domain) {
|
|
54
|
+
overlay[`${prefix}_DOMAIN`] = info.domain;
|
|
55
|
+
}
|
|
56
|
+
if (Array.isArray(info.scopes) && info.scopes.length > 0) {
|
|
57
|
+
// The catalog publishes scopes pre-parsed (the server splits the delimited column at the
|
|
58
|
+
// trust boundary). The drivers that read this key — Okta and Cognito — hand it straight to
|
|
59
|
+
// SDK config typed `string[]`. The Array.isArray guard keeps a malformed or older server's
|
|
60
|
+
// string value from reaching those SDKs: this module runs at bootstrap, where the contract
|
|
61
|
+
// is degrade-to-compiled-environment, never throw.
|
|
62
|
+
overlay[`${prefix}_SCOPES`] = info.scopes;
|
|
63
|
+
}
|
|
64
|
+
if (info.issuer) {
|
|
65
|
+
overlay[`${prefix}_ISSUER`] = info.issuer;
|
|
66
|
+
}
|
|
67
|
+
return overlay;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Produces the environment record to hand a driver for a catalog-selected provider.
|
|
71
|
+
*
|
|
72
|
+
* The app's compiled environment stays the base layer — it carries app-wide settings such as
|
|
73
|
+
* `GRAPHQL_URI` that have nothing to do with the identity provider — and the catalog overlays
|
|
74
|
+
* only the provider's own keys on top. Metadata therefore wins over a stale compiled value for
|
|
75
|
+
* the settings it owns, without the catalog having to restate the whole environment.
|
|
76
|
+
*
|
|
77
|
+
* @param environment The app's compiled environment object.
|
|
78
|
+
* @param info The provider selected from the catalog.
|
|
79
|
+
* @param providerClass The resolved `MJAuthBase` subclass, consulted for a custom mapper.
|
|
80
|
+
*/
|
|
81
|
+
export function mergeCatalogEnvironment(environment, info, providerClass) {
|
|
82
|
+
const overlay = typeof providerClass?.EnvironmentFromCatalog === 'function'
|
|
83
|
+
? providerClass.EnvironmentFromCatalog(info)
|
|
84
|
+
: buildGenericEnvironmentOverlay(info);
|
|
85
|
+
return { ...environment, ...overlay, AUTH_TYPE: info.driverClass };
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=catalog-environment.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"catalog-environment.js","sourceRoot":"","sources":["../../src/lib/catalog-environment.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAaH;;GAEG;AACH,SAAS,iBAAiB,CAAC,GAAW;IACpC,OAAO,GAAG;SACP,OAAO,CAAC,oBAAoB,EAAE,OAAO,CAAC;SACtC,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC;SACvB,WAAW,EAAE,CAAC;AACnB,CAAC;AAED;;GAEG;AACH,SAAS,iBAAiB,CAAC,WAAmB;IAC5C,OAAO,iBAAiB,CAAC,WAAW,CAAC,CAAC;AACxC,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,8BAA8B,CAAC,IAA4B;IACzE,MAAM,MAAM,GAAG,iBAAiB,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACnD,MAAM,OAAO,GAA4B,EAAE,CAAC;IAE5C,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,mBAAmB,IAAI,EAAE,CAAC,EAAE,CAAC;QAC1E,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YAC1C,OAAO,CAAC,GAAG,MAAM,IAAI,iBAAiB,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC;QACzD,CAAC;IACH,CAAC;IAED,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;QAClB,OAAO,CAAC,GAAG,MAAM,WAAW,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC;IAChD,CAAC;IACD,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;QAChB,OAAO,CAAC,GAAG,MAAM,SAAS,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC;IAC5C,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACzD,yFAAyF;QACzF,2FAA2F;QAC3F,2FAA2F;QAC3F,2FAA2F;QAC3F,mDAAmD;QACnD,OAAO,CAAC,GAAG,MAAM,SAAS,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC;IAC5C,CAAC;IACD,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;QAChB,OAAO,CAAC,GAAG,MAAM,SAAS,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC;IAC5C,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,uBAAuB,CACrC,WAAoC,EACpC,IAA4B,EAC5B,aAAwC;IAExC,MAAM,OAAO,GACX,OAAO,aAAa,EAAE,sBAAsB,KAAK,UAAU;QACzD,CAAC,CAAC,aAAa,CAAC,sBAAsB,CAAC,IAAI,CAAC;QAC5C,CAAC,CAAC,8BAA8B,CAAC,IAAI,CAAC,CAAC;IAE3C,OAAO,EAAE,GAAG,WAAW,EAAE,GAAG,OAAO,EAAE,SAAS,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC;AACrE,CAAC","sourcesContent":["/**\n * @fileoverview Bridges a metadata provider row to the flat `environment` record that the\n * browser auth drivers already consume.\n *\n * Every `MJAuthBase` subclass exposes `static angularProviderFactory(environment)` and reads\n * its settings from prefixed keys on that record (`AUTH0_DOMAIN`, `WORKOS_CLIENTID`, ...).\n * Rather than rewrite each driver to accept a catalog row, the catalog is projected INTO that\n * record — so a provider configured from metadata and one configured from `environment.ts`\n * take an identical code path, and drivers keep working untouched.\n *\n * @module @memberjunction/ng-auth-services\n */\n\nimport type { PublicAuthProviderInfo } from '@memberjunction/core';\n\n/**\n * Optional hook a provider class may expose when the generic prefixed-key convention does not\n * describe its configuration. MSAL is the built-in example: it reads unprefixed `CLIENT_ID` /\n * `CLIENT_AUTHORITY`, so it maps the catalog row itself.\n */\nexport interface CatalogEnvironmentMapper {\n EnvironmentFromCatalog?: (info: PublicAuthProviderInfo) => Record<string, unknown>;\n}\n\n/**\n * `redirectUri` → `REDIRECT_URI`, `apiHostname` → `API_HOSTNAME`, `userPoolId` → `USER_POOL_ID`.\n */\nfunction camelToUpperSnake(key: string): string {\n return key\n .replace(/([a-z0-9])([A-Z])/g, '$1_$2')\n .replace(/[-\\s]+/g, '_')\n .toUpperCase();\n}\n\n/**\n * The environment-key prefix for a driver: `workos` → `WORKOS`, `magic-link` → `MAGIC_LINK`.\n */\nfunction environmentPrefix(driverClass: string): string {\n return camelToUpperSnake(driverClass);\n}\n\n/**\n * Projects a catalog row onto the conventional prefixed environment keys.\n *\n * The convention — `<DRIVER>_CLIENTID`, `<DRIVER>_DOMAIN`, `<DRIVER>_SCOPES`, plus one key per\n * `clientConfiguration` entry — is exactly what the Auth0, Okta, Cognito and WorkOS drivers\n * already read, so those four need no per-driver mapping at all. A driver that deviates\n * supplies `EnvironmentFromCatalog`.\n *\n * Only defined values are emitted, so a catalog row never blanks out a key the app's compiled\n * environment legitimately supplies. `clientConfiguration` entries are projected FIRST so the\n * modelled columns win a key collision — the same precedence the server enforces in\n * `buildProviderConfig`, where a blob must not silently redefine a described, reviewable column\n * (`{\"scopes\": \"...\"}` in the blob would otherwise clobber the parsed `Scopes` column).\n */\nexport function buildGenericEnvironmentOverlay(info: PublicAuthProviderInfo): Record<string, unknown> {\n const prefix = environmentPrefix(info.driverClass);\n const overlay: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(info.clientConfiguration ?? {})) {\n if (value !== null && value !== undefined) {\n overlay[`${prefix}_${camelToUpperSnake(key)}`] = value;\n }\n }\n\n if (info.clientId) {\n overlay[`${prefix}_CLIENTID`] = info.clientId;\n }\n if (info.domain) {\n overlay[`${prefix}_DOMAIN`] = info.domain;\n }\n if (Array.isArray(info.scopes) && info.scopes.length > 0) {\n // The catalog publishes scopes pre-parsed (the server splits the delimited column at the\n // trust boundary). The drivers that read this key — Okta and Cognito — hand it straight to\n // SDK config typed `string[]`. The Array.isArray guard keeps a malformed or older server's\n // string value from reaching those SDKs: this module runs at bootstrap, where the contract\n // is degrade-to-compiled-environment, never throw.\n overlay[`${prefix}_SCOPES`] = info.scopes;\n }\n if (info.issuer) {\n overlay[`${prefix}_ISSUER`] = info.issuer;\n }\n\n return overlay;\n}\n\n/**\n * Produces the environment record to hand a driver for a catalog-selected provider.\n *\n * The app's compiled environment stays the base layer — it carries app-wide settings such as\n * `GRAPHQL_URI` that have nothing to do with the identity provider — and the catalog overlays\n * only the provider's own keys on top. Metadata therefore wins over a stale compiled value for\n * the settings it owns, without the catalog having to restate the whole environment.\n *\n * @param environment The app's compiled environment object.\n * @param info The provider selected from the catalog.\n * @param providerClass The resolved `MJAuthBase` subclass, consulted for a custom mapper.\n */\nexport function mergeCatalogEnvironment(\n environment: Record<string, unknown>,\n info: PublicAuthProviderInfo,\n providerClass?: CatalogEnvironmentMapper\n): Record<string, unknown> {\n const overlay =\n typeof providerClass?.EnvironmentFromCatalog === 'function'\n ? providerClass.EnvironmentFromCatalog(info)\n : buildGenericEnvironmentOverlay(info);\n\n return { ...environment, ...overlay, AUTH_TYPE: info.driverClass };\n}\n"]}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { EventEmitter } from '@angular/core';
|
|
2
|
+
import type { PublicAuthProviderInfo } from '@memberjunction/core';
|
|
3
|
+
import * as i0 from "@angular/core";
|
|
4
|
+
/**
|
|
5
|
+
* Reusable, app-agnostic multi-IdP login picker.
|
|
6
|
+
*
|
|
7
|
+
* Presentational by design: it takes a provider catalog in and emits the user's choice out. It
|
|
8
|
+
* owns no routing, no branding, and no knowledge of which app embeds it, so Explorer and any
|
|
9
|
+
* other MJ application get the identical accessible picker by dropping in the element and
|
|
10
|
+
* supplying their own surrounding login surface.
|
|
11
|
+
*
|
|
12
|
+
* Accessibility comes from the shared `mjButton` directive (focus ring, hit target, disabled
|
|
13
|
+
* semantics) rather than from styles restated here, so it cannot drift from the rest of the app.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```html
|
|
17
|
+
* <mj-login-picker
|
|
18
|
+
* [Providers]="Catalog"
|
|
19
|
+
* [Busy]="SigningIn"
|
|
20
|
+
* (ProviderSelected)="OnProviderSelected($event)">
|
|
21
|
+
* </mj-login-picker>
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export declare class MJLoginPickerComponent {
|
|
25
|
+
/** Providers to offer, already ordered by the caller. */
|
|
26
|
+
Providers: PublicAuthProviderInfo[];
|
|
27
|
+
/** Optional heading rendered above the list. */
|
|
28
|
+
Heading: string | null;
|
|
29
|
+
/** Optional supporting line rendered under the heading. */
|
|
30
|
+
Subheading: string | null;
|
|
31
|
+
/**
|
|
32
|
+
* Disables every row — set while a sign-in is being initiated so a second click cannot
|
|
33
|
+
* start a competing redirect.
|
|
34
|
+
*/
|
|
35
|
+
Busy: boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Whether to render the "Powered by MemberJunction" attribution.
|
|
38
|
+
*
|
|
39
|
+
* On by default, and a host-supplied flag rather than a code change, because full
|
|
40
|
+
* white-labelling is a commercial entitlement: a tenant that has paid to remove the
|
|
41
|
+
* attribution must be satisfiable by configuration alone.
|
|
42
|
+
*/
|
|
43
|
+
ShowPoweredBy: boolean;
|
|
44
|
+
/**
|
|
45
|
+
* Label for the call to action shown when the catalog published nothing.
|
|
46
|
+
*
|
|
47
|
+
* Lets one component cover all three states — 2+ providers (a list), exactly one (a single
|
|
48
|
+
* primary CTA naming it), and none at all (this generic CTA, for deployments still configured
|
|
49
|
+
* through `mj.config.cjs` / `AUTH_TYPE`). Null renders nothing, which is the prior behaviour
|
|
50
|
+
* for hosts that handle the empty case themselves.
|
|
51
|
+
*/
|
|
52
|
+
FallbackCtaLabel: string | null;
|
|
53
|
+
/** Emitted with the chosen provider. The host decides what signing in means. */
|
|
54
|
+
ProviderSelected: EventEmitter<PublicAuthProviderInfo>;
|
|
55
|
+
/** Emitted when the empty-catalog CTA is clicked. The host starts its configured sign-in. */
|
|
56
|
+
FallbackSelected: EventEmitter<void>;
|
|
57
|
+
/** A single option is not a choice — it renders as one primary call to action. */
|
|
58
|
+
get IsSingleProvider(): boolean;
|
|
59
|
+
/** "Continue with Okta" — the conventional modern auth phrasing. */
|
|
60
|
+
LabelFor(provider: PublicAuthProviderInfo): string;
|
|
61
|
+
/**
|
|
62
|
+
* Resolves the icon class for a provider: an explicit `Icon` from metadata wins, otherwise
|
|
63
|
+
* a known brand mapping for the driver, otherwise null (the chip falls back to an initial).
|
|
64
|
+
*/
|
|
65
|
+
IconClassFor(provider: PublicAuthProviderInfo): string | null;
|
|
66
|
+
/** Fallback chip content when no icon resolves. */
|
|
67
|
+
InitialFor(provider: PublicAuthProviderInfo): string;
|
|
68
|
+
Select(provider: PublicAuthProviderInfo): void;
|
|
69
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<MJLoginPickerComponent, never>;
|
|
70
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<MJLoginPickerComponent, "mj-login-picker", never, { "Providers": { "alias": "Providers"; "required": false; }; "Heading": { "alias": "Heading"; "required": false; }; "Subheading": { "alias": "Subheading"; "required": false; }; "Busy": { "alias": "Busy"; "required": false; }; "ShowPoweredBy": { "alias": "ShowPoweredBy"; "required": false; }; "FallbackCtaLabel": { "alias": "FallbackCtaLabel"; "required": false; }; }, { "ProviderSelected": "ProviderSelected"; "FallbackSelected": "FallbackSelected"; }, never, never, true, never>;
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=login-picker.component.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"login-picker.component.d.ts","sourceRoot":"","sources":["../../src/lib/login-picker.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,YAAY,EAAiB,MAAM,eAAe,CAAC;AAEvE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;;AAiBnE;;;;;;;;;;;;;;;;;;;GAmBG;AACH,qBAgMa,sBAAsB;IACjC,yDAAyD;IAChD,SAAS,EAAE,sBAAsB,EAAE,CAAM;IAElD,gDAAgD;IACvC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAY;IAE3C,2DAA2D;IAClD,UAAU,EAAE,MAAM,GAAG,IAAI,CAAQ;IAE1C;;;OAGG;IACM,IAAI,UAAS;IAEtB;;;;;;OAMG;IACM,aAAa,UAAQ;IAE9B;;;;;;;OAOG;IACM,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAQ;IAEhD,gFAAgF;IACtE,gBAAgB,uCAA8C;IAExE,6FAA6F;IACnF,gBAAgB,qBAA4B;IAEtD,kFAAkF;IAClF,IAAW,gBAAgB,IAAI,OAAO,CAErC;IAED,oEAAoE;IAC7D,QAAQ,CAAC,QAAQ,EAAE,sBAAsB,GAAG,MAAM;IAIzD;;;OAGG;IACI,YAAY,CAAC,QAAQ,EAAE,sBAAsB,GAAG,MAAM,GAAG,IAAI;IAOpE,mDAAmD;IAC5C,UAAU,CAAC,QAAQ,EAAE,sBAAsB,GAAG,MAAM;IAIpD,MAAM,CAAC,QAAQ,EAAE,sBAAsB,GAAG,IAAI;yCAnE1C,sBAAsB;2CAAtB,sBAAsB;CAwElC"}
|