@meshmakers/shared-auth 3.3.34 → 3.3.390

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/README.md CHANGED
@@ -1,24 +1,216 @@
1
- # SharedAuth
1
+ # @meshmakers/shared-auth
2
2
 
3
- This library was generated with [Angular CLI](https://github.com/angular/angular-cli) version 15.2.0.
3
+ OAuth2/OIDC authentication library for Angular applications using [angular-oauth2-oidc](https://github.com/manfredsteyer/angular-oauth2-oidc).
4
4
 
5
- ## Code scaffolding
5
+ ## Installation
6
6
 
7
- Run `ng generate component component-name --project shared-auth` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module --project shared-auth`.
8
- > Note: Don't forget to add `--project shared-auth` or else it will be added to the default project in your `angular.json` file.
7
+ ```bash
8
+ npm install @meshmakers/shared-auth
9
+ ```
9
10
 
10
- ## Build
11
+ ## Quick Start
12
+
13
+ ```typescript
14
+ // app.config.ts
15
+ import { ApplicationConfig } from '@angular/core';
16
+ import { provideHttpClient, withInterceptors } from '@angular/common/http';
17
+ import { provideMmSharedAuth, authorizeInterceptor } from '@meshmakers/shared-auth';
18
+
19
+ export const appConfig: ApplicationConfig = {
20
+ providers: [
21
+ provideHttpClient(withInterceptors([authorizeInterceptor])),
22
+ provideMmSharedAuth(),
23
+ ]
24
+ };
25
+ ```
26
+
27
+ ```typescript
28
+ // app.component.ts
29
+ import { Component, inject, OnInit } from '@angular/core';
30
+ import { AuthorizeService } from '@meshmakers/shared-auth';
31
+
32
+ @Component({...})
33
+ export class AppComponent implements OnInit {
34
+ private readonly authorizeService = inject(AuthorizeService);
35
+
36
+ async ngOnInit() {
37
+ await this.authorizeService.initialize({
38
+ issuer: 'https://your-identity-provider.com/',
39
+ redirectUri: window.location.origin,
40
+ clientId: 'your-client-id',
41
+ scope: 'openid profile email',
42
+ wellKnownServiceUris: ['https://api.your-service.com']
43
+ });
44
+ }
45
+ }
46
+ ```
47
+
48
+ ## API Reference
49
+
50
+ ### AuthorizeService
51
+
52
+ The main service for authentication.
53
+
54
+ #### Signals (Recommended)
55
+
56
+ | Signal | Type | Description |
57
+ |--------|------|-------------|
58
+ | `isAuthenticated` | `Signal<boolean>` | Current authentication status |
59
+ | `user` | `Signal<IUser \| null>` | Current user information |
60
+ | `accessToken` | `Signal<string \| null>` | OAuth access token |
61
+ | `userInitials` | `Signal<string \| null>` | User initials (e.g., "JD") |
62
+ | `issuer` | `Signal<string \| null>` | OAuth issuer URL |
63
+ | `sessionLoading` | `Signal<boolean>` | Session loading state |
64
+ | `roles` | `Signal<string[]>` | User roles (computed) |
65
+
66
+ ```typescript
67
+ // Usage in template
68
+ @if (authorizeService.isAuthenticated()) {
69
+ <p>Welcome, {{ authorizeService.user()?.name }}</p>
70
+ }
71
+
72
+ // Usage in TypeScript
73
+ const isAuth = this.authorizeService.isAuthenticated();
74
+ const roles = this.authorizeService.roles();
75
+ ```
76
+
77
+ #### Methods
78
+
79
+ | Method | Description |
80
+ |--------|-------------|
81
+ | `initialize(options)` | Initialize OAuth with configuration |
82
+ | `uninitialize()` | Clean up OAuth session |
83
+ | `login()` | Start OAuth login flow |
84
+ | `logout()` | Log out user |
85
+ | `isInRole(role)` | Check if user has specific role |
86
+ | `getAccessTokenSync()` | Get current access token synchronously |
87
+ | `getServiceUris()` | Get configured service URIs |
88
+
89
+ ### Guards
90
+
91
+ Functional guards for route protection.
92
+
93
+ ```typescript
94
+ import { authorizeGuard, authorizeMatchGuard } from '@meshmakers/shared-auth';
95
+
96
+ const routes: Routes = [
97
+ // Basic authentication check
98
+ {
99
+ path: 'dashboard',
100
+ component: DashboardComponent,
101
+ canActivate: [authorizeGuard]
102
+ },
11
103
 
12
- Run `ng build shared-auth` to build the project. The build artifacts will be stored in the `dist/` directory.
104
+ // With role requirement
105
+ {
106
+ path: 'admin',
107
+ component: AdminComponent,
108
+ canActivate: [authorizeGuard],
109
+ data: { roles: ['AdminPanelManagement'] }
110
+ },
13
111
 
14
- ## Publishing
112
+ // Lazy-loaded module
113
+ {
114
+ path: 'reports',
115
+ loadChildren: () => import('./reports/routes'),
116
+ canMatch: [authorizeMatchGuard]
117
+ }
118
+ ];
119
+ ```
15
120
 
16
- After building your library with `ng build shared-auth`, go to the dist folder `cd dist/shared-auth` and run `npm publish`.
121
+ | Guard | Type | Description |
122
+ |-------|------|-------------|
123
+ | `authorizeGuard` | `CanActivateFn` | Route activation with optional role check |
124
+ | `authorizeChildGuard` | `CanActivateFn` | Child route guard |
125
+ | `authorizeMatchGuard` | `CanMatchFn` | Lazy-load matching guard |
126
+ | `authorizeDeactivateGuard` | Function | Always allows deactivation |
17
127
 
18
- ## Running unit tests
128
+ ### Interceptor
129
+
130
+ Automatically adds Bearer token to HTTP requests.
131
+
132
+ ```typescript
133
+ import { provideHttpClient, withInterceptors } from '@angular/common/http';
134
+ import { authorizeInterceptor } from '@meshmakers/shared-auth';
135
+
136
+ providers: [
137
+ provideHttpClient(withInterceptors([authorizeInterceptor]))
138
+ ]
139
+ ```
140
+
141
+ The interceptor adds the `Authorization: Bearer <token>` header to:
142
+ - Same-origin requests (relative URLs)
143
+ - Requests to configured `wellKnownServiceUris`
144
+
145
+ ### UI Components
146
+
147
+ #### LoginAppBarSectionComponent
148
+
149
+ A pre-built login/logout component for the app bar.
150
+
151
+ ```typescript
152
+ import { LoginAppBarSectionComponent } from '@meshmakers/shared-auth';
153
+
154
+ @Component({
155
+ imports: [LoginAppBarSectionComponent],
156
+ template: `
157
+ <mm-login-app-bar-section
158
+ [showRegister]="true"
159
+ (register)="onRegister()">
160
+ </mm-login-app-bar-section>
161
+ `
162
+ })
163
+ ```
164
+
165
+ ## Configuration Options
166
+
167
+ ```typescript
168
+ interface AuthorizeOptions {
169
+ issuer?: string; // Identity Provider URL
170
+ redirectUri?: string; // Redirect after login
171
+ postLogoutRedirectUri?: string; // Redirect after logout
172
+ clientId?: string; // OAuth client ID
173
+ scope?: string; // OAuth scopes
174
+ showDebugInformation?: boolean; // Enable debug logging
175
+ sessionChecksEnabled?: boolean; // Enable session checks
176
+ wellKnownServiceUris?: string[]; // APIs that should receive auth token
177
+ }
178
+ ```
179
+
180
+ ## User Interface
181
+
182
+ ```typescript
183
+ interface IUser {
184
+ sub: string; // Subject (user ID)
185
+ name: string; // Display name
186
+ given_name: string | null; // First name
187
+ family_name: string | null; // Last name
188
+ email: string | null; // Email address
189
+ role: string[] | null; // User roles
190
+ idp: string; // Identity provider
191
+ }
192
+ ```
193
+
194
+ ## Roles
195
+
196
+ Pre-defined role constants:
197
+
198
+ ```typescript
199
+ import { Roles } from '@meshmakers/shared-auth';
200
+
201
+ if (authorizeService.isInRole(Roles.AdminPanelManagement)) {
202
+ // Admin access
203
+ }
204
+ ```
205
+
206
+ ## Build
19
207
 
20
- Run `ng test shared-auth` to execute the unit tests via [Karma](https://karma-runner.github.io).
208
+ ```bash
209
+ npm run build:shared-auth
210
+ ```
21
211
 
22
- ## Further help
212
+ ## Test
23
213
 
24
- To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.
214
+ ```bash
215
+ npm test -- --project=@meshmakers/shared-auth --watch=false
216
+ ```
@@ -0,0 +1,127 @@
1
+ import * as i0 from '@angular/core';
2
+ import { inject, EventEmitter, computed, signal, ElementRef, HostListener, Input, Output, ViewChild, Component } from '@angular/core';
3
+ import { AuthorizeService } from '@meshmakers/shared-auth';
4
+ import { AvatarComponent } from '@progress/kendo-angular-layout';
5
+ import { ButtonComponent } from '@progress/kendo-angular-buttons';
6
+ import { PopupComponent } from '@progress/kendo-angular-popup';
7
+ import { LoaderComponent } from '@progress/kendo-angular-indicators';
8
+
9
+ class LoginAppBarSectionComponent {
10
+ authorizeService = inject(AuthorizeService);
11
+ _register = new EventEmitter();
12
+ _showRegister = false;
13
+ _showPopup = false;
14
+ /**
15
+ * Computed signal for the user's display name.
16
+ */
17
+ userName = computed(() => this.authorizeService.user()?.name ?? null, ...(ngDevMode ? [{ debugName: "userName" }] : []));
18
+ /**
19
+ * Computed signal for the user's full name (given name + family name).
20
+ */
21
+ fullName = computed(() => {
22
+ const user = this.authorizeService.user();
23
+ if (user?.given_name && user?.family_name) {
24
+ return user.given_name + " " + user.family_name;
25
+ }
26
+ return null;
27
+ }, ...(ngDevMode ? [{ debugName: "fullName" }] : []));
28
+ /**
29
+ * Signal for the profile management URI.
30
+ */
31
+ profileUri = signal(null, ...(ngDevMode ? [{ debugName: "profileUri" }] : []));
32
+ anchor = null;
33
+ popup = null;
34
+ constructor() {
35
+ this._showPopup = false;
36
+ this._showRegister = false;
37
+ }
38
+ async ngOnInit() {
39
+ console.debug('mm-login-app-bar-section::created');
40
+ const issuerUri = this.authorizeService.issuer();
41
+ if (issuerUri) {
42
+ this.profileUri.set(issuerUri + "Manage");
43
+ }
44
+ }
45
+ get register() {
46
+ return this._register;
47
+ }
48
+ get showPopup() {
49
+ return this._showPopup;
50
+ }
51
+ set showPopup(value) {
52
+ this._showPopup = value;
53
+ }
54
+ get showRegister() {
55
+ return this._showRegister;
56
+ }
57
+ set showRegister(value) {
58
+ this._showRegister = value;
59
+ }
60
+ keydown(event) {
61
+ if (event.code === "Escape") {
62
+ this.onToggle(false);
63
+ }
64
+ }
65
+ documentClick(event) {
66
+ if (!this.contains(event.target)) {
67
+ this.onToggle(false);
68
+ }
69
+ }
70
+ contains(target) {
71
+ return (this.anchor?.nativeElement.contains(target) ||
72
+ (this.popup ? this.popup.nativeElement.contains(target) : false));
73
+ }
74
+ onToggle(show) {
75
+ this._showPopup = show !== undefined ? show : !this._showPopup;
76
+ }
77
+ onLogin() {
78
+ this.authorizeService.login();
79
+ }
80
+ onLogout() {
81
+ this.authorizeService.logout();
82
+ }
83
+ onRegister() {
84
+ this.register.emit();
85
+ }
86
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: LoginAppBarSectionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
87
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.5", type: LoginAppBarSectionComponent, isStandalone: true, selector: "mm-login-app-bar-section", inputs: { showRegister: "showRegister" }, outputs: { register: "register" }, host: { listeners: { "document:keydown": "keydown($event)", "document:click": "documentClick($event)" } }, viewQueries: [{ propertyName: "anchor", first: true, predicate: ["user"], descendants: true, read: ElementRef }, { propertyName: "popup", first: true, predicate: ["popup"], descendants: true, read: ElementRef }], ngImport: i0, template: "@if (authorizeService.sessionLoading()) {\n <kendo-loader type=\"pulsing\"></kendo-loader>\n}\n\n@if (!authorizeService.sessionLoading() && !authorizeService.isAuthenticated()) {\n <div>\n @if (showRegister) {\n <button kendoButton size=\"large\" class=\"login-button\" (click)='onRegister()'>Register</button>\n }\n <button kendoButton size=\"large\" themeColor=\"primary\" class=\"login-button\" (click)='onLogin()'>Login</button>\n </div>\n}\n\n@if (!authorizeService.sessionLoading() && authorizeService.isAuthenticated()) {\n <div>\n <button kendoButton #user fillMode=\"flat\" (click)=\"onToggle()\">\n <kendo-avatar [initials]=\"authorizeService.userInitials() || '??'\"\n shape=\"circle\"\n width=\"32px\"\n height=\"32px\"></kendo-avatar>\n </button>\n @if (showPopup) {\n <kendo-popup\n [anchor]=\"user.element\"\n (anchorViewportLeave)=\"showPopup = false\">\n <div class=\"content\">\n <kendo-avatar class=\"user-avatar\"\n [initials]=\"authorizeService.userInitials() || '??'\"\n width=\"80px\"\n height=\"80px\"\n shape=\"circle\"\n ></kendo-avatar>\n\n @if (fullName(); as name) {\n <p class=\"user-name\">{{ name }}</p>\n }\n <p class=\"user-name\">{{ userName() }}</p>\n\n <div class=\"buttons\">\n <button kendoButton themeColor=\"primary\" (click)=\"onLogout()\">\n Logout\n </button>\n @if (profileUri(); as uri) {\n <a class=\"k-button k-button-md k-rounded-md k-button-solid\" [href]=\"uri\" target=\"_blank\">\n Manage Profile\n </a>\n }\n </div>\n </div>\n </kendo-popup>\n }\n </div>\n}\n", styles: [".content{width:280px;padding:24px;display:flex;flex-direction:column;justify-content:center;align-items:center;gap:16px;background:linear-gradient(180deg,#394555,#1f2e40)}.user-avatar{border:2px solid rgba(100,206,185,.4);box-shadow:0 0 15px #64ceb94d}.user-avatar ::ng-deep .k-avatar-text{font-family:Montserrat,Roboto,sans-serif;font-weight:600;text-transform:uppercase}.user-name{font-family:Montserrat,Roboto,sans-serif;font-size:.85rem;color:#fff;margin:0;text-align:center;word-break:break-all}.buttons{display:flex;flex-direction:column;align-items:stretch;gap:10px;width:100%;margin-top:8px}.buttons .k-button{width:100%;display:flex;justify-content:center;align-items:center;font-family:Montserrat,Roboto,sans-serif;font-weight:500;text-transform:uppercase;font-size:.75rem;letter-spacing:.5px;padding:10px 16px;border-radius:4px;transition:all .2s ease;text-decoration:none;text-align:center;box-sizing:border-box;margin:0}.buttons button.k-button-solid-primary{background:linear-gradient(180deg,#64ceb9,#4db8a4);border:none;color:#07172b}.buttons button.k-button-solid-primary:hover{background:linear-gradient(180deg,#7ad9c7,#64ceb9);box-shadow:0 0 15px #64ceb980}.buttons a.k-button-solid{background:#64ceb91a;border:1px solid rgba(100,206,185,.3);color:#64ceb9}.buttons a.k-button-solid:hover{background:#64ceb933;box-shadow:0 0 10px #64ceb94d}.k-button~.k-button{margin-left:0}\n"], dependencies: [{ kind: "component", type: AvatarComponent, selector: "kendo-avatar", inputs: ["shape", "size", "rounded", "themeColor", "fillMode", "fill", "border", "iconClass", "width", "height", "cssStyle", "initials", "icon", "imageSrc", "svgIcon"] }, { kind: "component", type: ButtonComponent, selector: "button[kendoButton]", inputs: ["arrowIcon", "toggleable", "togglable", "selected", "tabIndex", "imageUrl", "iconClass", "icon", "disabled", "size", "rounded", "fillMode", "themeColor", "svgIcon", "primary", "look"], outputs: ["selectedChange", "click"], exportAs: ["kendoButton"] }, { kind: "component", type: PopupComponent, selector: "kendo-popup", inputs: ["animate", "anchor", "anchorAlign", "collision", "popupAlign", "copyAnchorStyles", "popupClass", "positionMode", "offset", "margin"], outputs: ["anchorViewportLeave", "close", "open", "positionChange"], exportAs: ["kendo-popup"] }, { kind: "component", type: LoaderComponent, selector: "kendo-loader", inputs: ["type", "themeColor", "size"] }] });
88
+ }
89
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: LoginAppBarSectionComponent, decorators: [{
90
+ type: Component,
91
+ args: [{ selector: 'mm-login-app-bar-section', imports: [
92
+ AvatarComponent,
93
+ ButtonComponent,
94
+ PopupComponent,
95
+ LoaderComponent
96
+ ], template: "@if (authorizeService.sessionLoading()) {\n <kendo-loader type=\"pulsing\"></kendo-loader>\n}\n\n@if (!authorizeService.sessionLoading() && !authorizeService.isAuthenticated()) {\n <div>\n @if (showRegister) {\n <button kendoButton size=\"large\" class=\"login-button\" (click)='onRegister()'>Register</button>\n }\n <button kendoButton size=\"large\" themeColor=\"primary\" class=\"login-button\" (click)='onLogin()'>Login</button>\n </div>\n}\n\n@if (!authorizeService.sessionLoading() && authorizeService.isAuthenticated()) {\n <div>\n <button kendoButton #user fillMode=\"flat\" (click)=\"onToggle()\">\n <kendo-avatar [initials]=\"authorizeService.userInitials() || '??'\"\n shape=\"circle\"\n width=\"32px\"\n height=\"32px\"></kendo-avatar>\n </button>\n @if (showPopup) {\n <kendo-popup\n [anchor]=\"user.element\"\n (anchorViewportLeave)=\"showPopup = false\">\n <div class=\"content\">\n <kendo-avatar class=\"user-avatar\"\n [initials]=\"authorizeService.userInitials() || '??'\"\n width=\"80px\"\n height=\"80px\"\n shape=\"circle\"\n ></kendo-avatar>\n\n @if (fullName(); as name) {\n <p class=\"user-name\">{{ name }}</p>\n }\n <p class=\"user-name\">{{ userName() }}</p>\n\n <div class=\"buttons\">\n <button kendoButton themeColor=\"primary\" (click)=\"onLogout()\">\n Logout\n </button>\n @if (profileUri(); as uri) {\n <a class=\"k-button k-button-md k-rounded-md k-button-solid\" [href]=\"uri\" target=\"_blank\">\n Manage Profile\n </a>\n }\n </div>\n </div>\n </kendo-popup>\n }\n </div>\n}\n", styles: [".content{width:280px;padding:24px;display:flex;flex-direction:column;justify-content:center;align-items:center;gap:16px;background:linear-gradient(180deg,#394555,#1f2e40)}.user-avatar{border:2px solid rgba(100,206,185,.4);box-shadow:0 0 15px #64ceb94d}.user-avatar ::ng-deep .k-avatar-text{font-family:Montserrat,Roboto,sans-serif;font-weight:600;text-transform:uppercase}.user-name{font-family:Montserrat,Roboto,sans-serif;font-size:.85rem;color:#fff;margin:0;text-align:center;word-break:break-all}.buttons{display:flex;flex-direction:column;align-items:stretch;gap:10px;width:100%;margin-top:8px}.buttons .k-button{width:100%;display:flex;justify-content:center;align-items:center;font-family:Montserrat,Roboto,sans-serif;font-weight:500;text-transform:uppercase;font-size:.75rem;letter-spacing:.5px;padding:10px 16px;border-radius:4px;transition:all .2s ease;text-decoration:none;text-align:center;box-sizing:border-box;margin:0}.buttons button.k-button-solid-primary{background:linear-gradient(180deg,#64ceb9,#4db8a4);border:none;color:#07172b}.buttons button.k-button-solid-primary:hover{background:linear-gradient(180deg,#7ad9c7,#64ceb9);box-shadow:0 0 15px #64ceb980}.buttons a.k-button-solid{background:#64ceb91a;border:1px solid rgba(100,206,185,.3);color:#64ceb9}.buttons a.k-button-solid:hover{background:#64ceb933;box-shadow:0 0 10px #64ceb94d}.k-button~.k-button{margin-left:0}\n"] }]
97
+ }], ctorParameters: () => [], propDecorators: { anchor: [{
98
+ type: ViewChild,
99
+ args: ["user", { read: ElementRef }]
100
+ }], popup: [{
101
+ type: ViewChild,
102
+ args: ["popup", { read: ElementRef }]
103
+ }], register: [{
104
+ type: Output
105
+ }], showRegister: [{
106
+ type: Input
107
+ }], keydown: [{
108
+ type: HostListener,
109
+ args: ["document:keydown", ["$event"]]
110
+ }], documentClick: [{
111
+ type: HostListener,
112
+ args: ["document:click", ["$event"]]
113
+ }] } });
114
+
115
+ /*
116
+ * Public API Surface of shared-auth/login-ui
117
+ *
118
+ * This secondary entry point contains the Kendo-based LoginAppBarSectionComponent.
119
+ * Import from '@meshmakers/shared-auth/login-ui' only in apps that use Kendo UI.
120
+ */
121
+
122
+ /**
123
+ * Generated bundle index. Do not edit.
124
+ */
125
+
126
+ export { LoginAppBarSectionComponent };
127
+ //# sourceMappingURL=meshmakers-shared-auth-login-ui.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"meshmakers-shared-auth-login-ui.mjs","sources":["../../../../projects/meshmakers/shared-auth/login-ui/src/mm-login-app-bar-section/login-app-bar-section.component.ts","../../../../projects/meshmakers/shared-auth/login-ui/src/mm-login-app-bar-section/login-app-bar-section.component.html","../../../../projects/meshmakers/shared-auth/login-ui/src/public-api.ts","../../../../projects/meshmakers/shared-auth/login-ui/src/meshmakers-shared-auth-login-ui.ts"],"sourcesContent":["import { Component, computed, ElementRef, EventEmitter, HostListener, Input, OnInit, Output, Signal, signal, ViewChild, WritableSignal, inject } from '@angular/core';\nimport { AuthorizeService } from '@meshmakers/shared-auth';\nimport { AvatarComponent } from '@progress/kendo-angular-layout';\nimport { ButtonComponent } from '@progress/kendo-angular-buttons';\nimport { PopupComponent } from '@progress/kendo-angular-popup';\nimport { LoaderComponent } from '@progress/kendo-angular-indicators';\n\n@Component({\n selector: 'mm-login-app-bar-section',\n imports: [\n AvatarComponent,\n ButtonComponent,\n PopupComponent,\n LoaderComponent\n ],\n templateUrl: './login-app-bar-section.component.html',\n styleUrl: './login-app-bar-section.component.scss'\n})\nexport class LoginAppBarSectionComponent implements OnInit {\n protected readonly authorizeService = inject(AuthorizeService);\n\n private readonly _register = new EventEmitter();\n private _showRegister = false;\n private _showPopup = false;\n\n /**\n * Computed signal for the user's display name.\n */\n protected readonly userName: Signal<string | null> = computed(() =>\n this.authorizeService.user()?.name ?? null\n );\n\n /**\n * Computed signal for the user's full name (given name + family name).\n */\n protected readonly fullName: Signal<string | null> = computed(() => {\n const user = this.authorizeService.user();\n if (user?.given_name && user?.family_name) {\n return user.given_name + \" \" + user.family_name;\n }\n return null;\n });\n\n /**\n * Signal for the profile management URI.\n */\n protected readonly profileUri: WritableSignal<string | null> = signal(null);\n\n @ViewChild(\"user\", { read: ElementRef })\n private anchor: ElementRef | null = null;\n\n @ViewChild(\"popup\", { read: ElementRef })\n private popup: ElementRef | null = null;\n\n constructor() {\n this._showPopup = false;\n this._showRegister = false;\n }\n\n async ngOnInit(): Promise<void> {\n console.debug('mm-login-app-bar-section::created');\n\n const issuerUri = this.authorizeService.issuer();\n if (issuerUri) {\n this.profileUri.set(issuerUri + \"Manage\");\n }\n }\n\n @Output() get register(): EventEmitter<any> {\n return this._register;\n }\n\n public get showPopup(): boolean {\n return this._showPopup;\n }\n\n public set showPopup(value: boolean) {\n this._showPopup = value;\n }\n\n public get showRegister(): boolean {\n return this._showRegister;\n }\n\n @Input()\n public set showRegister(value: boolean) {\n this._showRegister = value;\n }\n\n @HostListener(\"document:keydown\", [\"$event\"])\n public keydown(event: KeyboardEvent): void {\n if (event.code === \"Escape\") {\n this.onToggle(false);\n }\n }\n\n @HostListener(\"document:click\", [\"$event\"])\n public documentClick(event: MouseEvent): void {\n if (!this.contains(event.target)) {\n this.onToggle(false);\n }\n }\n\n private contains(target: EventTarget | null): boolean {\n return (\n this.anchor?.nativeElement.contains(target) ||\n (this.popup ? this.popup.nativeElement.contains(target) : false)\n );\n }\n\n public onToggle(show?: boolean): void {\n this._showPopup = show !== undefined ? show : !this._showPopup;\n }\n\n protected onLogin(): void {\n this.authorizeService.login();\n }\n\n protected onLogout(): void {\n this.authorizeService.logout();\n }\n\n protected onRegister(): void {\n this.register.emit();\n }\n}\n","@if (authorizeService.sessionLoading()) {\n <kendo-loader type=\"pulsing\"></kendo-loader>\n}\n\n@if (!authorizeService.sessionLoading() && !authorizeService.isAuthenticated()) {\n <div>\n @if (showRegister) {\n <button kendoButton size=\"large\" class=\"login-button\" (click)='onRegister()'>Register</button>\n }\n <button kendoButton size=\"large\" themeColor=\"primary\" class=\"login-button\" (click)='onLogin()'>Login</button>\n </div>\n}\n\n@if (!authorizeService.sessionLoading() && authorizeService.isAuthenticated()) {\n <div>\n <button kendoButton #user fillMode=\"flat\" (click)=\"onToggle()\">\n <kendo-avatar [initials]=\"authorizeService.userInitials() || '??'\"\n shape=\"circle\"\n width=\"32px\"\n height=\"32px\"></kendo-avatar>\n </button>\n @if (showPopup) {\n <kendo-popup\n [anchor]=\"user.element\"\n (anchorViewportLeave)=\"showPopup = false\">\n <div class=\"content\">\n <kendo-avatar class=\"user-avatar\"\n [initials]=\"authorizeService.userInitials() || '??'\"\n width=\"80px\"\n height=\"80px\"\n shape=\"circle\"\n ></kendo-avatar>\n\n @if (fullName(); as name) {\n <p class=\"user-name\">{{ name }}</p>\n }\n <p class=\"user-name\">{{ userName() }}</p>\n\n <div class=\"buttons\">\n <button kendoButton themeColor=\"primary\" (click)=\"onLogout()\">\n Logout\n </button>\n @if (profileUri(); as uri) {\n <a class=\"k-button k-button-md k-rounded-md k-button-solid\" [href]=\"uri\" target=\"_blank\">\n Manage Profile\n </a>\n }\n </div>\n </div>\n </kendo-popup>\n }\n </div>\n}\n","/*\n * Public API Surface of shared-auth/login-ui\n *\n * This secondary entry point contains the Kendo-based LoginAppBarSectionComponent.\n * Import from '@meshmakers/shared-auth/login-ui' only in apps that use Kendo UI.\n */\nexport * from './mm-login-app-bar-section/login-app-bar-section.component';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}