@grasp-labs/ds-microfrontends-integration 0.10.1 → 0.11.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
CHANGED
|
@@ -220,7 +220,7 @@ npm install
|
|
|
220
220
|
|
|
221
221
|
### Dev auth middleware
|
|
222
222
|
|
|
223
|
-
`devAuthMiddleware`
|
|
223
|
+
`devAuthMiddleware` provides an optional login page for local development and injects `Authorization: Bearer <token>` for backend calls when a token is available. Mount it before anything that proxies to your APIs so outbound requests receive the token automatically.
|
|
224
224
|
|
|
225
225
|
```ts
|
|
226
226
|
import express from "express";
|
|
@@ -229,11 +229,20 @@ import { devAuthMiddleware } from "@grasp-labs/ds-microfrontends-integration/dev
|
|
|
229
229
|
const app = express();
|
|
230
230
|
|
|
231
231
|
app.use(devAuthMiddleware());
|
|
232
|
+
|
|
233
|
+
// Or with custom options:
|
|
234
|
+
app.use(
|
|
235
|
+
devAuthMiddleware({
|
|
236
|
+
loginPagePath: "/__login", // Path for the login page
|
|
237
|
+
afterLoginRedirectPath: "/", // Where to redirect after login
|
|
238
|
+
authServerLoginUrl: "https://auth-dev.grasp-daas.com/rest-auth/login/", // Auth server endpoint
|
|
239
|
+
}),
|
|
240
|
+
);
|
|
232
241
|
```
|
|
233
242
|
|
|
234
|
-
|
|
243
|
+
The login page is available at `/__login` (configurable via `loginPagePath`). Navigate there manually when you need to authenticate. Alternatively, set `DEV_AUTH_TOKEN` (env var or `.env` file) before starting the server to skip the login form entirely.
|
|
235
244
|
|
|
236
|
-
After a successful login, the middleware redirects to `process.env.VITE_BASE_PATH` when set, otherwise `/` (override via `
|
|
245
|
+
After a successful login, the middleware redirects to `process.env.VITE_BASE_PATH` when set, otherwise `/` (override via `afterLoginRedirectPath` if needed).
|
|
237
246
|
|
|
238
247
|
### Scripts
|
|
239
248
|
|
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
import { Request, Response, NextFunction } from 'express';
|
|
2
2
|
export type DevLoginOptions = {
|
|
3
3
|
/**
|
|
4
|
-
* Path segment or absolute path for the login page.
|
|
5
|
-
*
|
|
4
|
+
* Path segment or absolute path for the login page. Automatically prefixed
|
|
5
|
+
* with "/" when missing (e.g. "__login" => "/__login").
|
|
6
6
|
* @default "/__login"
|
|
7
7
|
*/
|
|
8
|
-
|
|
8
|
+
loginPagePath?: string;
|
|
9
9
|
/**
|
|
10
10
|
* Location to redirect to after successful authentication. The same
|
|
11
|
-
* normalization rules apply as for {@link
|
|
11
|
+
* normalization rules apply as for {@link loginPagePath}.
|
|
12
12
|
* @default process.env.VITE_BASE_PATH || "/"
|
|
13
13
|
*/
|
|
14
|
-
|
|
14
|
+
afterLoginRedirectPath?: string;
|
|
15
15
|
/**
|
|
16
|
-
* Fully qualified URL of the
|
|
16
|
+
* Fully qualified URL of the auth server login endpoint.
|
|
17
17
|
* @default "https://auth-dev.grasp-daas.com/rest-auth/login/"
|
|
18
18
|
*/
|
|
19
|
-
|
|
19
|
+
authServerLoginUrl?: string;
|
|
20
20
|
};
|
|
21
21
|
/**
|
|
22
22
|
* Simple Express middleware that forces a one-page dev login and then injects
|
|
@@ -37,7 +37,7 @@ export type DevLoginOptions = {
|
|
|
37
37
|
* app.use(devAuthMiddleware());
|
|
38
38
|
* ```
|
|
39
39
|
*/
|
|
40
|
-
export declare function devAuthMiddleware(options?: DevLoginOptions): (req: Request, res: Response, next: NextFunction) => Promise<any
|
|
40
|
+
export declare function devAuthMiddleware(options?: DevLoginOptions): (req: Request, res: Response, next: NextFunction) => Promise<void | Response<any, Record<string, any>>>;
|
|
41
41
|
/**
|
|
42
42
|
* Simple in-memory token store backed by process.env.
|
|
43
43
|
* Preloading `DEV_AUTH_TOKEN` (e.g. via a `.env` file or shell env var) skips the login flow entirely.
|
|
@@ -45,5 +45,4 @@ export declare function devAuthMiddleware(options?: DevLoginOptions): (req: Requ
|
|
|
45
45
|
export declare const DEV_AUTH_TOKEN: {
|
|
46
46
|
readonly get: () => string | undefined;
|
|
47
47
|
readonly set: (token: string) => void;
|
|
48
|
-
readonly exists: () => boolean;
|
|
49
48
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { urlencoded as
|
|
2
|
-
function
|
|
3
|
-
const
|
|
1
|
+
import { urlencoded as g } from "express";
|
|
2
|
+
function m(e) {
|
|
3
|
+
const o = e.loginPath, r = e.errorMessage ?? "", n = f(r);
|
|
4
4
|
return `<!DOCTYPE html>
|
|
5
5
|
<html lang="en">
|
|
6
6
|
<head>
|
|
@@ -99,7 +99,7 @@ function w(e) {
|
|
|
99
99
|
<p>Provide credentials to unlock the dev server</p>
|
|
100
100
|
</div>
|
|
101
101
|
|
|
102
|
-
<form method="POST" action="${
|
|
102
|
+
<form method="POST" action="${o}">
|
|
103
103
|
<input type="text" placeholder="Email" name="email" required />
|
|
104
104
|
<input
|
|
105
105
|
type="password"
|
|
@@ -116,9 +116,9 @@ function w(e) {
|
|
|
116
116
|
</body>
|
|
117
117
|
</html>`;
|
|
118
118
|
}
|
|
119
|
-
function
|
|
120
|
-
return e.replace(/[&<>"']/g, (
|
|
121
|
-
switch (
|
|
119
|
+
function f(e) {
|
|
120
|
+
return e.replace(/[&<>"']/g, (o) => {
|
|
121
|
+
switch (o) {
|
|
122
122
|
case "&":
|
|
123
123
|
return "&";
|
|
124
124
|
case "<":
|
|
@@ -130,72 +130,79 @@ function y(e) {
|
|
|
130
130
|
case "'":
|
|
131
131
|
return "'";
|
|
132
132
|
default:
|
|
133
|
-
return
|
|
133
|
+
return o;
|
|
134
134
|
}
|
|
135
135
|
});
|
|
136
136
|
}
|
|
137
|
-
function
|
|
138
|
-
const
|
|
139
|
-
|
|
140
|
-
redirectAfterLoginPathTo: r = process.env.VITE_BASE_PATH || "/",
|
|
141
|
-
authEndpoint: n = "https://auth-dev.grasp-daas.com/rest-auth/login/"
|
|
142
|
-
} = e, o = g(t, "loginPath"), c = g(
|
|
143
|
-
r,
|
|
144
|
-
"redirectAfterLoginPathTo"
|
|
145
|
-
), m = k();
|
|
146
|
-
return async (s, i, p) => {
|
|
147
|
-
if (l.exists())
|
|
148
|
-
return s.path === o ? i.redirect(c) : (s.headers.authorization = `Bearer ${l.get()}`, p());
|
|
149
|
-
if (s.path !== o)
|
|
150
|
-
return i.redirect(o);
|
|
151
|
-
if (s.method === "GET")
|
|
152
|
-
return d(i, o);
|
|
153
|
-
if (s.method === "POST") {
|
|
154
|
-
try {
|
|
155
|
-
await m(s, i);
|
|
156
|
-
} catch (a) {
|
|
157
|
-
return p(a);
|
|
158
|
-
}
|
|
159
|
-
const { email: u, password: h } = s.body ?? {};
|
|
160
|
-
if (!u || !h)
|
|
161
|
-
return d(i, o, "Email and password are required.");
|
|
162
|
-
try {
|
|
163
|
-
const { access_token: a } = await x({
|
|
164
|
-
email: u,
|
|
165
|
-
password: h,
|
|
166
|
-
authEndpoint: n
|
|
167
|
-
});
|
|
168
|
-
return l.set(a), i.redirect(303, c);
|
|
169
|
-
} catch (a) {
|
|
170
|
-
const f = `${a instanceof Error ? a.message : String(a)}`;
|
|
171
|
-
return d(i, o, f);
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
return i.status(405).send("Method Not Allowed");
|
|
175
|
-
};
|
|
137
|
+
function T(e = {}) {
|
|
138
|
+
const o = b(e);
|
|
139
|
+
return k(o), async (r, n, t) => r.path === o.loginPagePath ? r.method === "GET" ? d(n, o.loginPagePath) : r.method === "POST" ? y(o, r, n, t) : n.status(405).send("Method Not Allowed") : P(r, t);
|
|
176
140
|
}
|
|
177
|
-
function
|
|
178
|
-
const
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
141
|
+
function b(e) {
|
|
142
|
+
const o = (r) => r.startsWith("/") ? r : `/${r}`;
|
|
143
|
+
return {
|
|
144
|
+
loginPagePath: o(e.loginPagePath ?? "/__login"),
|
|
145
|
+
afterLoginRedirectPath: o(
|
|
146
|
+
e.afterLoginRedirectPath ?? process.env.VITE_BASE_PATH ?? "/"
|
|
147
|
+
),
|
|
148
|
+
authServerLoginUrl: e.authServerLoginUrl ?? "https://auth-dev.grasp-daas.com/rest-auth/login/"
|
|
149
|
+
};
|
|
184
150
|
}
|
|
185
|
-
function d(e,
|
|
151
|
+
function d(e, o, r) {
|
|
186
152
|
return e.type("html").send(
|
|
187
|
-
|
|
188
|
-
loginPath:
|
|
153
|
+
m({
|
|
154
|
+
loginPath: o,
|
|
189
155
|
errorMessage: r
|
|
190
156
|
})
|
|
191
157
|
);
|
|
192
158
|
}
|
|
193
|
-
function k() {
|
|
194
|
-
const
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
159
|
+
function k({ loginPagePath: e }) {
|
|
160
|
+
const o = "\x1B[33m", r = "\x1B[34m", n = "\x1B[2m", t = "\x1B[0m", a = "─".repeat(50), s = `${o}┌${a}┐${t}`, c = `${o}└${a}┘${t}`, i = process.env.HOST ?? "localhost", l = process.env.PORT ?? "XXXX", p = `${r}http://${i}:${l}${e}${t}`, h = [
|
|
161
|
+
s,
|
|
162
|
+
"",
|
|
163
|
+
" 🔐 Dev Auth Middleware Active",
|
|
164
|
+
"",
|
|
165
|
+
` Login: ${p}`,
|
|
166
|
+
"",
|
|
167
|
+
` ${n}Tip: Set DEV_AUTH_TOKEN env var to skip login${t}`,
|
|
168
|
+
"",
|
|
169
|
+
c
|
|
170
|
+
];
|
|
171
|
+
console.log(h.join(`
|
|
172
|
+
`));
|
|
173
|
+
}
|
|
174
|
+
async function y({
|
|
175
|
+
authServerLoginUrl: e,
|
|
176
|
+
loginPagePath: o,
|
|
177
|
+
afterLoginRedirectPath: r
|
|
178
|
+
}, n, t, a) {
|
|
179
|
+
try {
|
|
180
|
+
await w(n, t);
|
|
181
|
+
} catch (i) {
|
|
182
|
+
return a(i);
|
|
183
|
+
}
|
|
184
|
+
const { email: s, password: c } = n.body ?? {};
|
|
185
|
+
if (!s || !c)
|
|
186
|
+
return d(t, o, "Email and password are required.");
|
|
187
|
+
try {
|
|
188
|
+
const { access_token: i } = await x({
|
|
189
|
+
email: s,
|
|
190
|
+
password: c,
|
|
191
|
+
authServerLoginUrl: e
|
|
192
|
+
});
|
|
193
|
+
return u.set(i), t.redirect(303, r);
|
|
194
|
+
} catch (i) {
|
|
195
|
+
const l = `${i instanceof Error ? i.message : String(i)}`;
|
|
196
|
+
return d(t, o, l);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
const w = v();
|
|
200
|
+
function v() {
|
|
201
|
+
const e = g({ extended: !0 });
|
|
202
|
+
return (o, r) => new Promise((n, t) => {
|
|
203
|
+
e(o, r, (a) => {
|
|
204
|
+
if (a) {
|
|
205
|
+
t(a);
|
|
199
206
|
return;
|
|
200
207
|
}
|
|
201
208
|
n();
|
|
@@ -204,8 +211,8 @@ function k() {
|
|
|
204
211
|
}
|
|
205
212
|
async function x({
|
|
206
213
|
email: e,
|
|
207
|
-
password:
|
|
208
|
-
|
|
214
|
+
password: o,
|
|
215
|
+
authServerLoginUrl: r
|
|
209
216
|
}) {
|
|
210
217
|
const n = await fetch(r, {
|
|
211
218
|
method: "POST",
|
|
@@ -214,7 +221,7 @@ async function x({
|
|
|
214
221
|
},
|
|
215
222
|
body: JSON.stringify({
|
|
216
223
|
email: e,
|
|
217
|
-
password:
|
|
224
|
+
password: o,
|
|
218
225
|
remember_me: !0
|
|
219
226
|
})
|
|
220
227
|
});
|
|
@@ -222,21 +229,28 @@ async function x({
|
|
|
222
229
|
throw new Error(
|
|
223
230
|
`Authentication failed with status ${n.status} and response: ${await n.text()}`
|
|
224
231
|
);
|
|
225
|
-
const
|
|
226
|
-
if (!("access_token" in
|
|
232
|
+
const t = await n.json();
|
|
233
|
+
if (!("access_token" in t && "refresh_token" in t))
|
|
227
234
|
throw new Error(
|
|
228
|
-
`Authentication response is missing access_token or refresh_token: ${JSON.stringify(
|
|
235
|
+
`Authentication response is missing access_token or refresh_token: ${JSON.stringify(t, null, 2)}`
|
|
229
236
|
);
|
|
230
|
-
return
|
|
237
|
+
return t;
|
|
231
238
|
}
|
|
232
|
-
const
|
|
233
|
-
get: () =>
|
|
239
|
+
const u = {
|
|
240
|
+
get: () => {
|
|
241
|
+
const e = process.env.DEV_AUTH_TOKEN?.trim();
|
|
242
|
+
if (e && e.length > 0)
|
|
243
|
+
return e;
|
|
244
|
+
},
|
|
234
245
|
set: (e) => {
|
|
235
246
|
process.env.DEV_AUTH_TOKEN = e;
|
|
236
|
-
}
|
|
237
|
-
exists: () => typeof process.env.DEV_AUTH_TOKEN == "string" && process.env.DEV_AUTH_TOKEN.length > 0
|
|
247
|
+
}
|
|
238
248
|
};
|
|
249
|
+
async function P(e, o) {
|
|
250
|
+
const r = u.get();
|
|
251
|
+
return r && (e.headers.authorization = `Bearer ${r}`), o();
|
|
252
|
+
}
|
|
239
253
|
export {
|
|
240
|
-
|
|
241
|
-
|
|
254
|
+
u as DEV_AUTH_TOKEN,
|
|
255
|
+
T as devAuthMiddleware
|
|
242
256
|
};
|