@voltro/plugin-auth-social 0.86.0 → 0.88.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/dist/providers.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { SocialConfigError as e } from "./errors.js";
2
- //#region src/providers.ts
2
+ // src/providers.ts
3
3
  var t = {
4
4
  id: "google",
5
5
  label: "Google",
@@ -44,21 +44,81 @@ var t = {
44
44
  assertsEmailVerification: !0,
45
45
  pkceEnforced: !0
46
46
  }, i = {
47
+ id: "discord",
48
+ label: "Discord",
49
+ authorizeUrl: "https://discord.com/oauth2/authorize",
50
+ tokenUrl: "https://discord.com/api/oauth2/token",
51
+ jwksUrl: null,
52
+ issuer: null,
53
+ apiBase: "https://discord.com/api/v10",
54
+ defaultScopes: ["identify", "email"],
55
+ kind: "oauth2",
56
+ responseMode: "query",
57
+ assertsEmailVerification: !0,
58
+ pkceEnforced: !1
59
+ }, a = {
60
+ id: "twitch",
61
+ label: "Twitch",
62
+ authorizeUrl: "https://id.twitch.tv/oauth2/authorize",
63
+ tokenUrl: "https://id.twitch.tv/oauth2/token",
64
+ jwksUrl: "https://id.twitch.tv/oauth2/keys",
65
+ issuer: "https://id.twitch.tv/oauth2",
66
+ apiBase: null,
67
+ defaultScopes: ["openid", "user:read:email"],
68
+ kind: "oidc",
69
+ responseMode: "query",
70
+ assertsEmailVerification: !0,
71
+ pkceEnforced: !1
72
+ }, o = {
73
+ id: "microsoft",
74
+ label: "Microsoft",
75
+ authorizeUrl: "https://login.microsoftonline.com/consumers/oauth2/v2.0/authorize",
76
+ tokenUrl: "https://login.microsoftonline.com/consumers/oauth2/v2.0/token",
77
+ jwksUrl: "https://login.microsoftonline.com/consumers/discovery/v2.0/keys",
78
+ issuer: "https://login.microsoftonline.com/9188040d-6c67-4c5b-b112-36a304b66dad/v2.0",
79
+ apiBase: null,
80
+ defaultScopes: [
81
+ "openid",
82
+ "email",
83
+ "profile"
84
+ ],
85
+ kind: "oidc",
86
+ responseMode: "query",
87
+ assertsEmailVerification: !1,
88
+ pkceEnforced: !0
89
+ }, s = {
90
+ id: "epic",
91
+ label: "Epic Games",
92
+ authorizeUrl: "https://www.epicgames.com/id/authorize",
93
+ tokenUrl: "https://api.epicgames.dev/epic/oauth/v2/token",
94
+ jwksUrl: null,
95
+ issuer: null,
96
+ apiBase: "https://api.epicgames.dev/epic/oauth/v2",
97
+ defaultScopes: ["basic_profile"],
98
+ kind: "oauth2",
99
+ responseMode: "query",
100
+ assertsEmailVerification: !1,
101
+ pkceEnforced: !1
102
+ }, c = {
47
103
  google: t,
48
104
  github: n,
49
- apple: r
50
- }, a = [
105
+ apple: r,
106
+ discord: i,
107
+ twitch: a,
108
+ microsoft: o,
109
+ epic: s
110
+ }, l = [
51
111
  t,
52
112
  n,
53
- r
54
- ], o = [
55
- "google",
56
- "github",
57
- "apple"
58
- ], s = (e) => e === "google" || e === "github" || e === "apple", c = (t, n = {}) => {
59
- if (!s(t)) throw new e(`unknown social provider '${t}' — expected one of ${o.join(", ")}`);
113
+ r,
114
+ i,
115
+ a,
116
+ o,
117
+ s
118
+ ], u = l.map((e) => e.id), d = (e) => Object.hasOwn(c, e), f = (t, n = {}) => {
119
+ if (!d(t)) throw new e(`unknown social provider '${t}' — expected one of ${u.join(", ")}`);
60
120
  return {
61
- ...i[t],
121
+ ...c[t],
62
122
  ...n.authorizeUrl === void 0 ? {} : { authorizeUrl: n.authorizeUrl },
63
123
  ...n.tokenUrl === void 0 ? {} : { tokenUrl: n.tokenUrl },
64
124
  ...n.jwksUrl === void 0 ? {} : { jwksUrl: n.jwksUrl },
@@ -66,5 +126,4 @@ var t = {
66
126
  ...n.apiBase === void 0 ? {} : { apiBase: n.apiBase }
67
127
  };
68
128
  };
69
- //#endregion
70
- export { a as SOCIAL_PROVIDERS, o as SOCIAL_PROVIDER_IDS, s as isSocialProviderId, c as socialProvider };
129
+ export { l as SOCIAL_PROVIDERS, u as SOCIAL_PROVIDER_IDS, d as isSocialProviderId, f as socialProvider };
package/dist/signIn.js CHANGED
@@ -2,7 +2,7 @@ import { SocialAccountLinkError as e } from "./errors.js";
2
2
  import { decideSocialLink as t } from "./linking.js";
3
3
  import { Effect as n } from "effect";
4
4
  import { issueUserSession as r, newAuthId as i, runSubjectGuards as a } from "@voltro/plugin-auth";
5
- //#region src/signIn.ts
5
+ // src/signIn.ts
6
6
  var o = (e) => ({
7
7
  kind: "refused",
8
8
  status: e.status,
@@ -39,13 +39,26 @@ var o = (e) => ({
39
39
  };
40
40
  }
41
41
  }
42
- p === null ? yield* l.insert({
43
- userId: g.id,
44
- provider: c.provider,
45
- providerAccountId: c.providerAccountId,
46
- email: c.email,
47
- emailVerified: c.emailVerified
48
- }) : yield* l.touch(p.id, /* @__PURE__ */ new Date());
42
+ if (p === null) {
43
+ if (!(yield* l.insert({
44
+ userId: g.id,
45
+ provider: c.provider,
46
+ providerAccountId: c.providerAccountId,
47
+ email: c.email,
48
+ emailVerified: c.emailVerified
49
+ }).pipe(n.as(!0), n.catchTag("SocialIdentityConflict", () => n.succeed(!1))))) {
50
+ let t = yield* l.findByProviderAccount(c.provider, c.providerAccountId), n = t === null ? null : yield* u.findById(t.userId);
51
+ if (n === null) return o(new e("account_exists_unlinked", "The account this login is linked to no longer exists.", 409));
52
+ let r = yield* a(d.subjectGuards ?? [], n);
53
+ if (!r.ok) return {
54
+ kind: "refused",
55
+ status: 403,
56
+ code: r.code,
57
+ message: r.message
58
+ };
59
+ g = n, _ = !1, v = !1;
60
+ }
61
+ } else yield* l.touch(p.id, /* @__PURE__ */ new Date());
49
62
  let y = yield* r(g, u, d, {
50
63
  ipAddress: s.ipAddress ?? null,
51
64
  userAgent: s.userAgent ?? null
@@ -55,6 +68,10 @@ var o = (e) => ({
55
68
  userId: g.id,
56
69
  subject: y.subject,
57
70
  setCookie: y.setCookie,
71
+ session: {
72
+ value: y.value,
73
+ expiresAt: y.expiresAt
74
+ },
58
75
  created: _,
59
76
  linked: v,
60
77
  oneTimeName: c.nameIsFirstAuthorizationOnly ? {
@@ -63,5 +80,4 @@ var o = (e) => ({
63
80
  } : null
64
81
  };
65
82
  });
66
- //#endregion
67
83
  export { s as completeSocialSignIn };
package/dist/steam.js ADDED
@@ -0,0 +1,151 @@
1
+ import { randomToken as e } from "./pkce.js";
2
+ import { PlatformTicketError as t, SocialAuthError as n, SocialConfigError as r, SocialExchangeError as i, SocialStateMismatchError as a } from "./errors.js";
3
+ import { Effect as o } from "effect";
4
+ import { timingSafeStringEqual as s } from "@voltro/protocol/session";
5
+ import { createHash as c } from "node:crypto";
6
+ // src/steam.ts
7
+ var l = "http://specs.openid.net/auth/2.0", u = "https://steamcommunity.com/openid/", d = `${l}/identifier_select`, f = (e) => typeof e == "string" && /^[0-9]{17,20}$/.test(e) && BigInt(e) > 0n && BigInt(e) <= 18446744073709551615n, p = 65536, m = 5e3, h = async (e) => {
8
+ let t = e.body?.getReader();
9
+ if (!t) return "";
10
+ let n = [], r = 0;
11
+ for (;;) {
12
+ let { done: e, value: i } = await t.read();
13
+ if (e) break;
14
+ if (r += i.byteLength, r > p) return await t.cancel().catch(() => void 0), null;
15
+ n.push(i);
16
+ }
17
+ return new TextDecoder().decode(Buffer.concat(n));
18
+ }, g = (e) => ({
19
+ provider: "steam",
20
+ providerAccountId: e,
21
+ email: null,
22
+ emailVerified: !1,
23
+ emailIsPrivateRelay: !1,
24
+ name: null,
25
+ firstName: null,
26
+ lastName: null,
27
+ avatarUrl: null,
28
+ nameIsFirstAuthorizationOnly: !1,
29
+ raw: { steamId: e }
30
+ }), _ = (e) => {
31
+ let t;
32
+ try {
33
+ t = new URL(e);
34
+ } catch {
35
+ throw new r("steam: invalid absolute callback URL");
36
+ }
37
+ if (!["https:", "http:"].includes(t.protocol) || t.username || t.password || t.hash) throw new r("steam: callback URL requires HTTP(S), without credentials or fragments");
38
+ return t;
39
+ }, v = (t) => {
40
+ let n = _(t.returnTo);
41
+ if (n.searchParams.has("state")) throw new r("steam: state must be server minted");
42
+ let i = _(t.realm ?? `${n.origin}/`);
43
+ if (i.search || i.origin !== n.origin || !(n.pathname === i.pathname || n.pathname.startsWith(i.pathname.endsWith("/") ? i.pathname : `${i.pathname}/`))) throw new r("steam: callback is outside the configured realm");
44
+ let a = e();
45
+ return n.searchParams.set("state", a), {
46
+ url: `${u}?${new URLSearchParams({
47
+ "openid.ns": l,
48
+ "openid.mode": "checkid_setup",
49
+ "openid.return_to": n.href,
50
+ "openid.realm": i.href,
51
+ "openid.identity": d,
52
+ "openid.claimed_id": d
53
+ })}`,
54
+ state: a,
55
+ returnTo: n.href,
56
+ realm: i.href
57
+ };
58
+ }, y = (e) => o.gen(function* () {
59
+ let t = yield* o.tryPromise({
60
+ try: async () => {
61
+ let t = e.maxNonceAgeMs ?? 3e5, n = e.clockSkewMs ?? 3e4;
62
+ if (!e.namespace || !Number.isSafeInteger(t) || t < 1 || t > 36e5 || !Number.isSafeInteger(n) || n < 0 || n > 3e5 || e.multiNode === !0 && !e.hotStore.capabilities.crossNode) throw new r("steam: invalid replay owner, lifetime or multi-node hot store");
63
+ if (e.callbackUrl.length > 16384) throw new a("steam: callback exceeds the admission budget");
64
+ let o = _(e.callbackUrl), c = _(e.expectedReturnTo), d = o.searchParams, p = /* @__PURE__ */ new Set();
65
+ for (let e of d.keys()) {
66
+ if (p.has(e)) throw new a("steam: duplicate callback parameter");
67
+ p.add(e);
68
+ }
69
+ if (!e.expectedState || d.get("state") === null || !s(d.get("state"), e.expectedState) || c.searchParams.get("state") !== e.expectedState || o.origin !== c.origin || o.pathname !== c.pathname) throw new a("steam: state or callback location mismatch");
70
+ for (let [e, t] of c.searchParams) if (d.get(e) !== t) throw new a("steam: callback query binding mismatch");
71
+ if (d.get("openid.ns") !== l || d.get("openid.mode") !== "id_res" || d.get("openid.op_endpoint") !== u || d.get("openid.return_to") !== e.expectedReturnTo) throw new a("steam: invalid OpenID provider or return binding");
72
+ let g = d.get("openid.claimed_id") ?? "", v = /^https?:\/\/steamcommunity\.com\/openid\/id\/([0-9]{17,20})$/.exec(g);
73
+ if (!v || !f(v[1]) || d.get("openid.identity") !== g) throw new a("steam: invalid Steam claimed identity");
74
+ let y = new Set((d.get("openid.signed") ?? "").split(","));
75
+ if ([
76
+ "op_endpoint",
77
+ "return_to",
78
+ "response_nonce",
79
+ "assoc_handle",
80
+ "claimed_id",
81
+ "identity"
82
+ ].some((e) => !y.has(e)) || !d.get("openid.assoc_handle") || !d.get("openid.sig")) throw new a("steam: assertion is missing required signed fields");
83
+ let b = d.get("openid.response_nonce") ?? "";
84
+ if (!/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z.+$/.test(b) || b.length > 255) throw new a("steam: malformed response nonce");
85
+ let x = Date.parse(b.slice(0, 20)), S = (e.now ?? Date.now)();
86
+ if (!Number.isFinite(S) || !Number.isFinite(x) || new Date(x).toISOString().replace(".000Z", "Z") !== b.slice(0, 20) || x > S + n || x < S - t) throw new a("steam: response nonce outside the admission lifetime");
87
+ let C = new URLSearchParams();
88
+ for (let [e, t] of d) e.startsWith("openid.") && C.set(e, t);
89
+ C.set("openid.mode", "check_authentication");
90
+ let w;
91
+ try {
92
+ w = await (e.fetchImpl ?? fetch)(e.verificationUrl ?? u, {
93
+ method: "POST",
94
+ headers: { "content-type": "application/x-www-form-urlencoded" },
95
+ body: C.toString(),
96
+ redirect: "error",
97
+ signal: AbortSignal.timeout(e.timeoutMs ?? m)
98
+ });
99
+ } catch {
100
+ throw new i("exchange_failed", "steam: OpenID verification request failed");
101
+ }
102
+ if (!w.ok) throw new i("exchange_failed", "steam: OpenID verification refused");
103
+ let T = await h(w).catch(() => null);
104
+ if (T === null || T.length > 8192) throw new i("exchange_failed", "steam: malformed OpenID verification response");
105
+ let E = /* @__PURE__ */ new Map();
106
+ for (let e of T.split("\n")) {
107
+ if (!e) continue;
108
+ let t = e.indexOf(":"), n = e.slice(0, t);
109
+ if (t < 1 || E.has(n)) throw new i("exchange_failed", "steam: malformed OpenID verification response");
110
+ E.set(n, e.slice(t + 1).replace(/\r$/, ""));
111
+ }
112
+ if (E.get("ns") !== l || E.get("is_valid") !== "true") throw new i("exchange_failed", "steam: OpenID assertion was not verified");
113
+ return {
114
+ value: b,
115
+ accountId: v[1],
116
+ ttlMs: t + n
117
+ };
118
+ },
119
+ catch: (e) => e instanceof n ? e : new i("exchange_failed", "steam: OpenID verification failed")
120
+ }), d = `${e.namespace}:steam:nonce:${c("sha256").update(JSON.stringify([u, t.value])).digest("hex")}`;
121
+ return (yield* e.hotStore.setNx(d, "consumed", t.ttlMs).pipe(o.catch(() => o.fail(new i("exchange_failed", "steam: replay admission unavailable"))), o.catchDefect(() => o.fail(new i("exchange_failed", "steam: replay admission unavailable"))))) ? g(t.accountId) : yield* o.fail(new a("steam: assertion already consumed"));
122
+ }), b = async (e) => {
123
+ if (!Number.isSafeInteger(e.appId) || e.appId < 1 || e.appId > 4294967295 || !e.identity || e.identity.length > 255 || !e.publisherKey) throw new r("steam: appId, ticket identity and publisher key are required");
124
+ if (!/^(?:[a-fA-F0-9]{2})+$/.test(e.ticket) || e.ticket.length > 16384) throw new t("steam: malformed Web API ticket");
125
+ let a = new URL(e.verificationUrl ?? "https://partner.steam-api.com/ISteamUserAuth/AuthenticateUserTicket/v1/");
126
+ a.searchParams.set("key", e.publisherKey), a.searchParams.set("appid", String(e.appId)), a.searchParams.set("ticket", e.ticket), a.searchParams.set("identity", e.identity);
127
+ let o;
128
+ try {
129
+ o = await (e.fetchImpl ?? fetch)(a.href, {
130
+ redirect: "error",
131
+ headers: { accept: "application/json" },
132
+ signal: AbortSignal.timeout(e.timeoutMs ?? m)
133
+ });
134
+ } catch {
135
+ throw new i("exchange_failed", "steam: ticket verification request failed");
136
+ }
137
+ if (!o.ok) throw new i("exchange_failed", "steam: ticket verification refused");
138
+ let s;
139
+ try {
140
+ let e = await h(o);
141
+ if (e === null) throw Error("oversized");
142
+ s = JSON.parse(e);
143
+ } catch {
144
+ throw new i("malformed_token_response", "steam: malformed ticket verification response");
145
+ }
146
+ let c = typeof s == "object" && s ? s : {}, l = typeof c.response == "object" && c.response !== null ? c.response : {}, u = typeof l.params == "object" && l.params !== null ? l.params : {};
147
+ if (u.result !== "OK" || !f(u.steamid)) throw new t("steam: invalid Web API ticket");
148
+ if (u.publisherbanned === !0 && e.allowPublisherBanned !== !0) throw new n(403, "guard_rejected", "steam: the publisher banned this account");
149
+ return g(u.steamid);
150
+ };
151
+ export { v as steamBeginOpenId, y as steamCompleteOpenId, b as verifySteamWebApiTicket };
@@ -0,0 +1,108 @@
1
+ import { isSocialProviderId as e } from "../providers.js";
2
+ import { parseGuestCredential as t } from "@voltro/plugin-auth/guest";
3
+ // src/web/guestUpgradeClient.ts
4
+ var n = class extends Error {
5
+ code;
6
+ status;
7
+ constructor(e, t) {
8
+ super("Guest upgrade: " + e), this.code = e, this.status = t, this.name = "GuestUpgradeClientError";
9
+ }
10
+ }, r = (e) => typeof e == "object" && e && !Array.isArray(e) ? e : null, i = (i) => {
11
+ let a = new URL(i.apiBaseUrl), o = i.prefix ?? "/auth/social";
12
+ if (!/^https?:$/.test(a.protocol) || a.username || a.password || a.search || a.hash || !/^\/[A-Za-z0-9/_-]+$/.test(o) || o.endsWith("/")) throw new n("invalid_response");
13
+ let s = i.apiBaseUrl.replace(/\/+$/, "") + o + "/upgrade", c = i.fetch ?? globalThis.fetch, l = async (e, t) => {
14
+ try {
15
+ let i = {};
16
+ if (t !== void 0) {
17
+ let e = await c(s + "/csrf", {
18
+ credentials: "include",
19
+ cache: "no-store",
20
+ redirect: "error"
21
+ });
22
+ if (!e.ok) throw new n("transport_failed", e.status);
23
+ let t = r(await e.json());
24
+ if (typeof t?.token != "string" || !t.token) throw new n("invalid_response");
25
+ i["x-csrf-token"] = t.token, i["content-type"] = "application/json";
26
+ }
27
+ let a = await c(s + e, {
28
+ credentials: "include",
29
+ cache: "no-store",
30
+ redirect: "error",
31
+ ...t === void 0 ? {} : {
32
+ method: "POST",
33
+ headers: i,
34
+ body: JSON.stringify(t)
35
+ }
36
+ }), o = r(await a.json());
37
+ if (!a.ok) {
38
+ let e = o?.error;
39
+ throw new n(e === "account_merge_required" || e === "credential_rejected" || e === "upgrade_handoff_rejected" ? e : "transport_failed", a.status);
40
+ }
41
+ if (!o) throw new n("invalid_response");
42
+ return o;
43
+ } catch (e) {
44
+ throw e instanceof n ? e : new n("transport_failed");
45
+ }
46
+ }, u = async (e) => {
47
+ try {
48
+ return await e();
49
+ } catch (e) {
50
+ throw e instanceof n ? e : new n("storage_unavailable");
51
+ }
52
+ }, d = async () => {
53
+ let e = await u(() => i.storage.read());
54
+ if (e === null || !t(e)) throw new n("credential_rejected");
55
+ return e;
56
+ }, f = (e, t) => {
57
+ let i = r(e.subject);
58
+ if (e.upgraded !== !0 || i?.type !== "user" || i.kind === "guest" || i.id !== t || typeof i.tenantId != "string" || !i.tenantId.trim()) throw new n("invalid_response");
59
+ return {
60
+ upgraded: !0,
61
+ subject: {
62
+ type: "user",
63
+ id: t,
64
+ tenantId: i.tenantId
65
+ }
66
+ };
67
+ };
68
+ return {
69
+ begin: (t) => u(() => i.storage.exclusive(async () => {
70
+ if (!e(t)) throw new n("invalid_response");
71
+ await d();
72
+ let r = await l("/" + t, {});
73
+ if (typeof r.authorizationUrl != "string") throw new n("invalid_response");
74
+ let i;
75
+ try {
76
+ i = new URL(r.authorizationUrl);
77
+ } catch {
78
+ throw new n("invalid_response");
79
+ }
80
+ if (!/^https?:$/.test(i.protocol) || i.username || i.password || i.hash) throw new n("invalid_response");
81
+ return i.toString();
82
+ })),
83
+ pending: async () => {
84
+ let t = await l("/pending");
85
+ if (typeof t.guestId != "string" || !/^guest_[A-Za-z0-9_-]+$/.test(t.guestId) || typeof t.provider != "string" || !e(t.provider) || t.email !== null && typeof t.email != "string" || typeof t.emailVerified != "boolean" || typeof t.expiresAt != "number" || !Number.isSafeInteger(t.expiresAt) || t.expiresAt <= Date.now()) throw new n("invalid_response");
86
+ return {
87
+ guestId: t.guestId,
88
+ provider: t.provider,
89
+ email: t.email,
90
+ emailVerified: t.emailVerified,
91
+ expiresAt: t.expiresAt
92
+ };
93
+ },
94
+ confirm: (e) => u(() => i.storage.exclusive(async () => {
95
+ if (e.confirmed !== !0) throw new n("credential_rejected");
96
+ let r = await d(), a = t(r), o = await l("/complete", {
97
+ credential: r,
98
+ confirmed: !0
99
+ }), s = f(o, a.guestId);
100
+ return await u(() => i.storage.remove()), s;
101
+ })),
102
+ recover: () => u(() => i.storage.exclusive(async () => {
103
+ let e = await d(), n = f(await l("/receipt"), t(e).guestId);
104
+ return await u(() => i.storage.remove()), n;
105
+ }))
106
+ };
107
+ };
108
+ export { n as GuestUpgradeClientError, i as createGuestUpgradeClient };
package/dist/web.d.ts ADDED
@@ -0,0 +1,54 @@
1
+ import { GuestCredentialStore } from '@voltro/plugin-auth/guest';
2
+
3
+ /** Browser cookie presentation only. Device proofs stay in durable storage and
4
+ * in the confirmed JSON POST, never navigation URLs or login-state cookies. */
5
+ export declare const createGuestUpgradeClient: (options: GuestUpgradeClientOptions) => {
6
+ /** Call after an authenticated guest session exists, then navigate to the
7
+ * returned authorization URL in this browser. Does not send device proof. */
8
+ begin: (provider: SocialProviderId) => Promise<string>;
9
+ pending: () => Promise<GuestUpgradePending>;
10
+ /** Call only after explicit confirmation. Storage remains untouched on an
11
+ * uncertain/refused response; no replacement guest identity is created. */
12
+ confirm: (confirmation: {
13
+ readonly confirmed: true;
14
+ }) => Promise<GuestUpgradeCompletion>;
15
+ /** After uncertain delivery, prove a live registered session for the exact
16
+ * stored guest ID. If its cookie was lost too, sign in with the linked
17
+ * provider first. Refusal never erases storage or creates another guest. */
18
+ recover: () => Promise<GuestUpgradeCompletion>;
19
+ };
20
+
21
+ export declare class GuestUpgradeClientError extends Error {
22
+ readonly code: 'storage_unavailable' | 'credential_rejected' | 'account_merge_required' | 'transport_failed' | 'invalid_response' | 'upgrade_handoff_rejected';
23
+ readonly status?: number | undefined;
24
+ constructor(code: 'storage_unavailable' | 'credential_rejected' | 'account_merge_required' | 'transport_failed' | 'invalid_response' | 'upgrade_handoff_rejected', status?: number | undefined);
25
+ }
26
+
27
+ export declare interface GuestUpgradeClientOptions {
28
+ readonly apiBaseUrl: string;
29
+ readonly storage: GuestCredentialStore;
30
+ readonly prefix?: string;
31
+ readonly fetch?: typeof globalThis.fetch;
32
+ }
33
+
34
+ export declare interface GuestUpgradeCompletion {
35
+ readonly upgraded: true;
36
+ readonly subject: {
37
+ readonly type: 'user';
38
+ readonly id: string;
39
+ readonly tenantId: string;
40
+ };
41
+ }
42
+
43
+ export declare interface GuestUpgradePending {
44
+ readonly guestId: string;
45
+ readonly provider: SocialProviderId;
46
+ readonly email: string | null;
47
+ readonly emailVerified: boolean;
48
+ readonly expiresAt: number;
49
+ }
50
+
51
+ /** The providers this package ships. */
52
+ declare type SocialProviderId = 'google' | 'github' | 'apple' | 'discord' | 'twitch' | 'microsoft' | 'epic';
53
+
54
+ export { }
package/dist/web.js ADDED
@@ -0,0 +1 @@
1
+ export { GuestUpgradeClientError, createGuestUpgradeClient } from "./web/guestUpgradeClient.js";
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@voltro/plugin-auth-social",
3
- "version": "0.86.0",
4
- "description": "First-party social login for the Voltro framework — Sign in with Google, GitHub and Apple. Mandatory PKCE (S256) + state, JWKS-verified ID tokens, a deliberate account-linking policy, and sessions issued through @voltro/plugin-auth. No identity vendor required.",
3
+ "version": "0.88.0",
4
+ "description": "First-party social login with state, S256, provider identity verification, explicit account linking and shared Auth sessions.",
5
5
  "keywords": [
6
6
  "voltro",
7
7
  "typescript",
@@ -23,6 +23,11 @@
23
23
  "import": "./dist/index.js",
24
24
  "default": "./dist/index.js"
25
25
  },
26
+ "./web": {
27
+ "types": "./dist/web.d.ts",
28
+ "import": "./dist/web.js",
29
+ "default": "./dist/web.js"
30
+ },
26
31
  "./package.json": "./package.json"
27
32
  },
28
33
  "main": "./dist/index.js",
@@ -33,12 +38,13 @@
33
38
  "node": ">=24.0.0"
34
39
  },
35
40
  "dependencies": {
36
- "@voltro/database": "0.86.0",
37
- "@voltro/plugin-auth": "0.86.0",
38
- "@voltro/protocol": "0.86.0"
41
+ "@voltro/database": "0.88.0",
42
+ "@voltro/plugin-auth": "0.88.0",
43
+ "@voltro/protocol": "0.88.0",
44
+ "@voltro/kv": "0.88.0"
39
45
  },
40
46
  "peerDependencies": {
41
- "effect": "4.0.1"
47
+ "effect": "4.1.0"
42
48
  },
43
49
  "publishConfig": {
44
50
  "access": "public"