@dereekb/dbx-firebase 13.2.2 → 13.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/dereekb-dbx-firebase-oidc.mjs +1378 -0
- package/fesm2022/dereekb-dbx-firebase-oidc.mjs.map +1 -0
- package/fesm2022/dereekb-dbx-firebase.mjs +122 -6
- package/fesm2022/dereekb-dbx-firebase.mjs.map +1 -1
- package/package.json +14 -10
- package/types/dereekb-dbx-firebase-oidc.d.ts +616 -0
- package/types/dereekb-dbx-firebase.d.ts +145 -9
|
@@ -0,0 +1,1378 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { input, computed, output, ChangeDetectionStrategy, Component, inject, Injectable, signal, effect, Directive, provideAppInitializer, makeEnvironmentProviders } from '@angular/core';
|
|
3
|
+
import { DbxFirebaseLoginComponent, DbxFirebaseAuthService, AbstractDbxFirebaseDocumentStore, firebaseDocumentStoreCreateFunction, firebaseDocumentStoreUpdateFunction, firebaseDocumentStoreDeleteFunction, AbstractDbxFirebaseCollectionStore, DbxFirebaseCollectionStoreDirective, provideDbxFirebaseCollectionStoreDirective, DbxFirebaseDocumentStoreDirective, provideDbxFirebaseDocumentStoreDirective } from '@dereekb/dbx-firebase';
|
|
4
|
+
import * as i1$1 from '@dereekb/dbx-web';
|
|
5
|
+
import { DbxBasicLoadingComponent, DbxErrorComponent, DbxButtonComponent, DbxAvatarComponent, DbxLoadingComponent, DbxButtonSpacerDirective, AbstractDbxSelectionListWrapperDirective, DbxListWrapperComponentImportsModule, provideDbxListViewWrapper, DEFAULT_LIST_WRAPPER_COMPONENT_CONFIGURATION_TEMPLATE, AbstractDbxSelectionListViewDirective, DbxSelectionValueListViewComponentImportsModule, provideDbxListView, DEFAULT_DBX_SELECTION_VALUE_LIST_COMPONENT_CONFIGURATION_TEMPLATE, AbstractDbxValueListViewItemComponent, DbxActionSnackbarErrorDirective, DbxContentPitDirective, DbxDetailBlockComponent, DbxClickToCopyTextComponent, DbxActionConfirmDirective } from '@dereekb/dbx-web';
|
|
6
|
+
import { readableError, SPACE_STRING_SPLIT_JOIN, separateValues } from '@dereekb/util';
|
|
7
|
+
import { DbxInjectionComponent, DBX_INJECTION_COMPONENT_DATA, DbxRouterService, dbxRouteParamReaderInstance, completeOnDestroy, DbxActionDirective, DbxActionEnforceModifiedDirective, DbxActionHandlerDirective, DbxActionButtonDirective, DbxAppAuthRouterService } from '@dereekb/dbx-core';
|
|
8
|
+
import { toSignal } from '@angular/core/rxjs-interop';
|
|
9
|
+
import { HttpClient } from '@angular/common/http';
|
|
10
|
+
import { first, switchMap, of, map, BehaviorSubject, tap } from 'rxjs';
|
|
11
|
+
import * as i1 from '@dereekb/dbx-form';
|
|
12
|
+
import { valueSelectionField, textField, searchableStringChipField, isWebsiteUrlValidator, pickableItemChipField, pickableValueFieldValuesConfigForStaticLabeledValues, AbstractConfigAsyncFormlyFormDirective, DbxFormlyFormComponentImportsModule, dbxFormlyFormComponentProviders, DBX_FORMLY_FORM_COMPONENT_TEMPLATE, DbxActionFormDirective, DbxFormSourceDirective, DbxFormValueChangeDirective } from '@dereekb/dbx-form';
|
|
13
|
+
import { ALL_OIDC_TOKEN_ENDPOINT_AUTH_METHOD_OPTIONS, PRIVATE_KEY_JWT_TOKEN_ENDPOINT_AUTH_METHOD, OIDC_ENTRY_CLIENT_TYPE, OidcModelFunctions, OidcModelFirestoreCollections } from '@dereekb/firebase';
|
|
14
|
+
import { CommonModule } from '@angular/common';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Presentational component for the OIDC OAuth login interaction.
|
|
18
|
+
*
|
|
19
|
+
* Renders the login UI based on the current state case. Supports ng-content
|
|
20
|
+
* projection to allow apps to provide a custom login view for the `'no_user'` state,
|
|
21
|
+
* falling back to the default `<dbx-firebase-login>` component.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```html
|
|
25
|
+
* <dbx-firebase-oauth-login-view [loginStateCase]="'no_user'">
|
|
26
|
+
* <my-custom-login />
|
|
27
|
+
* </dbx-firebase-oauth-login-view>
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
class DbxFirebaseOAuthLoginViewComponent {
|
|
31
|
+
loginStateCase = input.required(...(ngDevMode ? [{ debugName: "loginStateCase" }] : []));
|
|
32
|
+
error = input(...(ngDevMode ? [undefined, { debugName: "error" }] : []));
|
|
33
|
+
resolvedError = computed(() => {
|
|
34
|
+
const error = this.error();
|
|
35
|
+
return typeof error === 'string' ? readableError('ERROR', error) : error;
|
|
36
|
+
}, ...(ngDevMode ? [{ debugName: "resolvedError" }] : []));
|
|
37
|
+
retryClick = output();
|
|
38
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: DbxFirebaseOAuthLoginViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
39
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.0", type: DbxFirebaseOAuthLoginViewComponent, isStandalone: true, selector: "dbx-firebase-oauth-login-view", inputs: { loginStateCase: { classPropertyName: "loginStateCase", publicName: "loginStateCase", isSignal: true, isRequired: true, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { retryClick: "retryClick" }, host: { classAttribute: "d-block dbx-firebase-oauth-login-view" }, ngImport: i0, template: `
|
|
40
|
+
<div class="dbx-firebase-oauth-login-view">
|
|
41
|
+
@switch (loginStateCase()) {
|
|
42
|
+
@case ('no_user') {
|
|
43
|
+
<ng-content></ng-content>
|
|
44
|
+
}
|
|
45
|
+
@case ('user') {
|
|
46
|
+
<dbx-basic-loading [loading]="true" text="Signing in..."></dbx-basic-loading>
|
|
47
|
+
}
|
|
48
|
+
@case ('submitting') {
|
|
49
|
+
<dbx-basic-loading [loading]="true" text="Submitting authentication..."></dbx-basic-loading>
|
|
50
|
+
}
|
|
51
|
+
@case ('error') {
|
|
52
|
+
<dbx-button text="Retry" [raised]="true" (buttonClick)="retryClick.emit()"></dbx-button>
|
|
53
|
+
<dbx-error [error]="resolvedError()"></dbx-error>
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
</div>
|
|
57
|
+
`, isInline: true, dependencies: [{ kind: "component", type: DbxBasicLoadingComponent, selector: "dbx-basic-loading", inputs: ["diameter", "mode", "color", "text", "linear", "show", "loading", "error"] }, { kind: "component", type: DbxErrorComponent, selector: "dbx-error", inputs: ["error", "iconOnly"], outputs: ["popoverOpened"] }, { kind: "component", type: DbxButtonComponent, selector: "dbx-button", inputs: ["bar", "type", "buttonStyle", "color", "spinnerColor", "customButtonColor", "customTextColor", "customSpinnerColor", "basic", "tonal", "raised", "stroked", "flat", "iconOnly", "fab", "mode"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
58
|
+
}
|
|
59
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: DbxFirebaseOAuthLoginViewComponent, decorators: [{
|
|
60
|
+
type: Component,
|
|
61
|
+
args: [{
|
|
62
|
+
selector: 'dbx-firebase-oauth-login-view',
|
|
63
|
+
standalone: true,
|
|
64
|
+
imports: [DbxFirebaseLoginComponent, DbxBasicLoadingComponent, DbxErrorComponent, DbxButtonComponent],
|
|
65
|
+
template: `
|
|
66
|
+
<div class="dbx-firebase-oauth-login-view">
|
|
67
|
+
@switch (loginStateCase()) {
|
|
68
|
+
@case ('no_user') {
|
|
69
|
+
<ng-content></ng-content>
|
|
70
|
+
}
|
|
71
|
+
@case ('user') {
|
|
72
|
+
<dbx-basic-loading [loading]="true" text="Signing in..."></dbx-basic-loading>
|
|
73
|
+
}
|
|
74
|
+
@case ('submitting') {
|
|
75
|
+
<dbx-basic-loading [loading]="true" text="Submitting authentication..."></dbx-basic-loading>
|
|
76
|
+
}
|
|
77
|
+
@case ('error') {
|
|
78
|
+
<dbx-button text="Retry" [raised]="true" (buttonClick)="retryClick.emit()"></dbx-button>
|
|
79
|
+
<dbx-error [error]="resolvedError()"></dbx-error>
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
</div>
|
|
83
|
+
`,
|
|
84
|
+
host: {
|
|
85
|
+
class: 'd-block dbx-firebase-oauth-login-view'
|
|
86
|
+
},
|
|
87
|
+
changeDetection: ChangeDetectionStrategy.OnPush
|
|
88
|
+
}]
|
|
89
|
+
}], propDecorators: { loginStateCase: [{ type: i0.Input, args: [{ isSignal: true, alias: "loginStateCase", required: true }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], retryClick: [{ type: i0.Output, args: ["retryClick"] }] } });
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Presentational component for the OIDC OAuth consent screen.
|
|
93
|
+
*
|
|
94
|
+
* Accepts an `OAuthInteractionLoginDetails` input that contains all client and scope
|
|
95
|
+
* information. Renders the client name, logo, client URL, scopes (via `<dbx-injection>`),
|
|
96
|
+
* error/loading states, and approve/deny action buttons.
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```html
|
|
100
|
+
* <dbx-firebase-oauth-consent-view
|
|
101
|
+
* [details]="loginDetails"
|
|
102
|
+
* [loading]="false"
|
|
103
|
+
* [scopeInjectionConfig]="scopeConfig"
|
|
104
|
+
* (approveClick)="onApprove()"
|
|
105
|
+
* (denyClick)="onDeny()">
|
|
106
|
+
* </dbx-firebase-oauth-consent-view>
|
|
107
|
+
* ```
|
|
108
|
+
*/
|
|
109
|
+
class DbxFirebaseOAuthConsentViewComponent {
|
|
110
|
+
details = input(...(ngDevMode ? [undefined, { debugName: "details" }] : []));
|
|
111
|
+
loading = input(false, ...(ngDevMode ? [{ debugName: "loading" }] : []));
|
|
112
|
+
error = input(...(ngDevMode ? [undefined, { debugName: "error" }] : []));
|
|
113
|
+
scopeInjectionConfig = input.required(...(ngDevMode ? [{ debugName: "scopeInjectionConfig" }] : []));
|
|
114
|
+
clientName = computed(() => this.details()?.client_name ?? '', ...(ngDevMode ? [{ debugName: "clientName" }] : []));
|
|
115
|
+
clientUri = computed(() => this.details()?.client_uri, ...(ngDevMode ? [{ debugName: "clientUri" }] : []));
|
|
116
|
+
logoUri = computed(() => this.details()?.logo_uri, ...(ngDevMode ? [{ debugName: "logoUri" }] : []));
|
|
117
|
+
scopes = computed(() => SPACE_STRING_SPLIT_JOIN.splitStrings(this.details()?.scopes ?? ''), ...(ngDevMode ? [{ debugName: "scopes" }] : []));
|
|
118
|
+
resolvedError = computed(() => {
|
|
119
|
+
const error = this.error();
|
|
120
|
+
return typeof error === 'string' ? readableError('ERROR', error) : error;
|
|
121
|
+
}, ...(ngDevMode ? [{ debugName: "resolvedError" }] : []));
|
|
122
|
+
approveClick = output();
|
|
123
|
+
denyClick = output();
|
|
124
|
+
resolvedScopeInjectionConfig = computed(() => {
|
|
125
|
+
const data = {
|
|
126
|
+
details: this.details(),
|
|
127
|
+
scopes: this.scopes(),
|
|
128
|
+
clientName: this.clientName()
|
|
129
|
+
};
|
|
130
|
+
return { ...this.scopeInjectionConfig(), data };
|
|
131
|
+
}, ...(ngDevMode ? [{ debugName: "resolvedScopeInjectionConfig" }] : []));
|
|
132
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: DbxFirebaseOAuthConsentViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
133
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.0", type: DbxFirebaseOAuthConsentViewComponent, isStandalone: true, selector: "dbx-firebase-oauth-consent-view", inputs: { details: { classPropertyName: "details", publicName: "details", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null }, scopeInjectionConfig: { classPropertyName: "scopeInjectionConfig", publicName: "scopeInjectionConfig", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { approveClick: "approveClick", denyClick: "denyClick" }, host: { classAttribute: "d-block dbx-firebase-oauth-consent-view" }, ngImport: i0, template: `
|
|
134
|
+
<div class="dbx-firebase-oauth-consent-view">
|
|
135
|
+
@if (loading()) {
|
|
136
|
+
<dbx-loading [loading]="true" text="Processing..."></dbx-loading>
|
|
137
|
+
} @else {
|
|
138
|
+
<div class="dbx-firebase-oauth-consent-header">
|
|
139
|
+
@if (clientName()) {
|
|
140
|
+
<h2>You're signing in to {{ clientName() }}</h2>
|
|
141
|
+
}
|
|
142
|
+
<div class="dbx-firebase-oauth-consent-header-info dbx-flex">
|
|
143
|
+
<dbx-avatar [avatarUrl]="logoUri()" [avatarStyle]="'square'" avatarIcon="apps"></dbx-avatar>
|
|
144
|
+
<span>
|
|
145
|
+
@if (clientUri()) {
|
|
146
|
+
<a class="dbx-firebase-oauth-consent-client-uri" [href]="clientUri()" target="_blank" rel="noopener noreferrer">{{ clientUri() }}</a>
|
|
147
|
+
}
|
|
148
|
+
</span>
|
|
149
|
+
</div>
|
|
150
|
+
</div>
|
|
151
|
+
<dbx-injection [config]="resolvedScopeInjectionConfig()"></dbx-injection>
|
|
152
|
+
<div class="dbx-pt3 dbx-pb3 dbx-firebase-oauth-consent-actions">
|
|
153
|
+
<dbx-button text="Approve" [raised]="true" color="primary" (buttonClick)="approveClick.emit()"></dbx-button>
|
|
154
|
+
<dbx-button-spacer></dbx-button-spacer>
|
|
155
|
+
<dbx-button text="Deny" [flat]="true" color="warn" (buttonClick)="denyClick.emit()"></dbx-button>
|
|
156
|
+
</div>
|
|
157
|
+
@if (resolvedError()) {
|
|
158
|
+
<dbx-error [error]="resolvedError()"></dbx-error>
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
</div>
|
|
162
|
+
`, isInline: true, styles: [".dbx-firebase-oauth-consent-view .dbx-firebase-oauth-consent-header-info{align-items:center;gap:12px}\n"], dependencies: [{ kind: "component", type: DbxInjectionComponent, selector: "dbx-injection, [dbxInjection], [dbx-injection]", inputs: ["config", "template"] }, { kind: "component", type: DbxAvatarComponent, selector: "dbx-avatar", inputs: ["context", "avatarSelector", "avatarUid", "avatarUrl", "avatarKey", "avatarIcon", "avatarStyle", "avatarSize", "avatarHideOnError"] }, { kind: "component", type: DbxLoadingComponent, selector: "dbx-loading", inputs: ["padding", "show", "text", "mode", "color", "diameter", "linear", "loading", "error", "context"] }, { kind: "component", type: DbxErrorComponent, selector: "dbx-error", inputs: ["error", "iconOnly"], outputs: ["popoverOpened"] }, { kind: "component", type: DbxButtonComponent, selector: "dbx-button", inputs: ["bar", "type", "buttonStyle", "color", "spinnerColor", "customButtonColor", "customTextColor", "customSpinnerColor", "basic", "tonal", "raised", "stroked", "flat", "iconOnly", "fab", "mode"] }, { kind: "directive", type: DbxButtonSpacerDirective, selector: "dbx-button-spacer,[dbxButtonSpacer]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
163
|
+
}
|
|
164
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: DbxFirebaseOAuthConsentViewComponent, decorators: [{
|
|
165
|
+
type: Component,
|
|
166
|
+
args: [{ selector: 'dbx-firebase-oauth-consent-view', standalone: true, imports: [DbxInjectionComponent, DbxAvatarComponent, DbxLoadingComponent, DbxErrorComponent, DbxButtonComponent, DbxButtonSpacerDirective], template: `
|
|
167
|
+
<div class="dbx-firebase-oauth-consent-view">
|
|
168
|
+
@if (loading()) {
|
|
169
|
+
<dbx-loading [loading]="true" text="Processing..."></dbx-loading>
|
|
170
|
+
} @else {
|
|
171
|
+
<div class="dbx-firebase-oauth-consent-header">
|
|
172
|
+
@if (clientName()) {
|
|
173
|
+
<h2>You're signing in to {{ clientName() }}</h2>
|
|
174
|
+
}
|
|
175
|
+
<div class="dbx-firebase-oauth-consent-header-info dbx-flex">
|
|
176
|
+
<dbx-avatar [avatarUrl]="logoUri()" [avatarStyle]="'square'" avatarIcon="apps"></dbx-avatar>
|
|
177
|
+
<span>
|
|
178
|
+
@if (clientUri()) {
|
|
179
|
+
<a class="dbx-firebase-oauth-consent-client-uri" [href]="clientUri()" target="_blank" rel="noopener noreferrer">{{ clientUri() }}</a>
|
|
180
|
+
}
|
|
181
|
+
</span>
|
|
182
|
+
</div>
|
|
183
|
+
</div>
|
|
184
|
+
<dbx-injection [config]="resolvedScopeInjectionConfig()"></dbx-injection>
|
|
185
|
+
<div class="dbx-pt3 dbx-pb3 dbx-firebase-oauth-consent-actions">
|
|
186
|
+
<dbx-button text="Approve" [raised]="true" color="primary" (buttonClick)="approveClick.emit()"></dbx-button>
|
|
187
|
+
<dbx-button-spacer></dbx-button-spacer>
|
|
188
|
+
<dbx-button text="Deny" [flat]="true" color="warn" (buttonClick)="denyClick.emit()"></dbx-button>
|
|
189
|
+
</div>
|
|
190
|
+
@if (resolvedError()) {
|
|
191
|
+
<dbx-error [error]="resolvedError()"></dbx-error>
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
</div>
|
|
195
|
+
`, host: {
|
|
196
|
+
class: 'd-block dbx-firebase-oauth-consent-view'
|
|
197
|
+
}, changeDetection: ChangeDetectionStrategy.OnPush, styles: [".dbx-firebase-oauth-consent-view .dbx-firebase-oauth-consent-header-info{align-items:center;gap:12px}\n"] }]
|
|
198
|
+
}], propDecorators: { details: [{ type: i0.Input, args: [{ isSignal: true, alias: "details", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], scopeInjectionConfig: [{ type: i0.Input, args: [{ isSignal: true, alias: "scopeInjectionConfig", required: true }] }], approveClick: [{ type: i0.Output, args: ["approveClick"] }], denyClick: [{ type: i0.Output, args: ["denyClick"] }] } });
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Abstract base class for consent scope view components.
|
|
202
|
+
*
|
|
203
|
+
* Provides typed access to the `DbxFirebaseOAuthConsentScopesViewData` injected
|
|
204
|
+
* via `DBX_INJECTION_COMPONENT_DATA`. Subclasses only need to define a template.
|
|
205
|
+
*
|
|
206
|
+
* @example
|
|
207
|
+
* ```typescript
|
|
208
|
+
* @Component({ template: `...` })
|
|
209
|
+
* export class MyCustomScopesViewComponent extends AbstractDbxFirebaseOAuthConsentScopeViewComponent {}
|
|
210
|
+
* ```
|
|
211
|
+
*/
|
|
212
|
+
class AbstractDbxFirebaseOAuthConsentScopeViewComponent {
|
|
213
|
+
data = inject(DBX_INJECTION_COMPONENT_DATA);
|
|
214
|
+
details = computed(() => this.data?.details, ...(ngDevMode ? [{ debugName: "details" }] : []));
|
|
215
|
+
scopes = computed(() => this.data?.scopes ?? [], ...(ngDevMode ? [{ debugName: "scopes" }] : []));
|
|
216
|
+
clientName = computed(() => this.data?.clientName ?? '', ...(ngDevMode ? [{ debugName: "clientName" }] : []));
|
|
217
|
+
clientUri = computed(() => this.data?.details?.client_uri, ...(ngDevMode ? [{ debugName: "clientUri" }] : []));
|
|
218
|
+
logoUri = computed(() => this.data?.details?.logo_uri, ...(ngDevMode ? [{ debugName: "logoUri" }] : []));
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Standalone presentational component that renders a list of OAuth consent scopes.
|
|
223
|
+
*
|
|
224
|
+
* @example
|
|
225
|
+
* ```html
|
|
226
|
+
* <dbx-firebase-oauth-consent-scope-list [scopes]="mappedScopes"></dbx-firebase-oauth-consent-scope-list>
|
|
227
|
+
* ```
|
|
228
|
+
*/
|
|
229
|
+
class DbxFirebaseOAuthConsentScopeListComponent {
|
|
230
|
+
scopes = input([], ...(ngDevMode ? [{ debugName: "scopes" }] : []));
|
|
231
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: DbxFirebaseOAuthConsentScopeListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
232
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.0", type: DbxFirebaseOAuthConsentScopeListComponent, isStandalone: true, selector: "dbx-firebase-oauth-consent-scope-list", inputs: { scopes: { classPropertyName: "scopes", publicName: "scopes", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
|
|
233
|
+
@for (scope of scopes(); track scope.name) {
|
|
234
|
+
<div class="dbx-firebase-oauth-consent-scope-list-item dbx-mb2">
|
|
235
|
+
<span class="dbx-firebase-oauth-consent-scope-name dbx-pb2">{{ scope.name }}</span>
|
|
236
|
+
@if (scope.description) {
|
|
237
|
+
<span class="dbx-firebase-oauth-consent-scope-description">{{ scope.description }}</span>
|
|
238
|
+
}
|
|
239
|
+
</div>
|
|
240
|
+
}
|
|
241
|
+
`, isInline: true, styles: [".dbx-firebase-oauth-consent-scope-list-item{display:flex;flex-direction:column;padding:8px 12px;border-left:3px solid var(--dbx-primary-color);background:color-mix(in srgb,var(--dbx-color-current) 10%,transparent)}.dbx-firebase-oauth-consent-scope-list-item .dbx-firebase-oauth-consent-scope-name{font-weight:500}.dbx-firebase-oauth-consent-scope-list-item .dbx-firebase-oauth-consent-scope-description{font-size:.85em;opacity:.7}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
242
|
+
}
|
|
243
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: DbxFirebaseOAuthConsentScopeListComponent, decorators: [{
|
|
244
|
+
type: Component,
|
|
245
|
+
args: [{ selector: 'dbx-firebase-oauth-consent-scope-list', standalone: true, template: `
|
|
246
|
+
@for (scope of scopes(); track scope.name) {
|
|
247
|
+
<div class="dbx-firebase-oauth-consent-scope-list-item dbx-mb2">
|
|
248
|
+
<span class="dbx-firebase-oauth-consent-scope-name dbx-pb2">{{ scope.name }}</span>
|
|
249
|
+
@if (scope.description) {
|
|
250
|
+
<span class="dbx-firebase-oauth-consent-scope-description">{{ scope.description }}</span>
|
|
251
|
+
}
|
|
252
|
+
</div>
|
|
253
|
+
}
|
|
254
|
+
`, changeDetection: ChangeDetectionStrategy.OnPush, styles: [".dbx-firebase-oauth-consent-scope-list-item{display:flex;flex-direction:column;padding:8px 12px;border-left:3px solid var(--dbx-primary-color);background:color-mix(in srgb,var(--dbx-color-current) 10%,transparent)}.dbx-firebase-oauth-consent-scope-list-item .dbx-firebase-oauth-consent-scope-name{font-weight:500}.dbx-firebase-oauth-consent-scope-list-item .dbx-firebase-oauth-consent-scope-description{font-size:.85em;opacity:.7}\n"] }]
|
|
255
|
+
}], propDecorators: { scopes: [{ type: i0.Input, args: [{ isSignal: true, alias: "scopes", required: false }] }] } });
|
|
256
|
+
|
|
257
|
+
const DEFAULT_OIDC_AUTHORIZATION_ENDPOINT_PATH = '/oidc/auth';
|
|
258
|
+
const DEFAULT_OIDC_INTERACTION_ENDPOINT_PATH = '/interaction';
|
|
259
|
+
const DEFAULT_OIDC_INTERACTION_UID_PARAM_KEY = 'uid';
|
|
260
|
+
const DEFAULT_OIDC_CLIENT_ID_PARAM_KEY = 'client_id';
|
|
261
|
+
const DEFAULT_OIDC_CLIENT_NAME_PARAM_KEY = 'client_name';
|
|
262
|
+
const DEFAULT_OIDC_CLIENT_URI_PARAM_KEY = 'client_uri';
|
|
263
|
+
const DEFAULT_OIDC_LOGO_URI_PARAM_KEY = 'logo_uri';
|
|
264
|
+
const DEFAULT_OIDC_SCOPES_PARAM_KEY = 'scopes';
|
|
265
|
+
const DEFAULT_OIDC_TOKEN_ENDPOINT_AUTH_METHODS = ['client_secret_post', 'client_secret_basic'];
|
|
266
|
+
/**
|
|
267
|
+
* Abstract configuration class used as a DI token for app-level OIDC settings.
|
|
268
|
+
*
|
|
269
|
+
* Apps provide a concrete implementation via `provideDbxFirebaseOidc()`.
|
|
270
|
+
*/
|
|
271
|
+
class DbxFirebaseOidcConfig {
|
|
272
|
+
/** Path to the authorization endpoint. Defaults to '/oidc/auth'. */
|
|
273
|
+
oidcAuthorizationEndpointApiPath;
|
|
274
|
+
/** Base path for interaction endpoints. Defaults to '/interaction'. */
|
|
275
|
+
oidcInteractionEndpointApiPath;
|
|
276
|
+
/**
|
|
277
|
+
* Supported token endpoint authentication methods.
|
|
278
|
+
*
|
|
279
|
+
* Overrides the default methods (`client_secret_post`, `client_secret_basic`).
|
|
280
|
+
* Used by forms and UI components that need to know which auth methods are available.
|
|
281
|
+
*/
|
|
282
|
+
tokenEndpointAuthMethods;
|
|
283
|
+
/**
|
|
284
|
+
* Frontend route ref for the OAuth interaction pages (login/consent).
|
|
285
|
+
*
|
|
286
|
+
* When provided, this route is registered with {@link DbxAppAuthRouterService} as an
|
|
287
|
+
* ignored route, preventing auth effects from redirecting away during the OIDC flow.
|
|
288
|
+
*
|
|
289
|
+
* Uses hierarchical matching — a parent route ref (e.g., `'app.oauth'`) will cover
|
|
290
|
+
* all child routes (e.g., `'app.oauth.login'`, `'app.oauth.consent'`).
|
|
291
|
+
*/
|
|
292
|
+
oauthInteractionRoute;
|
|
293
|
+
/**
|
|
294
|
+
* Component class for rendering the consent scope list.
|
|
295
|
+
*
|
|
296
|
+
* When not provided, uses `DbxFirebaseOAuthConsentScopeDefaultViewComponent` which
|
|
297
|
+
* maps scope names to descriptions from `availableScopes`.
|
|
298
|
+
*/
|
|
299
|
+
consentScopeListViewClass;
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Service that exposes the app-level OIDC configuration.
|
|
303
|
+
*
|
|
304
|
+
* Inject this service in components to access centralized OIDC settings
|
|
305
|
+
* (scopes, endpoint paths, param keys, etc.) without requiring explicit inputs.
|
|
306
|
+
*/
|
|
307
|
+
class DbxFirebaseOidcConfigService {
|
|
308
|
+
config = inject(DbxFirebaseOidcConfig);
|
|
309
|
+
get availableScopes() {
|
|
310
|
+
return this.config.availableScopes;
|
|
311
|
+
}
|
|
312
|
+
get oidcAuthorizationEndpointApiPath() {
|
|
313
|
+
return this.config.oidcAuthorizationEndpointApiPath ?? DEFAULT_OIDC_AUTHORIZATION_ENDPOINT_PATH;
|
|
314
|
+
}
|
|
315
|
+
get oidcInteractionEndpointApiPath() {
|
|
316
|
+
return this.config.oidcInteractionEndpointApiPath ?? DEFAULT_OIDC_INTERACTION_ENDPOINT_PATH;
|
|
317
|
+
}
|
|
318
|
+
get tokenEndpointAuthMethods() {
|
|
319
|
+
return this.config.tokenEndpointAuthMethods ?? DEFAULT_OIDC_TOKEN_ENDPOINT_AUTH_METHODS;
|
|
320
|
+
}
|
|
321
|
+
get oauthInteractionRoute() {
|
|
322
|
+
return this.config.oauthInteractionRoute;
|
|
323
|
+
}
|
|
324
|
+
get consentScopeListViewClass() {
|
|
325
|
+
return this.config.consentScopeListViewClass;
|
|
326
|
+
}
|
|
327
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: DbxFirebaseOidcConfigService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
328
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: DbxFirebaseOidcConfigService });
|
|
329
|
+
}
|
|
330
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: DbxFirebaseOidcConfigService, decorators: [{
|
|
331
|
+
type: Injectable
|
|
332
|
+
}] });
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Default consent scope view component that maps scope names to descriptions
|
|
336
|
+
* using the `OidcScopeDetails` from the app-level OIDC configuration.
|
|
337
|
+
*
|
|
338
|
+
* Apps can override this by providing a custom `consentScopeListViewClass`
|
|
339
|
+
* in `DbxFirebaseOidcConfig` or `DbxOAuthConsentComponentConfig`.
|
|
340
|
+
*/
|
|
341
|
+
class DbxFirebaseOAuthConsentScopeDefaultViewComponent extends AbstractDbxFirebaseOAuthConsentScopeViewComponent {
|
|
342
|
+
oidcConfigService = inject(DbxFirebaseOidcConfigService);
|
|
343
|
+
mappedScopes = computed(() => {
|
|
344
|
+
const availableScopes = this.oidcConfigService.availableScopes;
|
|
345
|
+
const availableScopeValues = new Set(availableScopes.map((s) => s.value));
|
|
346
|
+
const { included: knownScopes, excluded: unknownScopes } = separateValues(this.scopes(), (name) => availableScopeValues.has(name));
|
|
347
|
+
return [
|
|
348
|
+
...knownScopes.map((name) => {
|
|
349
|
+
const details = availableScopes.find((s) => s.value === name);
|
|
350
|
+
return { name, description: details.description ?? '' };
|
|
351
|
+
}),
|
|
352
|
+
...unknownScopes.map((name) => ({ name, description: 'unknown' }))
|
|
353
|
+
];
|
|
354
|
+
}, ...(ngDevMode ? [{ debugName: "mappedScopes" }] : []));
|
|
355
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: DbxFirebaseOAuthConsentScopeDefaultViewComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
356
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.0", type: DbxFirebaseOAuthConsentScopeDefaultViewComponent, isStandalone: true, selector: "dbx-firebase-oauth-consent-scope-default-view", usesInheritance: true, ngImport: i0, template: `
|
|
357
|
+
<p>
|
|
358
|
+
<strong>{{ clientName() }}</strong>
|
|
359
|
+
is requesting these permissions:
|
|
360
|
+
</p>
|
|
361
|
+
<dbx-firebase-oauth-consent-scope-list [scopes]="mappedScopes()"></dbx-firebase-oauth-consent-scope-list>
|
|
362
|
+
`, isInline: true, dependencies: [{ kind: "component", type: DbxFirebaseOAuthConsentScopeListComponent, selector: "dbx-firebase-oauth-consent-scope-list", inputs: ["scopes"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
363
|
+
}
|
|
364
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: DbxFirebaseOAuthConsentScopeDefaultViewComponent, decorators: [{
|
|
365
|
+
type: Component,
|
|
366
|
+
args: [{
|
|
367
|
+
selector: 'dbx-firebase-oauth-consent-scope-default-view',
|
|
368
|
+
standalone: true,
|
|
369
|
+
imports: [DbxFirebaseOAuthConsentScopeListComponent],
|
|
370
|
+
template: `
|
|
371
|
+
<p>
|
|
372
|
+
<strong>{{ clientName() }}</strong>
|
|
373
|
+
is requesting these permissions:
|
|
374
|
+
</p>
|
|
375
|
+
<dbx-firebase-oauth-consent-scope-list [scopes]="mappedScopes()"></dbx-firebase-oauth-consent-scope-list>
|
|
376
|
+
`,
|
|
377
|
+
changeDetection: ChangeDetectionStrategy.OnPush
|
|
378
|
+
}]
|
|
379
|
+
}] });
|
|
380
|
+
|
|
381
|
+
// MARK: Service
|
|
382
|
+
/**
|
|
383
|
+
* Service for communicating with the backend OIDC interaction endpoints.
|
|
384
|
+
*
|
|
385
|
+
* Automatically includes the current user's Firebase Auth ID token
|
|
386
|
+
* with each request for server-side verification.
|
|
387
|
+
*
|
|
388
|
+
* After successful login/consent submission, the server returns a redirect URL.
|
|
389
|
+
* The component is responsible for navigating to it (e.g., via `window.location.href`).
|
|
390
|
+
*/
|
|
391
|
+
class DbxFirebaseOidcInteractionService {
|
|
392
|
+
http = inject(HttpClient);
|
|
393
|
+
_authService = inject(DbxFirebaseAuthService);
|
|
394
|
+
_oidcConfig = inject(DbxFirebaseOidcConfigService);
|
|
395
|
+
/**
|
|
396
|
+
* Base URL for the interaction API, derived from the OIDC config service.
|
|
397
|
+
*/
|
|
398
|
+
get baseUrl() {
|
|
399
|
+
return this._oidcConfig.oidcInteractionEndpointApiPath;
|
|
400
|
+
}
|
|
401
|
+
/**
|
|
402
|
+
* Submit login to complete the login interaction.
|
|
403
|
+
*
|
|
404
|
+
* Automatically attaches the current user's Firebase ID token.
|
|
405
|
+
*
|
|
406
|
+
* @returns Observable that emits the redirect URL from the server response.
|
|
407
|
+
*/
|
|
408
|
+
submitLogin(uid) {
|
|
409
|
+
return this._authService.idTokenString$.pipe(first(), switchMap((idToken) => this.http.post(`${this.baseUrl}/${uid}/login`, { idToken })));
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* Submit consent decision to complete the consent interaction.
|
|
413
|
+
*
|
|
414
|
+
* Automatically attaches the current user's Firebase ID token.
|
|
415
|
+
*
|
|
416
|
+
* @returns Observable that emits the redirect URL from the server response.
|
|
417
|
+
*/
|
|
418
|
+
submitConsent(uid, approved) {
|
|
419
|
+
return this._authService.idTokenString$.pipe(first(), switchMap((idToken) => this.http.post(`${this.baseUrl}/${uid}/consent`, { idToken, approved })));
|
|
420
|
+
}
|
|
421
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: DbxFirebaseOidcInteractionService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
422
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: DbxFirebaseOidcInteractionService, providedIn: 'root' });
|
|
423
|
+
}
|
|
424
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: DbxFirebaseOidcInteractionService, decorators: [{
|
|
425
|
+
type: Injectable,
|
|
426
|
+
args: [{ providedIn: 'root' }]
|
|
427
|
+
}] });
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* Container component for the OIDC OAuth login interaction flow.
|
|
431
|
+
*
|
|
432
|
+
* Manages all state: route param reading, Firebase Auth observation, ID token
|
|
433
|
+
* submission, and error handling. Delegates visual rendering to
|
|
434
|
+
* `DbxFirebaseOAuthLoginViewComponent`.
|
|
435
|
+
*
|
|
436
|
+
* Supports ng-content projection — any content provided is passed through to
|
|
437
|
+
* the view component, replacing the default `<dbx-firebase-login>` for the
|
|
438
|
+
* `'no_user'` state.
|
|
439
|
+
*
|
|
440
|
+
* Usage: Route to this component with `?uid=<interaction-uid>` query param.
|
|
441
|
+
*/
|
|
442
|
+
class DbxFirebaseOAuthLoginComponent {
|
|
443
|
+
dbxRouterService = inject(DbxRouterService);
|
|
444
|
+
dbxFirebaseAuthService = inject(DbxFirebaseAuthService);
|
|
445
|
+
interactionService = inject(DbxFirebaseOidcInteractionService);
|
|
446
|
+
uidParamReader = dbxRouteParamReaderInstance(this.dbxRouterService, DEFAULT_OIDC_INTERACTION_UID_PARAM_KEY);
|
|
447
|
+
interactionUid = toSignal(this.uidParamReader.value$);
|
|
448
|
+
isLoggedIn = toSignal(this.dbxFirebaseAuthService.isLoggedIn$, { initialValue: false });
|
|
449
|
+
submitting = signal(false, ...(ngDevMode ? [{ debugName: "submitting" }] : []));
|
|
450
|
+
errorMessage = signal(null, ...(ngDevMode ? [{ debugName: "errorMessage" }] : []));
|
|
451
|
+
loginStateCase = computed(() => {
|
|
452
|
+
if (this.submitting()) {
|
|
453
|
+
return 'submitting';
|
|
454
|
+
}
|
|
455
|
+
if (this.errorMessage()) {
|
|
456
|
+
return 'error';
|
|
457
|
+
}
|
|
458
|
+
if (!this.isLoggedIn()) {
|
|
459
|
+
return 'no_user';
|
|
460
|
+
}
|
|
461
|
+
return 'user';
|
|
462
|
+
}, ...(ngDevMode ? [{ debugName: "loginStateCase" }] : []));
|
|
463
|
+
constructor() {
|
|
464
|
+
// Auto-submit when user is logged in
|
|
465
|
+
effect(() => {
|
|
466
|
+
if (this.loginStateCase() === 'user') {
|
|
467
|
+
this._submitIdToken();
|
|
468
|
+
}
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
ngOnDestroy() {
|
|
472
|
+
this.uidParamReader.destroy();
|
|
473
|
+
}
|
|
474
|
+
retry() {
|
|
475
|
+
this.errorMessage.set(null);
|
|
476
|
+
this._submitIdToken();
|
|
477
|
+
}
|
|
478
|
+
_submitIdToken() {
|
|
479
|
+
const uid = this.interactionUid();
|
|
480
|
+
if (!uid) {
|
|
481
|
+
this.errorMessage.set('Missing interaction UID from route parameters.');
|
|
482
|
+
return;
|
|
483
|
+
}
|
|
484
|
+
this.submitting.set(true);
|
|
485
|
+
this.errorMessage.set(null);
|
|
486
|
+
this.interactionService.submitLogin(uid).subscribe({
|
|
487
|
+
next: (response) => {
|
|
488
|
+
this.submitting.set(false);
|
|
489
|
+
if (response.redirectTo) {
|
|
490
|
+
window.location.href = response.redirectTo;
|
|
491
|
+
}
|
|
492
|
+
},
|
|
493
|
+
error: () => {
|
|
494
|
+
this.submitting.set(false);
|
|
495
|
+
this.errorMessage.set('Failed to complete login. Please try again.');
|
|
496
|
+
}
|
|
497
|
+
});
|
|
498
|
+
}
|
|
499
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: DbxFirebaseOAuthLoginComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
500
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.0", type: DbxFirebaseOAuthLoginComponent, isStandalone: true, selector: "dbx-firebase-oauth-login", host: { classAttribute: "d-block dbx-firebase-oauth-login" }, ngImport: i0, template: `
|
|
501
|
+
<dbx-firebase-oauth-login-view [loginStateCase]="loginStateCase()" [error]="errorMessage()" (retryClick)="retry()">
|
|
502
|
+
<ng-content />
|
|
503
|
+
</dbx-firebase-oauth-login-view>
|
|
504
|
+
`, isInline: true, dependencies: [{ kind: "component", type: DbxFirebaseOAuthLoginViewComponent, selector: "dbx-firebase-oauth-login-view", inputs: ["loginStateCase", "error"], outputs: ["retryClick"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
505
|
+
}
|
|
506
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: DbxFirebaseOAuthLoginComponent, decorators: [{
|
|
507
|
+
type: Component,
|
|
508
|
+
args: [{
|
|
509
|
+
selector: 'dbx-firebase-oauth-login',
|
|
510
|
+
standalone: true,
|
|
511
|
+
imports: [DbxFirebaseOAuthLoginViewComponent],
|
|
512
|
+
template: `
|
|
513
|
+
<dbx-firebase-oauth-login-view [loginStateCase]="loginStateCase()" [error]="errorMessage()" (retryClick)="retry()">
|
|
514
|
+
<ng-content />
|
|
515
|
+
</dbx-firebase-oauth-login-view>
|
|
516
|
+
`,
|
|
517
|
+
host: {
|
|
518
|
+
class: 'd-block dbx-firebase-oauth-login'
|
|
519
|
+
},
|
|
520
|
+
changeDetection: ChangeDetectionStrategy.OnPush
|
|
521
|
+
}]
|
|
522
|
+
}], ctorParameters: () => [] });
|
|
523
|
+
|
|
524
|
+
/**
|
|
525
|
+
* Container component for the OIDC OAuth consent screen.
|
|
526
|
+
*
|
|
527
|
+
* Manages all state: route param reading, consent submission, and error handling.
|
|
528
|
+
* Delegates visual rendering to `DbxFirebaseOAuthConsentViewComponent`.
|
|
529
|
+
*
|
|
530
|
+
* Reads interaction UID and client details from route params (populated by
|
|
531
|
+
* the server redirect), then assembles them into `OAuthInteractionLoginDetails`.
|
|
532
|
+
*/
|
|
533
|
+
class DbxOAuthConsentComponent {
|
|
534
|
+
dbxRouterService = inject(DbxRouterService);
|
|
535
|
+
interactionService = inject(DbxFirebaseOidcInteractionService);
|
|
536
|
+
oidcConfigService = inject(DbxFirebaseOidcConfigService);
|
|
537
|
+
// Config input
|
|
538
|
+
config = input(...(ngDevMode ? [undefined, { debugName: "config" }] : []));
|
|
539
|
+
// Route param readers
|
|
540
|
+
interactionUidParamReader = dbxRouteParamReaderInstance(this.dbxRouterService, DEFAULT_OIDC_INTERACTION_UID_PARAM_KEY);
|
|
541
|
+
clientIdParamReader = dbxRouteParamReaderInstance(this.dbxRouterService, DEFAULT_OIDC_CLIENT_ID_PARAM_KEY);
|
|
542
|
+
clientNameParamReader = dbxRouteParamReaderInstance(this.dbxRouterService, DEFAULT_OIDC_CLIENT_NAME_PARAM_KEY);
|
|
543
|
+
clientUriParamReader = dbxRouteParamReaderInstance(this.dbxRouterService, DEFAULT_OIDC_CLIENT_URI_PARAM_KEY);
|
|
544
|
+
logoUriParamReader = dbxRouteParamReaderInstance(this.dbxRouterService, DEFAULT_OIDC_LOGO_URI_PARAM_KEY);
|
|
545
|
+
scopesParamReader = dbxRouteParamReaderInstance(this.dbxRouterService, DEFAULT_OIDC_SCOPES_PARAM_KEY);
|
|
546
|
+
// Signals from route params
|
|
547
|
+
routeUid = toSignal(this.interactionUidParamReader.value$);
|
|
548
|
+
routeClientId = toSignal(this.clientIdParamReader.value$);
|
|
549
|
+
routeClientName = toSignal(this.clientNameParamReader.value$);
|
|
550
|
+
routeClientUri = toSignal(this.clientUriParamReader.value$);
|
|
551
|
+
routeLogoUri = toSignal(this.logoUriParamReader.value$);
|
|
552
|
+
routeScopes = toSignal(this.scopesParamReader.value$);
|
|
553
|
+
// Resolved values
|
|
554
|
+
resolvedInteractionUid = computed(() => this.routeUid(), ...(ngDevMode ? [{ debugName: "resolvedInteractionUid" }] : []));
|
|
555
|
+
resolvedDetails = computed(() => {
|
|
556
|
+
const client_id = this.routeClientId() ?? '';
|
|
557
|
+
const client_name = this.routeClientName();
|
|
558
|
+
const client_uri = this.routeClientUri();
|
|
559
|
+
const logo_uri = this.routeLogoUri();
|
|
560
|
+
const scopes = this.routeScopes() ?? '';
|
|
561
|
+
return {
|
|
562
|
+
client_id,
|
|
563
|
+
client_name,
|
|
564
|
+
client_uri,
|
|
565
|
+
logo_uri,
|
|
566
|
+
scopes
|
|
567
|
+
};
|
|
568
|
+
}, ...(ngDevMode ? [{ debugName: "resolvedDetails" }] : []));
|
|
569
|
+
// Scope injection config: built from the configured scope list view class, falling back to config service, then the default
|
|
570
|
+
scopeInjectionConfig = computed(() => ({
|
|
571
|
+
componentClass: this.config()?.consentScopeListViewClass ?? this.oidcConfigService.consentScopeListViewClass ?? DbxFirebaseOAuthConsentScopeDefaultViewComponent
|
|
572
|
+
}), ...(ngDevMode ? [{ debugName: "scopeInjectionConfig" }] : []));
|
|
573
|
+
loading = signal(false, ...(ngDevMode ? [{ debugName: "loading" }] : []));
|
|
574
|
+
error = signal(null, ...(ngDevMode ? [{ debugName: "error" }] : []));
|
|
575
|
+
ngOnDestroy() {
|
|
576
|
+
this.interactionUidParamReader.destroy();
|
|
577
|
+
this.clientIdParamReader.destroy();
|
|
578
|
+
this.clientNameParamReader.destroy();
|
|
579
|
+
this.clientUriParamReader.destroy();
|
|
580
|
+
this.logoUriParamReader.destroy();
|
|
581
|
+
this.scopesParamReader.destroy();
|
|
582
|
+
}
|
|
583
|
+
approve() {
|
|
584
|
+
this._submitConsent(true);
|
|
585
|
+
}
|
|
586
|
+
deny() {
|
|
587
|
+
this._submitConsent(false);
|
|
588
|
+
}
|
|
589
|
+
_submitConsent(approved) {
|
|
590
|
+
const uid = this.resolvedInteractionUid();
|
|
591
|
+
if (!uid) {
|
|
592
|
+
this.error.set('Missing interaction UID');
|
|
593
|
+
return;
|
|
594
|
+
}
|
|
595
|
+
this.loading.set(true);
|
|
596
|
+
this.error.set(null);
|
|
597
|
+
this.interactionService.submitConsent(uid, approved).subscribe({
|
|
598
|
+
next: (response) => {
|
|
599
|
+
this.loading.set(false);
|
|
600
|
+
if (response.redirectTo) {
|
|
601
|
+
window.location.href = response.redirectTo;
|
|
602
|
+
}
|
|
603
|
+
},
|
|
604
|
+
error: () => {
|
|
605
|
+
this.loading.set(false);
|
|
606
|
+
this.error.set('Failed to process consent. Please try again.');
|
|
607
|
+
}
|
|
608
|
+
});
|
|
609
|
+
}
|
|
610
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: DbxOAuthConsentComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
611
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.0", type: DbxOAuthConsentComponent, isStandalone: true, selector: "dbx-firebase-oauth-consent", inputs: { config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "d-block dbx-firebase-oauth-consent" }, ngImport: i0, template: `
|
|
612
|
+
<dbx-firebase-oauth-consent-view [details]="resolvedDetails()" [loading]="loading()" [error]="error()" [scopeInjectionConfig]="scopeInjectionConfig()" (approveClick)="approve()" (denyClick)="deny()"></dbx-firebase-oauth-consent-view>
|
|
613
|
+
`, isInline: true, dependencies: [{ kind: "component", type: DbxFirebaseOAuthConsentViewComponent, selector: "dbx-firebase-oauth-consent-view", inputs: ["details", "loading", "error", "scopeInjectionConfig"], outputs: ["approveClick", "denyClick"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
614
|
+
}
|
|
615
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: DbxOAuthConsentComponent, decorators: [{
|
|
616
|
+
type: Component,
|
|
617
|
+
args: [{
|
|
618
|
+
selector: 'dbx-firebase-oauth-consent',
|
|
619
|
+
standalone: true,
|
|
620
|
+
imports: [DbxFirebaseOAuthConsentViewComponent],
|
|
621
|
+
template: `
|
|
622
|
+
<dbx-firebase-oauth-consent-view [details]="resolvedDetails()" [loading]="loading()" [error]="error()" [scopeInjectionConfig]="scopeInjectionConfig()" (approveClick)="approve()" (denyClick)="deny()"></dbx-firebase-oauth-consent-view>
|
|
623
|
+
`,
|
|
624
|
+
host: {
|
|
625
|
+
class: 'd-block dbx-firebase-oauth-consent'
|
|
626
|
+
},
|
|
627
|
+
changeDetection: ChangeDetectionStrategy.OnPush
|
|
628
|
+
}]
|
|
629
|
+
}], propDecorators: { config: [{ type: i0.Input, args: [{ isSignal: true, alias: "config", required: false }] }] } });
|
|
630
|
+
|
|
631
|
+
/**
|
|
632
|
+
* Creates fields for the OAuth client create form.
|
|
633
|
+
*
|
|
634
|
+
* Includes `token_endpoint_auth_method` which is immutable after creation.
|
|
635
|
+
*/
|
|
636
|
+
function oidcEntryClientFormFields(config) {
|
|
637
|
+
const fields = [];
|
|
638
|
+
if (config?.mode === 'create') {
|
|
639
|
+
fields.push(oidcClientTokenEndpointAuthMethodField(config));
|
|
640
|
+
}
|
|
641
|
+
fields.push(...oidcEntryClientUpdateFormFields());
|
|
642
|
+
return fields;
|
|
643
|
+
}
|
|
644
|
+
function oidcClientTokenEndpointAuthMethodField(config) {
|
|
645
|
+
const allowedAuthMethods = config?.tokenEndpointAuthMethods;
|
|
646
|
+
const options = allowedAuthMethods?.length ? ALL_OIDC_TOKEN_ENDPOINT_AUTH_METHOD_OPTIONS.filter((o) => allowedAuthMethods.includes(o.value)) : ALL_OIDC_TOKEN_ENDPOINT_AUTH_METHOD_OPTIONS;
|
|
647
|
+
return valueSelectionField({
|
|
648
|
+
key: 'token_endpoint_auth_method',
|
|
649
|
+
label: 'Token Endpoint Auth Method',
|
|
650
|
+
description: 'How the client authenticates when exchanging tokens. Cannot be changed after creation.',
|
|
651
|
+
required: true,
|
|
652
|
+
options
|
|
653
|
+
});
|
|
654
|
+
}
|
|
655
|
+
/**
|
|
656
|
+
* Creates fields for updating an existing OAuth client.
|
|
657
|
+
*
|
|
658
|
+
* Excludes `token_endpoint_auth_method` (immutable after creation).
|
|
659
|
+
*/
|
|
660
|
+
function oidcEntryClientUpdateFormFields() {
|
|
661
|
+
return [oidcClientNameField(), oidcClientRedirectUrisField(), oidcClientJwksUriField(), oidcClientLogoUriField(), oidcClientHomepageUriField()];
|
|
662
|
+
}
|
|
663
|
+
function oidcClientNameField() {
|
|
664
|
+
return textField({
|
|
665
|
+
key: 'client_name',
|
|
666
|
+
label: 'Client Name',
|
|
667
|
+
description: 'A human-readable name for this OAuth client.',
|
|
668
|
+
required: true,
|
|
669
|
+
maxLength: 200
|
|
670
|
+
});
|
|
671
|
+
}
|
|
672
|
+
function oidcClientRedirectUrisField() {
|
|
673
|
+
return searchableStringChipField({
|
|
674
|
+
key: 'redirect_uris',
|
|
675
|
+
label: 'Redirect URIs',
|
|
676
|
+
description: 'Type a redirect URI (e.g. https://example.com/callback) and press enter to add it.',
|
|
677
|
+
required: true,
|
|
678
|
+
searchOnEmptyText: false,
|
|
679
|
+
textInputValidator: isWebsiteUrlValidator({ requirePrefix: true, allowPorts: true }),
|
|
680
|
+
search: () => of([]),
|
|
681
|
+
displayForValue: (values) => of(values.map((v) => ({ ...v, label: v.value })))
|
|
682
|
+
});
|
|
683
|
+
}
|
|
684
|
+
function oidcClientJwksUriField() {
|
|
685
|
+
return textField({
|
|
686
|
+
key: 'jwks_uri',
|
|
687
|
+
label: 'JWKS URI',
|
|
688
|
+
description: "URL where the client's public JSON Web Key Set can be fetched. Required for private_key_jwt authentication.",
|
|
689
|
+
required: true,
|
|
690
|
+
expressions: {
|
|
691
|
+
hide: (field) => field.model?.token_endpoint_auth_method !== PRIVATE_KEY_JWT_TOKEN_ENDPOINT_AUTH_METHOD
|
|
692
|
+
}
|
|
693
|
+
});
|
|
694
|
+
}
|
|
695
|
+
function oidcClientLogoUriField() {
|
|
696
|
+
return textField({
|
|
697
|
+
key: 'logo_uri',
|
|
698
|
+
label: 'Logo URI',
|
|
699
|
+
description: 'URL of the client logo image (optional).',
|
|
700
|
+
required: false
|
|
701
|
+
});
|
|
702
|
+
}
|
|
703
|
+
function oidcClientHomepageUriField() {
|
|
704
|
+
return textField({
|
|
705
|
+
key: 'client_uri',
|
|
706
|
+
label: 'Homepage URL',
|
|
707
|
+
description: 'URL of the client homepage (optional).',
|
|
708
|
+
required: false
|
|
709
|
+
});
|
|
710
|
+
}
|
|
711
|
+
/**
|
|
712
|
+
* Assembles the form fields for the OAuth test client form.
|
|
713
|
+
*/
|
|
714
|
+
function oidcEntryClientTestFormFields(config) {
|
|
715
|
+
return [oidcClientTestClientIdField(), oidcClientTestRedirectUriField(config.redirectUris), oidcClientTestScopesField(config.availableScopes)];
|
|
716
|
+
}
|
|
717
|
+
function oidcClientTestClientIdField() {
|
|
718
|
+
return textField({
|
|
719
|
+
key: 'client_id',
|
|
720
|
+
label: 'Client ID',
|
|
721
|
+
readonly: true
|
|
722
|
+
});
|
|
723
|
+
}
|
|
724
|
+
function oidcClientTestRedirectUriField(redirectUris) {
|
|
725
|
+
const options = redirectUris.map((uri) => ({ label: uri, value: uri }));
|
|
726
|
+
return valueSelectionField({
|
|
727
|
+
key: 'redirect_uri',
|
|
728
|
+
label: 'Redirect URI',
|
|
729
|
+
description: 'Select the redirect URI to use for the test flow.',
|
|
730
|
+
required: true,
|
|
731
|
+
options
|
|
732
|
+
});
|
|
733
|
+
}
|
|
734
|
+
function oidcClientTestScopesField(availableScopes) {
|
|
735
|
+
return pickableItemChipField({
|
|
736
|
+
key: 'scopes',
|
|
737
|
+
label: 'Scopes',
|
|
738
|
+
description: 'Select the scopes to request.',
|
|
739
|
+
showSelectAllButton: true,
|
|
740
|
+
...pickableValueFieldValuesConfigForStaticLabeledValues(availableScopes)
|
|
741
|
+
});
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
/**
|
|
745
|
+
* Configurable form component for creating or updating an OAuth client.
|
|
746
|
+
*
|
|
747
|
+
* Pass `{ mode: 'create' }` to show all fields including `token_endpoint_auth_method`.
|
|
748
|
+
* Pass `{ mode: 'update' }` to exclude `token_endpoint_auth_method` (immutable after creation).
|
|
749
|
+
*
|
|
750
|
+
* Token endpoint auth methods are pulled from the injected {@link DbxFirebaseOidcConfigService}.
|
|
751
|
+
*/
|
|
752
|
+
class DbxFirebaseOidcEntryClientFormComponent extends AbstractConfigAsyncFormlyFormDirective {
|
|
753
|
+
_oidcConfigService = inject(DbxFirebaseOidcConfigService);
|
|
754
|
+
fields$ = this.config$.pipe(map((config) => oidcEntryClientFormFields({
|
|
755
|
+
...config,
|
|
756
|
+
tokenEndpointAuthMethods: this._oidcConfigService.tokenEndpointAuthMethods
|
|
757
|
+
})));
|
|
758
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: DbxFirebaseOidcEntryClientFormComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
759
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.0", type: DbxFirebaseOidcEntryClientFormComponent, isStandalone: true, selector: "dbx-firebase-oidc-client-form", providers: dbxFormlyFormComponentProviders(), usesInheritance: true, ngImport: i0, template: "<dbx-formly></dbx-formly>", isInline: true, dependencies: [{ kind: "ngmodule", type: DbxFormlyFormComponentImportsModule }, { kind: "component", type: i1.DbxFormlyComponent, selector: "dbx-formly", exportAs: ["formly"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
760
|
+
}
|
|
761
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: DbxFirebaseOidcEntryClientFormComponent, decorators: [{
|
|
762
|
+
type: Component,
|
|
763
|
+
args: [{
|
|
764
|
+
selector: 'dbx-firebase-oidc-client-form',
|
|
765
|
+
template: DBX_FORMLY_FORM_COMPONENT_TEMPLATE,
|
|
766
|
+
providers: dbxFormlyFormComponentProviders(),
|
|
767
|
+
imports: [DbxFormlyFormComponentImportsModule],
|
|
768
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
769
|
+
standalone: true
|
|
770
|
+
}]
|
|
771
|
+
}] });
|
|
772
|
+
|
|
773
|
+
/**
|
|
774
|
+
* Form component for configuring an OAuth test authorization request.
|
|
775
|
+
*
|
|
776
|
+
* Displays read-only client_id/secret, a redirect URI selector, and scope picker.
|
|
777
|
+
*/
|
|
778
|
+
class DbxFirebaseOidcEntryClientTestFormComponent extends AbstractConfigAsyncFormlyFormDirective {
|
|
779
|
+
fields$ = this.config$.pipe(map((config) => oidcEntryClientTestFormFields(config)));
|
|
780
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: DbxFirebaseOidcEntryClientTestFormComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
781
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.0", type: DbxFirebaseOidcEntryClientTestFormComponent, isStandalone: true, selector: "dbx-firebase-oidc-client-test-form", providers: dbxFormlyFormComponentProviders(), usesInheritance: true, ngImport: i0, template: "<dbx-formly></dbx-formly>", isInline: true, dependencies: [{ kind: "ngmodule", type: DbxFormlyFormComponentImportsModule }, { kind: "component", type: i1.DbxFormlyComponent, selector: "dbx-formly", exportAs: ["formly"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
782
|
+
}
|
|
783
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: DbxFirebaseOidcEntryClientTestFormComponent, decorators: [{
|
|
784
|
+
type: Component,
|
|
785
|
+
args: [{
|
|
786
|
+
selector: 'dbx-firebase-oidc-client-test-form',
|
|
787
|
+
template: DBX_FORMLY_FORM_COMPONENT_TEMPLATE,
|
|
788
|
+
providers: dbxFormlyFormComponentProviders(),
|
|
789
|
+
imports: [DbxFormlyFormComponentImportsModule],
|
|
790
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
791
|
+
standalone: true
|
|
792
|
+
}]
|
|
793
|
+
}] });
|
|
794
|
+
|
|
795
|
+
class DbxFirebaseOidcEntryClientListComponent extends AbstractDbxSelectionListWrapperDirective {
|
|
796
|
+
constructor() {
|
|
797
|
+
super({
|
|
798
|
+
componentClass: DbxFirebaseOidcEntryClientListViewComponent,
|
|
799
|
+
defaultSelectionMode: 'view'
|
|
800
|
+
});
|
|
801
|
+
}
|
|
802
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: DbxFirebaseOidcEntryClientListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
803
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.0", type: DbxFirebaseOidcEntryClientListComponent, isStandalone: true, selector: "dbx-firebase-oidc-client-list", providers: provideDbxListViewWrapper(DbxFirebaseOidcEntryClientListComponent), usesInheritance: true, ngImport: i0, template: "\n <dbx-list [state]=\"currentState$\" [config]=\"configSignal()\" [hasMore]=\"hasMore()\" [disabled]=\"disabled()\" [selectionMode]=\"selectionModeSignal()\">\n <ng-content top select=\"[top]\"></ng-content>\n <ng-content bottom select=\"[bottom]\"></ng-content>\n <ng-content empty select=\"[empty]\"></ng-content>\n <ng-content emptyLoading select=\"[emptyLoading]\"></ng-content>\n <ng-content end select=\"[end]\"></ng-content>\n </dbx-list>", isInline: true, dependencies: [{ kind: "ngmodule", type: DbxListWrapperComponentImportsModule }, { kind: "component", type: i1$1.DbxListComponent, selector: "dbx-list", inputs: ["padded", "state", "config", "disabled", "selectionMode", "hasMore"], outputs: ["contentScrolled"] }] });
|
|
804
|
+
}
|
|
805
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: DbxFirebaseOidcEntryClientListComponent, decorators: [{
|
|
806
|
+
type: Component,
|
|
807
|
+
args: [{
|
|
808
|
+
selector: 'dbx-firebase-oidc-client-list',
|
|
809
|
+
template: DEFAULT_LIST_WRAPPER_COMPONENT_CONFIGURATION_TEMPLATE,
|
|
810
|
+
providers: provideDbxListViewWrapper(DbxFirebaseOidcEntryClientListComponent),
|
|
811
|
+
standalone: true,
|
|
812
|
+
imports: [DbxListWrapperComponentImportsModule]
|
|
813
|
+
}]
|
|
814
|
+
}], ctorParameters: () => [] });
|
|
815
|
+
class DbxFirebaseOidcEntryClientListViewComponent extends AbstractDbxSelectionListViewDirective {
|
|
816
|
+
config = {
|
|
817
|
+
componentClass: DbxFirebaseOidcEntryClientListViewItemComponent,
|
|
818
|
+
mapValuesToItemValues: (x) => of(x.map((y) => ({ ...y, itemValue: y })))
|
|
819
|
+
};
|
|
820
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: DbxFirebaseOidcEntryClientListViewComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
821
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.0", type: DbxFirebaseOidcEntryClientListViewComponent, isStandalone: true, selector: "dbx-firebase-oidc-client-list-view", providers: provideDbxListView(DbxFirebaseOidcEntryClientListViewComponent), usesInheritance: true, ngImport: i0, template: "<dbx-selection-list-view [config]=\"config\"></dbx-selection-list-view>", isInline: true, dependencies: [{ kind: "ngmodule", type: DbxSelectionValueListViewComponentImportsModule }, { kind: "component", type: i1$1.DbxSelectionValueListViewComponent, selector: "dbx-selection-list-view" }] });
|
|
822
|
+
}
|
|
823
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: DbxFirebaseOidcEntryClientListViewComponent, decorators: [{
|
|
824
|
+
type: Component,
|
|
825
|
+
args: [{
|
|
826
|
+
selector: 'dbx-firebase-oidc-client-list-view',
|
|
827
|
+
template: DEFAULT_DBX_SELECTION_VALUE_LIST_COMPONENT_CONFIGURATION_TEMPLATE,
|
|
828
|
+
providers: provideDbxListView(DbxFirebaseOidcEntryClientListViewComponent),
|
|
829
|
+
standalone: true,
|
|
830
|
+
imports: [DbxSelectionValueListViewComponentImportsModule]
|
|
831
|
+
}]
|
|
832
|
+
}] });
|
|
833
|
+
// MARK: Item List
|
|
834
|
+
class DbxFirebaseOidcEntryClientListViewItemClientComponent {
|
|
835
|
+
entry = input.required(...(ngDevMode ? [{ debugName: "entry" }] : []));
|
|
836
|
+
get name() {
|
|
837
|
+
const payload = this.entry().payload;
|
|
838
|
+
return payload?.['client_name'] || 'OAuth Client';
|
|
839
|
+
}
|
|
840
|
+
get clientId() {
|
|
841
|
+
const payload = this.entry().payload;
|
|
842
|
+
return payload?.['client_id'] || '';
|
|
843
|
+
}
|
|
844
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: DbxFirebaseOidcEntryClientListViewItemClientComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
845
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.0", type: DbxFirebaseOidcEntryClientListViewItemClientComponent, isStandalone: true, selector: "dbx-firebase-oidc-client-list-view-item-client", inputs: { entry: { classPropertyName: "entry", publicName: "entry", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: `
|
|
846
|
+
<div>
|
|
847
|
+
<p>{{ name }}</p>
|
|
848
|
+
<p class="dbx-hint">{{ clientId }}</p>
|
|
849
|
+
</div>
|
|
850
|
+
`, isInline: true });
|
|
851
|
+
}
|
|
852
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: DbxFirebaseOidcEntryClientListViewItemClientComponent, decorators: [{
|
|
853
|
+
type: Component,
|
|
854
|
+
args: [{
|
|
855
|
+
selector: 'dbx-firebase-oidc-client-list-view-item-client',
|
|
856
|
+
template: `
|
|
857
|
+
<div>
|
|
858
|
+
<p>{{ name }}</p>
|
|
859
|
+
<p class="dbx-hint">{{ clientId }}</p>
|
|
860
|
+
</div>
|
|
861
|
+
`,
|
|
862
|
+
standalone: true
|
|
863
|
+
}]
|
|
864
|
+
}], propDecorators: { entry: [{ type: i0.Input, args: [{ isSignal: true, alias: "entry", required: true }] }] } });
|
|
865
|
+
class DbxFirebaseOidcEntryClientListViewItemDefaultComponent {
|
|
866
|
+
entry = input.required(...(ngDevMode ? [{ debugName: "entry" }] : []));
|
|
867
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: DbxFirebaseOidcEntryClientListViewItemDefaultComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
868
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.0", type: DbxFirebaseOidcEntryClientListViewItemDefaultComponent, isStandalone: true, selector: "dbx-firebase-oidc-client-list-view-item-default", inputs: { entry: { classPropertyName: "entry", publicName: "entry", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: `
|
|
869
|
+
<div>
|
|
870
|
+
<p>{{ entry().type }}</p>
|
|
871
|
+
</div>
|
|
872
|
+
`, isInline: true });
|
|
873
|
+
}
|
|
874
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: DbxFirebaseOidcEntryClientListViewItemDefaultComponent, decorators: [{
|
|
875
|
+
type: Component,
|
|
876
|
+
args: [{
|
|
877
|
+
selector: 'dbx-firebase-oidc-client-list-view-item-default',
|
|
878
|
+
template: `
|
|
879
|
+
<div>
|
|
880
|
+
<p>{{ entry().type }}</p>
|
|
881
|
+
</div>
|
|
882
|
+
`,
|
|
883
|
+
standalone: true
|
|
884
|
+
}]
|
|
885
|
+
}], propDecorators: { entry: [{ type: i0.Input, args: [{ isSignal: true, alias: "entry", required: true }] }] } });
|
|
886
|
+
class DbxFirebaseOidcEntryClientListViewItemComponent extends AbstractDbxValueListViewItemComponent {
|
|
887
|
+
clientType = OIDC_ENTRY_CLIENT_TYPE;
|
|
888
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: DbxFirebaseOidcEntryClientListViewItemComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
889
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.0", type: DbxFirebaseOidcEntryClientListViewItemComponent, isStandalone: true, selector: "ng-component", usesInheritance: true, ngImport: i0, template: `
|
|
890
|
+
@switch (itemValue.type) {
|
|
891
|
+
@case (clientType) {
|
|
892
|
+
<dbx-firebase-oidc-client-list-view-item-client [entry]="itemValue"></dbx-firebase-oidc-client-list-view-item-client>
|
|
893
|
+
}
|
|
894
|
+
@default {
|
|
895
|
+
<dbx-firebase-oidc-client-list-view-item-default [entry]="itemValue"></dbx-firebase-oidc-client-list-view-item-default>
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
`, isInline: true, dependencies: [{ kind: "component", type: DbxFirebaseOidcEntryClientListViewItemClientComponent, selector: "dbx-firebase-oidc-client-list-view-item-client", inputs: ["entry"] }, { kind: "component", type: DbxFirebaseOidcEntryClientListViewItemDefaultComponent, selector: "dbx-firebase-oidc-client-list-view-item-default", inputs: ["entry"] }] });
|
|
899
|
+
}
|
|
900
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: DbxFirebaseOidcEntryClientListViewItemComponent, decorators: [{
|
|
901
|
+
type: Component,
|
|
902
|
+
args: [{
|
|
903
|
+
template: `
|
|
904
|
+
@switch (itemValue.type) {
|
|
905
|
+
@case (clientType) {
|
|
906
|
+
<dbx-firebase-oidc-client-list-view-item-client [entry]="itemValue"></dbx-firebase-oidc-client-list-view-item-client>
|
|
907
|
+
}
|
|
908
|
+
@default {
|
|
909
|
+
<dbx-firebase-oidc-client-list-view-item-default [entry]="itemValue"></dbx-firebase-oidc-client-list-view-item-default>
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
`,
|
|
913
|
+
standalone: true,
|
|
914
|
+
imports: [DbxFirebaseOidcEntryClientListViewItemClientComponent, DbxFirebaseOidcEntryClientListViewItemDefaultComponent]
|
|
915
|
+
}]
|
|
916
|
+
}] });
|
|
917
|
+
|
|
918
|
+
/** Document store for a single {@link OidcEntry}. */
|
|
919
|
+
class OidcEntryDocumentStore extends AbstractDbxFirebaseDocumentStore {
|
|
920
|
+
oidcModelFunctions = inject(OidcModelFunctions);
|
|
921
|
+
_latestClientSecret$ = completeOnDestroy(new BehaviorSubject(undefined));
|
|
922
|
+
/**
|
|
923
|
+
* The client secret from the most recent create operation.
|
|
924
|
+
*
|
|
925
|
+
* Only available immediately after creation — the server does not return it again.
|
|
926
|
+
*/
|
|
927
|
+
latestClientSecret$ = this._latestClientSecret$.asObservable();
|
|
928
|
+
get latestClientSecret() {
|
|
929
|
+
return this._latestClientSecret$.value;
|
|
930
|
+
}
|
|
931
|
+
constructor() {
|
|
932
|
+
super({ firestoreCollection: inject(OidcModelFirestoreCollections).oidcEntryCollection });
|
|
933
|
+
}
|
|
934
|
+
createClient = firebaseDocumentStoreCreateFunction(this, this.oidcModelFunctions.oidcEntry.createOidcEntry.client, {
|
|
935
|
+
onResult: (_params, result) => {
|
|
936
|
+
this._latestClientSecret$.next(result.client_secret);
|
|
937
|
+
}
|
|
938
|
+
});
|
|
939
|
+
updateClient = firebaseDocumentStoreUpdateFunction(this, this.oidcModelFunctions.oidcEntry.updateOidcEntry.client);
|
|
940
|
+
rotateClientSecret = firebaseDocumentStoreUpdateFunction(this, this.oidcModelFunctions.oidcEntry.updateOidcEntry.rotateClientSecret, {
|
|
941
|
+
onResult: (_params, result) => {
|
|
942
|
+
this._latestClientSecret$.next(result.client_secret);
|
|
943
|
+
}
|
|
944
|
+
});
|
|
945
|
+
deleteClient = firebaseDocumentStoreDeleteFunction(this, this.oidcModelFunctions.oidcEntry.deleteOidcEntry.client);
|
|
946
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: OidcEntryDocumentStore, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
947
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: OidcEntryDocumentStore });
|
|
948
|
+
}
|
|
949
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: OidcEntryDocumentStore, decorators: [{
|
|
950
|
+
type: Injectable
|
|
951
|
+
}], ctorParameters: () => [] });
|
|
952
|
+
|
|
953
|
+
/**
|
|
954
|
+
* Container component for creating a new OAuth client.
|
|
955
|
+
*
|
|
956
|
+
* Wraps the client form in an action context with a submit button.
|
|
957
|
+
* Emits {@link clientCreated} with the result after successful creation.
|
|
958
|
+
*/
|
|
959
|
+
class DbxFirebaseOidcEntryClientCreateComponent {
|
|
960
|
+
oidcEntryDocumentStore = inject(OidcEntryDocumentStore);
|
|
961
|
+
formConfig = { mode: 'create' };
|
|
962
|
+
createClientOwnerTarget = input(...(ngDevMode ? [undefined, { debugName: "createClientOwnerTarget" }] : []));
|
|
963
|
+
clientCreated = output();
|
|
964
|
+
handleCreateClient = (value, context) => {
|
|
965
|
+
const params = value;
|
|
966
|
+
const target = this.createClientOwnerTarget();
|
|
967
|
+
if (target) {
|
|
968
|
+
params.key = target;
|
|
969
|
+
}
|
|
970
|
+
context.startWorkingWithLoadingStateObservable(this.oidcEntryDocumentStore.createClient(params).pipe(tap((state) => {
|
|
971
|
+
if (state.value) {
|
|
972
|
+
this.clientCreated.emit(state.value);
|
|
973
|
+
}
|
|
974
|
+
})));
|
|
975
|
+
};
|
|
976
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: DbxFirebaseOidcEntryClientCreateComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
977
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.0", type: DbxFirebaseOidcEntryClientCreateComponent, isStandalone: true, selector: "dbx-firebase-oidc-entry-client-create", inputs: { createClientOwnerTarget: { classPropertyName: "createClientOwnerTarget", publicName: "createClientOwnerTarget", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { clientCreated: "clientCreated" }, ngImport: i0, template: `
|
|
978
|
+
<div dbxAction dbxActionEnforceModified [dbxActionHandler]="handleCreateClient" dbxActionSnackbarError>
|
|
979
|
+
<dbx-firebase-oidc-client-form dbxActionForm [config]="formConfig"></dbx-firebase-oidc-client-form>
|
|
980
|
+
<dbx-button [raised]="true" dbxActionButton text="Create"></dbx-button>
|
|
981
|
+
</div>
|
|
982
|
+
`, isInline: true, dependencies: [{ kind: "directive", type: DbxActionSnackbarErrorDirective, selector: "[dbxActionSnackbarError]", inputs: ["dbxActionSnackbarError"] }, { kind: "directive", type: DbxActionDirective, selector: "dbx-action,[dbxAction]", exportAs: ["action", "dbxAction"] }, { kind: "directive", type: DbxActionEnforceModifiedDirective, selector: "[dbxActionEnforceModified]", inputs: ["dbxActionEnforceModified"] }, { kind: "directive", type: DbxActionHandlerDirective, selector: "[dbxActionHandler]", inputs: ["dbxActionHandler"] }, { kind: "directive", type: DbxActionFormDirective, selector: "[dbxActionForm]", inputs: ["dbxActionFormDisabledOnWorking", "dbxActionFormIsValid", "dbxActionFormIsEqual", "dbxActionFormIsModified", "dbxActionFormMapValue"] }, { kind: "component", type: DbxButtonComponent, selector: "dbx-button", inputs: ["bar", "type", "buttonStyle", "color", "spinnerColor", "customButtonColor", "customTextColor", "customSpinnerColor", "basic", "tonal", "raised", "stroked", "flat", "iconOnly", "fab", "mode"] }, { kind: "directive", type: DbxActionButtonDirective, selector: "[dbxActionButton]" }, { kind: "component", type: DbxFirebaseOidcEntryClientFormComponent, selector: "dbx-firebase-oidc-client-form" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
983
|
+
}
|
|
984
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: DbxFirebaseOidcEntryClientCreateComponent, decorators: [{
|
|
985
|
+
type: Component,
|
|
986
|
+
args: [{
|
|
987
|
+
selector: 'dbx-firebase-oidc-entry-client-create',
|
|
988
|
+
template: `
|
|
989
|
+
<div dbxAction dbxActionEnforceModified [dbxActionHandler]="handleCreateClient" dbxActionSnackbarError>
|
|
990
|
+
<dbx-firebase-oidc-client-form dbxActionForm [config]="formConfig"></dbx-firebase-oidc-client-form>
|
|
991
|
+
<dbx-button [raised]="true" dbxActionButton text="Create"></dbx-button>
|
|
992
|
+
</div>
|
|
993
|
+
`,
|
|
994
|
+
standalone: true,
|
|
995
|
+
imports: [DbxActionSnackbarErrorDirective, DbxActionDirective, DbxActionEnforceModifiedDirective, DbxActionHandlerDirective, DbxActionFormDirective, DbxButtonComponent, DbxActionButtonDirective, DbxFirebaseOidcEntryClientFormComponent],
|
|
996
|
+
changeDetection: ChangeDetectionStrategy.OnPush
|
|
997
|
+
}]
|
|
998
|
+
}], propDecorators: { createClientOwnerTarget: [{ type: i0.Input, args: [{ isSignal: true, alias: "createClientOwnerTarget", required: false }] }], clientCreated: [{ type: i0.Output, args: ["clientCreated"] }] } });
|
|
999
|
+
|
|
1000
|
+
/**
|
|
1001
|
+
* Generates a random PKCE code verifier string (43 characters, base64url-encoded).
|
|
1002
|
+
*
|
|
1003
|
+
* @returns A cryptographically random base64url string suitable for use as a PKCE code_verifier.
|
|
1004
|
+
*/
|
|
1005
|
+
function generatePkceCodeVerifier() {
|
|
1006
|
+
const bytes = new Uint8Array(32);
|
|
1007
|
+
crypto.getRandomValues(bytes);
|
|
1008
|
+
return base64UrlEncode(bytes);
|
|
1009
|
+
}
|
|
1010
|
+
/**
|
|
1011
|
+
* Generates a PKCE code challenge from a code verifier using SHA-256.
|
|
1012
|
+
*
|
|
1013
|
+
* @param verifier - The code verifier string to hash
|
|
1014
|
+
* @returns A base64url-encoded SHA-256 hash of the verifier
|
|
1015
|
+
*/
|
|
1016
|
+
async function generatePkceCodeChallenge(verifier) {
|
|
1017
|
+
const encoder = new TextEncoder();
|
|
1018
|
+
const data = encoder.encode(verifier);
|
|
1019
|
+
const digest = await crypto.subtle.digest('SHA-256', data);
|
|
1020
|
+
return base64UrlEncode(new Uint8Array(digest));
|
|
1021
|
+
}
|
|
1022
|
+
function base64UrlEncode(bytes) {
|
|
1023
|
+
const binString = Array.from(bytes, (byte) => String.fromCharCode(byte)).join('');
|
|
1024
|
+
return btoa(binString).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
/**
|
|
1028
|
+
* Container component for testing an OAuth authorization flow against a registered client.
|
|
1029
|
+
*
|
|
1030
|
+
* Displays a form with the client's ID, redirect URIs, and scopes,
|
|
1031
|
+
* then builds an authorization URL with PKCE parameters that can be opened in a new tab.
|
|
1032
|
+
*/
|
|
1033
|
+
class DbxFirebaseOidcEntryClientTestComponent {
|
|
1034
|
+
oidcEntryDocumentStore = inject(OidcEntryDocumentStore);
|
|
1035
|
+
oidcConfigService = inject(DbxFirebaseOidcConfigService);
|
|
1036
|
+
/** Scopes the user can pick from. Overrides the service default when provided. */
|
|
1037
|
+
availableScopes = input(undefined, ...(ngDevMode ? [{ debugName: "availableScopes" }] : []));
|
|
1038
|
+
/** Path to the authorization endpoint. Overrides the service default when provided. */
|
|
1039
|
+
oidcAuthorizationEndpointApiPath = input(undefined, ...(ngDevMode ? [{ debugName: "oidcAuthorizationEndpointApiPath" }] : []));
|
|
1040
|
+
resolvedAvailableScopes = computed(() => this.availableScopes() ?? this.oidcConfigService.availableScopes, ...(ngDevMode ? [{ debugName: "resolvedAvailableScopes" }] : []));
|
|
1041
|
+
resolvedAuthorizationEndpointPath = computed(() => this.oidcAuthorizationEndpointApiPath() ?? this.oidcConfigService.oidcAuthorizationEndpointApiPath, ...(ngDevMode ? [{ debugName: "resolvedAuthorizationEndpointPath" }] : []));
|
|
1042
|
+
// MARK: Derived Store Data
|
|
1043
|
+
redirectUrisSignal = toSignal(this.oidcEntryDocumentStore.data$.pipe(map((data) => data.payload?.redirect_uris ?? [])));
|
|
1044
|
+
clientIdSignal = toSignal(this.oidcEntryDocumentStore.data$.pipe(map((data) => data.payload?.client_id)));
|
|
1045
|
+
// MARK: Form Config
|
|
1046
|
+
formConfig = computed(() => {
|
|
1047
|
+
const redirectUris = this.redirectUrisSignal();
|
|
1048
|
+
const availableScopes = this.resolvedAvailableScopes();
|
|
1049
|
+
if (redirectUris) {
|
|
1050
|
+
return { redirectUris, availableScopes };
|
|
1051
|
+
}
|
|
1052
|
+
return undefined;
|
|
1053
|
+
}, ...(ngDevMode ? [{ debugName: "formConfig" }] : []));
|
|
1054
|
+
formTemplate$ = this.oidcEntryDocumentStore.data$.pipe(map((data) => {
|
|
1055
|
+
const payload = data.payload;
|
|
1056
|
+
const formValue = {
|
|
1057
|
+
client_id: payload?.client_id ?? '',
|
|
1058
|
+
redirect_uri: payload?.redirect_uris?.[0] ?? '',
|
|
1059
|
+
scopes: ['openid']
|
|
1060
|
+
};
|
|
1061
|
+
return formValue;
|
|
1062
|
+
}));
|
|
1063
|
+
// MARK: PKCE
|
|
1064
|
+
codeVerifier = signal(generatePkceCodeVerifier(), ...(ngDevMode ? [{ debugName: "codeVerifier" }] : []));
|
|
1065
|
+
codeChallenge = signal('', ...(ngDevMode ? [{ debugName: "codeChallenge" }] : []));
|
|
1066
|
+
state = signal(generateRandomString(), ...(ngDevMode ? [{ debugName: "state" }] : []));
|
|
1067
|
+
nonce = signal(generateRandomString(), ...(ngDevMode ? [{ debugName: "nonce" }] : []));
|
|
1068
|
+
/** The current form value, updated by the form via dbxFormValueChange. */
|
|
1069
|
+
formValue = signal(undefined, ...(ngDevMode ? [{ debugName: "formValue" }] : []));
|
|
1070
|
+
authorizationUrlSignal = computed(() => {
|
|
1071
|
+
const clientId = this.clientIdSignal();
|
|
1072
|
+
const codeChallenge = this.codeChallenge();
|
|
1073
|
+
const state = this.state();
|
|
1074
|
+
const nonce = this.nonce();
|
|
1075
|
+
const formValue = this.formValue();
|
|
1076
|
+
if (!clientId || !codeChallenge || !formValue?.redirect_uri) {
|
|
1077
|
+
return undefined;
|
|
1078
|
+
}
|
|
1079
|
+
const params = new URLSearchParams({
|
|
1080
|
+
response_type: 'code',
|
|
1081
|
+
client_id: clientId,
|
|
1082
|
+
redirect_uri: formValue.redirect_uri,
|
|
1083
|
+
scope: (formValue.scopes ?? ['openid']).join(' '),
|
|
1084
|
+
state,
|
|
1085
|
+
nonce,
|
|
1086
|
+
code_challenge: codeChallenge,
|
|
1087
|
+
code_challenge_method: 'S256'
|
|
1088
|
+
});
|
|
1089
|
+
return `${this.resolvedAuthorizationEndpointPath()}?${params.toString()}`;
|
|
1090
|
+
}, ...(ngDevMode ? [{ debugName: "authorizationUrlSignal" }] : []));
|
|
1091
|
+
constructor() {
|
|
1092
|
+
this._updateCodeChallenge();
|
|
1093
|
+
}
|
|
1094
|
+
onFormValueChange(value) {
|
|
1095
|
+
this.formValue.set(value);
|
|
1096
|
+
}
|
|
1097
|
+
openAuthorizationUrl() {
|
|
1098
|
+
const url = this.authorizationUrlSignal();
|
|
1099
|
+
if (url) {
|
|
1100
|
+
window.open(url, '_blank');
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
regeneratePkce() {
|
|
1104
|
+
this.codeVerifier.set(generatePkceCodeVerifier());
|
|
1105
|
+
this.state.set(generateRandomString());
|
|
1106
|
+
this.nonce.set(generateRandomString());
|
|
1107
|
+
this._updateCodeChallenge();
|
|
1108
|
+
}
|
|
1109
|
+
_updateCodeChallenge() {
|
|
1110
|
+
generatePkceCodeChallenge(this.codeVerifier()).then((challenge) => {
|
|
1111
|
+
this.codeChallenge.set(challenge);
|
|
1112
|
+
});
|
|
1113
|
+
}
|
|
1114
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: DbxFirebaseOidcEntryClientTestComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1115
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.0", type: DbxFirebaseOidcEntryClientTestComponent, isStandalone: true, selector: "dbx-firebase-oidc-entry-client-test", inputs: { availableScopes: { classPropertyName: "availableScopes", publicName: "availableScopes", isSignal: true, isRequired: false, transformFunction: null }, oidcAuthorizationEndpointApiPath: { classPropertyName: "oidcAuthorizationEndpointApiPath", publicName: "oidcAuthorizationEndpointApiPath", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
|
|
1116
|
+
@if (formConfig()) {
|
|
1117
|
+
<dbx-firebase-oidc-client-test-form [dbxFormSource]="formTemplate$" dbxFormSourceMode="always" [config]="formConfig()" (dbxFormValueChange)="onFormValueChange($event)"></dbx-firebase-oidc-client-test-form>
|
|
1118
|
+
<dbx-content-pit class="dbx-block dbx-mb3" [rounded]="true">
|
|
1119
|
+
<dbx-detail-block class="dbx-pb4" icon="link" header="Authorization URL">
|
|
1120
|
+
@if (authorizationUrlSignal()) {
|
|
1121
|
+
<dbx-click-to-copy-text [copyText]="authorizationUrlSignal()">
|
|
1122
|
+
<div class="dbx-small-text" style="word-break: break-all;">{{ authorizationUrlSignal() }}</div>
|
|
1123
|
+
</dbx-click-to-copy-text>
|
|
1124
|
+
} @else {
|
|
1125
|
+
<div class="dbx-hint">Fill in the form above to generate the URL.</div>
|
|
1126
|
+
}
|
|
1127
|
+
</dbx-detail-block>
|
|
1128
|
+
<dbx-detail-block icon="vpn_key" header="Code Verifier (for token exchange)">
|
|
1129
|
+
<dbx-click-to-copy-text [copyText]="codeVerifier()">{{ codeVerifier() }}</dbx-click-to-copy-text>
|
|
1130
|
+
</dbx-detail-block>
|
|
1131
|
+
</dbx-content-pit>
|
|
1132
|
+
<div class="dbx-mb3">
|
|
1133
|
+
<dbx-button class="dbx-button-spacer" [raised]="true" color="primary" text="Start Authorization Flow" icon="open_in_new" [disabled]="!authorizationUrlSignal()" (buttonClick)="openAuthorizationUrl()"></dbx-button>
|
|
1134
|
+
<dbx-button class="dbx-ml2" text="Regenerate PKCE" icon="refresh" (buttonClick)="regeneratePkce()"></dbx-button>
|
|
1135
|
+
</div>
|
|
1136
|
+
}
|
|
1137
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: DbxFirebaseOidcEntryClientTestFormComponent, selector: "dbx-firebase-oidc-client-test-form" }, { kind: "directive", type: DbxFormSourceDirective, selector: "[dbxFormSource]", inputs: ["dbxFormSourceMode", "dbxFormSource"] }, { kind: "directive", type: DbxFormValueChangeDirective, selector: "[dbxFormValueChange]", outputs: ["dbxFormValueChange"] }, { kind: "directive", type: DbxContentPitDirective, selector: "dbx-content-pit, [dbxContentPit]", inputs: ["scrollable", "rounded"] }, { kind: "component", type: DbxDetailBlockComponent, selector: "dbx-detail-block", inputs: ["icon", "header", "alignHeader", "bigHeader"] }, { kind: "component", type: DbxClickToCopyTextComponent, selector: "dbx-click-to-copy-text", inputs: ["copyText", "showIcon", "highlighted", "clipboardSnackbarMessagesConfig", "clipboardSnackbarMessagesEnabled", "clickToCopyIcon", "clickIconToCopyOnly"] }, { kind: "component", type: DbxButtonComponent, selector: "dbx-button", inputs: ["bar", "type", "buttonStyle", "color", "spinnerColor", "customButtonColor", "customTextColor", "customSpinnerColor", "basic", "tonal", "raised", "stroked", "flat", "iconOnly", "fab", "mode"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1138
|
+
}
|
|
1139
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: DbxFirebaseOidcEntryClientTestComponent, decorators: [{
|
|
1140
|
+
type: Component,
|
|
1141
|
+
args: [{
|
|
1142
|
+
selector: 'dbx-firebase-oidc-entry-client-test',
|
|
1143
|
+
template: `
|
|
1144
|
+
@if (formConfig()) {
|
|
1145
|
+
<dbx-firebase-oidc-client-test-form [dbxFormSource]="formTemplate$" dbxFormSourceMode="always" [config]="formConfig()" (dbxFormValueChange)="onFormValueChange($event)"></dbx-firebase-oidc-client-test-form>
|
|
1146
|
+
<dbx-content-pit class="dbx-block dbx-mb3" [rounded]="true">
|
|
1147
|
+
<dbx-detail-block class="dbx-pb4" icon="link" header="Authorization URL">
|
|
1148
|
+
@if (authorizationUrlSignal()) {
|
|
1149
|
+
<dbx-click-to-copy-text [copyText]="authorizationUrlSignal()">
|
|
1150
|
+
<div class="dbx-small-text" style="word-break: break-all;">{{ authorizationUrlSignal() }}</div>
|
|
1151
|
+
</dbx-click-to-copy-text>
|
|
1152
|
+
} @else {
|
|
1153
|
+
<div class="dbx-hint">Fill in the form above to generate the URL.</div>
|
|
1154
|
+
}
|
|
1155
|
+
</dbx-detail-block>
|
|
1156
|
+
<dbx-detail-block icon="vpn_key" header="Code Verifier (for token exchange)">
|
|
1157
|
+
<dbx-click-to-copy-text [copyText]="codeVerifier()">{{ codeVerifier() }}</dbx-click-to-copy-text>
|
|
1158
|
+
</dbx-detail-block>
|
|
1159
|
+
</dbx-content-pit>
|
|
1160
|
+
<div class="dbx-mb3">
|
|
1161
|
+
<dbx-button class="dbx-button-spacer" [raised]="true" color="primary" text="Start Authorization Flow" icon="open_in_new" [disabled]="!authorizationUrlSignal()" (buttonClick)="openAuthorizationUrl()"></dbx-button>
|
|
1162
|
+
<dbx-button class="dbx-ml2" text="Regenerate PKCE" icon="refresh" (buttonClick)="regeneratePkce()"></dbx-button>
|
|
1163
|
+
</div>
|
|
1164
|
+
}
|
|
1165
|
+
`,
|
|
1166
|
+
standalone: true,
|
|
1167
|
+
imports: [CommonModule, DbxFirebaseOidcEntryClientTestFormComponent, DbxFormSourceDirective, DbxFormValueChangeDirective, DbxContentPitDirective, DbxDetailBlockComponent, DbxClickToCopyTextComponent, DbxButtonComponent],
|
|
1168
|
+
changeDetection: ChangeDetectionStrategy.OnPush
|
|
1169
|
+
}]
|
|
1170
|
+
}], ctorParameters: () => [], propDecorators: { availableScopes: [{ type: i0.Input, args: [{ isSignal: true, alias: "availableScopes", required: false }] }], oidcAuthorizationEndpointApiPath: [{ type: i0.Input, args: [{ isSignal: true, alias: "oidcAuthorizationEndpointApiPath", required: false }] }] } });
|
|
1171
|
+
function generateRandomString() {
|
|
1172
|
+
const bytes = new Uint8Array(16);
|
|
1173
|
+
crypto.getRandomValues(bytes);
|
|
1174
|
+
return Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('');
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
/**
|
|
1178
|
+
* Container component for updating an existing OAuth client.
|
|
1179
|
+
*
|
|
1180
|
+
* Wraps the client update form in an action context with a save button.
|
|
1181
|
+
*/
|
|
1182
|
+
class DbxFirebaseOidcEntryClientUpdateComponent {
|
|
1183
|
+
oidcEntryDocumentStore = inject(OidcEntryDocumentStore);
|
|
1184
|
+
formConfig = { mode: 'update' };
|
|
1185
|
+
formTemplate$ = this.oidcEntryDocumentStore.data$.pipe(map((data) => {
|
|
1186
|
+
const payload = data.payload;
|
|
1187
|
+
const formValue = {
|
|
1188
|
+
client_name: payload.client_name ?? '',
|
|
1189
|
+
redirect_uris: payload.redirect_uris ?? [],
|
|
1190
|
+
logo_uri: payload.logo_uri,
|
|
1191
|
+
client_uri: payload.client_uri
|
|
1192
|
+
};
|
|
1193
|
+
return formValue;
|
|
1194
|
+
}));
|
|
1195
|
+
handleUpdateClient = (value, context) => {
|
|
1196
|
+
const params = value;
|
|
1197
|
+
context.startWorkingWithLoadingStateObservable(this.oidcEntryDocumentStore.updateClient(params));
|
|
1198
|
+
};
|
|
1199
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: DbxFirebaseOidcEntryClientUpdateComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1200
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.0", type: DbxFirebaseOidcEntryClientUpdateComponent, isStandalone: true, selector: "dbx-firebase-oidc-entry-client-update", ngImport: i0, template: `
|
|
1201
|
+
<div dbxAction dbxActionEnforceModified [dbxActionHandler]="handleUpdateClient" dbxActionSnackbarError>
|
|
1202
|
+
<dbx-firebase-oidc-client-form dbxActionForm [dbxFormSource]="formTemplate$" [config]="formConfig"></dbx-firebase-oidc-client-form>
|
|
1203
|
+
<dbx-button [raised]="true" dbxActionButton text="Save"></dbx-button>
|
|
1204
|
+
</div>
|
|
1205
|
+
`, isInline: true, dependencies: [{ kind: "directive", type: DbxActionSnackbarErrorDirective, selector: "[dbxActionSnackbarError]", inputs: ["dbxActionSnackbarError"] }, { kind: "directive", type: DbxActionDirective, selector: "dbx-action,[dbxAction]", exportAs: ["action", "dbxAction"] }, { kind: "directive", type: DbxActionEnforceModifiedDirective, selector: "[dbxActionEnforceModified]", inputs: ["dbxActionEnforceModified"] }, { kind: "directive", type: DbxActionHandlerDirective, selector: "[dbxActionHandler]", inputs: ["dbxActionHandler"] }, { kind: "directive", type: DbxActionFormDirective, selector: "[dbxActionForm]", inputs: ["dbxActionFormDisabledOnWorking", "dbxActionFormIsValid", "dbxActionFormIsEqual", "dbxActionFormIsModified", "dbxActionFormMapValue"] }, { kind: "directive", type: DbxFormSourceDirective, selector: "[dbxFormSource]", inputs: ["dbxFormSourceMode", "dbxFormSource"] }, { kind: "component", type: DbxButtonComponent, selector: "dbx-button", inputs: ["bar", "type", "buttonStyle", "color", "spinnerColor", "customButtonColor", "customTextColor", "customSpinnerColor", "basic", "tonal", "raised", "stroked", "flat", "iconOnly", "fab", "mode"] }, { kind: "directive", type: DbxActionButtonDirective, selector: "[dbxActionButton]" }, { kind: "component", type: DbxFirebaseOidcEntryClientFormComponent, selector: "dbx-firebase-oidc-client-form" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1206
|
+
}
|
|
1207
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: DbxFirebaseOidcEntryClientUpdateComponent, decorators: [{
|
|
1208
|
+
type: Component,
|
|
1209
|
+
args: [{
|
|
1210
|
+
selector: 'dbx-firebase-oidc-entry-client-update',
|
|
1211
|
+
template: `
|
|
1212
|
+
<div dbxAction dbxActionEnforceModified [dbxActionHandler]="handleUpdateClient" dbxActionSnackbarError>
|
|
1213
|
+
<dbx-firebase-oidc-client-form dbxActionForm [dbxFormSource]="formTemplate$" [config]="formConfig"></dbx-firebase-oidc-client-form>
|
|
1214
|
+
<dbx-button [raised]="true" dbxActionButton text="Save"></dbx-button>
|
|
1215
|
+
</div>
|
|
1216
|
+
`,
|
|
1217
|
+
standalone: true,
|
|
1218
|
+
imports: [DbxActionSnackbarErrorDirective, DbxActionDirective, DbxActionEnforceModifiedDirective, DbxActionHandlerDirective, DbxActionFormDirective, DbxFormSourceDirective, DbxButtonComponent, DbxActionButtonDirective, DbxFirebaseOidcEntryClientFormComponent],
|
|
1219
|
+
changeDetection: ChangeDetectionStrategy.OnPush
|
|
1220
|
+
}]
|
|
1221
|
+
}] });
|
|
1222
|
+
|
|
1223
|
+
/**
|
|
1224
|
+
* Displays the OIDC client ID and (when available) the one-time client secret.
|
|
1225
|
+
*
|
|
1226
|
+
* The client secret is only shown immediately after creation or after rotating.
|
|
1227
|
+
* When no secret is available, a "Rotate Secret" button is shown.
|
|
1228
|
+
*/
|
|
1229
|
+
class DbxFirebaseOidcEntryClientViewComponent {
|
|
1230
|
+
oidcEntryDocumentStore = inject(OidcEntryDocumentStore);
|
|
1231
|
+
clientIdSignal = toSignal(this.oidcEntryDocumentStore.data$.pipe(map((data) => data.payload?.client_id)));
|
|
1232
|
+
latestClientSecretSignal = toSignal(this.oidcEntryDocumentStore.latestClientSecret$);
|
|
1233
|
+
rotateSecretConfirmConfig = {
|
|
1234
|
+
title: 'Rotate Client Secret',
|
|
1235
|
+
prompt: 'This will invalidate the current client secret. Any applications using it will stop working. Are you sure?',
|
|
1236
|
+
confirmText: 'Rotate Secret'
|
|
1237
|
+
};
|
|
1238
|
+
handleRotateClientSecret = (_, context) => {
|
|
1239
|
+
context.startWorkingWithLoadingStateObservable(this.oidcEntryDocumentStore.rotateClientSecret({}));
|
|
1240
|
+
};
|
|
1241
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: DbxFirebaseOidcEntryClientViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1242
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.0", type: DbxFirebaseOidcEntryClientViewComponent, isStandalone: true, selector: "dbx-firebase-oidc-entry-client-view", ngImport: i0, template: `
|
|
1243
|
+
<dbx-content-pit [rounded]="true">
|
|
1244
|
+
<dbx-detail-block class="dbx-pb4" icon="key" header="Client ID">
|
|
1245
|
+
<dbx-click-to-copy-text [copyText]="clientIdSignal()">{{ clientIdSignal() }}</dbx-click-to-copy-text>
|
|
1246
|
+
</dbx-detail-block>
|
|
1247
|
+
<dbx-detail-block icon="lock" header="Client Secret">
|
|
1248
|
+
@if (latestClientSecretSignal()) {
|
|
1249
|
+
<dbx-click-to-copy-text class="dbx-block dbx-pb2" [copyText]="latestClientSecretSignal()">{{ latestClientSecretSignal() }}</dbx-click-to-copy-text>
|
|
1250
|
+
<dbx-click-to-copy-text [copyText]="latestClientSecretSignal()" [showIcon]="false"><div class="dbx-hint dbx-u">This secret is only shown once. Copy it now.</div></dbx-click-to-copy-text>
|
|
1251
|
+
} @else {
|
|
1252
|
+
<div>
|
|
1253
|
+
<div class="dbx-hint dbx-pb3">The client secret was shown once when created. You can invalidate the old one and get a new one.</div>
|
|
1254
|
+
<dbx-button dbxAction [dbxActionHandler]="handleRotateClientSecret" [dbxActionConfirm]="rotateSecretConfirmConfig" dbxActionButton text="Rotate Secret" icon="refresh" color="warn" [raised]="true"></dbx-button>
|
|
1255
|
+
</div>
|
|
1256
|
+
}
|
|
1257
|
+
</dbx-detail-block>
|
|
1258
|
+
</dbx-content-pit>
|
|
1259
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: DbxContentPitDirective, selector: "dbx-content-pit, [dbxContentPit]", inputs: ["scrollable", "rounded"] }, { kind: "component", type: DbxDetailBlockComponent, selector: "dbx-detail-block", inputs: ["icon", "header", "alignHeader", "bigHeader"] }, { kind: "component", type: DbxClickToCopyTextComponent, selector: "dbx-click-to-copy-text", inputs: ["copyText", "showIcon", "highlighted", "clipboardSnackbarMessagesConfig", "clipboardSnackbarMessagesEnabled", "clickToCopyIcon", "clickIconToCopyOnly"] }, { kind: "component", type: DbxButtonComponent, selector: "dbx-button", inputs: ["bar", "type", "buttonStyle", "color", "spinnerColor", "customButtonColor", "customTextColor", "customSpinnerColor", "basic", "tonal", "raised", "stroked", "flat", "iconOnly", "fab", "mode"] }, { kind: "directive", type: DbxActionDirective, selector: "dbx-action,[dbxAction]", exportAs: ["action", "dbxAction"] }, { kind: "directive", type: DbxActionHandlerDirective, selector: "[dbxActionHandler]", inputs: ["dbxActionHandler"] }, { kind: "directive", type: DbxActionButtonDirective, selector: "[dbxActionButton]" }, { kind: "directive", type: DbxActionConfirmDirective, selector: "[dbxActionConfirm]", inputs: ["dbxActionConfirm"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1260
|
+
}
|
|
1261
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: DbxFirebaseOidcEntryClientViewComponent, decorators: [{
|
|
1262
|
+
type: Component,
|
|
1263
|
+
args: [{
|
|
1264
|
+
selector: 'dbx-firebase-oidc-entry-client-view',
|
|
1265
|
+
template: `
|
|
1266
|
+
<dbx-content-pit [rounded]="true">
|
|
1267
|
+
<dbx-detail-block class="dbx-pb4" icon="key" header="Client ID">
|
|
1268
|
+
<dbx-click-to-copy-text [copyText]="clientIdSignal()">{{ clientIdSignal() }}</dbx-click-to-copy-text>
|
|
1269
|
+
</dbx-detail-block>
|
|
1270
|
+
<dbx-detail-block icon="lock" header="Client Secret">
|
|
1271
|
+
@if (latestClientSecretSignal()) {
|
|
1272
|
+
<dbx-click-to-copy-text class="dbx-block dbx-pb2" [copyText]="latestClientSecretSignal()">{{ latestClientSecretSignal() }}</dbx-click-to-copy-text>
|
|
1273
|
+
<dbx-click-to-copy-text [copyText]="latestClientSecretSignal()" [showIcon]="false"><div class="dbx-hint dbx-u">This secret is only shown once. Copy it now.</div></dbx-click-to-copy-text>
|
|
1274
|
+
} @else {
|
|
1275
|
+
<div>
|
|
1276
|
+
<div class="dbx-hint dbx-pb3">The client secret was shown once when created. You can invalidate the old one and get a new one.</div>
|
|
1277
|
+
<dbx-button dbxAction [dbxActionHandler]="handleRotateClientSecret" [dbxActionConfirm]="rotateSecretConfirmConfig" dbxActionButton text="Rotate Secret" icon="refresh" color="warn" [raised]="true"></dbx-button>
|
|
1278
|
+
</div>
|
|
1279
|
+
}
|
|
1280
|
+
</dbx-detail-block>
|
|
1281
|
+
</dbx-content-pit>
|
|
1282
|
+
`,
|
|
1283
|
+
standalone: true,
|
|
1284
|
+
imports: [CommonModule, DbxContentPitDirective, DbxDetailBlockComponent, DbxClickToCopyTextComponent, DbxButtonComponent, DbxActionDirective, DbxActionHandlerDirective, DbxActionButtonDirective, DbxActionConfirmDirective],
|
|
1285
|
+
changeDetection: ChangeDetectionStrategy.OnPush
|
|
1286
|
+
}]
|
|
1287
|
+
}] });
|
|
1288
|
+
|
|
1289
|
+
/** Collection store for querying {@link OidcEntry} documents. */
|
|
1290
|
+
class OidcEntryCollectionStore extends AbstractDbxFirebaseCollectionStore {
|
|
1291
|
+
constructor() {
|
|
1292
|
+
super({ firestoreCollection: inject(OidcModelFirestoreCollections).oidcEntryCollection });
|
|
1293
|
+
}
|
|
1294
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: OidcEntryCollectionStore, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1295
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: OidcEntryCollectionStore });
|
|
1296
|
+
}
|
|
1297
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: OidcEntryCollectionStore, decorators: [{
|
|
1298
|
+
type: Injectable
|
|
1299
|
+
}], ctorParameters: () => [] });
|
|
1300
|
+
|
|
1301
|
+
/** Directive providing a {@link OidcEntryCollectionStore} for querying {@link OidcEntry} documents. */
|
|
1302
|
+
class OidcEntryCollectionStoreDirective extends DbxFirebaseCollectionStoreDirective {
|
|
1303
|
+
constructor() {
|
|
1304
|
+
super(inject(OidcEntryCollectionStore));
|
|
1305
|
+
}
|
|
1306
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: OidcEntryCollectionStoreDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
1307
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.0", type: OidcEntryCollectionStoreDirective, isStandalone: true, selector: "[dbxOidcEntryCollection]", providers: provideDbxFirebaseCollectionStoreDirective(OidcEntryCollectionStoreDirective, OidcEntryCollectionStore), usesInheritance: true, ngImport: i0 });
|
|
1308
|
+
}
|
|
1309
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: OidcEntryCollectionStoreDirective, decorators: [{
|
|
1310
|
+
type: Directive,
|
|
1311
|
+
args: [{
|
|
1312
|
+
selector: '[dbxOidcEntryCollection]',
|
|
1313
|
+
providers: provideDbxFirebaseCollectionStoreDirective(OidcEntryCollectionStoreDirective, OidcEntryCollectionStore),
|
|
1314
|
+
standalone: true
|
|
1315
|
+
}]
|
|
1316
|
+
}], ctorParameters: () => [] });
|
|
1317
|
+
|
|
1318
|
+
/** Directive providing a {@link OidcEntryDocumentStore} for accessing a single {@link OidcEntry} document. */
|
|
1319
|
+
class OidcEntryDocumentStoreDirective extends DbxFirebaseDocumentStoreDirective {
|
|
1320
|
+
constructor() {
|
|
1321
|
+
super(inject(OidcEntryDocumentStore));
|
|
1322
|
+
}
|
|
1323
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: OidcEntryDocumentStoreDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
1324
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.0", type: OidcEntryDocumentStoreDirective, isStandalone: true, selector: "[dbxOidcEntryDocument]", providers: provideDbxFirebaseDocumentStoreDirective(OidcEntryDocumentStoreDirective, OidcEntryDocumentStore), usesInheritance: true, ngImport: i0 });
|
|
1325
|
+
}
|
|
1326
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: OidcEntryDocumentStoreDirective, decorators: [{
|
|
1327
|
+
type: Directive,
|
|
1328
|
+
args: [{
|
|
1329
|
+
selector: '[dbxOidcEntryDocument]',
|
|
1330
|
+
providers: provideDbxFirebaseDocumentStoreDirective(OidcEntryDocumentStoreDirective, OidcEntryDocumentStore),
|
|
1331
|
+
standalone: true
|
|
1332
|
+
}]
|
|
1333
|
+
}], ctorParameters: () => [] });
|
|
1334
|
+
|
|
1335
|
+
/**
|
|
1336
|
+
* Provider factory for the {@link OidcModelFirestoreCollections}.
|
|
1337
|
+
*/
|
|
1338
|
+
function provideOidcModelFirestoreCollections(appCollection) {
|
|
1339
|
+
if (!appCollection.oidcEntryCollection) {
|
|
1340
|
+
throw new Error(`OidcModelFirestoreCollections could not be provided using the app's app collection. Set provideOidcModelFirestoreCollections to false in ProvideDbxFirebaseOidcConfig to prevent auto-initialization, or update your app's collection class to implement OidcModelFirestoreCollections.`);
|
|
1341
|
+
}
|
|
1342
|
+
return appCollection;
|
|
1343
|
+
}
|
|
1344
|
+
/**
|
|
1345
|
+
* Provides the OIDC-related Angular services and collections for `@dereekb/dbx-firebase/oidc`.
|
|
1346
|
+
*
|
|
1347
|
+
* When `oauthInteractionRoute` is configured in {@link DbxFirebaseOidcConfig}, an app initializer
|
|
1348
|
+
* is registered that adds that route to the {@link DbxAppAuthRouterService} ignored routes set,
|
|
1349
|
+
* preventing auth effects from redirecting away during the OIDC interaction flow.
|
|
1350
|
+
*/
|
|
1351
|
+
function provideDbxFirebaseOidc(config) {
|
|
1352
|
+
const providers = [{ provide: DbxFirebaseOidcConfig, useValue: config.oidcConfig }, DbxFirebaseOidcConfigService];
|
|
1353
|
+
if (config.provideOidcModelFirestoreCollections !== false) {
|
|
1354
|
+
providers.push({
|
|
1355
|
+
provide: OidcModelFirestoreCollections,
|
|
1356
|
+
useFactory: provideOidcModelFirestoreCollections,
|
|
1357
|
+
deps: [config.appCollectionClass]
|
|
1358
|
+
});
|
|
1359
|
+
}
|
|
1360
|
+
// Register the OAuth interaction route as ignored by auth effects
|
|
1361
|
+
if (config.oidcConfig.oauthInteractionRoute) {
|
|
1362
|
+
const routeRef = config.oidcConfig.oauthInteractionRoute;
|
|
1363
|
+
providers.push(provideAppInitializer(() => {
|
|
1364
|
+
const authRouterService = inject(DbxAppAuthRouterService);
|
|
1365
|
+
authRouterService.addIgnoredRoute(routeRef);
|
|
1366
|
+
}));
|
|
1367
|
+
}
|
|
1368
|
+
return makeEnvironmentProviders(providers);
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1371
|
+
// @dereekb/dbx-firebase/oidc
|
|
1372
|
+
|
|
1373
|
+
/**
|
|
1374
|
+
* Generated bundle index. Do not edit.
|
|
1375
|
+
*/
|
|
1376
|
+
|
|
1377
|
+
export { AbstractDbxFirebaseOAuthConsentScopeViewComponent, DEFAULT_OIDC_AUTHORIZATION_ENDPOINT_PATH, DEFAULT_OIDC_CLIENT_ID_PARAM_KEY, DEFAULT_OIDC_CLIENT_NAME_PARAM_KEY, DEFAULT_OIDC_CLIENT_URI_PARAM_KEY, DEFAULT_OIDC_INTERACTION_ENDPOINT_PATH, DEFAULT_OIDC_INTERACTION_UID_PARAM_KEY, DEFAULT_OIDC_LOGO_URI_PARAM_KEY, DEFAULT_OIDC_SCOPES_PARAM_KEY, DEFAULT_OIDC_TOKEN_ENDPOINT_AUTH_METHODS, DbxFirebaseOAuthConsentScopeDefaultViewComponent, DbxFirebaseOAuthConsentScopeListComponent, DbxFirebaseOAuthConsentViewComponent, DbxFirebaseOAuthLoginComponent, DbxFirebaseOAuthLoginViewComponent, DbxFirebaseOidcConfig, DbxFirebaseOidcConfigService, DbxFirebaseOidcEntryClientCreateComponent, DbxFirebaseOidcEntryClientFormComponent, DbxFirebaseOidcEntryClientListComponent, DbxFirebaseOidcEntryClientListViewComponent, DbxFirebaseOidcEntryClientListViewItemClientComponent, DbxFirebaseOidcEntryClientListViewItemComponent, DbxFirebaseOidcEntryClientListViewItemDefaultComponent, DbxFirebaseOidcEntryClientTestComponent, DbxFirebaseOidcEntryClientTestFormComponent, DbxFirebaseOidcEntryClientUpdateComponent, DbxFirebaseOidcEntryClientViewComponent, DbxFirebaseOidcInteractionService, DbxOAuthConsentComponent, OidcEntryCollectionStore, OidcEntryCollectionStoreDirective, OidcEntryDocumentStore, OidcEntryDocumentStoreDirective, generatePkceCodeChallenge, generatePkceCodeVerifier, oidcClientHomepageUriField, oidcClientJwksUriField, oidcClientLogoUriField, oidcClientNameField, oidcClientRedirectUrisField, oidcClientTestClientIdField, oidcClientTestRedirectUriField, oidcClientTestScopesField, oidcClientTokenEndpointAuthMethodField, oidcEntryClientFormFields, oidcEntryClientTestFormFields, oidcEntryClientUpdateFormFields, provideDbxFirebaseOidc, provideOidcModelFirestoreCollections };
|
|
1378
|
+
//# sourceMappingURL=dereekb-dbx-firebase-oidc.mjs.map
|