@betterportal/auth-default 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -0
- package/bsb-plugin.json +23 -0
- package/bsb-tests.json +14 -0
- package/lib/defaultAuthManifest.d.ts +41 -0
- package/lib/defaultAuthManifest.d.ts.map +1 -0
- package/lib/defaultAuthManifest.js +67 -0
- package/lib/defaultAuthManifest.js.map +1 -0
- package/lib/index.d.ts +4 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +4 -0
- package/lib/index.js.map +1 -0
- package/lib/plugins/service-betterportal-auth-default/.bp-generated/registry.d.ts +3 -0
- package/lib/plugins/service-betterportal-auth-default/.bp-generated/registry.d.ts.map +1 -0
- package/lib/plugins/service-betterportal-auth-default/.bp-generated/registry.js +122 -0
- package/lib/plugins/service-betterportal-auth-default/.bp-generated/registry.js.map +1 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/login/_theme.bootstrap1/_nav.profile.d.ts +5 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/login/_theme.bootstrap1/_nav.profile.d.ts.map +1 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/login/_theme.bootstrap1/_nav.profile.js +10 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/login/_theme.bootstrap1/_nav.profile.js.map +1 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/login/_theme.bootstrap1/index.d.ts +4 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/login/_theme.bootstrap1/index.d.ts.map +1 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/login/_theme.bootstrap1/index.js +81 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/login/_theme.bootstrap1/index.js.map +1 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/login/index.d.ts +89 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/login/index.d.ts.map +1 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/login/index.js +186 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/login/index.js.map +1 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/logout/_theme.bootstrap1/index.d.ts +5 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/logout/_theme.bootstrap1/index.d.ts.map +1 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/logout/_theme.bootstrap1/index.js +7 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/logout/_theme.bootstrap1/index.js.map +1 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/logout/index.d.ts +25 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/logout/index.d.ts.map +1 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/logout/index.js +38 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/logout/index.js.map +1 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/refresh/index.d.ts +31 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/refresh/index.d.ts.map +1 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/refresh/index.js +92 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/refresh/index.js.map +1 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/register/_theme.bootstrap1/index.400.d.ts +7 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/register/_theme.bootstrap1/index.400.d.ts.map +1 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/register/_theme.bootstrap1/index.400.js +7 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/register/_theme.bootstrap1/index.400.js.map +1 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/register/_theme.bootstrap1/index.d.ts +4 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/register/_theme.bootstrap1/index.d.ts.map +1 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/register/_theme.bootstrap1/index.js +63 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/register/_theme.bootstrap1/index.js.map +1 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/register/index.d.ts +64 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/register/index.d.ts.map +1 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/register/index.js +106 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/register/index.js.map +1 -0
- package/lib/plugins/service-betterportal-auth-default/index.d.ts +115 -0
- package/lib/plugins/service-betterportal-auth-default/index.d.ts.map +1 -0
- package/lib/plugins/service-betterportal-auth-default/index.js +121 -0
- package/lib/plugins/service-betterportal-auth-default/index.js.map +1 -0
- package/lib/schemas/service-betterportal-auth-default.json +145 -0
- package/lib/schemas/service-betterportal-auth-default.plugin.json +158 -0
- package/lib/userStore.d.ts +52 -0
- package/lib/userStore.d.ts.map +1 -0
- package/lib/userStore.js +132 -0
- package/lib/userStore.js.map +1 -0
- package/package.json +68 -0
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import * as av from "anyvali";
|
|
2
|
+
import { createHandler } from "@betterportal/framework";
|
|
3
|
+
import { resolveDefaultAuthAppConfig } from "../../index.js";
|
|
4
|
+
export const QuerySchema = av.object({
|
|
5
|
+
action: av.optional(av.string()).describe("Optional login route action, currently supports logout."),
|
|
6
|
+
next: av.optional(av.string()).describe("The view path that redirected to the login page and should be redirected back to after a successful login.")
|
|
7
|
+
}, { unknownKeys: "strip" });
|
|
8
|
+
export const HeadersSchema = av.object({}, { unknownKeys: "strip" });
|
|
9
|
+
export const RequestSchema = av.object({
|
|
10
|
+
username: av.string().minLength(1).describe("Username for the account signing in."),
|
|
11
|
+
password: av.string().minLength(1).describe("Password for the account signing in."),
|
|
12
|
+
next: av.optional(av.string()).describe("The view path to soft-navigate to after a successful login.")
|
|
13
|
+
}, { unknownKeys: "strip" });
|
|
14
|
+
export const ResponseSchema = av.object({
|
|
15
|
+
status: av.enum_(["ok", "error"]).describe("Login request outcome."),
|
|
16
|
+
message: av.optional(av.string()).describe("Human-readable status or error message for the renderer."),
|
|
17
|
+
accessToken: av.optional(av.string()).describe("Signed JWT access token returned on successful login."),
|
|
18
|
+
refreshToken: av.optional(av.string()).describe("Signed JWT refresh token returned on successful login."),
|
|
19
|
+
expiresInSeconds: av.optional(av.int().min(1)).describe("Access token lifetime in seconds."),
|
|
20
|
+
user: av.optional(av.object({
|
|
21
|
+
id: av.string().describe("Stable UUIDv7 user id."),
|
|
22
|
+
username: av.string().describe("Account username."),
|
|
23
|
+
email: av.optional(av.string()).describe("User email address, when set."),
|
|
24
|
+
name: av.optional(av.string()).describe("Display name, when set.")
|
|
25
|
+
}, { unknownKeys: "strip" }).describe("Authenticated user summary.")),
|
|
26
|
+
// True while the auth service has zero users - the theme renderer redirects
|
|
27
|
+
// to the register view (first-admin setup) instead of showing the login form.
|
|
28
|
+
requiresFirstAdmin: av.optional(av.bool()).describe("True while the auth service has zero users; the theme renderer should redirect to first-admin registration instead of showing the login form."),
|
|
29
|
+
// Absolute URL of this auth service's register view (self-origin). Provided so
|
|
30
|
+
// the theme renderer can load it in-shell without knowing the auth origin.
|
|
31
|
+
firstAdminUrl: av.optional(av.string()).describe("Absolute self-origin URL of this auth service's register view so the theme can load it in-shell without knowing the auth origin."),
|
|
32
|
+
// True when the GET request already carried a valid access token - the theme
|
|
33
|
+
// renderer shows a "signed in" state instead of the login form.
|
|
34
|
+
alreadyLoggedIn: av.optional(av.bool()).describe("True when the GET request already carried a valid access token; the theme renderer should show a signed-in state instead of the login form."),
|
|
35
|
+
loggedOut: av.optional(av.bool()).describe("True after the login route handled ?action=logout."),
|
|
36
|
+
// Tenant-app path of the logout view (app config auth.logoutViewId).
|
|
37
|
+
logoutUrl: av.optional(av.string()).describe("Tenant-app path of the logout view from app auth config."),
|
|
38
|
+
// Echo of ?next= when already signed in - the theme renderer redirects there
|
|
39
|
+
// immediately instead of showing the signed-in card.
|
|
40
|
+
nextUrl: av.optional(av.string()).describe("Echo of the next path when already signed in; the theme renderer should redirect there immediately.")
|
|
41
|
+
}, { unknownKeys: "strip" });
|
|
42
|
+
export const title = "Login";
|
|
43
|
+
export const description = "Authenticate with username and password to receive a JWT.";
|
|
44
|
+
export const role = "auth.login";
|
|
45
|
+
export const dependencies = ["logout.index", "refresh.index", "register.index"];
|
|
46
|
+
export const chrome = { fullScreen: true };
|
|
47
|
+
export const auth = {
|
|
48
|
+
required: false,
|
|
49
|
+
permissions: []
|
|
50
|
+
};
|
|
51
|
+
export const cacheHints = {
|
|
52
|
+
ttlSeconds: 0,
|
|
53
|
+
varyBy: []
|
|
54
|
+
};
|
|
55
|
+
function runtimeFrom(ctx) {
|
|
56
|
+
const runtime = ctx.plugin?.runtime;
|
|
57
|
+
if (!runtime)
|
|
58
|
+
throw new Error("Auth runtime not available on handler context");
|
|
59
|
+
return runtime;
|
|
60
|
+
}
|
|
61
|
+
function normalizeRedirect(raw) {
|
|
62
|
+
const redirect = raw?.trim();
|
|
63
|
+
if (!redirect)
|
|
64
|
+
return "/";
|
|
65
|
+
if (redirect.startsWith("http://") || redirect.startsWith("https://"))
|
|
66
|
+
return redirect;
|
|
67
|
+
return redirect.startsWith("/") ? redirect : `/${redirect}`;
|
|
68
|
+
}
|
|
69
|
+
export const handleGet = createHandler({ response: ResponseSchema, query: QuerySchema }, (ctx) => {
|
|
70
|
+
const runtime = runtimeFrom(ctx);
|
|
71
|
+
const requiresFirstAdmin = runtime.userStore.hasNoUsers();
|
|
72
|
+
if (ctx.query.action === "logout") {
|
|
73
|
+
const config = resolveDefaultAuthAppConfig(ctx.config);
|
|
74
|
+
const nextUrl = normalizeRedirect(config.logoutRedirectPath);
|
|
75
|
+
ctx.bpHeaders?.remove("Authorization");
|
|
76
|
+
ctx.bpHeaders?.remove("X-BP-Refresh");
|
|
77
|
+
if (ctx.serviceId)
|
|
78
|
+
ctx.responseHeaders?.set("HX-Trigger", `bp:fragments:${ctx.serviceId}`);
|
|
79
|
+
return {
|
|
80
|
+
status: "ok",
|
|
81
|
+
message: "Signed out.",
|
|
82
|
+
loggedOut: true,
|
|
83
|
+
nextUrl
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
// Valid token already on the request - no point rendering a login form.
|
|
87
|
+
// First-admin setup still wins: a token can outlive a wiped user store.
|
|
88
|
+
if (ctx.user && !requiresFirstAdmin) {
|
|
89
|
+
const config = resolveDefaultAuthAppConfig(ctx.config);
|
|
90
|
+
const next = ctx.query.next || config.loginRedirectPath;
|
|
91
|
+
return {
|
|
92
|
+
status: "ok",
|
|
93
|
+
message: "Already signed in.",
|
|
94
|
+
alreadyLoggedIn: true,
|
|
95
|
+
user: {
|
|
96
|
+
id: ctx.user.sub,
|
|
97
|
+
username: ctx.user.name ?? ctx.user.email ?? ctx.user.sub,
|
|
98
|
+
email: ctx.user.email,
|
|
99
|
+
name: ctx.user.name
|
|
100
|
+
},
|
|
101
|
+
logoutUrl: "/login?action=logout",
|
|
102
|
+
...(next ? { nextUrl: next } : {})
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
let firstAdminUrl;
|
|
106
|
+
if (requiresFirstAdmin) {
|
|
107
|
+
const host = ctx.headers.host;
|
|
108
|
+
if (host) {
|
|
109
|
+
const proto = ctx.headers["x-forwarded-proto"] ?? "http";
|
|
110
|
+
const next = ctx.query.next;
|
|
111
|
+
firstAdminUrl = `${proto}://${host}/register${next ? `?next=${encodeURIComponent(next)}` : ""}`;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return {
|
|
115
|
+
status: "ok",
|
|
116
|
+
message: "Submit username + password via POST to authenticate.",
|
|
117
|
+
requiresFirstAdmin,
|
|
118
|
+
...(firstAdminUrl ? { firstAdminUrl } : {})
|
|
119
|
+
};
|
|
120
|
+
});
|
|
121
|
+
export const handlePost = createHandler({ response: ResponseSchema, request: RequestSchema, query: QuerySchema }, async (ctx) => {
|
|
122
|
+
const runtime = runtimeFrom(ctx);
|
|
123
|
+
const tenantId = ctx.tenant.id;
|
|
124
|
+
const appId = ctx.app.id;
|
|
125
|
+
const body = ctx.request;
|
|
126
|
+
const user = await runtime.userStore.authenticate(tenantId, appId, body.username, body.password);
|
|
127
|
+
if (!user) {
|
|
128
|
+
// Auth failures are 401, not 200-with-error-body.
|
|
129
|
+
ctx.setStatus?.(401);
|
|
130
|
+
return {
|
|
131
|
+
status: "error",
|
|
132
|
+
message: "Invalid username or password."
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
const issued = runtime.tokenIssuer.issueTokenPair({
|
|
136
|
+
sub: user.id,
|
|
137
|
+
tenantId: user.tenantId,
|
|
138
|
+
appId,
|
|
139
|
+
roles: user.roles,
|
|
140
|
+
name: user.name ?? user.username,
|
|
141
|
+
email: user.email,
|
|
142
|
+
picture: user.picture
|
|
143
|
+
});
|
|
144
|
+
if (!issued.refreshToken) {
|
|
145
|
+
throw new Error("Auth token issuer did not return a refresh token");
|
|
146
|
+
}
|
|
147
|
+
// Login is rendered in the theme's auth-only page state. After credentials
|
|
148
|
+
// are stored, navigate the browser back to the tenant route so the normal
|
|
149
|
+
// shell is rendered from a clean page request.
|
|
150
|
+
const config = resolveDefaultAuthAppConfig(ctx.config);
|
|
151
|
+
const next = body.next || ctx.query.next || config.loginRedirectPath || "/";
|
|
152
|
+
ctx.responseHeaders?.set("HX-Redirect", next);
|
|
153
|
+
// Auth state changed - reload this service's fragments (nav profile etc.).
|
|
154
|
+
// Fragments listening on this key re-fetch; absent fragments ignore it.
|
|
155
|
+
if (ctx.serviceId) {
|
|
156
|
+
ctx.responseHeaders?.set("HX-Trigger", `bp:fragments:${ctx.serviceId}`);
|
|
157
|
+
}
|
|
158
|
+
// Expire the stored header with the ACCESS token, not the refresh token -
|
|
159
|
+
// otherwise the client replays a dead JWT for days. The refresh token is
|
|
160
|
+
// stored separately so the profile fragment can renew before expiry.
|
|
161
|
+
ctx.bpHeaders?.set('Authorization', `Bearer ${issued.accessToken}`, {
|
|
162
|
+
expiresInSeconds: issued.accessTokenExpiresInSeconds,
|
|
163
|
+
locked: true,
|
|
164
|
+
refreshPath: "/refresh",
|
|
165
|
+
refreshBeforeSeconds: 60
|
|
166
|
+
});
|
|
167
|
+
ctx.bpHeaders?.set('X-BP-Refresh', issued.refreshToken, {
|
|
168
|
+
expiresInSeconds: issued.refreshTokenExpiresInSeconds ?? runtime.refreshTokenSeconds,
|
|
169
|
+
locked: true,
|
|
170
|
+
scopeToOwner: true
|
|
171
|
+
});
|
|
172
|
+
return {
|
|
173
|
+
status: "ok",
|
|
174
|
+
message: "logged in",
|
|
175
|
+
accessToken: issued.accessToken,
|
|
176
|
+
refreshToken: issued.refreshToken,
|
|
177
|
+
expiresInSeconds: issued.accessTokenExpiresInSeconds,
|
|
178
|
+
user: {
|
|
179
|
+
id: user.id,
|
|
180
|
+
username: user.username,
|
|
181
|
+
email: user.email,
|
|
182
|
+
name: user.name ?? user.username
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
});
|
|
186
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/plugins/service-betterportal-auth-default/bp-routes/login/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../src/plugins/service-betterportal-auth-default/bp-routes/logout/_theme.bootstrap1/index.tsx"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,wBAAgB,MAAM,CAAC,KAAK,EAAE,YAAY,GAAG,cAAc,CAU1D"}
|
package/lib/plugins/service-betterportal-auth-default/bp-routes/logout/_theme.bootstrap1/index.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { jsx as _jsx } from "jsx-htmx/jsx-runtime";
|
|
2
|
+
export function render(_data) {
|
|
3
|
+
// Transient view: the response's BP-RemoveHeader directives clear the stored
|
|
4
|
+
// tokens and HX-Location immediately soft-navigates to the login view.
|
|
5
|
+
return (_jsx("div", { class: "d-flex justify-content-center py-5", children: _jsx("div", { class: "spinner-border", role: "status", children: _jsx("span", { class: "visually-hidden", children: "Signing out..." }) }) }));
|
|
6
|
+
}
|
|
7
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../src/plugins/service-betterportal-auth-default/bp-routes/logout/_theme.bootstrap1/index.tsx"],"names":[],"mappings":";AAIA,MAAM,UAAU,MAAM,CAAC,KAAmB;IACxC,6EAA6E;IAC7E,uEAAuE;IACvE,OAAO,CACL,cAAK,KAAK,EAAC,oCAAoC,YAC7C,cAAK,KAAK,EAAC,gBAAgB,EAAC,IAAI,EAAC,QAAQ,YACvC,eAAM,KAAK,EAAC,iBAAiB,+BAAsB,GAC/C,GACF,CACP,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import * as av from "anyvali";
|
|
2
|
+
import type { Infer } from "anyvali";
|
|
3
|
+
import { type ApiAuthRequirement, type CacheHints } from "@betterportal/framework";
|
|
4
|
+
export declare const QuerySchema: av.ObjectSchema<{}>;
|
|
5
|
+
export declare const HeadersSchema: av.ObjectSchema<{}>;
|
|
6
|
+
export declare const RequestSchema: av.ObjectSchema<{}>;
|
|
7
|
+
export declare const ResponseSchema: av.ObjectSchema<{
|
|
8
|
+
status: av.EnumSchema<readonly ["ok"]>;
|
|
9
|
+
message: av.StringSchema;
|
|
10
|
+
}>;
|
|
11
|
+
export type ResponseData = Infer<typeof ResponseSchema>;
|
|
12
|
+
export declare const title = "Logout";
|
|
13
|
+
export declare const description = "Clear the authentication token from the client.";
|
|
14
|
+
export declare const role = "auth.logout";
|
|
15
|
+
export declare const auth: ApiAuthRequirement;
|
|
16
|
+
export declare const cacheHints: CacheHints;
|
|
17
|
+
export declare const handlePost: import("@betterportal/framework").RouteHandler<Record<string, string>, Record<string, unknown>, Record<string, string>, Record<string, unknown>, {
|
|
18
|
+
status: "ok";
|
|
19
|
+
message: string;
|
|
20
|
+
}, unknown, Record<string, unknown>>;
|
|
21
|
+
export declare const handleGet: import("@betterportal/framework").RouteHandler<Record<string, string>, Record<string, unknown>, Record<string, string>, Record<string, unknown>, {
|
|
22
|
+
status: "ok";
|
|
23
|
+
message: string;
|
|
24
|
+
}, unknown, Record<string, unknown>>;
|
|
25
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/plugins/service-betterportal-auth-default/bp-routes/logout/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAEL,KAAK,kBAAkB,EACvB,KAAK,UAAU,EAChB,MAAM,yBAAyB,CAAC;AAEjC,eAAO,MAAM,WAAW,qBAA0C,CAAC;AACnE,eAAO,MAAM,aAAa,qBAA0C,CAAC;AACrE,eAAO,MAAM,aAAa,qBAA0C,CAAC;AAErE,eAAO,MAAM,cAAc;;;EAGC,CAAC;AAC7B,MAAM,MAAM,YAAY,GAAG,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAExD,eAAO,MAAM,KAAK,WAAW,CAAC;AAC9B,eAAO,MAAM,WAAW,oDAAoD,CAAC;AAC7E,eAAO,MAAM,IAAI,gBAAgB,CAAC;AAElC,eAAO,MAAM,IAAI,EAAE,kBAGlB,CAAC;AAEF,eAAO,MAAM,UAAU,EAAE,UAGxB,CAAC;AAEF,eAAO,MAAM,UAAU;;;oCAkBtB,CAAC;AAEF,eAAO,MAAM,SAAS;;;oCAAa,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import * as av from "anyvali";
|
|
2
|
+
import { createHandler } from "@betterportal/framework";
|
|
3
|
+
export const QuerySchema = av.object({}, { unknownKeys: "strip" });
|
|
4
|
+
export const HeadersSchema = av.object({}, { unknownKeys: "strip" });
|
|
5
|
+
export const RequestSchema = av.object({}, { unknownKeys: "strip" });
|
|
6
|
+
export const ResponseSchema = av.object({
|
|
7
|
+
status: av.enum_(["ok"]).describe("Logout request outcome."),
|
|
8
|
+
message: av.string().describe("Human-readable logout status for the renderer.")
|
|
9
|
+
}, { unknownKeys: "strip" });
|
|
10
|
+
export const title = "Logout";
|
|
11
|
+
export const description = "Clear the authentication token from the client.";
|
|
12
|
+
export const role = "auth.logout";
|
|
13
|
+
export const auth = {
|
|
14
|
+
required: false,
|
|
15
|
+
permissions: []
|
|
16
|
+
};
|
|
17
|
+
export const cacheHints = {
|
|
18
|
+
ttlSeconds: 0,
|
|
19
|
+
varyBy: []
|
|
20
|
+
};
|
|
21
|
+
export const handlePost = createHandler({ response: ResponseSchema }, (ctx) => {
|
|
22
|
+
// Always emit BP-RemoveHeader so the client shim drops the stored token -
|
|
23
|
+
// logout must clear state even when called with a dead or missing token.
|
|
24
|
+
ctx.bpHeaders?.remove("Authorization");
|
|
25
|
+
ctx.bpHeaders?.remove("X-BP-Refresh");
|
|
26
|
+
// Compatibility shim: current UI uses /login?action=logout.
|
|
27
|
+
ctx.responseHeaders?.set("HX-Location", "/login?action=logout");
|
|
28
|
+
// Auth state changed - reload this service's fragments (nav profile etc.).
|
|
29
|
+
if (ctx.serviceId) {
|
|
30
|
+
ctx.responseHeaders?.set("HX-Trigger", `bp:fragments:${ctx.serviceId}`);
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
status: "ok",
|
|
34
|
+
message: "Client should clear stored Authorization header."
|
|
35
|
+
};
|
|
36
|
+
});
|
|
37
|
+
export const handleGet = handlePost;
|
|
38
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/plugins/service-betterportal-auth-default/bp-routes/logout/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAE9B,OAAO,EACL,aAAa,EAGd,MAAM,yBAAyB,CAAC;AAEjC,MAAM,CAAC,MAAM,WAAW,GAAG,EAAE,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC,CAAC;AACnE,MAAM,CAAC,MAAM,aAAa,GAAG,EAAE,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC,CAAC;AACrE,MAAM,CAAC,MAAM,aAAa,GAAG,EAAE,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC,CAAC;AAErE,MAAM,CAAC,MAAM,cAAc,GAAG,EAAE,CAAC,MAAM,CAAC;IACtC,MAAM,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,IAAI,CAAU,CAAC,CAAC,QAAQ,CAAC,yBAAyB,CAAC;IACrE,OAAO,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,gDAAgD,CAAC;CAChF,EAAE,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC,CAAC;AAG7B,MAAM,CAAC,MAAM,KAAK,GAAG,QAAQ,CAAC;AAC9B,MAAM,CAAC,MAAM,WAAW,GAAG,iDAAiD,CAAC;AAC7E,MAAM,CAAC,MAAM,IAAI,GAAG,aAAa,CAAC;AAElC,MAAM,CAAC,MAAM,IAAI,GAAuB;IACtC,QAAQ,EAAE,KAAK;IACf,WAAW,EAAE,EAAE;CAChB,CAAC;AAEF,MAAM,CAAC,MAAM,UAAU,GAAe;IACpC,UAAU,EAAE,CAAC;IACb,MAAM,EAAE,EAAE;CACX,CAAC;AAEF,MAAM,CAAC,MAAM,UAAU,GAAG,aAAa,CACrC,EAAE,QAAQ,EAAE,cAAc,EAAE,EAC5B,CAAC,GAAG,EAAE,EAAE;IACN,0EAA0E;IAC1E,yEAAyE;IACzE,GAAG,CAAC,SAAS,EAAE,MAAM,CAAC,eAAe,CAAC,CAAC;IACvC,GAAG,CAAC,SAAS,EAAE,MAAM,CAAC,cAAc,CAAC,CAAC;IACtC,4DAA4D;IAC5D,GAAG,CAAC,eAAe,EAAE,GAAG,CAAC,aAAa,EAAE,sBAAsB,CAAC,CAAC;IAChE,2EAA2E;IAC3E,IAAI,GAAG,CAAC,SAAS,EAAE,CAAC;QAClB,GAAG,CAAC,eAAe,EAAE,GAAG,CAAC,YAAY,EAAE,gBAAgB,GAAG,CAAC,SAAS,EAAE,CAAC,CAAC;IAC1E,CAAC;IACD,OAAO;QACL,MAAM,EAAE,IAAa;QACrB,OAAO,EAAE,kDAAkD;KAC5D,CAAC;AACJ,CAAC,CACF,CAAC;AAEF,MAAM,CAAC,MAAM,SAAS,GAAG,UAAU,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import * as av from "anyvali";
|
|
2
|
+
import type { Infer } from "anyvali";
|
|
3
|
+
import { type ApiAuthRequirement, type CacheHints } from "@betterportal/framework";
|
|
4
|
+
export declare const QuerySchema: av.ObjectSchema<{}>;
|
|
5
|
+
export declare const HeadersSchema: av.ObjectSchema<{
|
|
6
|
+
"x-bp-refresh": av.OptionalSchema<av.StringSchema>;
|
|
7
|
+
}>;
|
|
8
|
+
export declare const RequestSchema: av.ObjectSchema<{
|
|
9
|
+
refreshToken: av.OptionalSchema<av.StringSchema>;
|
|
10
|
+
}>;
|
|
11
|
+
export declare const ResponseSchema: av.ObjectSchema<{
|
|
12
|
+
status: av.EnumSchema<readonly ["ok", "error"]>;
|
|
13
|
+
message: av.OptionalSchema<av.StringSchema>;
|
|
14
|
+
accessToken: av.OptionalSchema<av.StringSchema>;
|
|
15
|
+
expiresInSeconds: av.OptionalSchema<av.IntSchema>;
|
|
16
|
+
}>;
|
|
17
|
+
export type ResponseData = Infer<typeof ResponseSchema>;
|
|
18
|
+
export declare const title = "Refresh Token";
|
|
19
|
+
export declare const description = "Exchange a refresh token for a new access token.";
|
|
20
|
+
export declare const role = "auth.refresh";
|
|
21
|
+
export declare const auth: ApiAuthRequirement;
|
|
22
|
+
export declare const cacheHints: CacheHints;
|
|
23
|
+
export declare const handlePost: import("@betterportal/framework").RouteHandler<Record<string, string>, Record<string, unknown>, Record<string, string>, {
|
|
24
|
+
refreshToken?: string | undefined;
|
|
25
|
+
}, {
|
|
26
|
+
status: "ok" | "error";
|
|
27
|
+
message?: string | undefined;
|
|
28
|
+
accessToken?: string | undefined;
|
|
29
|
+
expiresInSeconds?: number | undefined;
|
|
30
|
+
}, unknown, Record<string, unknown>>;
|
|
31
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/plugins/service-betterportal-auth-default/bp-routes/refresh/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAErC,OAAO,EAEL,KAAK,kBAAkB,EACvB,KAAK,UAAU,EAChB,MAAM,yBAAyB,CAAC;AAGjC,eAAO,MAAM,WAAW,qBAA0C,CAAC;AACnE,eAAO,MAAM,aAAa;;EAEE,CAAC;AAE7B,eAAO,MAAM,aAAa;;EAEE,CAAC;AAE7B,eAAO,MAAM,cAAc;;;;;EAKC,CAAC;AAC7B,MAAM,MAAM,YAAY,GAAG,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAExD,eAAO,MAAM,KAAK,kBAAkB,CAAC;AACrC,eAAO,MAAM,WAAW,qDAAqD,CAAC;AAC9E,eAAO,MAAM,IAAI,iBAAiB,CAAC;AAEnC,eAAO,MAAM,IAAI,EAAE,kBAGlB,CAAC;AAEF,eAAO,MAAM,UAAU,EAAE,UAGxB,CAAC;AAQF,eAAO,MAAM,UAAU;;;;;;;oCAiEtB,CAAC"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import * as av from "anyvali";
|
|
2
|
+
// Infer used at runtime cast for typed body access.
|
|
3
|
+
import { createHandler } from "@betterportal/framework";
|
|
4
|
+
export const QuerySchema = av.object({}, { unknownKeys: "strip" });
|
|
5
|
+
export const HeadersSchema = av.object({
|
|
6
|
+
"x-bp-refresh": av.optional(av.string().minLength(1))
|
|
7
|
+
}, { unknownKeys: "strip" });
|
|
8
|
+
export const RequestSchema = av.object({
|
|
9
|
+
refreshToken: av.optional(av.string().minLength(1).describe("Signed refresh token issued by the auth service."))
|
|
10
|
+
}, { unknownKeys: "strip" });
|
|
11
|
+
export const ResponseSchema = av.object({
|
|
12
|
+
status: av.enum_(["ok", "error"]).describe("Refresh request outcome."),
|
|
13
|
+
message: av.optional(av.string()).describe("Human-readable status or error message for the renderer."),
|
|
14
|
+
accessToken: av.optional(av.string()).describe("New signed JWT access token returned when refresh succeeds."),
|
|
15
|
+
expiresInSeconds: av.optional(av.int().min(1)).describe("New access token lifetime in seconds.")
|
|
16
|
+
}, { unknownKeys: "strip" });
|
|
17
|
+
export const title = "Refresh Token";
|
|
18
|
+
export const description = "Exchange a refresh token for a new access token.";
|
|
19
|
+
export const role = "auth.refresh";
|
|
20
|
+
export const auth = {
|
|
21
|
+
required: false,
|
|
22
|
+
permissions: []
|
|
23
|
+
};
|
|
24
|
+
export const cacheHints = {
|
|
25
|
+
ttlSeconds: 0,
|
|
26
|
+
varyBy: []
|
|
27
|
+
};
|
|
28
|
+
function runtimeFrom(ctx) {
|
|
29
|
+
const runtime = ctx.plugin?.runtime;
|
|
30
|
+
if (!runtime)
|
|
31
|
+
throw new Error("Auth runtime not available on handler context");
|
|
32
|
+
return runtime;
|
|
33
|
+
}
|
|
34
|
+
export const handlePost = createHandler({ response: ResponseSchema, request: RequestSchema }, async (ctx) => {
|
|
35
|
+
const runtime = runtimeFrom(ctx);
|
|
36
|
+
const tenantId = ctx.tenant.id;
|
|
37
|
+
const appId = ctx.app.id;
|
|
38
|
+
const body = ctx.request;
|
|
39
|
+
const headers = ctx.headers;
|
|
40
|
+
const refreshToken = body.refreshToken ?? headers["x-bp-refresh"];
|
|
41
|
+
if (!refreshToken) {
|
|
42
|
+
return {
|
|
43
|
+
status: "error",
|
|
44
|
+
message: "Refresh token missing."
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
let claims;
|
|
48
|
+
try {
|
|
49
|
+
claims = await runtime.tokenIssuer.verifyRefreshToken({
|
|
50
|
+
refreshToken,
|
|
51
|
+
tenantId,
|
|
52
|
+
appId
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return {
|
|
57
|
+
status: "error",
|
|
58
|
+
message: "Refresh token invalid or expired."
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
const user = runtime.userStore.findById(claims.sub);
|
|
62
|
+
if (!user || !user.enabled) {
|
|
63
|
+
return {
|
|
64
|
+
status: "error",
|
|
65
|
+
message: "User no longer exists or is disabled."
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
const roles = user.appRoles[appId] ?? [];
|
|
69
|
+
const issued = runtime.tokenIssuer.issueTokenPair({
|
|
70
|
+
sub: user.id,
|
|
71
|
+
tenantId: user.tenantId,
|
|
72
|
+
appId,
|
|
73
|
+
roles,
|
|
74
|
+
name: user.name ?? user.username,
|
|
75
|
+
email: user.email,
|
|
76
|
+
picture: user.picture
|
|
77
|
+
}, {
|
|
78
|
+
includeRefreshToken: false
|
|
79
|
+
});
|
|
80
|
+
ctx.bpHeaders?.set("Authorization", `Bearer ${issued.accessToken}`, {
|
|
81
|
+
expiresInSeconds: issued.accessTokenExpiresInSeconds,
|
|
82
|
+
locked: true,
|
|
83
|
+
refreshPath: "/refresh",
|
|
84
|
+
refreshBeforeSeconds: 60
|
|
85
|
+
});
|
|
86
|
+
return {
|
|
87
|
+
status: "ok",
|
|
88
|
+
accessToken: issued.accessToken,
|
|
89
|
+
expiresInSeconds: issued.accessTokenExpiresInSeconds
|
|
90
|
+
};
|
|
91
|
+
});
|
|
92
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/plugins/service-betterportal-auth-default/bp-routes/refresh/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 400 status view for register POST failures (duplicate user, weak input, no
|
|
3
|
+
* context). Same renderer as the main view - it re-renders the form with the
|
|
4
|
+
* error message so the response swaps into #bp-main like any other view.
|
|
5
|
+
*/
|
|
6
|
+
export { render } from "./index.js";
|
|
7
|
+
//# sourceMappingURL=index.400.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.400.d.ts","sourceRoot":"","sources":["../../../../../../src/plugins/service-betterportal-auth-default/bp-routes/register/_theme.bootstrap1/index.400.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 400 status view for register POST failures (duplicate user, weak input, no
|
|
3
|
+
* context). Same renderer as the main view - it re-renders the form with the
|
|
4
|
+
* error message so the response swaps into #bp-main like any other view.
|
|
5
|
+
*/
|
|
6
|
+
export { render } from "./index.js";
|
|
7
|
+
//# sourceMappingURL=index.400.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.400.js","sourceRoot":"","sources":["../../../../../../src/plugins/service-betterportal-auth-default/bp-routes/register/_theme.bootstrap1/index.400.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../src/plugins/service-betterportal-auth-default/bp-routes/register/_theme.bootstrap1/index.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AA6FhD,wBAAgB,MAAM,CAAC,IAAI,EAAE,YAAY,GAAG,cAAc,CAwCzD"}
|
package/lib/plugins/service-betterportal-auth-default/bp-routes/register/_theme.bootstrap1/index.js
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "jsx-htmx/jsx-runtime";
|
|
2
|
+
/** @jsxImportSource jsx-htmx */
|
|
3
|
+
import { js } from "jsx-htmx";
|
|
4
|
+
/** Tenant-relative path (+query) of an absolute self-origin URL, for hx-push-url. */
|
|
5
|
+
function pushPathOf(url, fallback) {
|
|
6
|
+
if (!url)
|
|
7
|
+
return fallback;
|
|
8
|
+
try {
|
|
9
|
+
const parsed = new URL(url);
|
|
10
|
+
return parsed.pathname + parsed.search;
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return fallback;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
/** Client-side confirm-password gate; everything else is server-rendered. */
|
|
17
|
+
function registerScript() {
|
|
18
|
+
// IIFE - htmx executes swapped <script> content as a plain script, where a
|
|
19
|
+
// top-level `return` inside a bare block is a SyntaxError.
|
|
20
|
+
return js(`(() => {
|
|
21
|
+
const form = document.getElementById("bp-register-form");
|
|
22
|
+
if (!form) return;
|
|
23
|
+
form.addEventListener("htmx:beforeRequest", (ev) => {
|
|
24
|
+
const errEl = document.getElementById("bp-register-error");
|
|
25
|
+
const pw = form.querySelector('input[name="password"]');
|
|
26
|
+
const confirm = document.getElementById("bp-register-confirm");
|
|
27
|
+
if (pw && confirm && pw.value !== confirm.value) {
|
|
28
|
+
if (errEl) { errEl.textContent = "Passwords do not match."; errEl.classList.remove("d-none"); }
|
|
29
|
+
ev.preventDefault();
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
if (errEl) errEl.classList.add("d-none");
|
|
33
|
+
});
|
|
34
|
+
})()`);
|
|
35
|
+
}
|
|
36
|
+
function redirectStub(url, pushUrl, label) {
|
|
37
|
+
return (_jsx("div", { "hx-get": url, "hx-trigger": "load delay:1200ms", "hx-target": "#bp-main", "hx-swap": "innerHTML", "hx-push-url": pushUrl, children: _jsx("div", { class: "d-flex justify-content-center py-3", children: _jsx("div", { class: "spinner-border spinner-border-sm", role: "status", children: _jsx("span", { class: "visually-hidden", children: label }) }) }) }));
|
|
38
|
+
}
|
|
39
|
+
function renderForm(data) {
|
|
40
|
+
return (_jsxs("div", { class: "container py-5", style: "max-width: 420px;", children: [_jsx("div", { class: "card border-0 shadow-sm", children: _jsxs("div", { class: "card-body", children: [_jsx("h3", { class: "card-title mb-1 text-center", children: "Create first admin" }), _jsx("p", { class: "text-secondary text-center small mb-4", children: "This deployment has no users yet. The account you create here becomes the platform administrator." }), _jsxs("form", { id: "bp-register-form", "hx-post": "this", "hx-target": "#bp-main", "hx-swap": "innerHTML", children: [_jsxs("div", { class: "mb-3", children: [_jsx("label", { class: "form-label", children: "Username *" }), _jsx("input", { type: "text", class: "form-control", name: "username", autocomplete: "username", required: true, autofocus: true })] }), _jsxs("div", { class: "mb-3", children: [_jsx("label", { class: "form-label", children: "Email" }), _jsx("input", { type: "email", class: "form-control", name: "email", autocomplete: "email" })] }), _jsxs("div", { class: "mb-3", children: [_jsxs("label", { class: "form-label", children: ["Password * ", _jsx("span", { class: "text-secondary small", children: "(min 8 chars)" })] }), _jsx("input", { type: "password", class: "form-control", name: "password", autocomplete: "new-password", minlength: "8", required: true })] }), _jsxs("div", { class: "mb-3", children: [_jsx("label", { class: "form-label", children: "Confirm password *" }), _jsx("input", { type: "password", class: "form-control", id: "bp-register-confirm", autocomplete: "new-password", minlength: "8", required: true })] }), _jsx("div", { class: `alert alert-danger ${data.status === "error" && data.message ? "" : "d-none"}`, id: "bp-register-error", children: data.status === "error" ? (data.message ?? "") : "" }), _jsx("button", { type: "submit", class: "btn btn-primary w-100", children: "Create admin account" })] })] }) }), _jsx("script", { children: registerScript() })] }));
|
|
41
|
+
}
|
|
42
|
+
export function render(data) {
|
|
43
|
+
// POST success - server-rendered confirmation, then on to sign-in.
|
|
44
|
+
if (data.status === "ok" && data.user) {
|
|
45
|
+
return (_jsx("div", { class: "container py-5", style: "max-width: 420px;", children: _jsx("div", { class: "card border-0 shadow-sm", children: _jsxs("div", { class: "card-body text-center", children: [_jsx("h3", { class: "card-title mb-2", children: "Admin created" }), _jsxs("div", { class: "alert alert-success mb-3", children: [_jsx("strong", { children: data.user.username }), " is now the platform administrator."] }), _jsx("p", { class: "text-secondary small mb-0", children: "Taking you to sign in..." }), data.loginUrl
|
|
46
|
+
? redirectStub(data.loginUrl, pushPathOf(data.loginUrl, "/login"), "Redirecting to sign in...")
|
|
47
|
+
: _jsx("a", { class: "btn btn-primary mt-3", href: "/login", children: "Sign in" })] }) }) }));
|
|
48
|
+
}
|
|
49
|
+
// POST validation error (rendered via the 400 status view) - form + message.
|
|
50
|
+
if (data.status === "error") {
|
|
51
|
+
return renderForm(data);
|
|
52
|
+
}
|
|
53
|
+
// GET with users already present - registrations are closed; bounce to login.
|
|
54
|
+
if (!data.registrationOpen) {
|
|
55
|
+
if (data.loginUrl) {
|
|
56
|
+
return redirectStub(data.loginUrl, pushPathOf(data.loginUrl, "/login"), "Redirecting to sign in...");
|
|
57
|
+
}
|
|
58
|
+
return (_jsx("div", { class: "container py-5", style: "max-width: 420px;", children: _jsxs("div", { class: "alert alert-secondary", children: ["Registration is closed. ", _jsx("a", { href: "/login", children: "Sign in" }), " instead."] }) }));
|
|
59
|
+
}
|
|
60
|
+
// GET, zero users - the first-admin form.
|
|
61
|
+
return renderForm(data);
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../src/plugins/service-betterportal-auth-default/bp-routes/register/_theme.bootstrap1/index.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import * as av from "anyvali";
|
|
2
|
+
import type { Infer } from "anyvali";
|
|
3
|
+
import { type ApiAuthRequirement, type CacheHints, type BetterPortalRouteChrome } from "@betterportal/framework";
|
|
4
|
+
export declare const QuerySchema: av.ObjectSchema<{
|
|
5
|
+
next: av.OptionalSchema<av.StringSchema>;
|
|
6
|
+
}>;
|
|
7
|
+
export declare const HeadersSchema: av.ObjectSchema<{}>;
|
|
8
|
+
export declare const RequestSchema: av.ObjectSchema<{
|
|
9
|
+
username: av.StringSchema;
|
|
10
|
+
password: av.StringSchema;
|
|
11
|
+
email: av.OptionalSchema<av.StringSchema>;
|
|
12
|
+
name: av.OptionalSchema<av.StringSchema>;
|
|
13
|
+
}>;
|
|
14
|
+
export declare const ResponseSchema: av.ObjectSchema<{
|
|
15
|
+
status: av.EnumSchema<readonly ["ok", "error"]>;
|
|
16
|
+
message: av.OptionalSchema<av.StringSchema>;
|
|
17
|
+
user: av.OptionalSchema<av.ObjectSchema<{
|
|
18
|
+
id: av.StringSchema;
|
|
19
|
+
username: av.StringSchema;
|
|
20
|
+
isFirstAdmin: av.BoolSchema;
|
|
21
|
+
}>>;
|
|
22
|
+
registrationOpen: av.OptionalSchema<av.BoolSchema>;
|
|
23
|
+
loginUrl: av.OptionalSchema<av.StringSchema>;
|
|
24
|
+
}>;
|
|
25
|
+
export type ResponseData = Infer<typeof ResponseSchema>;
|
|
26
|
+
export declare const title = "Register First Admin";
|
|
27
|
+
export declare const description = "Open registration for the very first user. Once any user exists, this endpoint requires admin auth.";
|
|
28
|
+
export declare const role = "auth.register";
|
|
29
|
+
export declare const dependencies: string[];
|
|
30
|
+
export declare const chrome: BetterPortalRouteChrome;
|
|
31
|
+
export declare const auth: ApiAuthRequirement;
|
|
32
|
+
export declare const cacheHints: CacheHints;
|
|
33
|
+
export declare const handleGet: import("@betterportal/framework").RouteHandler<Record<string, string>, {
|
|
34
|
+
next?: string | undefined;
|
|
35
|
+
}, Record<string, string>, Record<string, unknown>, {
|
|
36
|
+
status: "ok" | "error";
|
|
37
|
+
message?: string | undefined;
|
|
38
|
+
user?: {
|
|
39
|
+
id: string;
|
|
40
|
+
username: string;
|
|
41
|
+
isFirstAdmin: boolean;
|
|
42
|
+
} | undefined;
|
|
43
|
+
registrationOpen?: boolean | undefined;
|
|
44
|
+
loginUrl?: string | undefined;
|
|
45
|
+
}, unknown, Record<string, unknown>>;
|
|
46
|
+
export declare const handlePost: import("@betterportal/framework").RouteHandler<Record<string, string>, {
|
|
47
|
+
next?: string | undefined;
|
|
48
|
+
}, Record<string, string>, {
|
|
49
|
+
username: string;
|
|
50
|
+
password: string;
|
|
51
|
+
email?: string | undefined;
|
|
52
|
+
name?: string | undefined;
|
|
53
|
+
}, {
|
|
54
|
+
status: "ok" | "error";
|
|
55
|
+
message?: string | undefined;
|
|
56
|
+
user?: {
|
|
57
|
+
id: string;
|
|
58
|
+
username: string;
|
|
59
|
+
isFirstAdmin: boolean;
|
|
60
|
+
} | undefined;
|
|
61
|
+
registrationOpen?: boolean | undefined;
|
|
62
|
+
loginUrl?: string | undefined;
|
|
63
|
+
}, unknown, Record<string, unknown>>;
|
|
64
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/plugins/service-betterportal-auth-default/bp-routes/register/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAEL,KAAK,kBAAkB,EACvB,KAAK,UAAU,EACf,KAAK,uBAAuB,EAC7B,MAAM,yBAAyB,CAAC;AAGjC,eAAO,MAAM,WAAW;;EAEI,CAAC;AAC7B,eAAO,MAAM,aAAa,qBAA0C,CAAC;AAErE,eAAO,MAAM,aAAa;;;;;EAKE,CAAC;AAE7B,eAAO,MAAM,cAAc;;;;;;;;;;EAaC,CAAC;AAC7B,MAAM,MAAM,YAAY,GAAG,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAExD,eAAO,MAAM,KAAK,yBAAyB,CAAC;AAC5C,eAAO,MAAM,WAAW,wGAAwG,CAAC;AAEjI,eAAO,MAAM,IAAI,kBAAkB,CAAC;AACpC,eAAO,MAAM,YAAY,UAAkB,CAAC;AAC5C,eAAO,MAAM,MAAM,EAAE,uBAA8C,CAAC;AAEpE,eAAO,MAAM,IAAI,EAAE,kBAGlB,CAAC;AAEF,eAAO,MAAM,UAAU,EAAE,UAGxB,CAAC;AAgBF,eAAO,MAAM,SAAS;;;;;;;;;;;;oCAWrB,CAAC;AAEF,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;oCA8CtB,CAAC"}
|