@meshmakers/shared-auth 3.3.34 → 3.3.380
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 +205 -13
- package/fesm2022/meshmakers-shared-auth-login-ui.mjs +127 -0
- package/fesm2022/meshmakers-shared-auth-login-ui.mjs.map +1 -0
- package/fesm2022/meshmakers-shared-auth.mjs +336 -240
- package/fesm2022/meshmakers-shared-auth.mjs.map +1 -1
- package/package.json +27 -5
- package/types/meshmakers-shared-auth-login-ui.d.ts +42 -0
- package/types/meshmakers-shared-auth.d.ts +222 -0
- package/index.d.ts +0 -118
package/README.md
CHANGED
|
@@ -1,24 +1,216 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @meshmakers/shared-auth
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
OAuth2/OIDC authentication library for Angular applications using [angular-oauth2-oidc](https://github.com/manfredsteyer/angular-oauth2-oidc).
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Installation
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
```bash
|
|
8
|
+
npm install @meshmakers/shared-auth
|
|
9
|
+
```
|
|
9
10
|
|
|
10
|
-
##
|
|
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
|
-
|
|
104
|
+
// With role requirement
|
|
105
|
+
{
|
|
106
|
+
path: 'admin',
|
|
107
|
+
component: AdminComponent,
|
|
108
|
+
canActivate: [authorizeGuard],
|
|
109
|
+
data: { roles: ['AdminPanelManagement'] }
|
|
110
|
+
},
|
|
13
111
|
|
|
14
|
-
|
|
112
|
+
// Lazy-loaded module
|
|
113
|
+
{
|
|
114
|
+
path: 'reports',
|
|
115
|
+
loadChildren: () => import('./reports/routes'),
|
|
116
|
+
canMatch: [authorizeMatchGuard]
|
|
117
|
+
}
|
|
118
|
+
];
|
|
119
|
+
```
|
|
15
120
|
|
|
16
|
-
|
|
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
|
-
|
|
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
|
-
|
|
208
|
+
```bash
|
|
209
|
+
npm run build:shared-auth
|
|
210
|
+
```
|
|
21
211
|
|
|
22
|
-
##
|
|
212
|
+
## Test
|
|
23
213
|
|
|
24
|
-
|
|
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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"}
|