@merit-systems/echo-next-sdk 0.0.2 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client.d.ts +1 -2
- package/dist/client.js +0 -9
- package/dist/client.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +45 -44
- package/dist/index.js.map +1 -1
- package/package.json +4 -3
package/dist/client.d.ts
CHANGED
|
@@ -5,6 +5,5 @@ interface EchoClientConfig {
|
|
|
5
5
|
* Sign in to Echo (client-side only)
|
|
6
6
|
*/
|
|
7
7
|
declare function signIn(config?: EchoClientConfig): void;
|
|
8
|
-
declare function refreshToken(config?: EchoClientConfig): void;
|
|
9
8
|
|
|
10
|
-
export { type EchoClientConfig,
|
|
9
|
+
export { type EchoClientConfig, signIn };
|
package/dist/client.js
CHANGED
|
@@ -7,16 +7,7 @@ function signIn(config) {
|
|
|
7
7
|
const basePath = config?.basePath || "/api/echo";
|
|
8
8
|
window.location.href = `${window.location.origin}${basePath}/signin`;
|
|
9
9
|
}
|
|
10
|
-
function refreshToken(config) {
|
|
11
|
-
if (typeof window === "undefined") {
|
|
12
|
-
console.warn("refreshToken() can only be called in client components");
|
|
13
|
-
return;
|
|
14
|
-
}
|
|
15
|
-
const basePath = config?.basePath || "/api/echo";
|
|
16
|
-
window.location.href = `${window.location.origin}${basePath}/refresh`;
|
|
17
|
-
}
|
|
18
10
|
export {
|
|
19
|
-
refreshToken,
|
|
20
11
|
signIn
|
|
21
12
|
};
|
|
22
13
|
//# sourceMappingURL=client.js.map
|
package/dist/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/client.ts"],"sourcesContent":["export interface EchoClientConfig {\n basePath?: string;\n}\n\n/**\n * Sign in to Echo (client-side only)\n */\nexport function signIn(config?: EchoClientConfig) {\n if (typeof window === 'undefined') {\n console.warn('signIn() can only be called in client components');\n return;\n }\n\n const basePath = config?.basePath || '/api/echo';\n window.location.href = `${window.location.origin}${basePath}/signin`;\n}\n
|
|
1
|
+
{"version":3,"sources":["../src/client.ts"],"sourcesContent":["export interface EchoClientConfig {\n basePath?: string;\n}\n\n/**\n * Sign in to Echo (client-side only)\n */\nexport function signIn(config?: EchoClientConfig) {\n if (typeof window === 'undefined') {\n console.warn('signIn() can only be called in client components');\n return;\n }\n\n const basePath = config?.basePath || '/api/echo';\n window.location.href = `${window.location.origin}${basePath}/signin`;\n}\n"],"mappings":";AAOO,SAAS,OAAO,QAA2B;AAChD,MAAI,OAAO,WAAW,aAAa;AACjC,YAAQ,KAAK,kDAAkD;AAC/D;AAAA,EACF;AAEA,QAAM,WAAW,QAAQ,YAAY;AACrC,SAAO,SAAS,OAAO,GAAG,OAAO,SAAS,MAAM,GAAG,QAAQ;AAC7D;","names":[]}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { User, EchoOpenAIProvider, EchoAnthropicProvider } from '@merit-systems/echo-typescript-sdk';
|
|
1
|
+
import { User, EchoOpenAIProvider, EchoAnthropicProvider, EchoGoogleProvider } from '@merit-systems/echo-typescript-sdk';
|
|
2
2
|
import { NextRequest } from 'next/server';
|
|
3
3
|
|
|
4
4
|
interface EchoConfig {
|
|
5
5
|
appId: string;
|
|
6
6
|
basePath?: string;
|
|
7
7
|
baseRouterUrl?: string;
|
|
8
|
+
baseEchoUrl?: string;
|
|
8
9
|
}
|
|
9
10
|
type AppRouteHandlers = Record<'GET' | 'POST', (req: NextRequest) => Promise<Response>>;
|
|
10
11
|
type EchoResult = {
|
|
@@ -13,6 +14,7 @@ type EchoResult = {
|
|
|
13
14
|
isSignedIn: () => Promise<boolean>;
|
|
14
15
|
openai: EchoOpenAIProvider;
|
|
15
16
|
anthropic: EchoAnthropicProvider;
|
|
17
|
+
google: EchoGoogleProvider;
|
|
16
18
|
};
|
|
17
19
|
|
|
18
20
|
/**
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
|
+
import { cookies } from "next/headers";
|
|
2
3
|
import { NextResponse as NextResponse2 } from "next/server";
|
|
4
|
+
import { EchoClient as EchoClient2 } from "@merit-systems/echo-typescript-sdk";
|
|
5
|
+
|
|
6
|
+
// src/config.ts
|
|
7
|
+
import { ECHO_BASE_URL } from "@merit-systems/echo-typescript-sdk";
|
|
8
|
+
function resolveEchoBaseUrl(config) {
|
|
9
|
+
return config.baseEchoUrl || ECHO_BASE_URL;
|
|
10
|
+
}
|
|
3
11
|
|
|
4
12
|
// src/auth/token-manager.ts
|
|
5
13
|
import {
|
|
6
|
-
EchoClient
|
|
7
|
-
ECHO_BASE_URL
|
|
14
|
+
EchoClient
|
|
8
15
|
} from "@merit-systems/echo-typescript-sdk";
|
|
9
16
|
import { cookies as getCookies } from "next/headers";
|
|
10
17
|
|
|
@@ -23,14 +30,14 @@ function shouldRefreshToken(token) {
|
|
|
23
30
|
}
|
|
24
31
|
|
|
25
32
|
// src/auth/token-manager.ts
|
|
26
|
-
async function performTokenRefresh(refreshToken,
|
|
27
|
-
return fetch(`${
|
|
33
|
+
async function performTokenRefresh(refreshToken, config) {
|
|
34
|
+
return fetch(`${resolveEchoBaseUrl(config)}/api/oauth/token`, {
|
|
28
35
|
method: "POST",
|
|
29
36
|
headers: { "Content-Type": "application/json" },
|
|
30
37
|
body: JSON.stringify({
|
|
31
38
|
grant_type: "refresh_token",
|
|
32
39
|
refresh_token: refreshToken,
|
|
33
|
-
client_id: appId
|
|
40
|
+
client_id: config.appId
|
|
34
41
|
})
|
|
35
42
|
}).then(async (r) => {
|
|
36
43
|
if (r.ok) {
|
|
@@ -40,7 +47,7 @@ async function performTokenRefresh(refreshToken, appId) {
|
|
|
40
47
|
return Promise.reject(new Error(await r.text()));
|
|
41
48
|
});
|
|
42
49
|
}
|
|
43
|
-
async function getEchoToken(
|
|
50
|
+
async function getEchoToken(config) {
|
|
44
51
|
const cookies2 = await getCookies();
|
|
45
52
|
const accessToken = cookies2.get("echo_access_token")?.value;
|
|
46
53
|
if (!accessToken || shouldRefreshToken(accessToken)) {
|
|
@@ -50,7 +57,7 @@ async function getEchoToken(appId) {
|
|
|
50
57
|
return null;
|
|
51
58
|
}
|
|
52
59
|
try {
|
|
53
|
-
const refreshResult = await performTokenRefresh(refreshToken,
|
|
60
|
+
const refreshResult = await performTokenRefresh(refreshToken, config);
|
|
54
61
|
cookies2.set("echo_access_token", refreshResult.access_token, {
|
|
55
62
|
httpOnly: true,
|
|
56
63
|
secure: process.env.NODE_ENV === "production",
|
|
@@ -89,22 +96,30 @@ async function getEchoToken(appId) {
|
|
|
89
96
|
|
|
90
97
|
// src/providers/anthropic.ts
|
|
91
98
|
import {
|
|
92
|
-
createEchoAnthropic as createEchoAnthropicBase
|
|
93
|
-
ROUTER_BASE_URL
|
|
99
|
+
createEchoAnthropic as createEchoAnthropicBase
|
|
94
100
|
} from "@merit-systems/echo-typescript-sdk";
|
|
95
|
-
function createEchoAnthropic({
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
101
|
+
function createEchoAnthropic(config) {
|
|
102
|
+
return createEchoAnthropicBase(config, async () => getEchoToken(config));
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// src/providers/google.ts
|
|
106
|
+
import {
|
|
107
|
+
createEchoGoogle as createEchoGoogleBase
|
|
108
|
+
} from "@merit-systems/echo-typescript-sdk";
|
|
109
|
+
function createEchoGoogle(config) {
|
|
110
|
+
return createEchoGoogleBase(config, async () => getEchoToken(config));
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// src/providers/openai.ts
|
|
114
|
+
import {
|
|
115
|
+
createEchoOpenAI as createEchoOpenAIBase
|
|
116
|
+
} from "@merit-systems/echo-typescript-sdk";
|
|
117
|
+
function createEchoOpenAI(config) {
|
|
118
|
+
return createEchoOpenAIBase(config, async () => getEchoToken(config));
|
|
103
119
|
}
|
|
104
120
|
|
|
105
121
|
// src/auth/oauth-handlers.ts
|
|
106
122
|
import { NextResponse } from "next/server";
|
|
107
|
-
import { ECHO_BASE_URL as ECHO_BASE_URL2 } from "@merit-systems/echo-typescript-sdk";
|
|
108
123
|
async function generateCodeChallenge() {
|
|
109
124
|
const codeVerifier = Array.from(crypto.getRandomValues(new Uint8Array(32))).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
110
125
|
const encoder = new TextEncoder();
|
|
@@ -120,7 +135,8 @@ async function generateCodeChallenge() {
|
|
|
120
135
|
async function handleSignIn(req, config) {
|
|
121
136
|
const { origin } = req.nextUrl;
|
|
122
137
|
const basePath = config.basePath || "/api/echo";
|
|
123
|
-
const
|
|
138
|
+
const baseUrl = resolveEchoBaseUrl(config);
|
|
139
|
+
const redirectUrl = new URL(`${baseUrl}/api/oauth/authorize`);
|
|
124
140
|
redirectUrl.searchParams.set("client_id", config.appId);
|
|
125
141
|
redirectUrl.searchParams.set("redirect_uri", `${origin}${basePath}/callback`);
|
|
126
142
|
redirectUrl.searchParams.set("response_type", "code");
|
|
@@ -153,7 +169,7 @@ async function handleCallback(req, config) {
|
|
|
153
169
|
{ status: 400 }
|
|
154
170
|
);
|
|
155
171
|
}
|
|
156
|
-
const tokenEndpoint = `${
|
|
172
|
+
const tokenEndpoint = `${resolveEchoBaseUrl(config)}/api/oauth/token`;
|
|
157
173
|
const params = new URLSearchParams();
|
|
158
174
|
params.set("grant_type", "authorization_code");
|
|
159
175
|
params.set("code", code);
|
|
@@ -163,7 +179,9 @@ async function handleCallback(req, config) {
|
|
|
163
179
|
const tokenResponse = await fetch(tokenEndpoint, {
|
|
164
180
|
method: "POST",
|
|
165
181
|
headers: {
|
|
166
|
-
"Content-Type": "application/x-www-form-urlencoded"
|
|
182
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
183
|
+
"x-client-user-agent": req.headers.get("user-agent") || "",
|
|
184
|
+
"x-client-ip": req.headers.get("x-forwarded-for")?.split(",")[0]?.trim() || ""
|
|
167
185
|
},
|
|
168
186
|
body: params.toString()
|
|
169
187
|
});
|
|
@@ -214,7 +232,7 @@ async function handleCallback(req, config) {
|
|
|
214
232
|
}
|
|
215
233
|
async function handleRefresh(req, config) {
|
|
216
234
|
try {
|
|
217
|
-
const token = await getEchoToken(config
|
|
235
|
+
const token = await getEchoToken(config);
|
|
218
236
|
if (!token) {
|
|
219
237
|
return NextResponse.json({ error: "No token found" }, { status: 401 });
|
|
220
238
|
}
|
|
@@ -228,24 +246,7 @@ async function handleRefresh(req, config) {
|
|
|
228
246
|
}
|
|
229
247
|
}
|
|
230
248
|
|
|
231
|
-
// src/providers/openai.ts
|
|
232
|
-
import {
|
|
233
|
-
createEchoOpenAI as createEchoOpenAIBase,
|
|
234
|
-
ROUTER_BASE_URL as ROUTER_BASE_URL2
|
|
235
|
-
} from "@merit-systems/echo-typescript-sdk";
|
|
236
|
-
function createEchoOpenAI({
|
|
237
|
-
appId,
|
|
238
|
-
baseRouterUrl = ROUTER_BASE_URL2
|
|
239
|
-
}) {
|
|
240
|
-
return createEchoOpenAIBase(
|
|
241
|
-
{ appId, baseRouterUrl },
|
|
242
|
-
async () => getEchoToken(appId)
|
|
243
|
-
);
|
|
244
|
-
}
|
|
245
|
-
|
|
246
249
|
// src/index.ts
|
|
247
|
-
import { EchoClient as EchoClient2, ECHO_BASE_URL as ECHO_BASE_URL3 } from "@merit-systems/echo-typescript-sdk";
|
|
248
|
-
import { cookies } from "next/headers";
|
|
249
250
|
function Echo(config) {
|
|
250
251
|
const httpHandler = async (req) => {
|
|
251
252
|
const { pathname } = req.nextUrl;
|
|
@@ -268,10 +269,8 @@ function Echo(config) {
|
|
|
268
269
|
if (!accessToken) {
|
|
269
270
|
return null;
|
|
270
271
|
}
|
|
271
|
-
const
|
|
272
|
-
|
|
273
|
-
baseUrl: ECHO_BASE_URL3
|
|
274
|
-
});
|
|
272
|
+
const baseUrl = resolveEchoBaseUrl(config);
|
|
273
|
+
const echo = new EchoClient2({ apiKey: accessToken, baseUrl });
|
|
275
274
|
const user = await echo.users.getUserInfo();
|
|
276
275
|
return user;
|
|
277
276
|
};
|
|
@@ -288,6 +287,7 @@ function Echo(config) {
|
|
|
288
287
|
return expiryTime > now;
|
|
289
288
|
};
|
|
290
289
|
return {
|
|
290
|
+
// http handlers
|
|
291
291
|
handlers: {
|
|
292
292
|
GET: httpHandler,
|
|
293
293
|
POST: httpHandler
|
|
@@ -297,7 +297,8 @@ function Echo(config) {
|
|
|
297
297
|
isSignedIn,
|
|
298
298
|
// providers
|
|
299
299
|
openai: createEchoOpenAI(config),
|
|
300
|
-
anthropic: createEchoAnthropic(config)
|
|
300
|
+
anthropic: createEchoAnthropic(config),
|
|
301
|
+
google: createEchoGoogle(config)
|
|
301
302
|
};
|
|
302
303
|
}
|
|
303
304
|
export {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/auth/token-manager.ts","../src/auth/jwt-utils.ts","../src/providers/anthropic.ts","../src/auth/oauth-handlers.ts","../src/providers/openai.ts"],"sourcesContent":["import { NextRequest, NextResponse } from 'next/server';\nimport { createEchoAnthropic } from 'providers/anthropic';\nimport {\n handleCallback,\n handleRefresh,\n handleSignIn,\n} from './auth/oauth-handlers';\nimport { createEchoOpenAI } from './providers/openai';\nimport { EchoConfig, EchoResult } from './types';\nimport { EchoClient, ECHO_BASE_URL } from '@merit-systems/echo-typescript-sdk';\nimport { cookies } from 'next/headers';\n\n/**\n * Echo SDK for Next.js\n * Provides OAuth authentication and token management for Echo API integration\n */\nexport default function Echo(config: EchoConfig): EchoResult {\n /**\n * HTTP handler for OAuth routes (signin and callback)\n */\n const httpHandler = async (req: NextRequest): Promise<Response> => {\n const { pathname } = req.nextUrl;\n const basePath = config.basePath || '/api/echo';\n const path = pathname.replace(basePath, '');\n\n switch (path) {\n case '/signin':\n return handleSignIn(req, config);\n\n case '/callback':\n return handleCallback(req, config);\n\n case '/refresh':\n return handleRefresh(req, config);\n\n default:\n return NextResponse.error();\n }\n };\n\n /**\n * Get current user info with automatic token refresh\n */\n const getUser = async () => {\n // read only access token, if expired we are fucked\n const cookieStore = await cookies();\n const accessToken = cookieStore.get('echo_access_token')?.value;\n if (!accessToken) {\n return null;\n }\n const echo = new EchoClient({\n apiKey: accessToken,\n baseUrl: ECHO_BASE_URL,\n });\n const user = await echo.users.getUserInfo();\n return user;\n };\n\n const isSignedIn = async () => {\n const cookieStore = await cookies();\n const refreshTokenExpiry = cookieStore.get(\n 'echo_refresh_token_expires'\n )?.value;\n\n if (!refreshTokenExpiry) {\n return false; // No expiry stored\n }\n\n const expiryTime = parseInt(refreshTokenExpiry);\n const now = Math.floor(Date.now() / 1000);\n\n return expiryTime > now; // True if not expired\n };\n\n return {\n handlers: {\n GET: httpHandler,\n POST: httpHandler,\n },\n // echo auth\n getUser,\n isSignedIn,\n // providers\n openai: createEchoOpenAI(config),\n anthropic: createEchoAnthropic(config),\n };\n}\n","import {\n EchoClient,\n ECHO_BASE_URL,\n User,\n} from '@merit-systems/echo-typescript-sdk';\nimport { cookies as getCookies } from 'next/headers';\nimport { shouldRefreshToken } from './jwt-utils';\n\nexport interface RefreshTokenResponse {\n access_token: string;\n token_type: string;\n expires_in: number;\n refresh_token: string;\n refresh_token_expires_in: number;\n scope: string;\n user: {\n id: string;\n email: string;\n name: string;\n };\n echo_app: {\n id: string;\n name: string;\n description: string;\n };\n}\n\n/**\n * Refresh an access token using a refresh token\n * @param refreshToken - The refresh token to use\n * @param appId - The Echo app ID\n * @returns Promise resolving to the new token data\n */\nexport async function performTokenRefresh(\n refreshToken: string,\n appId: string\n): Promise<RefreshTokenResponse> {\n return fetch(`${ECHO_BASE_URL}/api/oauth/token`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n grant_type: 'refresh_token',\n refresh_token: refreshToken,\n client_id: appId,\n }),\n }).then(async r => {\n if (r.ok) {\n const tokenData = (await r.json()) as RefreshTokenResponse;\n return tokenData;\n }\n return Promise.reject(new Error(await r.text()));\n });\n}\n\n/**\n * Get a valid Echo token, refreshing if necessary\n * @param appId - The Echo app ID for token refresh\n * @returns Promise resolving to a valid access token or null if authentication failed\n */\nexport async function getEchoToken(appId: string): Promise<string | null> {\n const cookies = await getCookies();\n const accessToken = cookies.get('echo_access_token')?.value;\n\n // Check if token needs refresh\n if (!accessToken || shouldRefreshToken(accessToken)) {\n const refreshToken = cookies.get('echo_refresh_token')?.value;\n\n if (!refreshToken) {\n console.log('No refresh token found');\n return null;\n }\n\n try {\n const refreshResult = await performTokenRefresh(refreshToken, appId);\n\n // Set new tokens in cookies\n cookies.set('echo_access_token', refreshResult.access_token, {\n httpOnly: true,\n secure: process.env.NODE_ENV === 'production',\n sameSite: 'lax',\n maxAge: refreshResult.expires_in,\n path: '/',\n });\n\n cookies.set('echo_refresh_token', refreshResult.refresh_token, {\n httpOnly: true,\n secure: process.env.NODE_ENV === 'production',\n sameSite: 'lax',\n maxAge: refreshResult.refresh_token_expires_in,\n path: '/',\n });\n\n // Store refresh token expiry time for checking\n cookies.set(\n 'echo_refresh_token_expires',\n String(\n Math.floor(Date.now() / 1000) + refreshResult.refresh_token_expires_in\n ),\n {\n httpOnly: true,\n secure: process.env.NODE_ENV === 'production',\n sameSite: 'lax',\n maxAge: refreshResult.refresh_token_expires_in,\n path: '/',\n }\n );\n\n return refreshResult.access_token;\n } catch (error) {\n console.error('Token refresh failed:', refreshToken, error);\n return null;\n }\n }\n\n return accessToken;\n}\n\nexport async function getUser(appId: string): Promise<User> {\n const echo = await getEchoClient(appId);\n if (!echo) {\n throw new Error('User not signed in');\n }\n return echo.users.getUserInfo();\n}\n\nexport async function getEchoClient(appId: string): Promise<EchoClient | null> {\n const token = await getEchoToken(appId);\n if (!token) {\n return null;\n }\n return new EchoClient({\n baseUrl: ECHO_BASE_URL,\n apiKey: token,\n });\n}\n","import { jwtDecode } from 'jwt-decode';\n\ninterface JwtPayload {\n exp?: number;\n}\n\n/**\n * Check if a JWT token needs to be refreshed\n * @param token - The JWT access token to check\n * @returns true if token should be refreshed, false otherwise\n */\nexport function shouldRefreshToken(token: string): boolean {\n try {\n const decoded = jwtDecode<JwtPayload>(token);\n if (!decoded.exp) return true;\n\n const now = Math.floor(Date.now() / 1000);\n // Refresh if token expires within 30 seconds\n const bufferTime = 30;\n return decoded.exp <= now + bufferTime;\n } catch {\n return true; // If we can't decode, assume it needs refresh\n }\n}\n","import { getEchoToken } from '../auth/token-manager';\nimport {\n createEchoAnthropic as createEchoAnthropicBase,\n EchoAnthropicProvider,\n EchoConfig,\n ROUTER_BASE_URL,\n} from '@merit-systems/echo-typescript-sdk';\n\nexport function createEchoAnthropic({\n appId,\n baseRouterUrl = ROUTER_BASE_URL,\n}: EchoConfig): EchoAnthropicProvider {\n return createEchoAnthropicBase({ appId, baseRouterUrl }, async () =>\n getEchoToken(appId)\n );\n}\n","import { NextRequest, NextResponse } from 'next/server';\nimport { getEchoToken, RefreshTokenResponse } from './token-manager';\nimport { EchoConfig, ECHO_BASE_URL } from '@merit-systems/echo-typescript-sdk';\n\n/**\n * Generate PKCE code challenge and verifier\n * @returns Object containing code verifier and challenge\n */\nasync function generateCodeChallenge(): Promise<{\n codeVerifier: string;\n codeChallenge: string;\n}> {\n const codeVerifier = Array.from(crypto.getRandomValues(new Uint8Array(32)))\n .map(b => b.toString(16).padStart(2, '0'))\n .join('');\n\n const encoder = new TextEncoder();\n const codeChallengeBuffer = await crypto.subtle.digest(\n 'SHA-256',\n encoder.encode(codeVerifier)\n );\n\n const codeChallenge = btoa(\n String.fromCharCode(...new Uint8Array(codeChallengeBuffer))\n )\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n .replace(/=+$/, '');\n\n return { codeVerifier, codeChallenge };\n}\n\n/**\n * Handle OAuth signin request - redirect to Echo authorization server\n * @param req - The incoming Next.js request\n * @param config - Echo configuration\n * @returns NextResponse with redirect to authorization server\n */\nexport async function handleSignIn(\n req: NextRequest,\n config: EchoConfig\n): Promise<NextResponse> {\n const { origin } = req.nextUrl;\n const basePath = config.basePath || '/api/echo';\n\n const redirectUrl = new URL(`${ECHO_BASE_URL}/api/oauth/authorize`);\n redirectUrl.searchParams.set('client_id', config.appId);\n redirectUrl.searchParams.set('redirect_uri', `${origin}${basePath}/callback`);\n redirectUrl.searchParams.set('response_type', 'code');\n\n const { codeVerifier, codeChallenge } = await generateCodeChallenge();\n\n redirectUrl.searchParams.set('code_challenge', codeChallenge);\n redirectUrl.searchParams.set('code_challenge_method', 'S256');\n\n // Create response with redirect and set cookie for code verifier\n const response = NextResponse.redirect(redirectUrl.toString());\n\n // Set the code verifier in a secure cookie that will be available during callback\n response.cookies.set('echo_code_verifier', codeVerifier, {\n httpOnly: true,\n secure: process.env.NODE_ENV === 'production',\n sameSite: 'lax',\n maxAge: 300, // 5 minutes - same as auth code TTL\n path: '/',\n });\n\n return response;\n}\n\n/**\n * Handle OAuth callback - exchange authorization code for tokens\n * @param req - The incoming Next.js request\n * @param config - Echo configuration\n * @returns NextResponse with redirect to home page and tokens set as cookies\n */\nexport async function handleCallback(\n req: NextRequest,\n config: EchoConfig\n): Promise<NextResponse> {\n const { origin } = req.nextUrl;\n const basePath = config.basePath || '/api/echo';\n\n const code = req.nextUrl.searchParams.get('code');\n const state = req.nextUrl.searchParams.get('state');\n\n if (!code || !state) {\n return NextResponse.json({ error: 'Invalid request' }, { status: 400 });\n }\n\n // Retrieve the code verifier from the cookie\n const codeVerifier = req.cookies.get('echo_code_verifier')?.value;\n\n if (!codeVerifier) {\n return NextResponse.json(\n { error: 'Code verifier not found. Please try signing in again.' },\n { status: 400 }\n );\n }\n\n // Exchange the code for a token from the token endpoint\n const tokenEndpoint = `${ECHO_BASE_URL}/api/oauth/token`;\n\n const params = new URLSearchParams();\n params.set('grant_type', 'authorization_code');\n params.set('code', code);\n params.set('redirect_uri', `${origin}${basePath}/callback`);\n params.set('client_id', config.appId);\n params.set('code_verifier', codeVerifier);\n\n const tokenResponse = await fetch(tokenEndpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n body: params.toString(),\n });\n\n if (!tokenResponse.ok) {\n return NextResponse.json(\n { error: 'Token exchange failed' },\n { status: 500 }\n );\n }\n\n const tokenData = (await tokenResponse.json()) as RefreshTokenResponse;\n\n const response = NextResponse.redirect(`${origin}`);\n\n // Clear the code verifier cookie after successful token exchange\n response.cookies.set('echo_code_verifier', '', {\n httpOnly: true,\n secure: process.env.NODE_ENV === 'production',\n sameSite: 'lax',\n maxAge: 0, // Expire immediately\n path: '/',\n });\n\n // Set the access token as a cookie\n response.cookies.set('echo_access_token', tokenData.access_token, {\n httpOnly: true,\n secure: process.env.NODE_ENV === 'production',\n sameSite: 'lax',\n maxAge: tokenData.expires_in, // expires_in is typically in seconds\n path: '/',\n });\n\n tokenData.refresh_token_expires_in;\n\n response.cookies.set('echo_refresh_token', tokenData.refresh_token, {\n httpOnly: true,\n secure: process.env.NODE_ENV === 'production',\n sameSite: 'lax',\n maxAge: tokenData.refresh_token_expires_in,\n path: '/',\n });\n\n // Store refresh token expiry time for checking\n response.cookies.set(\n 'echo_refresh_token_expires',\n String(Math.floor(Date.now() / 1000) + tokenData.refresh_token_expires_in),\n {\n httpOnly: true,\n secure: process.env.NODE_ENV === 'production',\n sameSite: 'lax',\n maxAge: tokenData.refresh_token_expires_in,\n path: '/',\n }\n );\n\n return response;\n}\n\nexport async function handleRefresh(\n req: NextRequest,\n config: EchoConfig\n): Promise<NextResponse> {\n try {\n const token = await getEchoToken(config.appId);\n if (!token) {\n return NextResponse.json({ error: 'No token found' }, { status: 401 });\n }\n\n return NextResponse.json({ success: true });\n } catch (error) {\n console.error('Error refreshing token:', error);\n return NextResponse.json(\n { error: 'Error refreshing token' },\n { status: 500 }\n );\n }\n}\n","import { getEchoToken } from '../auth/token-manager';\nimport {\n createEchoOpenAI as createEchoOpenAIBase,\n EchoConfig,\n EchoOpenAIProvider,\n ROUTER_BASE_URL,\n} from '@merit-systems/echo-typescript-sdk';\n\nexport function createEchoOpenAI({\n appId,\n baseRouterUrl = ROUTER_BASE_URL,\n}: EchoConfig): EchoOpenAIProvider {\n return createEchoOpenAIBase({ appId, baseRouterUrl }, async () =>\n getEchoToken(appId)\n );\n}\n"],"mappings":";AAAA,SAAsB,gBAAAA,qBAAoB;;;ACA1C;AAAA,EACE;AAAA,EACA;AAAA,OAEK;AACP,SAAS,WAAW,kBAAkB;;;ACLtC,SAAS,iBAAiB;AAWnB,SAAS,mBAAmB,OAAwB;AACzD,MAAI;AACF,UAAM,UAAU,UAAsB,KAAK;AAC3C,QAAI,CAAC,QAAQ,IAAK,QAAO;AAEzB,UAAM,MAAM,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AAExC,UAAM,aAAa;AACnB,WAAO,QAAQ,OAAO,MAAM;AAAA,EAC9B,QAAQ;AACN,WAAO;AAAA,EACT;AACF;;;ADUA,eAAsB,oBACpB,cACA,OAC+B;AAC/B,SAAO,MAAM,GAAG,aAAa,oBAAoB;AAAA,IAC/C,QAAQ;AAAA,IACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,IAC9C,MAAM,KAAK,UAAU;AAAA,MACnB,YAAY;AAAA,MACZ,eAAe;AAAA,MACf,WAAW;AAAA,IACb,CAAC;AAAA,EACH,CAAC,EAAE,KAAK,OAAM,MAAK;AACjB,QAAI,EAAE,IAAI;AACR,YAAM,YAAa,MAAM,EAAE,KAAK;AAChC,aAAO;AAAA,IACT;AACA,WAAO,QAAQ,OAAO,IAAI,MAAM,MAAM,EAAE,KAAK,CAAC,CAAC;AAAA,EACjD,CAAC;AACH;AAOA,eAAsB,aAAa,OAAuC;AACxE,QAAMC,WAAU,MAAM,WAAW;AACjC,QAAM,cAAcA,SAAQ,IAAI,mBAAmB,GAAG;AAGtD,MAAI,CAAC,eAAe,mBAAmB,WAAW,GAAG;AACnD,UAAM,eAAeA,SAAQ,IAAI,oBAAoB,GAAG;AAExD,QAAI,CAAC,cAAc;AACjB,cAAQ,IAAI,wBAAwB;AACpC,aAAO;AAAA,IACT;AAEA,QAAI;AACF,YAAM,gBAAgB,MAAM,oBAAoB,cAAc,KAAK;AAGnE,MAAAA,SAAQ,IAAI,qBAAqB,cAAc,cAAc;AAAA,QAC3D,UAAU;AAAA,QACV,QAAQ,QAAQ,IAAI,aAAa;AAAA,QACjC,UAAU;AAAA,QACV,QAAQ,cAAc;AAAA,QACtB,MAAM;AAAA,MACR,CAAC;AAED,MAAAA,SAAQ,IAAI,sBAAsB,cAAc,eAAe;AAAA,QAC7D,UAAU;AAAA,QACV,QAAQ,QAAQ,IAAI,aAAa;AAAA,QACjC,UAAU;AAAA,QACV,QAAQ,cAAc;AAAA,QACtB,MAAM;AAAA,MACR,CAAC;AAGD,MAAAA,SAAQ;AAAA,QACN;AAAA,QACA;AAAA,UACE,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI,IAAI,cAAc;AAAA,QAChD;AAAA,QACA;AAAA,UACE,UAAU;AAAA,UACV,QAAQ,QAAQ,IAAI,aAAa;AAAA,UACjC,UAAU;AAAA,UACV,QAAQ,cAAc;AAAA,UACtB,MAAM;AAAA,QACR;AAAA,MACF;AAEA,aAAO,cAAc;AAAA,IACvB,SAAS,OAAO;AACd,cAAQ,MAAM,yBAAyB,cAAc,KAAK;AAC1D,aAAO;AAAA,IACT;AAAA,EACF;AAEA,SAAO;AACT;;;AElHA;AAAA,EACE,uBAAuB;AAAA,EAGvB;AAAA,OACK;AAEA,SAAS,oBAAoB;AAAA,EAClC;AAAA,EACA,gBAAgB;AAClB,GAAsC;AACpC,SAAO;AAAA,IAAwB,EAAE,OAAO,cAAc;AAAA,IAAG,YACvD,aAAa,KAAK;AAAA,EACpB;AACF;;;ACfA,SAAsB,oBAAoB;AAE1C,SAAqB,iBAAAC,sBAAqB;AAM1C,eAAe,wBAGZ;AACD,QAAM,eAAe,MAAM,KAAK,OAAO,gBAAgB,IAAI,WAAW,EAAE,CAAC,CAAC,EACvE,IAAI,OAAK,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,EACxC,KAAK,EAAE;AAEV,QAAM,UAAU,IAAI,YAAY;AAChC,QAAM,sBAAsB,MAAM,OAAO,OAAO;AAAA,IAC9C;AAAA,IACA,QAAQ,OAAO,YAAY;AAAA,EAC7B;AAEA,QAAM,gBAAgB;AAAA,IACpB,OAAO,aAAa,GAAG,IAAI,WAAW,mBAAmB,CAAC;AAAA,EAC5D,EACG,QAAQ,OAAO,GAAG,EAClB,QAAQ,OAAO,GAAG,EAClB,QAAQ,OAAO,EAAE;AAEpB,SAAO,EAAE,cAAc,cAAc;AACvC;AAQA,eAAsB,aACpB,KACA,QACuB;AACvB,QAAM,EAAE,OAAO,IAAI,IAAI;AACvB,QAAM,WAAW,OAAO,YAAY;AAEpC,QAAM,cAAc,IAAI,IAAI,GAAGA,cAAa,sBAAsB;AAClE,cAAY,aAAa,IAAI,aAAa,OAAO,KAAK;AACtD,cAAY,aAAa,IAAI,gBAAgB,GAAG,MAAM,GAAG,QAAQ,WAAW;AAC5E,cAAY,aAAa,IAAI,iBAAiB,MAAM;AAEpD,QAAM,EAAE,cAAc,cAAc,IAAI,MAAM,sBAAsB;AAEpE,cAAY,aAAa,IAAI,kBAAkB,aAAa;AAC5D,cAAY,aAAa,IAAI,yBAAyB,MAAM;AAG5D,QAAM,WAAW,aAAa,SAAS,YAAY,SAAS,CAAC;AAG7D,WAAS,QAAQ,IAAI,sBAAsB,cAAc;AAAA,IACvD,UAAU;AAAA,IACV,QAAQ,QAAQ,IAAI,aAAa;AAAA,IACjC,UAAU;AAAA,IACV,QAAQ;AAAA;AAAA,IACR,MAAM;AAAA,EACR,CAAC;AAED,SAAO;AACT;AAQA,eAAsB,eACpB,KACA,QACuB;AACvB,QAAM,EAAE,OAAO,IAAI,IAAI;AACvB,QAAM,WAAW,OAAO,YAAY;AAEpC,QAAM,OAAO,IAAI,QAAQ,aAAa,IAAI,MAAM;AAChD,QAAM,QAAQ,IAAI,QAAQ,aAAa,IAAI,OAAO;AAElD,MAAI,CAAC,QAAQ,CAAC,OAAO;AACnB,WAAO,aAAa,KAAK,EAAE,OAAO,kBAAkB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACxE;AAGA,QAAM,eAAe,IAAI,QAAQ,IAAI,oBAAoB,GAAG;AAE5D,MAAI,CAAC,cAAc;AACjB,WAAO,aAAa;AAAA,MAClB,EAAE,OAAO,wDAAwD;AAAA,MACjE,EAAE,QAAQ,IAAI;AAAA,IAChB;AAAA,EACF;AAGA,QAAM,gBAAgB,GAAGA,cAAa;AAEtC,QAAM,SAAS,IAAI,gBAAgB;AACnC,SAAO,IAAI,cAAc,oBAAoB;AAC7C,SAAO,IAAI,QAAQ,IAAI;AACvB,SAAO,IAAI,gBAAgB,GAAG,MAAM,GAAG,QAAQ,WAAW;AAC1D,SAAO,IAAI,aAAa,OAAO,KAAK;AACpC,SAAO,IAAI,iBAAiB,YAAY;AAExC,QAAM,gBAAgB,MAAM,MAAM,eAAe;AAAA,IAC/C,QAAQ;AAAA,IACR,SAAS;AAAA,MACP,gBAAgB;AAAA,IAClB;AAAA,IACA,MAAM,OAAO,SAAS;AAAA,EACxB,CAAC;AAED,MAAI,CAAC,cAAc,IAAI;AACrB,WAAO,aAAa;AAAA,MAClB,EAAE,OAAO,wBAAwB;AAAA,MACjC,EAAE,QAAQ,IAAI;AAAA,IAChB;AAAA,EACF;AAEA,QAAM,YAAa,MAAM,cAAc,KAAK;AAE5C,QAAM,WAAW,aAAa,SAAS,GAAG,MAAM,EAAE;AAGlD,WAAS,QAAQ,IAAI,sBAAsB,IAAI;AAAA,IAC7C,UAAU;AAAA,IACV,QAAQ,QAAQ,IAAI,aAAa;AAAA,IACjC,UAAU;AAAA,IACV,QAAQ;AAAA;AAAA,IACR,MAAM;AAAA,EACR,CAAC;AAGD,WAAS,QAAQ,IAAI,qBAAqB,UAAU,cAAc;AAAA,IAChE,UAAU;AAAA,IACV,QAAQ,QAAQ,IAAI,aAAa;AAAA,IACjC,UAAU;AAAA,IACV,QAAQ,UAAU;AAAA;AAAA,IAClB,MAAM;AAAA,EACR,CAAC;AAED,YAAU;AAEV,WAAS,QAAQ,IAAI,sBAAsB,UAAU,eAAe;AAAA,IAClE,UAAU;AAAA,IACV,QAAQ,QAAQ,IAAI,aAAa;AAAA,IACjC,UAAU;AAAA,IACV,QAAQ,UAAU;AAAA,IAClB,MAAM;AAAA,EACR,CAAC;AAGD,WAAS,QAAQ;AAAA,IACf;AAAA,IACA,OAAO,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI,IAAI,UAAU,wBAAwB;AAAA,IACzE;AAAA,MACE,UAAU;AAAA,MACV,QAAQ,QAAQ,IAAI,aAAa;AAAA,MACjC,UAAU;AAAA,MACV,QAAQ,UAAU;AAAA,MAClB,MAAM;AAAA,IACR;AAAA,EACF;AAEA,SAAO;AACT;AAEA,eAAsB,cACpB,KACA,QACuB;AACvB,MAAI;AACF,UAAM,QAAQ,MAAM,aAAa,OAAO,KAAK;AAC7C,QAAI,CAAC,OAAO;AACV,aAAO,aAAa,KAAK,EAAE,OAAO,iBAAiB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,IACvE;AAEA,WAAO,aAAa,KAAK,EAAE,SAAS,KAAK,CAAC;AAAA,EAC5C,SAAS,OAAO;AACd,YAAQ,MAAM,2BAA2B,KAAK;AAC9C,WAAO,aAAa;AAAA,MAClB,EAAE,OAAO,yBAAyB;AAAA,MAClC,EAAE,QAAQ,IAAI;AAAA,IAChB;AAAA,EACF;AACF;;;AC9LA;AAAA,EACE,oBAAoB;AAAA,EAGpB,mBAAAC;AAAA,OACK;AAEA,SAAS,iBAAiB;AAAA,EAC/B;AAAA,EACA,gBAAgBA;AAClB,GAAmC;AACjC,SAAO;AAAA,IAAqB,EAAE,OAAO,cAAc;AAAA,IAAG,YACpD,aAAa,KAAK;AAAA,EACpB;AACF;;;ALNA,SAAS,cAAAC,aAAY,iBAAAC,sBAAqB;AAC1C,SAAS,eAAe;AAMT,SAAR,KAAsB,QAAgC;AAI3D,QAAM,cAAc,OAAO,QAAwC;AACjE,UAAM,EAAE,SAAS,IAAI,IAAI;AACzB,UAAM,WAAW,OAAO,YAAY;AACpC,UAAM,OAAO,SAAS,QAAQ,UAAU,EAAE;AAE1C,YAAQ,MAAM;AAAA,MACZ,KAAK;AACH,eAAO,aAAa,KAAK,MAAM;AAAA,MAEjC,KAAK;AACH,eAAO,eAAe,KAAK,MAAM;AAAA,MAEnC,KAAK;AACH,eAAO,cAAc,KAAK,MAAM;AAAA,MAElC;AACE,eAAOC,cAAa,MAAM;AAAA,IAC9B;AAAA,EACF;AAKA,QAAM,UAAU,YAAY;AAE1B,UAAM,cAAc,MAAM,QAAQ;AAClC,UAAM,cAAc,YAAY,IAAI,mBAAmB,GAAG;AAC1D,QAAI,CAAC,aAAa;AAChB,aAAO;AAAA,IACT;AACA,UAAM,OAAO,IAAIF,YAAW;AAAA,MAC1B,QAAQ;AAAA,MACR,SAASC;AAAA,IACX,CAAC;AACD,UAAM,OAAO,MAAM,KAAK,MAAM,YAAY;AAC1C,WAAO;AAAA,EACT;AAEA,QAAM,aAAa,YAAY;AAC7B,UAAM,cAAc,MAAM,QAAQ;AAClC,UAAM,qBAAqB,YAAY;AAAA,MACrC;AAAA,IACF,GAAG;AAEH,QAAI,CAAC,oBAAoB;AACvB,aAAO;AAAA,IACT;AAEA,UAAM,aAAa,SAAS,kBAAkB;AAC9C,UAAM,MAAM,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AAExC,WAAO,aAAa;AAAA,EACtB;AAEA,SAAO;AAAA,IACL,UAAU;AAAA,MACR,KAAK;AAAA,MACL,MAAM;AAAA,IACR;AAAA;AAAA,IAEA;AAAA,IACA;AAAA;AAAA,IAEA,QAAQ,iBAAiB,MAAM;AAAA,IAC/B,WAAW,oBAAoB,MAAM;AAAA,EACvC;AACF;","names":["NextResponse","cookies","ECHO_BASE_URL","ROUTER_BASE_URL","EchoClient","ECHO_BASE_URL","NextResponse"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/config.ts","../src/auth/token-manager.ts","../src/auth/jwt-utils.ts","../src/providers/anthropic.ts","../src/providers/google.ts","../src/providers/openai.ts","../src/auth/oauth-handlers.ts"],"sourcesContent":["import { cookies } from 'next/headers';\nimport { NextRequest, NextResponse } from 'next/server';\n\nimport { EchoClient } from '@merit-systems/echo-typescript-sdk';\nimport { resolveEchoBaseUrl } from './config';\nimport { EchoConfig, EchoResult } from './types';\n\nimport { createEchoAnthropic } from 'providers/anthropic';\nimport { createEchoGoogle } from 'providers/google';\nimport { createEchoOpenAI } from 'providers/openai';\n\nimport {\n handleCallback,\n handleRefresh,\n handleSignIn,\n} from './auth/oauth-handlers';\n\n/**\n * Echo SDK for Next.js\n * Provides OAuth authentication and token management for Echo API integration\n */\nexport default function Echo(config: EchoConfig): EchoResult {\n /**\n * HTTP handler for OAuth routes (signin and callback)\n */\n const httpHandler = async (req: NextRequest): Promise<Response> => {\n const { pathname } = req.nextUrl;\n const basePath = config.basePath || '/api/echo';\n const path = pathname.replace(basePath, '');\n\n switch (path) {\n case '/signin':\n return handleSignIn(req, config);\n\n case '/callback':\n return handleCallback(req, config);\n\n case '/refresh':\n return handleRefresh(req, config);\n\n default:\n return NextResponse.error();\n }\n };\n\n /**\n * Get current user info with automatic token refresh\n */\n const getUser = async () => {\n // read only access token, if expired we are fucked\n const cookieStore = await cookies();\n const accessToken = cookieStore.get('echo_access_token')?.value;\n if (!accessToken) {\n return null;\n }\n const baseUrl = resolveEchoBaseUrl(config);\n const echo = new EchoClient({ apiKey: accessToken, baseUrl });\n const user = await echo.users.getUserInfo();\n return user;\n };\n\n const isSignedIn = async () => {\n const cookieStore = await cookies();\n const refreshTokenExpiry = cookieStore.get(\n 'echo_refresh_token_expires'\n )?.value;\n\n if (!refreshTokenExpiry) {\n return false; // No expiry stored\n }\n\n const expiryTime = parseInt(refreshTokenExpiry);\n const now = Math.floor(Date.now() / 1000);\n\n return expiryTime > now; // True if not expired\n };\n\n return {\n // http handlers\n handlers: {\n GET: httpHandler,\n POST: httpHandler,\n },\n\n // echo auth\n getUser,\n isSignedIn,\n\n // providers\n openai: createEchoOpenAI(config),\n anthropic: createEchoAnthropic(config),\n google: createEchoGoogle(config),\n };\n}\n","import { ECHO_BASE_URL } from '@merit-systems/echo-typescript-sdk';\nimport type { EchoConfig } from './types';\n\nexport function resolveEchoBaseUrl(config: EchoConfig): string {\n return config.baseEchoUrl || ECHO_BASE_URL;\n}\n","import {\n EchoClient,\n EchoConfig,\n User,\n} from '@merit-systems/echo-typescript-sdk';\nimport { cookies as getCookies } from 'next/headers';\nimport { resolveEchoBaseUrl } from '../config';\nimport { shouldRefreshToken } from './jwt-utils';\n\nexport interface RefreshTokenResponse {\n access_token: string;\n token_type: string;\n expires_in: number;\n refresh_token: string;\n refresh_token_expires_in: number;\n scope: string;\n user: {\n id: string;\n email: string;\n name: string;\n };\n echo_app: {\n id: string;\n name: string;\n description: string;\n };\n}\n\n/**\n * Refresh an access token using a refresh token\n * @param refreshToken - The refresh token to use\n * @param appId - The Echo app ID\n * @returns Promise resolving to the new token data\n */\nexport async function performTokenRefresh(\n refreshToken: string,\n config: EchoConfig\n): Promise<RefreshTokenResponse> {\n return fetch(`${resolveEchoBaseUrl(config)}/api/oauth/token`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n grant_type: 'refresh_token',\n refresh_token: refreshToken,\n client_id: config.appId,\n }),\n }).then(async r => {\n if (r.ok) {\n const tokenData = (await r.json()) as RefreshTokenResponse;\n return tokenData;\n }\n return Promise.reject(new Error(await r.text()));\n });\n}\n\n/**\n * Get a valid Echo token, refreshing if necessary\n * @param appId - The Echo app ID for token refresh\n * @returns Promise resolving to a valid access token or null if authentication failed\n */\nexport async function getEchoToken(config: EchoConfig): Promise<string | null> {\n const cookies = await getCookies();\n const accessToken = cookies.get('echo_access_token')?.value;\n\n // Check if token needs refresh\n if (!accessToken || shouldRefreshToken(accessToken)) {\n const refreshToken = cookies.get('echo_refresh_token')?.value;\n\n if (!refreshToken) {\n console.log('No refresh token found');\n return null;\n }\n\n try {\n const refreshResult = await performTokenRefresh(refreshToken, config);\n\n // Set new tokens in cookies\n cookies.set('echo_access_token', refreshResult.access_token, {\n httpOnly: true,\n secure: process.env.NODE_ENV === 'production',\n sameSite: 'lax',\n maxAge: refreshResult.expires_in,\n path: '/',\n });\n\n cookies.set('echo_refresh_token', refreshResult.refresh_token, {\n httpOnly: true,\n secure: process.env.NODE_ENV === 'production',\n sameSite: 'lax',\n maxAge: refreshResult.refresh_token_expires_in,\n path: '/',\n });\n\n // Store refresh token expiry time for checking\n cookies.set(\n 'echo_refresh_token_expires',\n String(\n Math.floor(Date.now() / 1000) + refreshResult.refresh_token_expires_in\n ),\n {\n httpOnly: true,\n secure: process.env.NODE_ENV === 'production',\n sameSite: 'lax',\n maxAge: refreshResult.refresh_token_expires_in,\n path: '/',\n }\n );\n\n return refreshResult.access_token;\n } catch (error) {\n console.error('Token refresh failed:', refreshToken, error);\n return null;\n }\n }\n\n return accessToken;\n}\n\nexport async function getUser(config: EchoConfig): Promise<User> {\n const echo = await getEchoClient(config);\n if (!echo) {\n throw new Error('User not signed in');\n }\n return echo.users.getUserInfo();\n}\n\nexport async function getEchoClient(\n config: EchoConfig\n): Promise<EchoClient | null> {\n const token = await getEchoToken(config);\n if (!token) {\n return null;\n }\n return new EchoClient({\n baseUrl: resolveEchoBaseUrl(config),\n apiKey: token,\n });\n}\n","import { jwtDecode } from 'jwt-decode';\n\ninterface JwtPayload {\n exp?: number;\n}\n\n/**\n * Check if a JWT token needs to be refreshed\n * @param token - The JWT access token to check\n * @returns true if token should be refreshed, false otherwise\n */\nexport function shouldRefreshToken(token: string): boolean {\n try {\n const decoded = jwtDecode<JwtPayload>(token);\n if (!decoded.exp) return true;\n\n const now = Math.floor(Date.now() / 1000);\n // Refresh if token expires within 30 seconds\n const bufferTime = 30;\n return decoded.exp <= now + bufferTime;\n } catch {\n return true; // If we can't decode, assume it needs refresh\n }\n}\n","import { getEchoToken } from '../auth/token-manager';\nimport {\n createEchoAnthropic as createEchoAnthropicBase,\n EchoAnthropicProvider,\n EchoConfig,\n} from '@merit-systems/echo-typescript-sdk';\n\nexport function createEchoAnthropic(config: EchoConfig): EchoAnthropicProvider {\n return createEchoAnthropicBase(config, async () => getEchoToken(config));\n}\n","import {\n createEchoGoogle as createEchoGoogleBase,\n EchoConfig,\n EchoGoogleProvider,\n} from '@merit-systems/echo-typescript-sdk';\nimport { getEchoToken } from '../auth/token-manager';\n\nexport function createEchoGoogle(config: EchoConfig): EchoGoogleProvider {\n return createEchoGoogleBase(config, async () => getEchoToken(config));\n}\n","import { getEchoToken } from '../auth/token-manager';\nimport {\n createEchoOpenAI as createEchoOpenAIBase,\n EchoConfig,\n EchoOpenAIProvider,\n} from '@merit-systems/echo-typescript-sdk';\n\nexport function createEchoOpenAI(config: EchoConfig): EchoOpenAIProvider {\n return createEchoOpenAIBase(config, async () => getEchoToken(config));\n}\n","import { EchoConfig } from '@merit-systems/echo-typescript-sdk';\nimport { NextRequest, NextResponse } from 'next/server';\nimport { resolveEchoBaseUrl } from '../config';\nimport { getEchoToken, RefreshTokenResponse } from './token-manager';\n\n/**\n * Generate PKCE code challenge and verifier\n * @returns Object containing code verifier and challenge\n */\nasync function generateCodeChallenge(): Promise<{\n codeVerifier: string;\n codeChallenge: string;\n}> {\n const codeVerifier = Array.from(crypto.getRandomValues(new Uint8Array(32)))\n .map(b => b.toString(16).padStart(2, '0'))\n .join('');\n\n const encoder = new TextEncoder();\n const codeChallengeBuffer = await crypto.subtle.digest(\n 'SHA-256',\n encoder.encode(codeVerifier)\n );\n\n const codeChallenge = btoa(\n String.fromCharCode(...new Uint8Array(codeChallengeBuffer))\n )\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n .replace(/=+$/, '');\n\n return { codeVerifier, codeChallenge };\n}\n\n/**\n * Handle OAuth signin request - redirect to Echo authorization server\n * @param req - The incoming Next.js request\n * @param config - Echo configuration\n * @returns NextResponse with redirect to authorization server\n */\nexport async function handleSignIn(\n req: NextRequest,\n config: EchoConfig\n): Promise<NextResponse> {\n const { origin } = req.nextUrl;\n const basePath = config.basePath || '/api/echo';\n\n const baseUrl = resolveEchoBaseUrl(config);\n\n const redirectUrl = new URL(`${baseUrl}/api/oauth/authorize`);\n redirectUrl.searchParams.set('client_id', config.appId);\n redirectUrl.searchParams.set('redirect_uri', `${origin}${basePath}/callback`);\n redirectUrl.searchParams.set('response_type', 'code');\n\n const { codeVerifier, codeChallenge } = await generateCodeChallenge();\n\n redirectUrl.searchParams.set('code_challenge', codeChallenge);\n redirectUrl.searchParams.set('code_challenge_method', 'S256');\n\n // Create response with redirect and set cookie for code verifier\n const response = NextResponse.redirect(redirectUrl.toString());\n\n // Set the code verifier in a secure cookie that will be available during callback\n response.cookies.set('echo_code_verifier', codeVerifier, {\n httpOnly: true,\n secure: process.env.NODE_ENV === 'production',\n sameSite: 'lax',\n maxAge: 300, // 5 minutes - same as auth code TTL\n path: '/',\n });\n\n return response;\n}\n\n/**\n * Handle OAuth callback - exchange authorization code for tokens\n * @param req - The incoming Next.js request\n * @param config - Echo configuration\n * @returns NextResponse with redirect to home page and tokens set as cookies\n */\nexport async function handleCallback(\n req: NextRequest,\n config: EchoConfig\n): Promise<NextResponse> {\n const { origin } = req.nextUrl;\n const basePath = config.basePath || '/api/echo';\n\n const code = req.nextUrl.searchParams.get('code');\n const state = req.nextUrl.searchParams.get('state');\n\n if (!code || !state) {\n return NextResponse.json({ error: 'Invalid request' }, { status: 400 });\n }\n\n // Retrieve the code verifier from the cookie\n const codeVerifier = req.cookies.get('echo_code_verifier')?.value;\n\n if (!codeVerifier) {\n return NextResponse.json(\n { error: 'Code verifier not found. Please try signing in again.' },\n { status: 400 }\n );\n }\n\n // Exchange the code for a token from the token endpoint\n const tokenEndpoint = `${resolveEchoBaseUrl(config)}/api/oauth/token`;\n\n const params = new URLSearchParams();\n params.set('grant_type', 'authorization_code');\n params.set('code', code);\n params.set('redirect_uri', `${origin}${basePath}/callback`);\n params.set('client_id', config.appId);\n params.set('code_verifier', codeVerifier);\n\n const tokenResponse = await fetch(tokenEndpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n 'x-client-user-agent': req.headers.get('user-agent') || '',\n 'x-client-ip':\n req.headers.get('x-forwarded-for')?.split(',')[0]?.trim() || '',\n },\n body: params.toString(),\n });\n\n if (!tokenResponse.ok) {\n return NextResponse.json(\n { error: 'Token exchange failed' },\n { status: 500 }\n );\n }\n\n const tokenData = (await tokenResponse.json()) as RefreshTokenResponse;\n\n const response = NextResponse.redirect(`${origin}`);\n\n // Clear the code verifier cookie after successful token exchange\n response.cookies.set('echo_code_verifier', '', {\n httpOnly: true,\n secure: process.env.NODE_ENV === 'production',\n sameSite: 'lax',\n maxAge: 0, // Expire immediately\n path: '/',\n });\n\n // Set the access token as a cookie\n response.cookies.set('echo_access_token', tokenData.access_token, {\n httpOnly: true,\n secure: process.env.NODE_ENV === 'production',\n sameSite: 'lax',\n maxAge: tokenData.expires_in, // expires_in is typically in seconds\n path: '/',\n });\n\n tokenData.refresh_token_expires_in;\n\n response.cookies.set('echo_refresh_token', tokenData.refresh_token, {\n httpOnly: true,\n secure: process.env.NODE_ENV === 'production',\n sameSite: 'lax',\n maxAge: tokenData.refresh_token_expires_in,\n path: '/',\n });\n\n // Store refresh token expiry time for checking\n response.cookies.set(\n 'echo_refresh_token_expires',\n String(Math.floor(Date.now() / 1000) + tokenData.refresh_token_expires_in),\n {\n httpOnly: true,\n secure: process.env.NODE_ENV === 'production',\n sameSite: 'lax',\n maxAge: tokenData.refresh_token_expires_in,\n path: '/',\n }\n );\n\n return response;\n}\n\nexport async function handleRefresh(\n req: NextRequest,\n config: EchoConfig\n): Promise<NextResponse> {\n try {\n const token = await getEchoToken(config);\n if (!token) {\n return NextResponse.json({ error: 'No token found' }, { status: 401 });\n }\n\n return NextResponse.json({ success: true });\n } catch (error) {\n console.error('Error refreshing token:', error);\n return NextResponse.json(\n { error: 'Error refreshing token' },\n { status: 500 }\n );\n }\n}\n"],"mappings":"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","names":["NextResponse","EchoClient","cookies","NextResponse","EchoClient"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@merit-systems/echo-next-sdk",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"description": "Next.js SDK for Echo",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -25,7 +25,8 @@
|
|
|
25
25
|
"test": "echo 'no tests'",
|
|
26
26
|
"test:unit": "echo 'no tests'",
|
|
27
27
|
"test:watch": "echo 'no tests'",
|
|
28
|
-
"prepublishOnly": "pnpm run build"
|
|
28
|
+
"prepublishOnly": "pnpm run build",
|
|
29
|
+
"prepare": "tsup src/index.ts src/client.ts --dts --format esm --out-dir dist"
|
|
29
30
|
},
|
|
30
31
|
"keywords": [
|
|
31
32
|
"echo",
|
|
@@ -36,7 +37,7 @@
|
|
|
36
37
|
"author": "Merit Systems",
|
|
37
38
|
"license": "MIT",
|
|
38
39
|
"dependencies": {
|
|
39
|
-
"@merit-systems/echo-typescript-sdk": "1.0.
|
|
40
|
+
"@merit-systems/echo-typescript-sdk": "1.0.7",
|
|
40
41
|
"ai": "^5.0.17",
|
|
41
42
|
"jwt-decode": "^4.0.0"
|
|
42
43
|
},
|