@singlebase/elements 0.1.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/LICENSE +21 -0
- package/README.md +29 -0
- package/dist/authui.cjs +2 -0
- package/dist/authui.cjs.map +1 -0
- package/dist/authui.d.ts +22 -0
- package/dist/authui.js +24 -0
- package/dist/authui.js.map +1 -0
- package/dist/controllers/auth-controller.d.ts +17 -0
- package/dist/controllers/event-bridge.d.ts +27 -0
- package/dist/controllers/settings-controller.d.ts +18 -0
- package/dist/display-8HEcKYR4.js +3444 -0
- package/dist/display-8HEcKYR4.js.map +1 -0
- package/dist/display-CLDNNZQy.cjs +976 -0
- package/dist/display-CLDNNZQy.cjs.map +1 -0
- package/dist/elements/account-screen.d.ts +99 -0
- package/dist/elements/auth-screen.d.ts +215 -0
- package/dist/elements/base.d.ts +69 -0
- package/dist/elements/buttons.d.ts +45 -0
- package/dist/elements/display.d.ts +51 -0
- package/dist/elements/form-base.d.ts +23 -0
- package/dist/elements/guard.d.ts +65 -0
- package/dist/elements/uploader.d.ts +225 -0
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +30 -0
- package/dist/index.js.map +1 -0
- package/dist/messages.d.ts +147 -0
- package/dist/singlebase-elements.min.js +7616 -0
- package/dist/singlebase-elements.min.js.map +1 -0
- package/dist/standalone.d.ts +2 -0
- package/dist/styles/shared.d.ts +8 -0
- package/dist/styles/tokens.d.ts +44 -0
- package/dist/upload-messages.d.ts +45 -0
- package/dist/uploader-D-Da1trc.js +2290 -0
- package/dist/uploader-D-Da1trc.js.map +1 -0
- package/dist/uploader-eFYKpk8b.cjs +1390 -0
- package/dist/uploader-eFYKpk8b.cjs.map +1 -0
- package/dist/uploader.cjs +2 -0
- package/dist/uploader.cjs.map +1 -0
- package/dist/uploader.d.ts +10 -0
- package/dist/uploader.js +9 -0
- package/dist/uploader.js.map +1 -0
- package/dist/utils/files.d.ts +38 -0
- package/dist/utils/inspect.d.ts +23 -0
- package/dist/utils/profile.d.ts +9 -0
- package/dist/utils/redirect.d.ts +15 -0
- package/package.json +76 -0
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { type PropertyValues } from "lit";
|
|
2
|
+
import { type AuthSettings } from "@singlebase/singlebase-sdk";
|
|
3
|
+
import { SinglebaseFormBase } from "./form-base.js";
|
|
4
|
+
import "./uploader.js";
|
|
5
|
+
/**
|
|
6
|
+
* The authenticated account view, laid out exactly as the design mock's
|
|
7
|
+
* isAuthed branch: identity header, profile summary with grouped edit
|
|
8
|
+
* cards, password section, two-factor, connected accounts and danger zone.
|
|
9
|
+
*
|
|
10
|
+
* Two-factor setup and account deletion have no backing operation yet, so
|
|
11
|
+
* they are hidden (SHOW_TWO_FACTOR, SHOW_DELETE_ACCOUNT). Listing and
|
|
12
|
+
* unlinking providers isn't supported either, and says so; connecting
|
|
13
|
+
* a *new* provider is supported, so those tiles are live.
|
|
14
|
+
*/
|
|
15
|
+
export declare class SinglebaseAccountScreen extends SinglebaseFormBase {
|
|
16
|
+
accessor logoText: string;
|
|
17
|
+
/** A logo image, used instead of `logo-text` when set. */
|
|
18
|
+
accessor logoUrl: string;
|
|
19
|
+
accessor settings: AuthSettings | null;
|
|
20
|
+
accessor nonceStorageKey: string;
|
|
21
|
+
private accessor editMode;
|
|
22
|
+
private accessor toast;
|
|
23
|
+
private accessor photoBusy;
|
|
24
|
+
/** A photo URL that failed to load, so the initials show instead of a broken image. */
|
|
25
|
+
private accessor photoFailed;
|
|
26
|
+
private accessor profileSaved;
|
|
27
|
+
private accessor passSaved;
|
|
28
|
+
private accessor profileError;
|
|
29
|
+
private accessor newPassError;
|
|
30
|
+
private accessor deleteStage;
|
|
31
|
+
private accessor confirmText;
|
|
32
|
+
private accessor dFirst;
|
|
33
|
+
private accessor dLast;
|
|
34
|
+
private accessor dPhone;
|
|
35
|
+
private accessor dEmail;
|
|
36
|
+
private accessor emailStep;
|
|
37
|
+
private accessor dNewEmail;
|
|
38
|
+
private accessor emailCode;
|
|
39
|
+
private accessor emailError;
|
|
40
|
+
private accessor emailSaved;
|
|
41
|
+
private accessor curPass;
|
|
42
|
+
private accessor newPass;
|
|
43
|
+
private accessor confirmPass;
|
|
44
|
+
private settingsCtl;
|
|
45
|
+
private flashTimer?;
|
|
46
|
+
private uidBase;
|
|
47
|
+
private uid;
|
|
48
|
+
willUpdate(changed: PropertyValues): void;
|
|
49
|
+
disconnectedCallback(): void;
|
|
50
|
+
private flash;
|
|
51
|
+
/** Matches the guest screens: image when given one, else the text label. */
|
|
52
|
+
private renderLogo;
|
|
53
|
+
private get user();
|
|
54
|
+
private get fullName();
|
|
55
|
+
private get initials();
|
|
56
|
+
private uploadClientFor;
|
|
57
|
+
private uploadClientCache;
|
|
58
|
+
/**
|
|
59
|
+
* The upload API bound to the same dispatcher as this widget's auth client,
|
|
60
|
+
* so the photo goes to the same project with the same bearer — including on
|
|
61
|
+
* a page where this widget was given its own `.client`.
|
|
62
|
+
*/
|
|
63
|
+
private get uploadClient();
|
|
64
|
+
private onChoosePhoto;
|
|
65
|
+
private onPhotoSelected;
|
|
66
|
+
/** The server has set the photo; read the account back to get its URL. */
|
|
67
|
+
private onPhotoUploaded;
|
|
68
|
+
private onPhotoProblem;
|
|
69
|
+
private startProfileEdit;
|
|
70
|
+
private onSaveProfile;
|
|
71
|
+
private startPassEdit;
|
|
72
|
+
private onChangePass;
|
|
73
|
+
private startEmailEdit;
|
|
74
|
+
/**
|
|
75
|
+
* Step one: ask for a code. The code goes to the address on file, not to
|
|
76
|
+
* the new one — that is what proves the person asking already controls the
|
|
77
|
+
* account rather than merely knowing an address.
|
|
78
|
+
*/
|
|
79
|
+
private onRequestEmailCode;
|
|
80
|
+
/** Step two: confirm the code together with the new address. */
|
|
81
|
+
private onConfirmEmailChange;
|
|
82
|
+
private renderEmailSection;
|
|
83
|
+
private onSignOut;
|
|
84
|
+
private onConnectProvider;
|
|
85
|
+
private get availableProviders();
|
|
86
|
+
private renderIdentity;
|
|
87
|
+
private renderProfileSection;
|
|
88
|
+
private renderPasswordSection;
|
|
89
|
+
/** Layout preserved from the mock; no enrolment operation exists yet. */
|
|
90
|
+
private renderTwoFactorSection;
|
|
91
|
+
private renderConnectedSection;
|
|
92
|
+
private renderDangerSection;
|
|
93
|
+
protected render(): import("lit").TemplateResult<1>;
|
|
94
|
+
}
|
|
95
|
+
declare global {
|
|
96
|
+
interface HTMLElementTagNameMap {
|
|
97
|
+
"singlebase-authui-account": SinglebaseAccountScreen;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import { nothing, type PropertyValues } from "lit";
|
|
2
|
+
import { type AuthSettings } from "@singlebase/singlebase-sdk";
|
|
3
|
+
import { SinglebaseElementBase } from "./base.js";
|
|
4
|
+
import "./account-screen.js";
|
|
5
|
+
import "./buttons.js";
|
|
6
|
+
/** Screens a signed-out visitor may reach. */
|
|
7
|
+
export type GuestScreen = "signin" | "signup" | "forgot" | "verify" | "otp" | "newpass" | "invite";
|
|
8
|
+
/** Screens that require a live session. */
|
|
9
|
+
export type ProtectedScreen = "account";
|
|
10
|
+
/** Every value `screen` accepts. "oauth-callback" is entered by URL, not by hand. */
|
|
11
|
+
export type Screen = GuestScreen | ProtectedScreen | "oauth-callback";
|
|
12
|
+
export declare function isGuestScreen(value: string): value is GuestScreen;
|
|
13
|
+
export declare function isProtectedScreen(value: string): value is ProtectedScreen;
|
|
14
|
+
export declare function isScreen(value: string): value is Screen;
|
|
15
|
+
/**
|
|
16
|
+
* The complete auth widget: routes among signin, signup, recovery, one-time
|
|
17
|
+
* code, invite and OAuth-callback views (spec: AuthScreen), and renders the
|
|
18
|
+
* design's full card chrome — brand panel, logo, method accordion, step bar,
|
|
19
|
+
* banner/notice, actions and footer.
|
|
20
|
+
*
|
|
21
|
+
* Structure and styling follow project/AuthWidget.dc.html; the individual
|
|
22
|
+
* sb-* form elements remain available for hosts that want to compose their
|
|
23
|
+
* own screen instead of using this one.
|
|
24
|
+
*/
|
|
25
|
+
export declare class SinglebaseAuthScreen extends SinglebaseElementBase {
|
|
26
|
+
static styles: import("lit").CSSResult[];
|
|
27
|
+
accessor initialScreen: Screen;
|
|
28
|
+
/**
|
|
29
|
+
* Two-way screen binding for SPA routers: set it to deep-link a screen,
|
|
30
|
+
* read it (or listen for `singlebase-screen-change`) to mirror the widget
|
|
31
|
+
* into your URL. `goto()` is the imperative form.
|
|
32
|
+
*/
|
|
33
|
+
accessor screen: Screen | "";
|
|
34
|
+
/**
|
|
35
|
+
* By default the widget swaps to the account view once authenticated.
|
|
36
|
+
* SPAs usually want to route away and unmount instead — set this and the
|
|
37
|
+
* widget renders nothing when signed in, leaving navigation to the host.
|
|
38
|
+
*/
|
|
39
|
+
accessor noAccountView: boolean;
|
|
40
|
+
accessor settings: AuthSettings | null;
|
|
41
|
+
accessor authEnabled: boolean;
|
|
42
|
+
accessor allowEmailSignin: boolean;
|
|
43
|
+
accessor allowEmailSignup: boolean;
|
|
44
|
+
accessor allowEmailOtp: boolean;
|
|
45
|
+
accessor allowOauth: boolean;
|
|
46
|
+
accessor allowAccountCreation: boolean;
|
|
47
|
+
accessor layout: "card" | "split";
|
|
48
|
+
accessor oauthPlacement: "top" | "bottom";
|
|
49
|
+
accessor stepped: boolean;
|
|
50
|
+
/**
|
|
51
|
+
* Branding is the host's, so these default to empty and the corresponding
|
|
52
|
+
* slots simply do not render. ("KEYRING" and friends were the fictional
|
|
53
|
+
* brand in the design mock — they must never ship as defaults.)
|
|
54
|
+
*/
|
|
55
|
+
accessor logoText: string;
|
|
56
|
+
/**
|
|
57
|
+
* A logo image, used instead of `logo-text` when set. `logo-text` is still
|
|
58
|
+
* worth supplying alongside it — it becomes the image's alt text.
|
|
59
|
+
*/
|
|
60
|
+
accessor logoUrl: string;
|
|
61
|
+
accessor brandLine: string;
|
|
62
|
+
accessor brandFoot: string;
|
|
63
|
+
accessor signInTitle: string;
|
|
64
|
+
/**
|
|
65
|
+
* The "Auth by Singlebase" credit under the card. On by default; turn it
|
|
66
|
+
* off with branding="false", exactly like the allow-* flags.
|
|
67
|
+
*
|
|
68
|
+
* It is a plain link — no pixel, no beacon, no request of any kind. A
|
|
69
|
+
* third-party network call originating from a sign-in form is the sort of
|
|
70
|
+
* thing that fails a security review, and deservedly.
|
|
71
|
+
*/
|
|
72
|
+
accessor branding: boolean;
|
|
73
|
+
/**
|
|
74
|
+
* Where to send the user once a session exists. Same-origin only — an
|
|
75
|
+
* absolute URL pointing anywhere else is refused, because a login widget
|
|
76
|
+
* that forwards to an attacker-supplied host is an open redirect.
|
|
77
|
+
*
|
|
78
|
+
* When unset, a `?redirect=` (or `?next=`) parameter on the current URL is
|
|
79
|
+
* used instead, so "you were sent to the login page, now go back where you
|
|
80
|
+
* were headed" works without the host wiring anything.
|
|
81
|
+
*/
|
|
82
|
+
accessor redirectUrl: string;
|
|
83
|
+
/** Shown as "Terms" in the consent line under the primary action. */
|
|
84
|
+
accessor tosUrl: string;
|
|
85
|
+
/** Shown as "Privacy" in the consent line under the primary action. */
|
|
86
|
+
accessor privacyUrl: string;
|
|
87
|
+
/** Where the OAuth nonce is stashed between starting a flow and returning. */
|
|
88
|
+
accessor nonceStorageKey: string;
|
|
89
|
+
accessor inviteEmail: string;
|
|
90
|
+
accessor inviteOrg: string;
|
|
91
|
+
/** Invite code carried by the invite link; when absent the widget asks for it. */
|
|
92
|
+
accessor inviteCode: string;
|
|
93
|
+
private accessor step;
|
|
94
|
+
private accessor method;
|
|
95
|
+
private accessor email;
|
|
96
|
+
private accessor password;
|
|
97
|
+
private accessor name;
|
|
98
|
+
private accessor firstName;
|
|
99
|
+
private accessor lastName;
|
|
100
|
+
private accessor phone;
|
|
101
|
+
private accessor newPass;
|
|
102
|
+
private accessor confirmPass;
|
|
103
|
+
private accessor code;
|
|
104
|
+
private accessor verifyNext;
|
|
105
|
+
private accessor loading;
|
|
106
|
+
private accessor banner;
|
|
107
|
+
private accessor notice;
|
|
108
|
+
private accessor emailError;
|
|
109
|
+
private accessor passError;
|
|
110
|
+
private accessor newPassError;
|
|
111
|
+
private accessor attempts;
|
|
112
|
+
private accessor needsMfaCode;
|
|
113
|
+
private settingsCtl;
|
|
114
|
+
private bridge;
|
|
115
|
+
private uidBase;
|
|
116
|
+
private uid;
|
|
117
|
+
willUpdate(changed: PropertyValues): void;
|
|
118
|
+
disconnectedCallback(): void;
|
|
119
|
+
private gotoUnsubscribe?;
|
|
120
|
+
private gotoClient;
|
|
121
|
+
/**
|
|
122
|
+
* Subscribes to auth.goto() so a host can drive every widget on the page
|
|
123
|
+
* with one call. Bound here rather than in connectedCallback because the
|
|
124
|
+
* app usually creates its SinglebaseAuth() instance *after* the widget
|
|
125
|
+
* upgrades — at connect time there is often no client to subscribe to yet.
|
|
126
|
+
*/
|
|
127
|
+
private bindGoto;
|
|
128
|
+
/** Shows a screen. The SPA-facing counterpart of the `screen` property. */
|
|
129
|
+
goto(screen: Screen): void;
|
|
130
|
+
private get cfg();
|
|
131
|
+
/** The screen the host asked for, before any guarding is applied. */
|
|
132
|
+
private get requestedScreen();
|
|
133
|
+
/**
|
|
134
|
+
* Resolves the screen actually rendered.
|
|
135
|
+
*
|
|
136
|
+
* This is the single place navigation is decided, so a protected screen
|
|
137
|
+
* cannot be reached by setting an attribute, editing the URL, or calling
|
|
138
|
+
* goto() — every route runs through the same auth check. The guest
|
|
139
|
+
* fallbacks below additionally reflect what the project has enabled.
|
|
140
|
+
*/
|
|
141
|
+
private get activeScreen();
|
|
142
|
+
/** True when a signed-in user is sitting on a guest screen (spec: offer continue/sign out). */
|
|
143
|
+
private get showsSignedInInterstitial();
|
|
144
|
+
/**
|
|
145
|
+
* The post-login destination, or null when there is none.
|
|
146
|
+
*
|
|
147
|
+
* Only same-origin targets are accepted. A login widget that forwards to
|
|
148
|
+
* an attacker-chosen host is a textbook open redirect, and the `redirect`
|
|
149
|
+
* parameter it reads comes straight off the URL bar, so it is untrusted
|
|
150
|
+
* input by definition.
|
|
151
|
+
*/
|
|
152
|
+
private get redirectTarget();
|
|
153
|
+
private get redirectParam();
|
|
154
|
+
/** Navigates to the post-login destination if there is a safe one. */
|
|
155
|
+
private followRedirect;
|
|
156
|
+
private get isOAuthCallback();
|
|
157
|
+
private accessor callbackDone;
|
|
158
|
+
/** Strips the provider's parameters so a reload cannot replay the exchange. */
|
|
159
|
+
private cleanCallbackUrl;
|
|
160
|
+
/**
|
|
161
|
+
* Completes the provider round trip. Runs once per landing: the URL is
|
|
162
|
+
* cleaned immediately so a refresh cannot resend a single-use access code.
|
|
163
|
+
*/
|
|
164
|
+
private handleOAuthCallback;
|
|
165
|
+
/** The small logo above a screen title: image when given one, else text. */
|
|
166
|
+
private renderLogo;
|
|
167
|
+
/** The larger logo in the split layout's brand panel. */
|
|
168
|
+
private renderBrandMark;
|
|
169
|
+
private renderBranding;
|
|
170
|
+
/** Every screen renders inside this, so the credit appears once and only once. */
|
|
171
|
+
private panel;
|
|
172
|
+
private renderOAuthCallback;
|
|
173
|
+
private setScreen;
|
|
174
|
+
private withLoading;
|
|
175
|
+
private describeError;
|
|
176
|
+
private get codeValue();
|
|
177
|
+
private onSubmit;
|
|
178
|
+
private abortController;
|
|
179
|
+
private signal;
|
|
180
|
+
private onCodeInput;
|
|
181
|
+
private onCodePaste;
|
|
182
|
+
private fillCode;
|
|
183
|
+
private onResend;
|
|
184
|
+
private goBack;
|
|
185
|
+
private titleFor;
|
|
186
|
+
private subFor;
|
|
187
|
+
private ctaFor;
|
|
188
|
+
private stepPct;
|
|
189
|
+
private renderPrimary;
|
|
190
|
+
private renderEmailField;
|
|
191
|
+
private renderPasswordField;
|
|
192
|
+
private renderCodeRow;
|
|
193
|
+
/** The email/password method accordion body for signin, signup and otp. */
|
|
194
|
+
private renderEmailMethodBody;
|
|
195
|
+
private renderAccordion;
|
|
196
|
+
private renderScreenBody;
|
|
197
|
+
private renderGuest;
|
|
198
|
+
private renderBlocked;
|
|
199
|
+
/**
|
|
200
|
+
* Shown when a signed-in visitor lands on a guest screen. Rather than
|
|
201
|
+
* presenting a sign-in form to someone already signed in, offer the two
|
|
202
|
+
* things they could actually want: carry on, or switch accounts.
|
|
203
|
+
*/
|
|
204
|
+
private renderSignedIn;
|
|
205
|
+
private onContinue;
|
|
206
|
+
private onInterstitialSignOut;
|
|
207
|
+
/** The Terms/Privacy consent line, rendered only when the host supplies a link. */
|
|
208
|
+
private renderConsent;
|
|
209
|
+
protected render(): import("lit").TemplateResult<1> | typeof nothing;
|
|
210
|
+
}
|
|
211
|
+
declare global {
|
|
212
|
+
interface HTMLElementTagNameMap {
|
|
213
|
+
"singlebase-authui": SinglebaseAuthScreen;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { LitElement, type PropertyValues } from "lit";
|
|
2
|
+
import { type AuthUIConfig } from "@singlebase/core";
|
|
3
|
+
import type { AuthClient, UserProfile } from "@singlebase/singlebase-sdk";
|
|
4
|
+
import { AuthController } from "../controllers/auth-controller.js";
|
|
5
|
+
import { defaultMessages, type SinglebaseAuthMessages } from "../messages.js";
|
|
6
|
+
/**
|
|
7
|
+
* Base class for every singlebase-* element: resolves an AuthClient, exposes
|
|
8
|
+
* .messages overrides, and gives subclasses a live AuthController bound to
|
|
9
|
+
* whichever client won.
|
|
10
|
+
*/
|
|
11
|
+
export declare abstract class SinglebaseElementBase extends LitElement {
|
|
12
|
+
static styles: import("lit").CSSResult[];
|
|
13
|
+
accessor client: AuthClient | null;
|
|
14
|
+
accessor messages: Partial<SinglebaseAuthMessages>;
|
|
15
|
+
/**
|
|
16
|
+
* theme/density/field-style are plain reflected attributes so a composing
|
|
17
|
+
* parent can forward them verbatim to the singlebase-* children it renders.
|
|
18
|
+
* CSS custom properties inherit through shadow boundaries on their own,
|
|
19
|
+
* but an *attribute* selector like :host([theme="dark"]) only matches
|
|
20
|
+
* the element that actually carries the attribute, so it must be
|
|
21
|
+
* explicitly propagated down the composition tree.
|
|
22
|
+
*/
|
|
23
|
+
accessor theme: "light" | "dark" | undefined;
|
|
24
|
+
accessor density: "comfortable" | "compact" | undefined;
|
|
25
|
+
/**
|
|
26
|
+
* Input chrome: boxed (default) or a single bottom rule. Reflected for the
|
|
27
|
+
* same reason as theme/density — the CSS is an attribute selector.
|
|
28
|
+
*/
|
|
29
|
+
accessor fieldStyle: "outline" | "underline" | undefined;
|
|
30
|
+
protected auth: AuthController;
|
|
31
|
+
/**
|
|
32
|
+
* Resolution order: an explicit `.client`, then the page's default
|
|
33
|
+
* SinglebaseClient()'s auth.
|
|
34
|
+
*
|
|
35
|
+
* That fallback is what lets several widgets sit on one page — a header
|
|
36
|
+
* sign-in, a modal, an account panel — and share one session with no
|
|
37
|
+
* wiring, since SinglebaseClient() is cached per project. A page talking to
|
|
38
|
+
* two projects sets `.client` on the widgets belonging to the second.
|
|
39
|
+
*
|
|
40
|
+
* Reading `.auth` is also what *constructs* auth: a page that only drops
|
|
41
|
+
* in a widget initializes it with no bootstrap code at all.
|
|
42
|
+
*/
|
|
43
|
+
get resolvedClient(): AuthClient | null;
|
|
44
|
+
/** Page-wide UI config from SinglebaseClient({ authui }), if there is one. */
|
|
45
|
+
get uiConfig(): AuthUIConfig | null;
|
|
46
|
+
/**
|
|
47
|
+
* Config messages sit *under* the element's own, so a page-wide translation
|
|
48
|
+
* can be overridden per widget without repeating the whole bag.
|
|
49
|
+
*/
|
|
50
|
+
get msg(): SinglebaseAuthMessages;
|
|
51
|
+
/** Config keys already applied, so a later render does not fight the host. */
|
|
52
|
+
private appliedConfig;
|
|
53
|
+
/**
|
|
54
|
+
* Copies the page-wide config onto this element.
|
|
55
|
+
*
|
|
56
|
+
* An attribute present in markup always wins — that is the whole precedence
|
|
57
|
+
* rule, and it is checked per key rather than wholesale, so a widget can
|
|
58
|
+
* override one thing and inherit the rest. Keys the element does not have
|
|
59
|
+
* are skipped, which is how one config can serve every singlebase-authui-*
|
|
60
|
+
* element without any of them knowing about the others.
|
|
61
|
+
*/
|
|
62
|
+
protected applyUIConfig(): void;
|
|
63
|
+
isAuthenticated(): boolean;
|
|
64
|
+
getUser(): UserProfile | null;
|
|
65
|
+
refreshSession(): Promise<import("@singlebase/singlebase-sdk").AuthSession | undefined>;
|
|
66
|
+
logout(): Promise<void>;
|
|
67
|
+
willUpdate(changed: PropertyValues): void;
|
|
68
|
+
}
|
|
69
|
+
export { defaultMessages };
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { nothing, type PropertyValues } from "lit";
|
|
2
|
+
import type { AuthSettings, OAuthIntent } from "@singlebase/singlebase-sdk";
|
|
3
|
+
import { SinglebaseFormBase } from "./form-base.js";
|
|
4
|
+
export type ButtonsType = "signout" | "oauth" | "link";
|
|
5
|
+
/**
|
|
6
|
+
* The widget's action buttons, selected with `type`:
|
|
7
|
+
*
|
|
8
|
+
* <singlebase-authui-buttons type="signout"></singlebase-authui-buttons>
|
|
9
|
+
* <singlebase-authui-buttons type="oauth" intent="signup"></singlebase-authui-buttons>
|
|
10
|
+
* <singlebase-authui-buttons type="link" provider="github"></singlebase-authui-buttons>
|
|
11
|
+
*
|
|
12
|
+
* They live under one tag rather than three because they are the same thing
|
|
13
|
+
* from a host's point of view — a button that performs one auth action — and
|
|
14
|
+
* every custom element name is a global, unversioned claim on any page this
|
|
15
|
+
* widget is dropped into.
|
|
16
|
+
*/
|
|
17
|
+
export declare class SinglebaseAuthButtons extends SinglebaseFormBase {
|
|
18
|
+
accessor type: ButtonsType;
|
|
19
|
+
/** oauth: which flow the provider buttons start. */
|
|
20
|
+
accessor intent: OAuthIntent;
|
|
21
|
+
/** link: the provider to connect to the signed-in account. */
|
|
22
|
+
accessor provider: string;
|
|
23
|
+
/** link: display name, when it differs from the provider id. */
|
|
24
|
+
accessor providerName: string;
|
|
25
|
+
/** Inside a card that already supplies chrome, drop this element's own. */
|
|
26
|
+
accessor embedded: boolean;
|
|
27
|
+
accessor settings: AuthSettings | null;
|
|
28
|
+
accessor nonceStorageKey: string;
|
|
29
|
+
private settingsCtl;
|
|
30
|
+
willUpdate(changed: PropertyValues): void;
|
|
31
|
+
private get effectiveSettings();
|
|
32
|
+
private get providers();
|
|
33
|
+
/** Shared by the oauth and link flows: nonce, start, redirect. */
|
|
34
|
+
private startOAuth;
|
|
35
|
+
private renderOAuth;
|
|
36
|
+
private renderLink;
|
|
37
|
+
private onSignOut;
|
|
38
|
+
private renderSignOut;
|
|
39
|
+
protected render(): import("lit").TemplateResult<1> | typeof nothing;
|
|
40
|
+
}
|
|
41
|
+
declare global {
|
|
42
|
+
interface HTMLElementTagNameMap {
|
|
43
|
+
"singlebase-authui-buttons": SinglebaseAuthButtons;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { nothing, type PropertyValues } from "lit";
|
|
2
|
+
import { SinglebaseElementBase } from "./base.js";
|
|
3
|
+
/**
|
|
4
|
+
* Displays one value from the signed-in profile, as text or as an avatar.
|
|
5
|
+
*
|
|
6
|
+
* Hello <singlebase-authui-display path="first_name" fallback="there"></singlebase-authui-display>
|
|
7
|
+
* <singlebase-authui-display avatar></singlebase-authui-display>
|
|
8
|
+
*
|
|
9
|
+
* Three rules shape the behaviour:
|
|
10
|
+
*
|
|
11
|
+
* - While auth state is still loading it renders nothing. Showing the
|
|
12
|
+
* fallback first would mean every signed-in visitor watches "Hello there"
|
|
13
|
+
* flip to "Hello Ada" on each page load.
|
|
14
|
+
* - Values resolve against `user_profile` and nothing else. The element
|
|
15
|
+
* deliberately cannot reach session or token data, which must never be
|
|
16
|
+
* written into the DOM.
|
|
17
|
+
* - Values are rendered as text, never as markup. Fields like first_name are
|
|
18
|
+
* supplied by the user at signup, so they are untrusted by definition.
|
|
19
|
+
*/
|
|
20
|
+
export declare class SinglebaseAuthDisplay extends SinglebaseElementBase {
|
|
21
|
+
static styles: import("lit").CSSResult[];
|
|
22
|
+
/**
|
|
23
|
+
* Dot-notated path into the profile: "first_name", "metadata.tier". In
|
|
24
|
+
* avatar mode it names the image field and defaults to "profile_photo".
|
|
25
|
+
*/
|
|
26
|
+
accessor path: string;
|
|
27
|
+
/** Shown when signed out, or when the value is empty. Text mode only. */
|
|
28
|
+
accessor fallback: string;
|
|
29
|
+
/** Render an image with an initials fallback instead of text. */
|
|
30
|
+
accessor avatar: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Alternative text for the avatar image. Left empty the image is treated as
|
|
33
|
+
* decorative, which is right when a name sits beside it — pass a value only
|
|
34
|
+
* when the avatar is the sole identifier.
|
|
35
|
+
*/
|
|
36
|
+
accessor alt: string;
|
|
37
|
+
/** Set when the image 404s, so a dead URL falls back to initials. */
|
|
38
|
+
private accessor imageFailed;
|
|
39
|
+
willUpdate(changed: PropertyValues): void;
|
|
40
|
+
private get profile();
|
|
41
|
+
/** The resolved value as a string, or "" when there is nothing to show. */
|
|
42
|
+
private get textValue();
|
|
43
|
+
private get avatarSrc();
|
|
44
|
+
protected render(): import("lit").TemplateResult<1> | typeof nothing;
|
|
45
|
+
private renderAvatar;
|
|
46
|
+
}
|
|
47
|
+
declare global {
|
|
48
|
+
interface HTMLElementTagNameMap {
|
|
49
|
+
"singlebase-authui-display": SinglebaseAuthDisplay;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { SinglebaseElementBase } from "./base.js";
|
|
2
|
+
export type FormPhase = "idle" | "submitting" | "success" | "error";
|
|
3
|
+
/**
|
|
4
|
+
* Adds the spec's required cross-cutting form behavior to SinglebaseElementBase:
|
|
5
|
+
* idle/submitting/success/error phase, a form-level error message, disabled
|
|
6
|
+
* controls while submitting, an aria-live status region, and cancellation of
|
|
7
|
+
* in-flight work on disconnect.
|
|
8
|
+
*/
|
|
9
|
+
export declare abstract class SinglebaseFormBase extends SinglebaseElementBase {
|
|
10
|
+
protected accessor phase: FormPhase;
|
|
11
|
+
protected accessor formError: string;
|
|
12
|
+
protected accessor noticeMessage: string;
|
|
13
|
+
protected abortController: AbortController | null;
|
|
14
|
+
disconnectedCallback(): void;
|
|
15
|
+
protected get signal(): AbortSignal;
|
|
16
|
+
/** Runs task, tracking idle→submitting→success/error and mapping errors to a message. */
|
|
17
|
+
protected run(task: () => Promise<void>): Promise<void>;
|
|
18
|
+
private isAbortError;
|
|
19
|
+
protected describeError(error: unknown): string;
|
|
20
|
+
protected get submitting(): boolean;
|
|
21
|
+
/** aria-live region for async status — spec requires one per form. */
|
|
22
|
+
protected renderStatus(): import("lit").TemplateResult<1>;
|
|
23
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { type Predicate } from "@singlebase/core";
|
|
2
|
+
import { SinglebaseElementBase } from "./base.js";
|
|
3
|
+
export type GuardState = "loading" | "authenticated" | "unauthenticated";
|
|
4
|
+
/**
|
|
5
|
+
* Renders content conditionally on auth state, and optionally on a predicate
|
|
6
|
+
* over the signed-in profile.
|
|
7
|
+
*
|
|
8
|
+
* Two shapes, because wrapping a single line of markup in a slotted <div> is
|
|
9
|
+
* a lot of ceremony:
|
|
10
|
+
*
|
|
11
|
+
* <!-- long form: one guard, several states -->
|
|
12
|
+
* <singlebase-authui-guard>
|
|
13
|
+
* <div slot="loading">Checking…</div>
|
|
14
|
+
* <div slot="authenticated">Welcome back</div>
|
|
15
|
+
* <div slot="unauthenticated"><singlebase-authui></singlebase-authui></div>
|
|
16
|
+
* </singlebase-authui-guard>
|
|
17
|
+
*
|
|
18
|
+
* <!-- short form: the guard itself carries the state -->
|
|
19
|
+
* <singlebase-authui-guard slot="loading">Checking…</singlebase-authui-guard>
|
|
20
|
+
*
|
|
21
|
+
* With a predicate it also filters on the profile:
|
|
22
|
+
*
|
|
23
|
+
* <singlebase-authui-guard slot="authenticated" predicate='{"roles":{"$in":["admin"]}}'>
|
|
24
|
+
* <admin-panel></admin-panel>
|
|
25
|
+
* </singlebase-authui-guard>
|
|
26
|
+
*
|
|
27
|
+
* A predicate only ever narrows the *authenticated* state: a signed-out
|
|
28
|
+
* visitor is unauthenticated whether or not a filter is present, so a guard
|
|
29
|
+
* can never leak content by failing open.
|
|
30
|
+
*/
|
|
31
|
+
export declare class SinglebaseAuthGuard extends SinglebaseElementBase {
|
|
32
|
+
/**
|
|
33
|
+
* Short form. When set, every child of this guard belongs to that state and
|
|
34
|
+
* no per-child `slot` attributes are needed.
|
|
35
|
+
*
|
|
36
|
+
* Note this reuses the standard `slot` attribute name, which is inert here:
|
|
37
|
+
* the guard is not itself inside another shadow root's slot, so the name is
|
|
38
|
+
* free to mean "the state these children belong to".
|
|
39
|
+
*/
|
|
40
|
+
accessor slot: string;
|
|
41
|
+
/**
|
|
42
|
+
* One-level, Mongo-style filter over the signed-in `user_profile`.
|
|
43
|
+
* Dot notation reaches nested values; see core's predicate module for the
|
|
44
|
+
* supported operators.
|
|
45
|
+
*/
|
|
46
|
+
accessor predicate: Predicate | null;
|
|
47
|
+
/**
|
|
48
|
+
* What to show when the user is signed in but the predicate does not match.
|
|
49
|
+
* Defaults to "unauthenticated" so a filtered guard degrades to the
|
|
50
|
+
* signed-out branch; set "hidden" to render nothing at all.
|
|
51
|
+
*/
|
|
52
|
+
accessor onMismatch: "unauthenticated" | "hidden";
|
|
53
|
+
private warnedFor;
|
|
54
|
+
protected createRenderRoot(): this;
|
|
55
|
+
/** The state this guard currently resolves to, after applying the predicate. */
|
|
56
|
+
get activeState(): GuardState | "hidden";
|
|
57
|
+
/** Surfaces a malformed predicate once, rather than silently never matching. */
|
|
58
|
+
private warnOnce;
|
|
59
|
+
render(): import("lit").TemplateResult<1>;
|
|
60
|
+
}
|
|
61
|
+
declare global {
|
|
62
|
+
interface HTMLElementTagNameMap {
|
|
63
|
+
"singlebase-authui-guard": SinglebaseAuthGuard;
|
|
64
|
+
}
|
|
65
|
+
}
|