@metaloot/auth 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Metaloot
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,178 @@
1
+ # Metaloot Auth
2
+
3
+ Drop-in Metaloot auth adapters for browser games.
4
+
5
+ The package owns the OAuth callback and game session so a game does not need to
6
+ hand-roll code exchange, signed cookies, session JSON, or sign-in UI state.
7
+
8
+ ## Install
9
+
10
+ ```bash
11
+ npm install @metaloot/auth
12
+ ```
13
+
14
+ ## Environment
15
+
16
+ ```bash
17
+ METALOOT_CLIENT_ID=mtl_client_...
18
+ METALOOT_CLIENT_SECRET=mtl_secret_...
19
+ METALOOT_SESSION_SECRET=replace-with-a-long-random-secret
20
+ ```
21
+
22
+ Register this callback URL in Metaloot:
23
+
24
+ ```txt
25
+ https://your-game.com/auth/metaloot/callback
26
+ ```
27
+
28
+ ## Static Node or Express Game
29
+
30
+ ```js
31
+ import express from "express";
32
+ import { metalootAuth } from "@metaloot/auth/node";
33
+
34
+ const app = express();
35
+
36
+ app.use(
37
+ metalootAuth({
38
+ clientId: process.env.METALOOT_CLIENT_ID,
39
+ clientSecret: process.env.METALOOT_CLIENT_SECRET,
40
+ sessionSecret: process.env.METALOOT_SESSION_SECRET,
41
+ redirectUri: "https://your-game.com/auth/metaloot/callback",
42
+ })
43
+ );
44
+
45
+ app.use(
46
+ "/vendor/@metaloot/auth",
47
+ express.static("node_modules/@metaloot/auth/dist")
48
+ );
49
+ app.use(express.static("public"));
50
+ app.listen(process.env.PORT || 3000);
51
+ ```
52
+
53
+ Add the button to the game:
54
+
55
+ ```html
56
+ <div id="metaloot-auth"></div>
57
+ <script type="module">
58
+ import { mountMetalootAuth } from "/vendor/@metaloot/auth/browser.js";
59
+
60
+ mountMetalootAuth("#metaloot-auth", {
61
+ gameName: "Your Game"
62
+ });
63
+ </script>
64
+ ```
65
+
66
+ The middleware provides:
67
+
68
+ ```txt
69
+ GET /auth/metaloot/start
70
+ GET /auth/metaloot/callback
71
+ GET /auth/metaloot/session
72
+ GET /auth/metaloot/logout
73
+ POST /auth/metaloot/logout
74
+ ```
75
+
76
+ ## Next.js App Router
77
+
78
+ Create `app/api/auth/metaloot/[...metaloot]/route.ts`:
79
+
80
+ ```ts
81
+ import { createMetalootRouteHandlers } from "@metaloot/auth/next";
82
+
83
+ export const { GET, POST } = createMetalootRouteHandlers({
84
+ clientId: process.env.METALOOT_CLIENT_ID!,
85
+ clientSecret: process.env.METALOOT_CLIENT_SECRET!,
86
+ sessionSecret: process.env.METALOOT_SESSION_SECRET!,
87
+ redirectUri: "https://your-game.com/auth/metaloot/callback",
88
+ startPath: "/api/auth/metaloot/start",
89
+ callbackPath: "/api/auth/metaloot/callback",
90
+ sessionPath: "/api/auth/metaloot/session",
91
+ logoutPath: "/api/auth/metaloot/logout",
92
+ });
93
+ ```
94
+
95
+ Register the matching callback URL for this example:
96
+
97
+ ```txt
98
+ https://your-game.com/api/auth/metaloot/callback
99
+ ```
100
+
101
+ Mount the browser button from a client component:
102
+
103
+ ```tsx
104
+ "use client";
105
+
106
+ import { useEffect, useRef } from "react";
107
+ import { mountMetalootAuth } from "@metaloot/auth/browser";
108
+
109
+ export function MetalootSignIn() {
110
+ const ref = useRef<HTMLDivElement>(null);
111
+
112
+ useEffect(() => {
113
+ if (!ref.current) return;
114
+ mountMetalootAuth(ref.current, {
115
+ gameName: "Your Game",
116
+ startUrl: "/api/auth/metaloot/start",
117
+ sessionUrl: "/api/auth/metaloot/session",
118
+ logoutUrl: "/api/auth/metaloot/logout",
119
+ });
120
+ }, []);
121
+
122
+ return <div ref={ref} />;
123
+ }
124
+ ```
125
+
126
+ ## Read the Session
127
+
128
+ Browser code can check the current player:
129
+
130
+ ```js
131
+ const session = await fetch("/auth/metaloot/session").then((res) => res.json());
132
+
133
+ if (session.signedIn) {
134
+ console.log(session.user.id, session.user.name);
135
+ }
136
+ ```
137
+
138
+ Server code can read a signed session from cookies:
139
+
140
+ ```ts
141
+ import { getMetalootSessionFromCookieHeader } from "@metaloot/auth/server";
142
+
143
+ const session = getMetalootSessionFromCookieHeader(request.headers.get("cookie"), {
144
+ clientId: process.env.METALOOT_CLIENT_ID!,
145
+ clientSecret: process.env.METALOOT_CLIENT_SECRET!,
146
+ sessionSecret: process.env.METALOOT_SESSION_SECRET!,
147
+ redirectUri: "https://your-game.com/auth/metaloot/callback",
148
+ });
149
+ ```
150
+
151
+ ## Agent Prompt
152
+
153
+ Use this when asking an implementation agent to add Metaloot auth:
154
+
155
+ ```txt
156
+ Install @metaloot/auth and wire Metaloot sign-in.
157
+
158
+ Do not implement OAuth by hand.
159
+
160
+ Requirements:
161
+ 1. Add the Metaloot server adapter for this stack.
162
+ 2. Configure METALOOT_CLIENT_ID, METALOOT_CLIENT_SECRET, and METALOOT_SESSION_SECRET on the server only.
163
+ 3. Use /auth/metaloot/start, /auth/metaloot/callback, /auth/metaloot/session, and /auth/metaloot/logout.
164
+ 4. Mount the browser sign-in button with mountMetalootAuth.
165
+ 5. On game boot, call the session endpoint and treat signedIn:true as the player being authenticated.
166
+ 6. Hide or replace any old guest-login, #mlt token, or vendored Metaloot SDK logic.
167
+ 7. Verify locally and in production that /auth/metaloot/session returns signedIn:true after login.
168
+ ```
169
+
170
+ ## Publishing
171
+
172
+ ```bash
173
+ npm login
174
+ npm publish --access public
175
+ ```
176
+
177
+ If the `@metaloot` npm scope is not available on your account, change the
178
+ package name in `package.json` before publishing.
@@ -0,0 +1,15 @@
1
+ export type MetalootBrowserOptions = {
2
+ startUrl?: string;
3
+ sessionUrl?: string;
4
+ logoutUrl?: string;
5
+ buttonText?: string;
6
+ gameName?: string;
7
+ showUser?: boolean;
8
+ };
9
+ export type MetalootBrowserMount = {
10
+ refresh(): Promise<void>;
11
+ signIn(): void;
12
+ signOut(): void;
13
+ };
14
+ export declare function mountMetalootAuth(target: string | HTMLElement, options?: MetalootBrowserOptions): MetalootBrowserMount;
15
+ //# sourceMappingURL=browser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,sBAAsB,GAAG;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACzB,MAAM,IAAI,IAAI,CAAC;IACf,OAAO,IAAI,IAAI,CAAC;CACjB,CAAC;AAEF,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,MAAM,GAAG,WAAW,EAC5B,OAAO,GAAE,sBAA2B,GACnC,oBAAoB,CAiCtB"}
@@ -0,0 +1,91 @@
1
+ export function mountMetalootAuth(target, options = {}) {
2
+ const element = typeof target === "string" ? document.querySelector(target) : target;
3
+ if (!element)
4
+ throw new Error("Metaloot auth target not found.");
5
+ const mountElement = element;
6
+ const normalized = {
7
+ startUrl: options.startUrl ?? "/auth/metaloot/start",
8
+ sessionUrl: options.sessionUrl ?? "/auth/metaloot/session",
9
+ logoutUrl: options.logoutUrl ?? "/auth/metaloot/logout",
10
+ buttonText: options.buttonText ?? "Sign in with Metaloot",
11
+ gameName: options.gameName ?? document.title ?? "this game",
12
+ showUser: options.showUser ?? true,
13
+ };
14
+ injectStyles();
15
+ function signIn() {
16
+ window.location.assign(normalized.startUrl);
17
+ }
18
+ function signOut() {
19
+ window.location.assign(normalized.logoutUrl);
20
+ }
21
+ async function refresh() {
22
+ const session = await loadSession(normalized.sessionUrl);
23
+ render(mountElement, normalized, session, signIn, signOut);
24
+ }
25
+ void refresh();
26
+ return { refresh, signIn, signOut };
27
+ }
28
+ async function loadSession(sessionUrl) {
29
+ try {
30
+ const response = await fetch(sessionUrl, {
31
+ credentials: "same-origin",
32
+ headers: { Accept: "application/json" },
33
+ });
34
+ if (!response.ok)
35
+ return { signedIn: false };
36
+ return (await response.json());
37
+ }
38
+ catch {
39
+ return { signedIn: false };
40
+ }
41
+ }
42
+ function render(element, options, session, signIn, signOut) {
43
+ element.innerHTML = "";
44
+ element.classList.add("metaloot-auth-root");
45
+ if (session.signedIn && options.showUser) {
46
+ const wrapper = document.createElement("div");
47
+ wrapper.className = "metaloot-auth-user";
48
+ const label = document.createElement("span");
49
+ label.textContent = `${session.user.name ?? session.user.email ?? "Player"} · Metaloot`;
50
+ const logout = document.createElement("button");
51
+ logout.type = "button";
52
+ logout.className = "metaloot-auth-logout";
53
+ logout.textContent = "Sign out";
54
+ logout.addEventListener("click", signOut);
55
+ wrapper.append(label, logout);
56
+ element.appendChild(wrapper);
57
+ return;
58
+ }
59
+ if (session.signedIn) {
60
+ element.hidden = true;
61
+ return;
62
+ }
63
+ element.hidden = false;
64
+ const button = document.createElement("button");
65
+ button.type = "button";
66
+ button.className = "metaloot-auth-button";
67
+ button.innerHTML = `<span class="metaloot-auth-mark">${iconSvg()}</span><span>${escapeHtml(options.buttonText)}</span>`;
68
+ button.setAttribute("aria-label", `Sign in to ${options.gameName} with Metaloot`);
69
+ button.addEventListener("click", signIn);
70
+ element.appendChild(button);
71
+ }
72
+ function injectStyles() {
73
+ if (document.getElementById("metaloot-auth-adapter-styles"))
74
+ return;
75
+ const style = document.createElement("style");
76
+ style.id = "metaloot-auth-adapter-styles";
77
+ style.textContent =
78
+ ".metaloot-auth-root[hidden]{display:none!important}.metaloot-auth-button{align-items:center;background:#8b5cf6;border:0;border-radius:12px;box-shadow:0 12px 30px rgba(139,92,246,.26);color:#fff;cursor:pointer;display:inline-flex;font:600 14px/1.2 system-ui,-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;gap:10px;justify-content:center;padding:12px 16px;transition:transform .18s ease,background .18s ease,box-shadow .18s ease}.metaloot-auth-button:hover{background:#7c3aed;box-shadow:0 14px 34px rgba(139,92,246,.34);transform:translateY(-1px)}.metaloot-auth-button:active{transform:translateY(0)}.metaloot-auth-mark{align-items:center;background:rgba(255,255,255,.18);border-radius:8px;display:inline-flex;height:24px;justify-content:center;width:24px}.metaloot-auth-user{align-items:center;background:rgba(10,7,28,.82);border:1px solid rgba(255,255,255,.14);border-radius:12px;color:#f4f1ff;display:inline-flex;font:600 13px/1.2 system-ui,-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;gap:10px;padding:10px 12px}.metaloot-auth-logout{background:rgba(255,255,255,.1);border:0;border-radius:8px;color:inherit;cursor:pointer;font:600 12px/1 system-ui,-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;padding:7px 9px}.metaloot-auth-logout:hover{background:rgba(255,255,255,.16)}";
79
+ document.head.appendChild(style);
80
+ }
81
+ function escapeHtml(value) {
82
+ return value
83
+ .replaceAll("&", "&amp;")
84
+ .replaceAll("<", "&lt;")
85
+ .replaceAll(">", "&gt;")
86
+ .replaceAll('"', "&quot;");
87
+ }
88
+ function iconSvg() {
89
+ return '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" aria-hidden="true" xmlns="http://www.w3.org/2000/svg"><path d="M7 12h4M9 10v4M15.5 12h.01M18 10.5h.01" stroke="currentColor" stroke-width="2" stroke-linecap="round"/><path d="M17.32 5H6.68a4 4 0 0 0-3.98 3.59C2.61 9.42 2 14.46 2 16a3 3 0 0 0 3 3c1 0 1.5-.5 2-1l1.41-1.41A2 2 0 0 1 9.83 16h4.34a2 2 0 0 1 1.42.59L17 18c.5.5 1 1 2 1a3 3 0 0 0 3-3c0-1.54-.61-6.58-.7-7.41A4 4 0 0 0 17.32 5Z" stroke="currentColor" stroke-width="2" stroke-linejoin="round"/></svg>';
90
+ }
91
+ //# sourceMappingURL=browser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser.js","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ export { createMetalootAuthHandler, getMetalootSessionFromCookieHeader, handleMetalootRequest, } from "./server.js";
2
+ export { metalootAuth } from "./node.js";
3
+ export { createMetalootRouteHandlers } from "./next.js";
4
+ export type { MetalootAuthOptions, MetalootSession, MetalootSessionResponse, MetalootUser, } from "./types.js";
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,yBAAyB,EACzB,kCAAkC,EAClC,qBAAqB,GACtB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,EAAE,2BAA2B,EAAE,MAAM,WAAW,CAAC;AACxD,YAAY,EACV,mBAAmB,EACnB,eAAe,EACf,uBAAuB,EACvB,YAAY,GACb,MAAM,YAAY,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export { createMetalootAuthHandler, getMetalootSessionFromCookieHeader, handleMetalootRequest, } from "./server.js";
2
+ export { metalootAuth } from "./node.js";
3
+ export { createMetalootRouteHandlers } from "./next.js";
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,yBAAyB,EACzB,kCAAkC,EAClC,qBAAqB,GACtB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,EAAE,2BAA2B,EAAE,MAAM,WAAW,CAAC"}
package/dist/next.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ import type { MetalootAuthOptions } from "./types.js";
2
+ export declare function createMetalootRouteHandlers(options: MetalootAuthOptions): {
3
+ GET: (request: Request) => Promise<Response>;
4
+ POST: (request: Request) => Promise<Response>;
5
+ };
6
+ //# sourceMappingURL=next.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"next.d.ts","sourceRoot":"","sources":["../src/next.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEtD,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,mBAAmB;mBACtC,OAAO,KAAG,OAAO,CAAC,QAAQ,CAAC;oBAA3B,OAAO,KAAG,OAAO,CAAC,QAAQ,CAAC;EAS5D"}
package/dist/next.js ADDED
@@ -0,0 +1,12 @@
1
+ import { handleMetalootRequest } from "./server.js";
2
+ export function createMetalootRouteHandlers(options) {
3
+ async function handler(request) {
4
+ const response = await handleMetalootRequest(request, options);
5
+ return response ?? new Response("Not found", { status: 404 });
6
+ }
7
+ return {
8
+ GET: handler,
9
+ POST: handler,
10
+ };
11
+ }
12
+ //# sourceMappingURL=next.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"next.js","sourceRoot":"","sources":["../src/next.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAGpD,MAAM,UAAU,2BAA2B,CAAC,OAA4B;IACtE,KAAK,UAAU,OAAO,CAAC,OAAgB;QACrC,MAAM,QAAQ,GAAG,MAAM,qBAAqB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAC/D,OAAO,QAAQ,IAAI,IAAI,QAAQ,CAAC,WAAW,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;IAChE,CAAC;IAED,OAAO;QACL,GAAG,EAAE,OAAO;QACZ,IAAI,EAAE,OAAO;KACd,CAAC;AACJ,CAAC"}
package/dist/node.d.ts ADDED
@@ -0,0 +1,10 @@
1
+ import type { MetalootAuthOptions } from "./types.js";
2
+ import type { IncomingMessage, ServerResponse } from "node:http";
3
+ export type NodeLikeRequest = IncomingMessage & {
4
+ protocol?: string;
5
+ originalUrl?: string;
6
+ };
7
+ export type NodeLikeResponse = ServerResponse;
8
+ export type NodeNextFunction = (error?: unknown) => void;
9
+ export declare function metalootAuth(options: MetalootAuthOptions): (req: NodeLikeRequest, res: NodeLikeResponse, next?: NodeNextFunction) => Promise<void>;
10
+ //# sourceMappingURL=node.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../src/node.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACtD,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEjE,MAAM,MAAM,eAAe,GAAG,eAAe,GAAG;IAC9C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG,cAAc,CAAC;AAE9C,MAAM,MAAM,gBAAgB,GAAG,CAAC,KAAK,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;AAEzD,wBAAgB,YAAY,CAAC,OAAO,EAAE,mBAAmB,IAErD,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,OAAO,gBAAgB,mBAkB1B"}
package/dist/node.js ADDED
@@ -0,0 +1,54 @@
1
+ import { handleMetalootRequest } from "./server.js";
2
+ export function metalootAuth(options) {
3
+ return async function metalootAuthMiddleware(req, res, next) {
4
+ try {
5
+ const request = toWebRequest(req, options);
6
+ const response = await handleMetalootRequest(request, options);
7
+ if (!response) {
8
+ if (next)
9
+ return next();
10
+ res.statusCode = 404;
11
+ res.end("Not found");
12
+ return;
13
+ }
14
+ await sendWebResponse(res, response);
15
+ }
16
+ catch (error) {
17
+ if (next)
18
+ return next(error);
19
+ res.statusCode = 500;
20
+ res.end("Metaloot auth error");
21
+ }
22
+ };
23
+ }
24
+ function toWebRequest(req, options) {
25
+ const host = req.headers.host ?? "localhost";
26
+ const protocol = req.protocol ??
27
+ (req.headers["x-forwarded-proto"]
28
+ ? String(req.headers["x-forwarded-proto"]).split(",")[0]
29
+ : "http");
30
+ const baseUrl = `${protocol}://${host}`;
31
+ const url = new URL(req.originalUrl ?? req.url ?? "/", baseUrl).toString();
32
+ return new Request(url, {
33
+ method: req.method,
34
+ headers: req.headers,
35
+ });
36
+ }
37
+ async function sendWebResponse(res, response) {
38
+ res.statusCode = response.status;
39
+ response.headers.forEach((value, key) => {
40
+ res.setHeader(key, value);
41
+ });
42
+ const setCookies = "getSetCookie" in response.headers
43
+ ? response.headers.getSetCookie()
44
+ : [];
45
+ if (setCookies.length > 0)
46
+ res.setHeader("Set-Cookie", setCookies);
47
+ if (!response.body) {
48
+ res.end();
49
+ return;
50
+ }
51
+ const body = Buffer.from(await response.arrayBuffer());
52
+ res.end(body);
53
+ }
54
+ //# sourceMappingURL=node.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node.js","sourceRoot":"","sources":["../src/node.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAapD,MAAM,UAAU,YAAY,CAAC,OAA4B;IACvD,OAAO,KAAK,UAAU,sBAAsB,CAC1C,GAAoB,EACpB,GAAqB,EACrB,IAAuB;QAEvB,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,YAAY,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;YAC3C,MAAM,QAAQ,GAAG,MAAM,qBAAqB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YAC/D,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACd,IAAI,IAAI;oBAAE,OAAO,IAAI,EAAE,CAAC;gBACxB,GAAG,CAAC,UAAU,GAAG,GAAG,CAAC;gBACrB,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;gBACrB,OAAO;YACT,CAAC;YACD,MAAM,eAAe,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;QACvC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,IAAI;gBAAE,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC;YAC7B,GAAG,CAAC,UAAU,GAAG,GAAG,CAAC;YACrB,GAAG,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC;QACjC,CAAC;IACH,CAAC,CAAC;AACJ,CAAC;AAED,SAAS,YAAY,CAAC,GAAoB,EAAE,OAA4B;IACtE,MAAM,IAAI,GAAG,GAAG,CAAC,OAAO,CAAC,IAAI,IAAI,WAAW,CAAC;IAC7C,MAAM,QAAQ,GACZ,GAAG,CAAC,QAAQ;QACZ,CAAC,GAAG,CAAC,OAAO,CAAC,mBAAmB,CAAC;YAC/B,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YACxD,CAAC,CAAC,MAAM,CAAC,CAAC;IACd,MAAM,OAAO,GAAG,GAAG,QAAQ,MAAM,IAAI,EAAE,CAAC;IACxC,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,WAAW,IAAI,GAAG,CAAC,GAAG,IAAI,GAAG,EAAE,OAAO,CAAC,CAAC,QAAQ,EAAE,CAAC;IAC3E,OAAO,IAAI,OAAO,CAAC,GAAG,EAAE;QACtB,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,OAAO,EAAE,GAAG,CAAC,OAAsB;KACpC,CAAC,CAAC;AACL,CAAC;AAED,KAAK,UAAU,eAAe,CAC5B,GAAqB,EACrB,QAAkB;IAElB,GAAG,CAAC,UAAU,GAAG,QAAQ,CAAC,MAAM,CAAC;IACjC,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;QACtC,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IAC5B,CAAC,CAAC,CAAC;IAEH,MAAM,UAAU,GACd,cAAc,IAAI,QAAQ,CAAC,OAAO;QAChC,CAAC,CAAE,QAAQ,CAAC,OAAkD,CAAC,YAAY,EAAE;QAC7E,CAAC,CAAC,EAAE,CAAC;IACT,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC;QAAE,GAAG,CAAC,SAAS,CAAC,YAAY,EAAE,UAAU,CAAC,CAAC;IAEnE,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;QACnB,GAAG,CAAC,GAAG,EAAE,CAAC;QACV,OAAO;IACT,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC;IACvD,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AAChB,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { MetalootAuthOptions, MetalootSession, NormalizedMetalootAuthOptions } from "./types.js";
2
+ export type MetalootRequestHandler = (request: Request) => Promise<Response | null>;
3
+ export declare function createMetalootAuthHandler(options: MetalootAuthOptions): MetalootRequestHandler;
4
+ export declare function handleMetalootRequest(request: Request, options: MetalootAuthOptions | NormalizedMetalootAuthOptions): Promise<Response | null>;
5
+ export declare function getMetalootSessionFromCookieHeader(cookieHeader: string | null | undefined, options: MetalootAuthOptions): MetalootSession | null;
6
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EACV,mBAAmB,EACnB,eAAe,EAIf,6BAA6B,EAC9B,MAAM,YAAY,CAAC;AAEpB,MAAM,MAAM,sBAAsB,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAC;AAEpF,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,mBAAmB,GAC3B,sBAAsB,CAGxB;AAED,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,mBAAmB,GAAG,6BAA6B,GAC3D,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAyB1B;AAED,wBAAgB,kCAAkC,CAChD,YAAY,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EACvC,OAAO,EAAE,mBAAmB,GAC3B,eAAe,GAAG,IAAI,CAKxB"}
package/dist/server.js ADDED
@@ -0,0 +1,155 @@
1
+ import { createAuthorizeUrl, normalizeOptions, samePath } from "./shared.js";
2
+ import { clearCookie, parseCookieHeader, randomState, serializeCookie, signSession, verifySession, } from "./session.js";
3
+ export function createMetalootAuthHandler(options) {
4
+ const normalized = normalizeOptions(options);
5
+ return (request) => handleMetalootRequest(request, normalized);
6
+ }
7
+ export async function handleMetalootRequest(request, options) {
8
+ const normalized = normalizeOptions(options);
9
+ const url = new URL(request.url);
10
+ const secure = shouldUseSecureCookies(request, normalized);
11
+ if (request.method === "GET" && samePath(url, normalized.startPath)) {
12
+ return startAuth(normalized, secure);
13
+ }
14
+ if (request.method === "GET" && samePath(url, normalized.callbackPath)) {
15
+ return callbackAuth(request, normalized, secure);
16
+ }
17
+ if (request.method === "GET" && samePath(url, normalized.sessionPath)) {
18
+ return sessionResponse(request, normalized);
19
+ }
20
+ if ((request.method === "GET" || request.method === "POST") &&
21
+ samePath(url, normalized.logoutPath)) {
22
+ return logoutAuth(normalized, secure);
23
+ }
24
+ return null;
25
+ }
26
+ export function getMetalootSessionFromCookieHeader(cookieHeader, options) {
27
+ const normalized = normalizeOptions(options);
28
+ const cookies = parseCookieHeader(cookieHeader);
29
+ const token = cookies.get(normalized.sessionCookieName);
30
+ return token ? verifySession(token, normalized.sessionSecret) : null;
31
+ }
32
+ async function startAuth(options, secure) {
33
+ const state = randomState();
34
+ const headers = new Headers({
35
+ Location: createAuthorizeUrl(options, state),
36
+ });
37
+ headers.append("Set-Cookie", serializeCookie(options.stateCookieName, state, {
38
+ httpOnly: true,
39
+ maxAge: 10 * 60,
40
+ path: "/",
41
+ sameSite: "Lax",
42
+ secure,
43
+ }));
44
+ return new Response(null, { status: 302, headers });
45
+ }
46
+ async function callbackAuth(request, options, secure) {
47
+ const url = new URL(request.url);
48
+ const code = url.searchParams.get("code");
49
+ const error = url.searchParams.get("error");
50
+ const returnedState = url.searchParams.get("state");
51
+ const cookies = parseCookieHeader(request.headers.get("cookie"));
52
+ const expectedState = cookies.get(options.stateCookieName);
53
+ if (error) {
54
+ return json({ error }, 400);
55
+ }
56
+ if (!code) {
57
+ return json({ error: "missing_code" }, 400);
58
+ }
59
+ if (expectedState && returnedState !== expectedState) {
60
+ return json({ error: "invalid_state" }, 400);
61
+ }
62
+ const token = await exchangeMetalootCode(code, options);
63
+ const user = token.user
64
+ ? normalizeUser(token.user)
65
+ : await fetchMetalootUser(token, options.authOrigin);
66
+ const expiresAt = new Date(Date.now() + token.expires_in * 1000).toISOString();
67
+ const sessionToken = signSession({
68
+ signedIn: true,
69
+ user,
70
+ accessToken: token.access_token,
71
+ scope: token.scope,
72
+ expiresAt,
73
+ }, options.sessionSecret);
74
+ const headers = new Headers({ Location: options.afterSignInPath });
75
+ headers.append("Set-Cookie", serializeCookie(options.sessionCookieName, sessionToken, {
76
+ httpOnly: true,
77
+ maxAge: token.expires_in,
78
+ path: "/",
79
+ sameSite: "Lax",
80
+ secure,
81
+ }));
82
+ headers.append("Set-Cookie", clearCookie(options.stateCookieName, secure));
83
+ return new Response(null, { status: 302, headers });
84
+ }
85
+ async function sessionResponse(request, options) {
86
+ const cookies = parseCookieHeader(request.headers.get("cookie"));
87
+ const token = cookies.get(options.sessionCookieName);
88
+ const session = token ? verifySession(token, options.sessionSecret) : null;
89
+ const body = session
90
+ ? {
91
+ signedIn: true,
92
+ user: session.user,
93
+ scope: session.scope,
94
+ expiresAt: session.expiresAt,
95
+ }
96
+ : { signedIn: false };
97
+ return json(body);
98
+ }
99
+ async function logoutAuth(options, secure) {
100
+ const headers = new Headers({ Location: options.afterSignOutPath });
101
+ headers.append("Set-Cookie", clearCookie(options.sessionCookieName, secure));
102
+ return new Response(null, { status: 302, headers });
103
+ }
104
+ async function exchangeMetalootCode(code, options) {
105
+ const response = await fetch(new URL("/api/oauth/token", options.authOrigin), {
106
+ method: "POST",
107
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
108
+ body: new URLSearchParams({
109
+ grant_type: "authorization_code",
110
+ client_id: options.clientId,
111
+ client_secret: options.clientSecret,
112
+ code,
113
+ redirect_uri: options.redirectUri,
114
+ }),
115
+ });
116
+ const body = (await response.json());
117
+ if (!response.ok || !("access_token" in body)) {
118
+ throw new Error("Metaloot token exchange failed.");
119
+ }
120
+ return body;
121
+ }
122
+ async function fetchMetalootUser(token, authOrigin) {
123
+ const response = await fetch(new URL("/api/oauth/userinfo", authOrigin), {
124
+ headers: { Authorization: `Bearer ${token.access_token}` },
125
+ });
126
+ if (!response.ok)
127
+ throw new Error("Metaloot userinfo request failed.");
128
+ const user = (await response.json());
129
+ return {
130
+ id: user.id ?? user.sub ?? "",
131
+ email: user.email,
132
+ name: user.name,
133
+ imageUrl: user.picture,
134
+ };
135
+ }
136
+ function normalizeUser(user) {
137
+ return {
138
+ id: user.id,
139
+ email: user.email,
140
+ name: user.name,
141
+ imageUrl: user.imageUrl ?? user.image_url,
142
+ };
143
+ }
144
+ function shouldUseSecureCookies(request, options) {
145
+ if (typeof options.cookieSecure === "boolean")
146
+ return options.cookieSecure;
147
+ return new URL(request.url).protocol === "https:";
148
+ }
149
+ function json(body, status = 200) {
150
+ return new Response(JSON.stringify(body), {
151
+ status,
152
+ headers: { "Content-Type": "application/json" },
153
+ });
154
+ }
155
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import type { MetalootSession } from "./types.js";
2
+ export declare function randomState(): string;
3
+ export declare function signSession(session: MetalootSession, secret: string): string;
4
+ export declare function verifySession(token: string, secret: string): MetalootSession | null;
5
+ export declare function parseCookieHeader(header: string | null | undefined): Map<string, string>;
6
+ export declare function serializeCookie(name: string, value: string, options?: {
7
+ httpOnly?: boolean;
8
+ maxAge?: number;
9
+ path?: string;
10
+ sameSite?: "Lax" | "Strict" | "None";
11
+ secure?: boolean;
12
+ }): string;
13
+ export declare function clearCookie(name: string, secure: boolean): string;
14
+ //# sourceMappingURL=session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAIlD,wBAAgB,WAAW,IAAI,MAAM,CAEpC;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAI5E;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,CAiBnF;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAaxF;AAED,wBAAgB,eAAe,CAC7B,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,OAAO,GAAE;IACP,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;IACrC,MAAM,CAAC,EAAE,OAAO,CAAC;CACb,GACL,MAAM,CAQR;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,MAAM,CAQjE"}
@@ -0,0 +1,77 @@
1
+ import { createHmac, randomBytes, timingSafeEqual } from "node:crypto";
2
+ const TEXT_ENCODER = new TextEncoder();
3
+ export function randomState() {
4
+ return base64Url(randomBytes(32));
5
+ }
6
+ export function signSession(session, secret) {
7
+ const payload = base64Url(JSON.stringify(session));
8
+ const signature = createSignature(payload, secret);
9
+ return `${payload}.${signature}`;
10
+ }
11
+ export function verifySession(token, secret) {
12
+ const [payload, signature] = token.split(".");
13
+ if (!payload || !signature)
14
+ return null;
15
+ const expected = createSignature(payload, secret);
16
+ if (!safeEqual(signature, expected))
17
+ return null;
18
+ try {
19
+ const session = JSON.parse(Buffer.from(payload, "base64url").toString("utf8"));
20
+ if (!session?.signedIn || !session.user?.id || !session.expiresAt)
21
+ return null;
22
+ if (new Date(session.expiresAt).getTime() <= Date.now())
23
+ return null;
24
+ return session;
25
+ }
26
+ catch {
27
+ return null;
28
+ }
29
+ }
30
+ export function parseCookieHeader(header) {
31
+ const cookies = new Map();
32
+ if (!header)
33
+ return cookies;
34
+ for (const part of header.split(";")) {
35
+ const index = part.indexOf("=");
36
+ if (index === -1)
37
+ continue;
38
+ const key = part.slice(0, index).trim();
39
+ const value = part.slice(index + 1).trim();
40
+ if (key)
41
+ cookies.set(key, decodeURIComponent(value));
42
+ }
43
+ return cookies;
44
+ }
45
+ export function serializeCookie(name, value, options = {}) {
46
+ const parts = [`${name}=${encodeURIComponent(value)}`];
47
+ parts.push(`Path=${options.path ?? "/"}`);
48
+ if (typeof options.maxAge === "number")
49
+ parts.push(`Max-Age=${options.maxAge}`);
50
+ if (options.httpOnly ?? true)
51
+ parts.push("HttpOnly");
52
+ parts.push(`SameSite=${options.sameSite ?? "Lax"}`);
53
+ if (options.secure)
54
+ parts.push("Secure");
55
+ return parts.join("; ");
56
+ }
57
+ export function clearCookie(name, secure) {
58
+ return serializeCookie(name, "", {
59
+ httpOnly: true,
60
+ maxAge: 0,
61
+ path: "/",
62
+ sameSite: "Lax",
63
+ secure,
64
+ });
65
+ }
66
+ function createSignature(payload, secret) {
67
+ return base64Url(createHmac("sha256", secret).update(payload).digest());
68
+ }
69
+ function safeEqual(a, b) {
70
+ const left = TEXT_ENCODER.encode(a);
71
+ const right = TEXT_ENCODER.encode(b);
72
+ return left.length === right.length && timingSafeEqual(left, right);
73
+ }
74
+ function base64Url(value) {
75
+ return Buffer.from(value).toString("base64url");
76
+ }
77
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import type { MetalootAuthOptions, NormalizedMetalootAuthOptions } from "./types.js";
2
+ export declare const DEFAULT_AUTH_ORIGIN = "https://metaloot.app";
3
+ export declare function normalizeOptions(options: MetalootAuthOptions): NormalizedMetalootAuthOptions;
4
+ export declare function trimTrailingSlash(value: string): string;
5
+ export declare function createAuthorizeUrl(options: NormalizedMetalootAuthOptions, state: string): string;
6
+ export declare function samePath(requestUrl: URL, path: string): boolean;
7
+ //# sourceMappingURL=shared.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../src/shared.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,6BAA6B,EAAE,MAAM,YAAY,CAAC;AAErF,eAAO,MAAM,mBAAmB,yBAAyB,CAAC;AAE1D,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,mBAAmB,GAC3B,6BAA6B,CAsB/B;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,6BAA6B,EACtC,KAAK,EAAE,MAAM,GACZ,MAAM,CAQR;AAED,wBAAgB,QAAQ,CAAC,UAAU,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAE/D"}
package/dist/shared.js ADDED
@@ -0,0 +1,42 @@
1
+ export const DEFAULT_AUTH_ORIGIN = "https://metaloot.app";
2
+ export function normalizeOptions(options) {
3
+ if (!options.clientId)
4
+ throw new Error("Metaloot auth requires clientId.");
5
+ if (!options.clientSecret)
6
+ throw new Error("Metaloot auth requires clientSecret.");
7
+ if (!options.redirectUri)
8
+ throw new Error("Metaloot auth requires redirectUri.");
9
+ return {
10
+ clientId: options.clientId,
11
+ clientSecret: options.clientSecret,
12
+ redirectUri: options.redirectUri,
13
+ authOrigin: trimTrailingSlash(options.authOrigin ?? DEFAULT_AUTH_ORIGIN),
14
+ scope: options.scope ?? "profile email",
15
+ sessionSecret: options.sessionSecret ?? options.clientSecret,
16
+ startPath: options.startPath ?? "/auth/metaloot/start",
17
+ callbackPath: options.callbackPath ?? "/auth/metaloot/callback",
18
+ sessionPath: options.sessionPath ?? "/auth/metaloot/session",
19
+ logoutPath: options.logoutPath ?? "/auth/metaloot/logout",
20
+ afterSignInPath: options.afterSignInPath ?? "/",
21
+ afterSignOutPath: options.afterSignOutPath ?? "/",
22
+ sessionCookieName: options.sessionCookieName ?? "metaloot_session",
23
+ stateCookieName: options.stateCookieName ?? "metaloot_oauth_state",
24
+ cookieSecure: options.cookieSecure ?? "auto",
25
+ };
26
+ }
27
+ export function trimTrailingSlash(value) {
28
+ return value.replace(/\/+$/, "");
29
+ }
30
+ export function createAuthorizeUrl(options, state) {
31
+ const url = new URL("/oauth/authorize", options.authOrigin);
32
+ url.searchParams.set("response_type", "code");
33
+ url.searchParams.set("client_id", options.clientId);
34
+ url.searchParams.set("redirect_uri", options.redirectUri);
35
+ url.searchParams.set("scope", options.scope);
36
+ url.searchParams.set("state", state);
37
+ return url.toString();
38
+ }
39
+ export function samePath(requestUrl, path) {
40
+ return requestUrl.pathname.replace(/\/+$/, "") === path.replace(/\/+$/, "");
41
+ }
42
+ //# sourceMappingURL=shared.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared.js","sourceRoot":"","sources":["../src/shared.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,mBAAmB,GAAG,sBAAsB,CAAC;AAE1D,MAAM,UAAU,gBAAgB,CAC9B,OAA4B;IAE5B,IAAI,CAAC,OAAO,CAAC,QAAQ;QAAE,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;IAC3E,IAAI,CAAC,OAAO,CAAC,YAAY;QAAE,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;IACnF,IAAI,CAAC,OAAO,CAAC,WAAW;QAAE,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;IAEjF,OAAO;QACL,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,YAAY,EAAE,OAAO,CAAC,YAAY;QAClC,WAAW,EAAE,OAAO,CAAC,WAAW;QAChC,UAAU,EAAE,iBAAiB,CAAC,OAAO,CAAC,UAAU,IAAI,mBAAmB,CAAC;QACxE,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,eAAe;QACvC,aAAa,EAAE,OAAO,CAAC,aAAa,IAAI,OAAO,CAAC,YAAY;QAC5D,SAAS,EAAE,OAAO,CAAC,SAAS,IAAI,sBAAsB;QACtD,YAAY,EAAE,OAAO,CAAC,YAAY,IAAI,yBAAyB;QAC/D,WAAW,EAAE,OAAO,CAAC,WAAW,IAAI,wBAAwB;QAC5D,UAAU,EAAE,OAAO,CAAC,UAAU,IAAI,uBAAuB;QACzD,eAAe,EAAE,OAAO,CAAC,eAAe,IAAI,GAAG;QAC/C,gBAAgB,EAAE,OAAO,CAAC,gBAAgB,IAAI,GAAG;QACjD,iBAAiB,EAAE,OAAO,CAAC,iBAAiB,IAAI,kBAAkB;QAClE,eAAe,EAAE,OAAO,CAAC,eAAe,IAAI,sBAAsB;QAClE,YAAY,EAAE,OAAO,CAAC,YAAY,IAAI,MAAM;KAC7C,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,KAAa;IAC7C,OAAO,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AACnC,CAAC;AAED,MAAM,UAAU,kBAAkB,CAChC,OAAsC,EACtC,KAAa;IAEb,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,kBAAkB,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;IAC5D,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC;IAC9C,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;IACpD,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,cAAc,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;IAC1D,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;IAC7C,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IACrC,OAAO,GAAG,CAAC,QAAQ,EAAE,CAAC;AACxB,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,UAAe,EAAE,IAAY;IACpD,OAAO,UAAU,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,KAAK,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AAC9E,CAAC"}
@@ -0,0 +1,55 @@
1
+ export type MetalootUser = {
2
+ id: string;
3
+ email?: string;
4
+ name?: string;
5
+ imageUrl?: string;
6
+ };
7
+ export type MetalootSession = {
8
+ signedIn: true;
9
+ user: MetalootUser;
10
+ accessToken: string;
11
+ scope: string;
12
+ expiresAt: string;
13
+ };
14
+ export type MetalootSessionResponse = {
15
+ signedIn: true;
16
+ user: MetalootUser;
17
+ scope: string;
18
+ expiresAt: string;
19
+ } | {
20
+ signedIn: false;
21
+ };
22
+ export type MetalootAuthOptions = {
23
+ clientId: string;
24
+ clientSecret: string;
25
+ redirectUri: string;
26
+ authOrigin?: string;
27
+ scope?: string;
28
+ sessionSecret?: string;
29
+ startPath?: string;
30
+ callbackPath?: string;
31
+ sessionPath?: string;
32
+ logoutPath?: string;
33
+ afterSignInPath?: string;
34
+ afterSignOutPath?: string;
35
+ sessionCookieName?: string;
36
+ stateCookieName?: string;
37
+ cookieSecure?: boolean | "auto";
38
+ };
39
+ export type NormalizedMetalootAuthOptions = Required<Pick<MetalootAuthOptions, "clientId" | "clientSecret" | "redirectUri" | "authOrigin" | "scope" | "sessionSecret" | "startPath" | "callbackPath" | "sessionPath" | "logoutPath" | "afterSignInPath" | "afterSignOutPath" | "sessionCookieName" | "stateCookieName">> & {
40
+ cookieSecure: boolean | "auto";
41
+ };
42
+ export type MetalootTokenResponse = {
43
+ access_token: string;
44
+ token_type: string;
45
+ expires_in: number;
46
+ scope: string;
47
+ user?: {
48
+ id: string;
49
+ email?: string;
50
+ name?: string;
51
+ image_url?: string;
52
+ imageUrl?: string;
53
+ };
54
+ };
55
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,YAAY,GAAG;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,EAAE,IAAI,CAAC;IACf,IAAI,EAAE,YAAY,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAC/B;IACE,QAAQ,EAAE,IAAI,CAAC;IACf,IAAI,EAAE,YAAY,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;CACnB,GACD;IACE,QAAQ,EAAE,KAAK,CAAC;CACjB,CAAC;AAEN,MAAM,MAAM,mBAAmB,GAAG;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;CACjC,CAAC;AAEF,MAAM,MAAM,6BAA6B,GAAG,QAAQ,CAClD,IAAI,CACF,mBAAmB,EACjB,UAAU,GACV,cAAc,GACd,aAAa,GACb,YAAY,GACZ,OAAO,GACP,eAAe,GACf,WAAW,GACX,cAAc,GACd,aAAa,GACb,YAAY,GACZ,iBAAiB,GACjB,kBAAkB,GAClB,mBAAmB,GACnB,iBAAiB,CACpB,CACF,GAAG;IACF,YAAY,EAAE,OAAO,GAAG,MAAM,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE;QACL,EAAE,EAAE,MAAM,CAAC;QACX,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC;CACH,CAAC"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,65 @@
1
+ {
2
+ "name": "@metaloot/auth",
3
+ "version": "0.1.0",
4
+ "description": "Drop-in Metaloot auth adapters for browser games.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "author": "Metaloot",
8
+ "homepage": "https://metaloot.app",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "https://github.com/Complexia/metaloot-auth.git"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/Complexia/metaloot-auth/issues"
15
+ },
16
+ "files": [
17
+ "dist",
18
+ "README.md",
19
+ "LICENSE"
20
+ ],
21
+ "sideEffects": false,
22
+ "exports": {
23
+ ".": {
24
+ "types": "./dist/index.d.ts",
25
+ "import": "./dist/index.js"
26
+ },
27
+ "./browser": {
28
+ "types": "./dist/browser.d.ts",
29
+ "import": "./dist/browser.js"
30
+ },
31
+ "./node": {
32
+ "types": "./dist/node.d.ts",
33
+ "import": "./dist/node.js"
34
+ },
35
+ "./next": {
36
+ "types": "./dist/next.d.ts",
37
+ "import": "./dist/next.js"
38
+ },
39
+ "./server": {
40
+ "types": "./dist/server.d.ts",
41
+ "import": "./dist/server.js"
42
+ }
43
+ },
44
+ "scripts": {
45
+ "build": "tsc -p tsconfig.json",
46
+ "clean": "rm -rf dist",
47
+ "prepack": "npm run clean && npm run build",
48
+ "test": "npm run build && node --test test/*.test.mjs",
49
+ "typecheck": "tsc -p tsconfig.json --noEmit"
50
+ },
51
+ "engines": {
52
+ "node": ">=20"
53
+ },
54
+ "keywords": [
55
+ "metaloot",
56
+ "auth",
57
+ "oauth",
58
+ "games",
59
+ "browser-games"
60
+ ],
61
+ "devDependencies": {
62
+ "@types/node": "^20.19.0",
63
+ "typescript": "^5.9.0"
64
+ }
65
+ }