@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 +21 -0
- package/README.md +178 -0
- package/dist/browser.d.ts +15 -0
- package/dist/browser.d.ts.map +1 -0
- package/dist/browser.js +91 -0
- package/dist/browser.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/next.d.ts +6 -0
- package/dist/next.d.ts.map +1 -0
- package/dist/next.js +12 -0
- package/dist/next.js.map +1 -0
- package/dist/node.d.ts +10 -0
- package/dist/node.d.ts.map +1 -0
- package/dist/node.js +54 -0
- package/dist/node.js.map +1 -0
- package/dist/server.d.ts +6 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +155 -0
- package/dist/server.js.map +1 -0
- package/dist/session.d.ts +14 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +77 -0
- package/dist/session.js.map +1 -0
- package/dist/shared.d.ts +7 -0
- package/dist/shared.d.ts.map +1 -0
- package/dist/shared.js +42 -0
- package/dist/shared.js.map +1 -0
- package/dist/types.d.ts +55 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +65 -0
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"}
|
package/dist/browser.js
ADDED
|
@@ -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("&", "&")
|
|
84
|
+
.replaceAll("<", "<")
|
|
85
|
+
.replaceAll(">", ">")
|
|
86
|
+
.replaceAll('"', """);
|
|
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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"}
|
package/dist/index.d.ts
ADDED
|
@@ -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 @@
|
|
|
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
|
package/dist/next.js.map
ADDED
|
@@ -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
|
package/dist/node.js.map
ADDED
|
@@ -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"}
|
package/dist/server.d.ts
ADDED
|
@@ -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"}
|
package/dist/session.js
ADDED
|
@@ -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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"}
|
package/dist/shared.d.ts
ADDED
|
@@ -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"}
|
package/dist/types.d.ts
ADDED
|
@@ -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 @@
|
|
|
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
|
+
}
|