@plitzi/sdk-auth 0.37.2 → 0.37.4

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.
@@ -1,15 +1,16 @@
1
1
  import { AuthContext as e } from "./AuthContext.mjs";
2
- import t from "./hooks/useAuth/index.mjs";
3
- import n from "./hooks/useSessionQueryReset/index.mjs";
4
- import { use as r, useMemo as i, useRef as a } from "react";
5
- import { QueryBuilderEvaluator as o } from "@plitzi/plitzi-ui/QueryBuilder";
6
- import s from "@plitzi/sdk-navigation/hooks/useNavigation";
7
- import { processTwig as c } from "@plitzi/sdk-shared/helpers/twigWrapper";
8
- import l from "@plitzi/sdk-shared/network/NetworkContext";
9
- import { useCommonStore as u, useRenderSettings as d } from "@plitzi/sdk-shared/store";
10
- import { jsx as f } from "react/jsx-runtime";
2
+ import { publishedSession as t } from "./helpers/publishedSession.mjs";
3
+ import n from "./hooks/useAuth/index.mjs";
4
+ import r from "./hooks/useSessionQueryReset/index.mjs";
5
+ import { use as i, useMemo as a, useRef as o } from "react";
6
+ import { QueryBuilderEvaluator as s } from "@plitzi/plitzi-ui/QueryBuilder";
7
+ import c from "@plitzi/sdk-navigation/hooks/useNavigation";
8
+ import { processTwig as l } from "@plitzi/sdk-shared/helpers/twigWrapper";
9
+ import u from "@plitzi/sdk-shared/network/NetworkContext";
10
+ import { useCommonStore as d, useRenderSettings as f } from "@plitzi/sdk-shared/store";
11
+ import { jsx as p } from "react/jsx-runtime";
11
12
  //#region src/AuthContextProvider.tsx
12
- var p = [
13
+ var m = [
13
14
  "loginUrl",
14
15
  "userUrl",
15
16
  "refreshUrl",
@@ -20,28 +21,29 @@ var p = [
20
21
  "expirationTimePath",
21
22
  "refreshExpirationTimePath",
22
23
  "sessionHintCookie",
23
- "sessionExchangeUrl"
24
- ], m = (e, t) => {
24
+ "sessionExchangeUrl",
25
+ "mfaUrl"
26
+ ], h = (e, t) => {
25
27
  if (e === t) return !0;
26
28
  if (!e || !t) return !1;
27
29
  let n = e, r = t;
28
30
  return [.../* @__PURE__ */ new Set([...Object.keys(n), ...Object.keys(r)])].every((e) => JSON.stringify(n[e]) === JSON.stringify(r[e]));
29
- }, h = ({ children: h, server: g }) => {
30
- let { previewMode: _, isHydrating: v, environment: y } = d(), { webKey: b } = r(l), [[x, S]] = u(["schema.settings", "schema.variables"]), { queryParams: C, hostname: w } = s({ server: g }), T = i(() => ({
31
- queryParams: C,
32
- hostname: w,
33
- environment: y
31
+ }, g = ({ children: g, server: _ }) => {
32
+ let { previewMode: v, isHydrating: y, environment: b } = f(), { webKey: x } = i(u), [[S, C]] = d(["schema.settings", "schema.variables"]), { queryParams: w, hostname: T } = c({ server: _ }), E = a(() => ({
33
+ queryParams: w,
34
+ hostname: T,
35
+ environment: b
34
36
  }), [
35
- C,
36
37
  w,
37
- y
38
- ]), E = i(() => Array.isArray(S) ? S.reduce((e, t) => {
38
+ T,
39
+ b
40
+ ]), D = a(() => Array.isArray(C) ? C.reduce((e, t) => {
39
41
  let { name: n, value: r, subValues: i } = t;
40
42
  if (!Array.isArray(i) || i.length === 0) return {
41
43
  ...e,
42
44
  [n]: r
43
45
  };
44
- let a = i.find((e) => o(e.when, T));
46
+ let a = i.find((e) => s(e.when, E));
45
47
  return a ? {
46
48
  ...e,
47
49
  [n]: a.value
@@ -49,57 +51,71 @@ var p = [
49
51
  ...e,
50
52
  [n]: r
51
53
  };
52
- }, {}) : {}, [S, T]), D = i(() => {
53
- let e = Object.fromEntries(p.map((e) => [e, x[e]]));
54
+ }, {}) : {}, [C, E]), O = a(() => {
55
+ let e = Object.fromEntries(m.map((e) => [e, S[e]]));
54
56
  try {
55
- return JSON.parse(c(JSON.stringify(e), E));
57
+ return JSON.parse(l(JSON.stringify(e), D));
56
58
  } catch {
57
59
  return e;
58
60
  }
59
- }, [x, E]), O = i(() => ({
60
- ...D,
61
- spaceKey: b,
62
- tokenStorage: x.tokenStorage ?? "localStorage",
63
- sessionGate: x.sessionGate,
64
- sessionRevalidateSeconds: x.sessionRevalidateSeconds
61
+ }, [S, D]), k = a(() => ({
62
+ ...O,
63
+ spaceKey: x,
64
+ tokenStorage: S.tokenStorage ?? "localStorage",
65
+ sessionGate: S.sessionGate,
66
+ sessionRevalidateSeconds: S.sessionRevalidateSeconds
65
67
  }), [
66
- D,
67
- x,
68
- b
69
- ]), { manager: k, loading: A, authenticated: j, state: M, bootstrapUser: N, bootstrapToken: P, peekedUser: F, peekedToken: I } = t({
70
- server: g,
71
- isHydrating: v,
72
- provider: x.userProvider ?? "",
73
- settings: O
74
- }), L = k.getProvider()?.user, R = L ?? N ?? F, z = L ? k.getProvider()?.token?.accessToken : N ? P : I, B = a(void 0), V = i(() => {
75
- let e = R ? {
76
- details: R,
77
- accessToken: z
78
- } : void 0, t = B.current;
79
- return t?.accessToken === e?.accessToken && m(t?.details, e?.details) || (B.current = e), B.current;
80
- }, [R, z]);
81
- n(j ? String(V?.details?.id ?? "") : "");
82
- let H = i(() => ({
83
- login: k.login.bind(k),
84
- refresh: k.refresh.bind(k),
85
- revalidate: k.revalidate.bind(k),
86
- invalidate: k.invalidate.bind(k),
87
- can: k.can.bind(k),
88
- logout: k.logout.bind(k),
89
- state: M,
90
- authenticated: j || !_,
91
- user: V
68
+ O,
69
+ S,
70
+ x
71
+ ]), { manager: A, loading: j, authenticated: M, state: N, bootstrapUser: P, bootstrapToken: F, peekedUser: I, peekedToken: L } = n({
72
+ server: _,
73
+ isHydrating: y,
74
+ provider: S.userProvider ?? "",
75
+ settings: k
76
+ }), R = A.getProvider(), { user: z, accessToken: B } = t({
77
+ state: N,
78
+ provider: {
79
+ user: R?.user,
80
+ accessToken: R?.token?.accessToken
81
+ },
82
+ bootstrap: {
83
+ user: P,
84
+ accessToken: F
85
+ },
86
+ peeked: {
87
+ user: I,
88
+ accessToken: L
89
+ }
90
+ }), V = o(void 0), H = a(() => {
91
+ let e = z ? {
92
+ details: z,
93
+ accessToken: B
94
+ } : void 0, t = V.current;
95
+ return t?.accessToken === e?.accessToken && h(t?.details, e?.details) || (V.current = e), V.current;
96
+ }, [z, B]);
97
+ r(M ? String(H?.details?.id ?? "") : "");
98
+ let U = a(() => ({
99
+ login: A.login.bind(A),
100
+ refresh: A.refresh.bind(A),
101
+ revalidate: A.revalidate.bind(A),
102
+ invalidate: A.invalidate.bind(A),
103
+ can: A.can.bind(A),
104
+ logout: A.logout.bind(A),
105
+ state: N,
106
+ authenticated: M || !v,
107
+ user: H
92
108
  }), [
93
- k,
109
+ A,
110
+ N,
94
111
  M,
95
- j,
96
- _,
97
- V
112
+ v,
113
+ H
98
114
  ]);
99
- return /* @__PURE__ */ f(e, {
100
- value: H,
101
- children: !A && h
115
+ return /* @__PURE__ */ p(e, {
116
+ value: U,
117
+ children: !j && g
102
118
  });
103
119
  };
104
120
  //#endregion
105
- export { h as default };
121
+ export { g as default };
@@ -1,15 +1,16 @@
1
1
  import e from "./providers/BasicAuthProvider.mjs";
2
+ import t from "./providers/ServerAuthProvider.mjs";
2
3
  //#region src/AuthManager.ts
3
- var t = /* @__PURE__ */ new Map([["basic", (t) => new e(t)]]), n = (e, n) => {
4
- t.set(e, n);
5
- }, r = () => [...t.keys()], i = class {
4
+ var n = /* @__PURE__ */ new Map([["basic", (t) => new e(t)], ["server", (e) => new t(e)]]), r = (e, t) => {
5
+ n.set(e, t);
6
+ }, i = () => [...n.keys()], a = class {
6
7
  providerType;
7
8
  provider;
8
9
  settings;
9
10
  listeners;
10
- constructor(e, n, r) {
11
- this.providerType = e, this.settings = r, this.listeners = Array.isArray(n) ? n : [n];
12
- let i = t.get(e);
11
+ constructor(e, t, r) {
12
+ this.providerType = e, this.settings = r, this.listeners = Array.isArray(t) ? t : [t];
13
+ let i = n.get(e);
13
14
  i && (this.provider = i(r), this.provider.on(this.listeners));
14
15
  }
15
16
  getProviderType() {
@@ -67,4 +68,4 @@ var t = /* @__PURE__ */ new Map([["basic", (t) => new e(t)]]), n = (e, n) => {
67
68
  }
68
69
  };
69
70
  //#endregion
70
- export { i as AuthManager, r as getAuthProviderNames, n as registerAuthProvider };
71
+ export { a as AuthManager, i as getAuthProviderNames, r as registerAuthProvider };
@@ -1,4 +1,4 @@
1
- import { AuthFailureReason, AuthResult, AuthState, LoginResult, Schema, TokenResult } from '@plitzi/sdk-shared';
1
+ import { AuthFailureReason, AuthResult, AuthState, LoginResult, MfaChallenge, Schema, TokenResult } from '@plitzi/sdk-shared';
2
2
  /** Why a state changed. Not control flow — it is what makes an auth trace readable, and every transition below
3
3
  * names one, because "it went to guest" is not a diagnosis and "the hint cookie was gone" is. */
4
4
  export type AuthStateReason = 'bootstrap' | 'restored' | 'granted' | 'renewed' | 'identity' | 'redirect' | 'exchanged' | 'no-provider' | 'skip-auth' | 'hint-missing' | 'no-evidence' | 'signed-out' | 'another-tab' | 'settled' | 'authenticating';
@@ -52,6 +52,15 @@ declare abstract class AuthProvider<U = Record<string, unknown>> {
52
52
  private readonly revalidateSeconds;
53
53
  private readonly listeners;
54
54
  private renewal?;
55
+ /**
56
+ * Which session an answer belongs to. Moved on by every change of WHO is signed in — a sign-out, a sign-in, another
57
+ * tab's — and read around each request that can adopt or end a session.
58
+ *
59
+ * An identity asked for before a sign-out and answered after it carried the old cookie, so it came back `ok` and
60
+ * signed the person straight back in: the router then read a session on the guest's own page and answered "Access
61
+ * Denied". Under load that is not a corner case — the account screen asks who is there while its button is pressed.
62
+ */
63
+ private epoch;
55
64
  private renewalTimer?;
56
65
  private detachListeners?;
57
66
  /** Set when a request failed to reach the backend, so the next `online` event retries instead of waiting. */
@@ -65,7 +74,8 @@ declare abstract class AuthProvider<U = Record<string, unknown>> {
65
74
  /** The backend's endpoints. Refusals reported from elsewhere are matched against these, so a session only reacts
66
75
  * to the API that issued it. Empty means "unknown", and then every refusal is taken as ours. */
67
76
  protected get endpoints(): string[];
68
- protected abstract requestLogin(params: Record<string, unknown>): Promise<AuthResult<U>>;
77
+ /** A session, why there is none — or, for an account with a second factor, the challenge its code completes. */
78
+ protected abstract requestLogin(params: Record<string, unknown>): Promise<AuthResult<U> | MfaChallenge>;
69
79
  protected abstract requestRenewal(refreshToken?: string): Promise<AuthResult<U>>;
70
80
  protected abstract requestIdentity(): Promise<AuthResult<U>>;
71
81
  protected abstract requestLogout(): Promise<void>;
@@ -12,6 +12,7 @@ var s = 60, c = 300, l = 2147483647, u = class {
12
12
  revalidateSeconds;
13
13
  listeners = /* @__PURE__ */ new Set();
14
14
  renewal;
15
+ epoch = 0;
15
16
  renewalTimer;
16
17
  detachListeners;
17
18
  offline = !1;
@@ -126,8 +127,9 @@ var s = 60, c = 300, l = 2147483647, u = class {
126
127
  clearTimeout(this.renewalTimer), this.renewalTimer = void 0, this.detachListeners?.(), this.detachListeners = void 0;
127
128
  }
128
129
  async login(e) {
129
- this.setState("authenticating", "authenticating");
130
+ this.epoch += 1, this.setState("authenticating", "authenticating");
130
131
  let t = await this.requestLogin(e);
132
+ if (!t.ok && t.reason === "mfa") return this.endSession(), t;
131
133
  if (!t.ok) return this.endSession(t.reason), {
132
134
  ok: !1,
133
135
  reason: t.reason
@@ -206,12 +208,12 @@ var s = 60, c = 300, l = 2147483647, u = class {
206
208
  }), this.renewal;
207
209
  }
208
210
  async performRenewal() {
209
- let e = await this.requestRenewal(this.session.token?.refreshToken ?? void 0);
210
- return e.ok ? (this.adopt(e), !this.session.user && this.capabilities.identity ? this.loadIdentity() : this.state === "authenticated") : (this.handleFailure(e.reason), !1);
211
+ let e = this.epoch, t = await this.requestRenewal(this.session.token?.refreshToken ?? void 0);
212
+ return e === this.epoch ? t.ok ? (this.adopt(t), !this.session.user && this.capabilities.identity ? this.loadIdentity() : this.state === "authenticated") : (this.handleFailure(t.reason), !1) : this.state === "authenticated";
211
213
  }
212
214
  async loadIdentity() {
213
- let e = await this.requestIdentity();
214
- return e.ok ? (this.adopt(e), this.state === "authenticated") : e.reason === "expired" && this.capabilities.renew && !this.renewal ? this.renew() : (this.handleFailure(e.reason), !1);
215
+ let e = this.epoch, t = await this.requestIdentity();
216
+ return e === this.epoch ? t.ok ? (this.adopt(t), this.state === "authenticated") : t.reason === "expired" && this.capabilities.renew && !this.renewal ? this.renew() : (this.handleFailure(t.reason), !1) : this.state === "authenticated";
215
217
  }
216
218
  handleFailure(e) {
217
219
  if (e === "network") {
@@ -228,7 +230,7 @@ var s = 60, c = 300, l = 2147483647, u = class {
228
230
  }, this.offline = !1, t && this.store.write(this.session), this.scheduleRenewal(), this.setState(this.session.user || this.hasLiveToken() ? "authenticated" : "guest", r);
229
231
  }
230
232
  endSession(e, t = "signed-out") {
231
- clearTimeout(this.renewalTimer), this.renewalTimer = void 0, this.session = { validatedAt: n() }, this.store.write(this.session), this.setState("guest", t), e && this.emit({
233
+ this.epoch += 1, clearTimeout(this.renewalTimer), this.renewalTimer = void 0, this.session = { validatedAt: n() }, this.store.write(this.session), this.setState("guest", t), e && this.emit({
232
234
  type: "expired",
233
235
  reason: e
234
236
  });
@@ -282,7 +284,7 @@ var s = 60, c = 300, l = 2147483647, u = class {
282
284
  };
283
285
  }
284
286
  adoptFromStorage(e) {
285
- if (!e?.token && !e?.user) {
287
+ if (this.epoch += 1, !e?.token && !e?.user) {
286
288
  clearTimeout(this.renewalTimer), this.renewalTimer = void 0, this.session = { validatedAt: n() }, this.setState("guest", "another-tab"), this.emit({ type: "logout" });
287
289
  return;
288
290
  }
@@ -0,0 +1,31 @@
1
+ import { AuthState } from '@plitzi/sdk-shared';
2
+ /**
3
+ * Who the page says is signed in, from what is known about them at this moment.
4
+ *
5
+ * The provider is authoritative once it has settled. Before that — the first commit of a server-rendered page, a reload
6
+ * with a session in storage — it holds nothing yet, and the page falls back to what it already knows: the user the
7
+ * server rendered with (`bootstrap`), then the one peeked from storage (`peeked`).
8
+ *
9
+ * Only before. Once the provider says `guest` there is nobody, whatever the page was rendered with: the fallback used
10
+ * to outlive a sign-out, so the page went on publishing the previous person and every flow that asked "is anybody
11
+ * signed in?" was told yes — a sign-in screen that sends signed-in visitors to their account sent this one there, the
12
+ * account (rightly) sent them back, and the two bounced off each other hundreds of times a second.
13
+ */
14
+ export declare const publishedSession: <U>(input: {
15
+ state: AuthState;
16
+ provider?: {
17
+ user?: U;
18
+ accessToken?: string;
19
+ };
20
+ bootstrap?: {
21
+ user?: U;
22
+ accessToken?: string;
23
+ };
24
+ peeked?: {
25
+ user?: U;
26
+ accessToken?: string;
27
+ };
28
+ }) => {
29
+ user?: U;
30
+ accessToken?: string;
31
+ };
@@ -0,0 +1,16 @@
1
+ //#region src/helpers/publishedSession.ts
2
+ var e = (e) => {
3
+ let { state: t, provider: n, bootstrap: r, peeked: i } = e;
4
+ return n?.user ? {
5
+ user: n.user,
6
+ accessToken: n.accessToken
7
+ } : t === "guest" ? {} : r?.user ? {
8
+ user: r.user,
9
+ accessToken: r.accessToken
10
+ } : i?.user ? {
11
+ user: i.user,
12
+ accessToken: i.accessToken
13
+ } : {};
14
+ };
15
+ //#endregion
16
+ export { e as publishedSession };
@@ -1,11 +1,12 @@
1
1
  import { default as AuthProvider, AuthProviderProps } from '../AuthProvider';
2
- import { AuthResult, Schema } from '@plitzi/sdk-shared';
2
+ import { AuthResult, MfaChallenge, Schema } from '@plitzi/sdk-shared';
3
3
  export type BasicAuthProviderProps = AuthProviderProps & {
4
4
  loginUrl?: string;
5
5
  userUrl?: string;
6
6
  refreshUrl?: string;
7
7
  logoutUrl?: string;
8
8
  sessionExchangeUrl?: Schema['settings']['sessionExchangeUrl'];
9
+ mfaUrl?: Schema['settings']['mfaUrl'];
9
10
  detailsPath?: Schema['settings']['detailsPath'];
10
11
  tokenPath?: Schema['settings']['tokenPath'];
11
12
  refreshTokenPath?: Schema['settings']['refreshTokenPath'];
@@ -15,7 +16,7 @@ export type BasicAuthProviderProps = AuthProviderProps & {
15
16
  declare class BasicAuthProvider<U = Record<string, unknown>> extends AuthProvider<U> {
16
17
  readonly name = "basic";
17
18
  private readonly options;
18
- constructor({ loginUrl, userUrl, refreshUrl, logoutUrl, sessionExchangeUrl, detailsPath, tokenPath, refreshTokenPath, expirationTimePath, refreshExpirationTimePath, ...providerProps }?: BasicAuthProviderProps);
19
+ constructor({ loginUrl, userUrl, refreshUrl, logoutUrl, sessionExchangeUrl, mfaUrl, detailsPath, tokenPath, refreshTokenPath, expirationTimePath, refreshExpirationTimePath, ...providerProps }?: BasicAuthProviderProps);
19
20
  protected get capabilities(): {
20
21
  renew: boolean;
21
22
  identity: boolean;
@@ -29,7 +30,7 @@ declare class BasicAuthProvider<U = Record<string, unknown>> extends AuthProvide
29
30
  * taken on trust: an unusable token is refused here rather than becoming a session that fails on its first real
30
31
  * request. `mode: 'normal'` (the default) posts the credentials.
31
32
  */
32
- protected requestLogin(params: Record<string, unknown>): Promise<AuthResult<U>>;
33
+ protected requestLogin(params: Record<string, unknown>): Promise<AuthResult<U> | MfaChallenge>;
33
34
  /** Confirms a token by asking who it belongs to, and only then reports it as a session. */
34
35
  private adoptToken;
35
36
  /**
@@ -46,6 +47,11 @@ declare class BasicAuthProvider<U = Record<string, unknown>> extends AuthProvide
46
47
  protected requestIdentity(): Promise<AuthResult<U>>;
47
48
  protected requestLogout(): Promise<void>;
48
49
  private request;
50
+ /**
51
+ * A right password on an account with a second factor: a challenge, not a session with nobody in it. Read as the
52
+ * latter it ended as `inactive`, and told somebody with a perfectly good account that it could not be used.
53
+ */
54
+ private challengeOf;
49
55
  private toResult;
50
56
  private tokenFrom;
51
57
  }
@@ -11,18 +11,19 @@ var r = (e, n) => t(e, n, void 0), i = (e, t) => {
11
11
  }, o = (e) => typeof e == "string" ? e : typeof e == "number" && Number.isFinite(e) ? String(e) : "", s = class extends e {
12
12
  name = "basic";
13
13
  options;
14
- constructor({ loginUrl: e = "", userUrl: t = "", refreshUrl: n = "", logoutUrl: r = "", sessionExchangeUrl: i = "", detailsPath: a = "details", tokenPath: o = "access_token", refreshTokenPath: s = "refresh_token", expirationTimePath: c = "expire_at", refreshExpirationTimePath: l = "refresh_expire_at", ...u } = {}) {
15
- super(u), this.options = {
14
+ constructor({ loginUrl: e = "", userUrl: t = "", refreshUrl: n = "", logoutUrl: r = "", sessionExchangeUrl: i = "", mfaUrl: a = "", detailsPath: o = "details", tokenPath: s = "access_token", refreshTokenPath: c = "refresh_token", expirationTimePath: l = "expire_at", refreshExpirationTimePath: u = "refresh_expire_at", ...d } = {}) {
15
+ super(d), this.options = {
16
16
  loginUrl: e,
17
17
  userUrl: t,
18
18
  refreshUrl: n,
19
19
  logoutUrl: r,
20
20
  sessionExchangeUrl: i,
21
- detailsPath: a,
22
- tokenPath: o,
23
- refreshTokenPath: s,
24
- expirationTimePath: c,
25
- refreshExpirationTimePath: l
21
+ mfaUrl: a,
22
+ detailsPath: o,
23
+ tokenPath: s,
24
+ refreshTokenPath: c,
25
+ expirationTimePath: l,
26
+ refreshExpirationTimePath: u
26
27
  };
27
28
  }
28
29
  get capabilities() {
@@ -46,6 +47,19 @@ var r = (e, n) => t(e, n, void 0), i = (e, t) => {
46
47
  ok: !1,
47
48
  reason: "missing"
48
49
  };
50
+ if (e.mode === "mfa") {
51
+ let t = o(e.mfaToken).trim();
52
+ return !this.options.mfaUrl || !t ? {
53
+ ok: !1,
54
+ reason: "missing"
55
+ } : this.toResult(await this.request(this.options.mfaUrl, {
56
+ method: "POST",
57
+ body: JSON.stringify({
58
+ mfaToken: t,
59
+ code: o(e.code).trim()
60
+ })
61
+ }));
62
+ }
49
63
  if (!this.options.loginUrl) return {
50
64
  ok: !1,
51
65
  reason: "missing"
@@ -57,7 +71,7 @@ var r = (e, n) => t(e, n, void 0), i = (e, t) => {
57
71
  password: o(e.password)
58
72
  })
59
73
  });
60
- return this.toResult(n);
74
+ return this.challengeOf(n) ?? this.toResult(n);
61
75
  }
62
76
  async adoptToken(e) {
63
77
  if (!this.options.userUrl) return {
@@ -145,6 +159,14 @@ var r = (e, n) => t(e, n, void 0), i = (e, t) => {
145
159
  reason: r.ok ? void 0 : n(r.status, i) ?? "network"
146
160
  };
147
161
  }
162
+ challengeOf(e) {
163
+ let t = r(e.data, "mfaToken");
164
+ return !e.reason && r(e.data, "mfaRequired") === !0 && typeof t == "string" && t ? {
165
+ ok: !1,
166
+ reason: "mfa",
167
+ mfaToken: t
168
+ } : void 0;
169
+ }
148
170
  toResult(e) {
149
171
  if (e.reason) return {
150
172
  ok: !1,
@@ -0,0 +1,41 @@
1
+ import { default as AuthProvider, AuthProviderProps } from '../AuthProvider';
2
+ import { AuthResult, MfaChallenge } from '@plitzi/sdk-shared';
3
+ export type ServerAuthProviderProps = AuthProviderProps & {
4
+ /** Where the page server starts a sign-in by redirect. Default `/auth/sign-in` (`createServer({ signIn })`). */
5
+ loginUrl?: string;
6
+ /** Where the page server ends the session on its host. Default `/auth/logout`. */
7
+ logoutUrl?: string;
8
+ };
9
+ /**
10
+ * A space whose people sign in THROUGH the server that renders it: signing in is a redirect out and back
11
+ * (`createServer({ signIn })` — "Sign in with Plitzi" on the platform), and the session is a cookie on the space's
12
+ * own host that the page never holds.
13
+ *
14
+ * So there is nothing here to store, renew or ask about. Who is signed in is what the server rendered the page for —
15
+ * it resolves the visitor on every request and hands the answer over with the page — and it is the server that ends a
16
+ * session. What this provider does is the two things only a browser can: go and sign in, and ask to sign out.
17
+ */
18
+ declare class ServerAuthProvider<U = Record<string, unknown>> extends AuthProvider<U> {
19
+ readonly name = "server";
20
+ private readonly loginUrl;
21
+ private readonly logoutUrl;
22
+ constructor({ loginUrl, logoutUrl, ...providerProps }?: ServerAuthProviderProps);
23
+ protected get capabilities(): {
24
+ renew: boolean;
25
+ identity: boolean;
26
+ };
27
+ /**
28
+ * Off to sign in, and back to this very page. The promise never settles: the page is being left, and whatever it
29
+ * said next would be said to nobody.
30
+ */
31
+ protected requestLogin(): Promise<AuthResult<U> | MfaChallenge>;
32
+ protected requestRenewal(): Promise<AuthResult<U>>;
33
+ protected requestIdentity(): Promise<AuthResult<U>>;
34
+ protected requestLogout(): Promise<void>;
35
+ /**
36
+ * Nothing to confirm from here: the page cannot see its own session, and the server answers who is signed in on
37
+ * every page it serves. The session stands as the server rendered it until the server says otherwise.
38
+ */
39
+ revalidate(): Promise<boolean>;
40
+ }
41
+ export default ServerAuthProvider;
@@ -0,0 +1,46 @@
1
+ import e from "../AuthProvider.mjs";
2
+ //#region src/providers/ServerAuthProvider.ts
3
+ var t = class extends e {
4
+ name = "server";
5
+ loginUrl;
6
+ logoutUrl;
7
+ constructor({ loginUrl: e = "", logoutUrl: t = "", ...n } = {}) {
8
+ super({
9
+ ...n,
10
+ tokenStorage: ""
11
+ }), this.loginUrl = e || "/auth/sign-in", this.logoutUrl = t || "/auth/logout";
12
+ }
13
+ get capabilities() {
14
+ return {
15
+ renew: !1,
16
+ identity: !1
17
+ };
18
+ }
19
+ requestLogin() {
20
+ let e = `${window.location.pathname}${window.location.search}`;
21
+ return window.location.assign(`${this.loginUrl}?return=${encodeURIComponent(e)}`), new Promise(() => void 0);
22
+ }
23
+ requestRenewal() {
24
+ return Promise.resolve({
25
+ ok: !1,
26
+ reason: "expired"
27
+ });
28
+ }
29
+ requestIdentity() {
30
+ return Promise.resolve({
31
+ ok: !1,
32
+ reason: "missing"
33
+ });
34
+ }
35
+ async requestLogout() {
36
+ await fetch(this.logoutUrl, {
37
+ method: "POST",
38
+ credentials: "same-origin"
39
+ });
40
+ }
41
+ revalidate() {
42
+ return Promise.resolve(this.getState() === "authenticated");
43
+ }
44
+ };
45
+ //#endregion
46
+ export { t as default };
package/dist/types.d.ts CHANGED
@@ -3,4 +3,4 @@ import { Schema } from '@plitzi/sdk-shared';
3
3
  * Everything a space declares about its auth backend, handed to whichever provider it selected. Derived from the
4
4
  * schema rather than restated, so a setting added there reaches providers without a second definition to keep in step.
5
5
  */
6
- export type AuthProviderSettings = Pick<Schema['settings'], 'tokenStorage' | 'loginUrl' | 'userUrl' | 'refreshUrl' | 'logoutUrl' | 'detailsPath' | 'tokenPath' | 'refreshTokenPath' | 'expirationTimePath' | 'refreshExpirationTimePath' | 'sessionHintCookie' | 'sessionExchangeUrl' | 'sessionGate' | 'sessionRevalidateSeconds'>;
6
+ export type AuthProviderSettings = Pick<Schema['settings'], 'tokenStorage' | 'loginUrl' | 'userUrl' | 'refreshUrl' | 'logoutUrl' | 'detailsPath' | 'tokenPath' | 'refreshTokenPath' | 'expirationTimePath' | 'refreshExpirationTimePath' | 'sessionHintCookie' | 'sessionExchangeUrl' | 'mfaUrl' | 'sessionGate' | 'sessionRevalidateSeconds'>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plitzi/sdk-auth",
3
- "version": "0.37.2",
3
+ "version": "0.37.4",
4
4
  "license": "AGPL-3.0",
5
5
  "files": [
6
6
  "dist"
@@ -30,6 +30,10 @@
30
30
  "types": "./dist/helpers/SessionStore.d.ts",
31
31
  "import": "./dist/helpers/SessionStore.mjs"
32
32
  },
33
+ "./helpers/publishedSession": {
34
+ "types": "./dist/helpers/publishedSession.d.ts",
35
+ "import": "./dist/helpers/publishedSession.mjs"
36
+ },
33
37
  "./helpers/sessionHint": {
34
38
  "types": "./dist/helpers/sessionHint.d.ts",
35
39
  "import": "./dist/helpers/sessionHint.mjs"
@@ -70,6 +74,10 @@
70
74
  "types": "./dist/providers/BasicAuthProvider.d.ts",
71
75
  "import": "./dist/providers/BasicAuthProvider.mjs"
72
76
  },
77
+ "./providers/ServerAuthProvider": {
78
+ "types": "./dist/providers/ServerAuthProvider.d.ts",
79
+ "import": "./dist/providers/ServerAuthProvider.mjs"
80
+ },
73
81
  "./types": {
74
82
  "types": "./dist/types.d.ts"
75
83
  }
@@ -87,16 +95,16 @@
87
95
  "build:prod": "vite build && node ../sdk-shared/scripts/generate-exports.mjs"
88
96
  },
89
97
  "dependencies": {
90
- "@plitzi/nexus": "^1.2.2",
91
- "@plitzi/plitzi-ui": "^1.6.22",
92
- "@plitzi/sdk-navigation": "0.37.2",
93
- "@plitzi/sdk-schema": "0.37.2",
94
- "@plitzi/sdk-shared": "0.37.2"
98
+ "@plitzi/nexus": "^1.3.1",
99
+ "@plitzi/plitzi-ui": "^1.6.24",
100
+ "@plitzi/sdk-navigation": "0.37.4",
101
+ "@plitzi/sdk-schema": "0.37.4",
102
+ "@plitzi/sdk-shared": "0.37.4"
95
103
  },
96
104
  "devDependencies": {
97
105
  "eslint": "^9.39.5",
98
106
  "typescript": "^6.0.3",
99
- "vite": "^8.3.0",
107
+ "vite": "^8.3.1",
100
108
  "vitest": "^5.0.1"
101
109
  }
102
110
  }