@merit-systems/echo-next-sdk 0.0.8 → 0.0.10

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 CHANGED
@@ -1,27 +1,20 @@
1
- import { EchoConfig, EchoClient } from '@merit-systems/echo-typescript-sdk';
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ReactNode } from 'react';
3
+ import { EchoUser } from '@merit-systems/echo-react-sdk';
4
+ export { EchoSignIn, EchoSignOut, EchoTokens, InsufficientFundsModal, useEcho } from '@merit-systems/echo-react-sdk';
2
5
 
3
- interface EchoClientConfig {
6
+ interface EchoProxyConfig {
7
+ appId: string;
4
8
  basePath?: string;
9
+ initialSession?: {
10
+ isAuthenticated: boolean;
11
+ user?: EchoUser | null;
12
+ };
5
13
  }
6
- /**
7
- * Sign in to Echo (client-side only)
8
- */
9
- declare function signIn(config?: EchoClientConfig): void;
10
- declare function signOut(config?: EchoClientConfig): void;
11
- /**
12
- * Create an Echo client for client-side usage (React components, hooks)
13
- * Uses relative URLs that work in the browser
14
- */
15
- declare function createEchoClient(config?: EchoConfig): EchoClient;
16
- /**
17
- * Pre-configured Echo client for client-side usage
18
- * Ready to use in React components
19
- */
20
- declare const echoClient: EchoClient;
21
- /**
22
- * React hook to get the Echo client
23
- * Provides a consistent way to access the client in components
24
- */
25
- declare function useEcho(config?: EchoConfig): EchoClient;
14
+ interface EchoProviderProps {
15
+ config: EchoProxyConfig;
16
+ children: ReactNode;
17
+ }
18
+ declare function EchoProvider({ config, children }: EchoProviderProps): react_jsx_runtime.JSX.Element;
26
19
 
27
- export { type EchoClientConfig, createEchoClient, echoClient, signIn, signOut, useEcho };
20
+ export { EchoProvider };
package/dist/client.js CHANGED
@@ -1,41 +1,86 @@
1
- // src/client.ts
1
+ // src/components/EchoProvider.tsx
2
+ import { useCallback, useMemo } from "react";
3
+ import { EchoProviderRaw } from "@merit-systems/echo-react-sdk/advanced";
2
4
  import { EchoClient } from "@merit-systems/echo-typescript-sdk";
3
- function signIn(config) {
4
- if (typeof window === "undefined") {
5
- console.warn("signIn() can only be called in client components");
6
- return;
7
- }
8
- const basePath = config?.basePath || "/api/echo";
9
- window.location.href = `${window.location.origin}${basePath}/signin`;
10
- }
11
- function signOut(config) {
12
- if (typeof window === "undefined") {
13
- console.warn("signOut() can only be called in client components");
14
- return;
15
- }
16
- const basePath = config?.basePath || "/api/echo";
17
- window.location.href = `${window.location.origin}${basePath}/signout`;
5
+ import useSWR from "swr";
6
+ import { jsx } from "react/jsx-runtime";
7
+ function useProxySession(basePath, initial) {
8
+ const {
9
+ data: session,
10
+ error,
11
+ isLoading
12
+ } = useSWR(
13
+ ["session", basePath],
14
+ async () => {
15
+ const res = await fetch(`${basePath}/session`, {
16
+ credentials: "include"
17
+ });
18
+ if (!res.ok) throw new Error(`Session ${res.status}`);
19
+ return res.json();
20
+ },
21
+ {
22
+ fallbackData: initial,
23
+ revalidateOnFocus: true,
24
+ revalidateOnReconnect: true,
25
+ errorRetryCount: 2
26
+ }
27
+ );
28
+ return {
29
+ isAuthenticated: session?.isAuthenticated ?? false,
30
+ isLoading,
31
+ error: error?.message ?? null
32
+ };
18
33
  }
19
- function createEchoClient(config) {
20
- const proxyPath = config?.basePath ? `${config.basePath}/proxy` : "/api/echo/proxy";
21
- return new EchoClient({
22
- baseUrl: proxyPath,
23
- // No apiKey needed - proxy handles authentication automatically
24
- apiKey: "next-sdk-proxy"
25
- });
26
- }
27
- var echoClient = createEchoClient();
28
- function useEcho(config) {
29
- if (config) {
30
- return createEchoClient(config);
31
- }
32
- return echoClient;
34
+ function EchoProvider({ config, children }) {
35
+ const basePath = config.basePath || "/api/echo";
36
+ const session = useProxySession(basePath, config.initialSession);
37
+ const echoClient = useMemo(
38
+ () => session.isAuthenticated ? new EchoClient({
39
+ baseUrl: `${basePath}/proxy`,
40
+ apiKey: "next-sdk-proxy"
41
+ }) : null,
42
+ [basePath, session.isAuthenticated]
43
+ );
44
+ const signIn = useCallback(async () => {
45
+ const returnTo = typeof window !== "undefined" ? window.location.href : void 0;
46
+ const url = `${basePath}/signin${returnTo ? `?returnTo=${encodeURIComponent(returnTo)}` : ""}`;
47
+ window.location.href = url;
48
+ }, [basePath]);
49
+ const signOut = useCallback(async () => {
50
+ window.location.href = `${basePath}/signout`;
51
+ }, [basePath]);
52
+ const getToken = useCallback(async () => null, []);
53
+ return /* @__PURE__ */ jsx(
54
+ EchoProviderRaw,
55
+ {
56
+ config: { appId: config.appId },
57
+ rawUser: null,
58
+ isLoggedIn: session.isAuthenticated,
59
+ isAuthLoading: session.isLoading,
60
+ authError: session.error ? new Error(session.error) : null,
61
+ echoClient,
62
+ signIn,
63
+ signOut,
64
+ getToken,
65
+ children
66
+ }
67
+ );
33
68
  }
69
+
70
+ // src/client.ts
71
+ import {
72
+ EchoSignIn,
73
+ EchoSignOut,
74
+ EchoTokens,
75
+ InsufficientFundsModal
76
+ } from "@merit-systems/echo-react-sdk";
77
+ import { useEcho } from "@merit-systems/echo-react-sdk";
34
78
  export {
35
- createEchoClient,
36
- echoClient,
37
- signIn,
38
- signOut,
79
+ EchoProvider,
80
+ EchoSignIn,
81
+ EchoSignOut,
82
+ EchoTokens,
83
+ InsufficientFundsModal,
39
84
  useEcho
40
85
  };
41
86
  //# sourceMappingURL=client.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/client.ts"],"sourcesContent":["import { EchoClient, EchoConfig } from '@merit-systems/echo-typescript-sdk';\n\nexport 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\nexport function signOut(config?: EchoClientConfig) {\n if (typeof window === 'undefined') {\n console.warn('signOut() can only be called in client components');\n return;\n }\n const basePath = config?.basePath || '/api/echo';\n window.location.href = `${window.location.origin}${basePath}/signout`;\n}\n\n/**\n * Create an Echo client for client-side usage (React components, hooks)\n * Uses relative URLs that work in the browser\n */\nexport function createEchoClient(config?: EchoConfig): EchoClient {\n const proxyPath = config?.basePath\n ? `${config.basePath}/proxy`\n : '/api/echo/proxy';\n\n return new EchoClient({\n baseUrl: proxyPath,\n // No apiKey needed - proxy handles authentication automatically\n apiKey: 'next-sdk-proxy',\n });\n}\n\n/**\n * Pre-configured Echo client for client-side usage\n * Ready to use in React components\n */\nexport const echoClient = createEchoClient();\n\n/**\n * React hook to get the Echo client\n * Provides a consistent way to access the client in components\n */\nexport function useEcho(config?: EchoConfig): EchoClient {\n if (config) {\n return createEchoClient(config);\n }\n return echoClient;\n}\n"],"mappings":";AAAA,SAAS,kBAA8B;AAShC,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;AAEO,SAAS,QAAQ,QAA2B;AACjD,MAAI,OAAO,WAAW,aAAa;AACjC,YAAQ,KAAK,mDAAmD;AAChE;AAAA,EACF;AACA,QAAM,WAAW,QAAQ,YAAY;AACrC,SAAO,SAAS,OAAO,GAAG,OAAO,SAAS,MAAM,GAAG,QAAQ;AAC7D;AAMO,SAAS,iBAAiB,QAAiC;AAChE,QAAM,YAAY,QAAQ,WACtB,GAAG,OAAO,QAAQ,WAClB;AAEJ,SAAO,IAAI,WAAW;AAAA,IACpB,SAAS;AAAA;AAAA,IAET,QAAQ;AAAA,EACV,CAAC;AACH;AAMO,IAAM,aAAa,iBAAiB;AAMpC,SAAS,QAAQ,QAAiC;AACvD,MAAI,QAAQ;AACV,WAAO,iBAAiB,MAAM;AAAA,EAChC;AACA,SAAO;AACT;","names":[]}
1
+ {"version":3,"sources":["../src/components/EchoProvider.tsx","../src/client.ts"],"sourcesContent":["'use client';\n\nimport { ReactNode, useCallback, useMemo } from 'react';\nimport { EchoProviderRaw } from '@merit-systems/echo-react-sdk/advanced';\nimport type { EchoUser } from '@merit-systems/echo-react-sdk';\nimport { EchoClient } from '@merit-systems/echo-typescript-sdk';\nimport useSWR from 'swr';\n\nexport interface EchoProxyConfig {\n appId: string;\n basePath?: string; // default '/api/echo'\n initialSession?: { isAuthenticated: boolean; user?: EchoUser | null };\n}\n\nfunction useProxySession(\n basePath: string,\n initial?: EchoProxyConfig['initialSession']\n) {\n const {\n data: session,\n error,\n isLoading,\n } = useSWR(\n ['session', basePath],\n async () => {\n const res = await fetch(`${basePath}/session`, {\n credentials: 'include',\n });\n if (!res.ok) throw new Error(`Session ${res.status}`);\n return res.json();\n },\n {\n fallbackData: initial,\n revalidateOnFocus: true,\n revalidateOnReconnect: true,\n errorRetryCount: 2,\n }\n );\n\n return {\n isAuthenticated: session?.isAuthenticated ?? false,\n isLoading,\n error: error?.message ?? null,\n };\n}\n\ninterface EchoProviderProps {\n config: EchoProxyConfig;\n children: ReactNode;\n}\n\nexport function EchoProvider({ config, children }: EchoProviderProps) {\n const basePath = config.basePath || '/api/echo';\n const session = useProxySession(basePath, config.initialSession);\n\n const echoClient = useMemo(\n () =>\n session.isAuthenticated\n ? new EchoClient({\n baseUrl: `${basePath}/proxy`,\n apiKey: 'next-sdk-proxy',\n })\n : null,\n [basePath, session.isAuthenticated]\n );\n\n const signIn = useCallback(async () => {\n const returnTo =\n typeof window !== 'undefined' ? window.location.href : undefined;\n const url = `${basePath}/signin${returnTo ? `?returnTo=${encodeURIComponent(returnTo)}` : ''}`;\n window.location.href = url;\n }, [basePath]);\n\n const signOut = useCallback(async () => {\n window.location.href = `${basePath}/signout`;\n }, [basePath]);\n\n const getToken = useCallback(async () => null, []);\n\n return (\n <EchoProviderRaw\n config={{ appId: config.appId } as any}\n rawUser={null}\n isLoggedIn={session.isAuthenticated}\n isAuthLoading={session.isLoading}\n authError={session.error ? new Error(session.error) : null}\n echoClient={echoClient}\n signIn={signIn}\n signOut={signOut}\n getToken={getToken}\n >\n {children}\n </EchoProviderRaw>\n );\n}\n","export { EchoProvider } from './components/EchoProvider';\nexport {\n EchoSignIn,\n EchoSignOut,\n EchoTokens,\n InsufficientFundsModal,\n} from '@merit-systems/echo-react-sdk';\n\nexport { useEcho } from '@merit-systems/echo-react-sdk';\n"],"mappings":";AAEA,SAAoB,aAAa,eAAe;AAChD,SAAS,uBAAuB;AAEhC,SAAS,kBAAkB;AAC3B,OAAO,YAAY;AA0Ef;AAlEJ,SAAS,gBACP,UACA,SACA;AACA,QAAM;AAAA,IACJ,MAAM;AAAA,IACN;AAAA,IACA;AAAA,EACF,IAAI;AAAA,IACF,CAAC,WAAW,QAAQ;AAAA,IACpB,YAAY;AACV,YAAM,MAAM,MAAM,MAAM,GAAG,QAAQ,YAAY;AAAA,QAC7C,aAAa;AAAA,MACf,CAAC;AACD,UAAI,CAAC,IAAI,GAAI,OAAM,IAAI,MAAM,WAAW,IAAI,MAAM,EAAE;AACpD,aAAO,IAAI,KAAK;AAAA,IAClB;AAAA,IACA;AAAA,MACE,cAAc;AAAA,MACd,mBAAmB;AAAA,MACnB,uBAAuB;AAAA,MACvB,iBAAiB;AAAA,IACnB;AAAA,EACF;AAEA,SAAO;AAAA,IACL,iBAAiB,SAAS,mBAAmB;AAAA,IAC7C;AAAA,IACA,OAAO,OAAO,WAAW;AAAA,EAC3B;AACF;AAOO,SAAS,aAAa,EAAE,QAAQ,SAAS,GAAsB;AACpE,QAAM,WAAW,OAAO,YAAY;AACpC,QAAM,UAAU,gBAAgB,UAAU,OAAO,cAAc;AAE/D,QAAM,aAAa;AAAA,IACjB,MACE,QAAQ,kBACJ,IAAI,WAAW;AAAA,MACb,SAAS,GAAG,QAAQ;AAAA,MACpB,QAAQ;AAAA,IACV,CAAC,IACD;AAAA,IACN,CAAC,UAAU,QAAQ,eAAe;AAAA,EACpC;AAEA,QAAM,SAAS,YAAY,YAAY;AACrC,UAAM,WACJ,OAAO,WAAW,cAAc,OAAO,SAAS,OAAO;AACzD,UAAM,MAAM,GAAG,QAAQ,UAAU,WAAW,aAAa,mBAAmB,QAAQ,CAAC,KAAK,EAAE;AAC5F,WAAO,SAAS,OAAO;AAAA,EACzB,GAAG,CAAC,QAAQ,CAAC;AAEb,QAAM,UAAU,YAAY,YAAY;AACtC,WAAO,SAAS,OAAO,GAAG,QAAQ;AAAA,EACpC,GAAG,CAAC,QAAQ,CAAC;AAEb,QAAM,WAAW,YAAY,YAAY,MAAM,CAAC,CAAC;AAEjD,SACE;AAAA,IAAC;AAAA;AAAA,MACC,QAAQ,EAAE,OAAO,OAAO,MAAM;AAAA,MAC9B,SAAS;AAAA,MACT,YAAY,QAAQ;AAAA,MACpB,eAAe,QAAQ;AAAA,MACvB,WAAW,QAAQ,QAAQ,IAAI,MAAM,QAAQ,KAAK,IAAI;AAAA,MACtD;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MAEC;AAAA;AAAA,EACH;AAEJ;;;AC7FA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEP,SAAS,eAAe;","names":[]}
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  // src/index.ts
2
- import { cookies } from "next/headers";
2
+ import { cookies as cookies2 } from "next/headers";
3
3
  import { NextResponse as NextResponse3 } from "next/server";
4
4
 
5
- // src/providers/anthropic.ts
5
+ // src/ai-providers/anthropic.ts
6
6
  import {
7
7
  createEchoAnthropic as createEchoAnthropicBase
8
8
  } from "@merit-systems/echo-typescript-sdk";
@@ -19,6 +19,18 @@ function resolveEchoBaseUrl(config) {
19
19
  return config.baseEchoUrl || ECHO_BASE_URL;
20
20
  }
21
21
 
22
+ // src/auth/cookie-names.ts
23
+ var ECHO_COOKIE = {
24
+ ACCESS_TOKEN: "echo_access_token",
25
+ REFRESH_TOKEN: "echo_refresh_token",
26
+ REFRESH_TOKEN_EXPIRES: "echo_refresh_token_expires",
27
+ USER_INFO: "echo_user_info",
28
+ CODE_VERIFIER: "echo_code_verifier"
29
+ };
30
+ function namespacedCookie(name, appId) {
31
+ return `${name}_${appId}`;
32
+ }
33
+
22
34
  // src/auth/jwt-utils.ts
23
35
  import { jwtDecode } from "jwt-decode";
24
36
  function shouldRefreshToken(token) {
@@ -52,32 +64,44 @@ async function performTokenRefresh(refreshToken, config) {
52
64
  });
53
65
  }
54
66
  async function getEchoToken(config) {
55
- const cookies2 = await getCookies();
56
- const accessToken = cookies2.get("echo_access_token")?.value;
67
+ const cookies3 = await getCookies();
68
+ const accessToken = cookies3.get(
69
+ namespacedCookie(ECHO_COOKIE.ACCESS_TOKEN, config.appId)
70
+ )?.value;
57
71
  if (!accessToken || shouldRefreshToken(accessToken)) {
58
- const refreshToken = cookies2.get("echo_refresh_token")?.value;
72
+ const refreshToken = cookies3.get(
73
+ namespacedCookie(ECHO_COOKIE.REFRESH_TOKEN, config.appId)
74
+ )?.value;
59
75
  if (!refreshToken) {
60
76
  console.log("No refresh token found");
61
77
  return null;
62
78
  }
63
79
  try {
64
80
  const refreshResult = await performTokenRefresh(refreshToken, config);
65
- cookies2.set("echo_access_token", refreshResult.access_token, {
66
- httpOnly: true,
67
- secure: process.env.NODE_ENV === "production",
68
- sameSite: "lax",
69
- maxAge: refreshResult.expires_in,
70
- path: "/"
71
- });
72
- cookies2.set("echo_refresh_token", refreshResult.refresh_token, {
73
- httpOnly: true,
74
- secure: process.env.NODE_ENV === "production",
75
- sameSite: "lax",
76
- maxAge: refreshResult.refresh_token_expires_in,
77
- path: "/"
78
- });
79
- cookies2.set(
80
- "echo_refresh_token_expires",
81
+ cookies3.set(
82
+ namespacedCookie(ECHO_COOKIE.ACCESS_TOKEN, config.appId),
83
+ refreshResult.access_token,
84
+ {
85
+ httpOnly: true,
86
+ secure: process.env.NODE_ENV === "production",
87
+ sameSite: "lax",
88
+ maxAge: refreshResult.expires_in,
89
+ path: "/"
90
+ }
91
+ );
92
+ cookies3.set(
93
+ namespacedCookie(ECHO_COOKIE.REFRESH_TOKEN, config.appId),
94
+ refreshResult.refresh_token,
95
+ {
96
+ httpOnly: true,
97
+ secure: process.env.NODE_ENV === "production",
98
+ sameSite: "lax",
99
+ maxAge: refreshResult.refresh_token_expires_in,
100
+ path: "/"
101
+ }
102
+ );
103
+ cookies3.set(
104
+ namespacedCookie(ECHO_COOKIE.REFRESH_TOKEN_EXPIRES, config.appId),
81
105
  String(
82
106
  Math.floor(Date.now() / 1e3) + refreshResult.refresh_token_expires_in
83
107
  ),
@@ -98,12 +122,12 @@ async function getEchoToken(config) {
98
122
  return accessToken;
99
123
  }
100
124
 
101
- // src/providers/anthropic.ts
125
+ // src/ai-providers/anthropic.ts
102
126
  function createEchoAnthropic(config) {
103
127
  return createEchoAnthropicBase(config, async () => getEchoToken(config));
104
128
  }
105
129
 
106
- // src/providers/google.ts
130
+ // src/ai-providers/google.ts
107
131
  import {
108
132
  createEchoGoogle as createEchoGoogleBase
109
133
  } from "@merit-systems/echo-typescript-sdk";
@@ -111,7 +135,7 @@ function createEchoGoogle(config) {
111
135
  return createEchoGoogleBase(config, async () => getEchoToken(config));
112
136
  }
113
137
 
114
- // src/providers/openai.ts
138
+ // src/ai-providers/openai.ts
115
139
  import {
116
140
  createEchoOpenAI as createEchoOpenAIBase
117
141
  } from "@merit-systems/echo-typescript-sdk";
@@ -159,6 +183,7 @@ async function handleEchoClientProxy(req, config) {
159
183
  }
160
184
 
161
185
  // src/auth/oauth-handlers.ts
186
+ import { cookies } from "next/headers";
162
187
  import { NextResponse as NextResponse2 } from "next/server";
163
188
  async function generateCodeChallenge() {
164
189
  const codeVerifier = Array.from(crypto.getRandomValues(new Uint8Array(32))).map((b) => b.toString(16).padStart(2, "0")).join("");
@@ -184,30 +209,35 @@ async function handleSignIn(req, config) {
184
209
  redirectUrl.searchParams.set("code_challenge", codeChallenge);
185
210
  redirectUrl.searchParams.set("code_challenge_method", "S256");
186
211
  const response = NextResponse2.redirect(redirectUrl.toString());
187
- response.cookies.set("echo_code_verifier", codeVerifier, {
188
- httpOnly: true,
189
- secure: process.env.NODE_ENV === "production",
190
- sameSite: "lax",
191
- maxAge: 300,
192
- // 5 minutes - same as auth code TTL
193
- path: "/"
194
- });
212
+ response.cookies.set(
213
+ namespacedCookie(ECHO_COOKIE.CODE_VERIFIER, config.appId),
214
+ codeVerifier,
215
+ {
216
+ httpOnly: true,
217
+ secure: process.env.NODE_ENV === "production",
218
+ sameSite: "lax",
219
+ maxAge: 300,
220
+ // 5 minutes - same as auth code TTL
221
+ path: "/"
222
+ }
223
+ );
195
224
  return response;
196
225
  }
197
226
  async function handleSignOut(req, config) {
198
227
  const { origin } = req.nextUrl;
199
228
  const response = NextResponse2.redirect(`${origin}`);
200
- const cookieOptions = {
201
- httpOnly: true,
202
- secure: process.env.NODE_ENV === "production",
203
- sameSite: "lax",
204
- maxAge: 0,
205
- path: "/"
206
- };
207
- response.cookies.set("echo_access_token", "", cookieOptions);
208
- response.cookies.set("echo_refresh_token", "", cookieOptions);
209
- response.cookies.set("echo_user_info", "", cookieOptions);
210
- response.cookies.set("echo_refresh_token_expires", "", cookieOptions);
229
+ response.cookies.delete(
230
+ namespacedCookie(ECHO_COOKIE.ACCESS_TOKEN, config.appId)
231
+ );
232
+ response.cookies.delete(
233
+ namespacedCookie(ECHO_COOKIE.REFRESH_TOKEN, config.appId)
234
+ );
235
+ response.cookies.delete(
236
+ namespacedCookie(ECHO_COOKIE.USER_INFO, config.appId)
237
+ );
238
+ response.cookies.delete(
239
+ namespacedCookie(ECHO_COOKIE.REFRESH_TOKEN_EXPIRES, config.appId)
240
+ );
211
241
  return response;
212
242
  }
213
243
  async function handleCallback(req, config) {
@@ -218,7 +248,9 @@ async function handleCallback(req, config) {
218
248
  if (!code || !state) {
219
249
  return NextResponse2.json({ error: "Invalid request" }, { status: 400 });
220
250
  }
221
- const codeVerifier = req.cookies.get("echo_code_verifier")?.value;
251
+ const codeVerifier = req.cookies.get(
252
+ namespacedCookie(ECHO_COOKIE.CODE_VERIFIER, config.appId)
253
+ )?.value;
222
254
  if (!codeVerifier) {
223
255
  return NextResponse2.json(
224
256
  { error: "Code verifier not found. Please try signing in again." },
@@ -249,39 +281,54 @@ async function handleCallback(req, config) {
249
281
  }
250
282
  const tokenData = await tokenResponse.json();
251
283
  const response = NextResponse2.redirect(`${origin}`);
252
- response.cookies.set("echo_code_verifier", "", {
253
- httpOnly: true,
254
- secure: process.env.NODE_ENV === "production",
255
- sameSite: "lax",
256
- maxAge: 0,
257
- // Expire immediately
258
- path: "/"
259
- });
260
- response.cookies.set("echo_access_token", tokenData.access_token, {
261
- httpOnly: true,
262
- secure: process.env.NODE_ENV === "production",
263
- sameSite: "lax",
264
- maxAge: tokenData.expires_in,
265
- // expires_in is typically in seconds
266
- path: "/"
267
- });
268
- tokenData.refresh_token_expires_in;
269
- response.cookies.set("echo_refresh_token", tokenData.refresh_token, {
270
- httpOnly: true,
271
- secure: process.env.NODE_ENV === "production",
272
- sameSite: "lax",
273
- maxAge: tokenData.refresh_token_expires_in,
274
- path: "/"
275
- });
276
- response.cookies.set("echo_user_info", JSON.stringify(tokenData.user), {
277
- httpOnly: true,
278
- secure: process.env.NODE_ENV === "production",
279
- sameSite: "lax",
280
- maxAge: tokenData.refresh_token_expires_in,
281
- path: "/"
282
- });
283
284
  response.cookies.set(
284
- "echo_refresh_token_expires",
285
+ namespacedCookie(ECHO_COOKIE.CODE_VERIFIER, config.appId),
286
+ "",
287
+ {
288
+ httpOnly: true,
289
+ secure: process.env.NODE_ENV === "production",
290
+ sameSite: "lax",
291
+ maxAge: 0,
292
+ // Expire immediately
293
+ path: "/"
294
+ }
295
+ );
296
+ response.cookies.set(
297
+ namespacedCookie(ECHO_COOKIE.ACCESS_TOKEN, config.appId),
298
+ tokenData.access_token,
299
+ {
300
+ httpOnly: true,
301
+ secure: process.env.NODE_ENV === "production",
302
+ sameSite: "lax",
303
+ maxAge: tokenData.expires_in,
304
+ // expires_in is typically in seconds
305
+ path: "/"
306
+ }
307
+ );
308
+ response.cookies.set(
309
+ namespacedCookie(ECHO_COOKIE.REFRESH_TOKEN, config.appId),
310
+ tokenData.refresh_token,
311
+ {
312
+ httpOnly: true,
313
+ secure: process.env.NODE_ENV === "production",
314
+ sameSite: "lax",
315
+ maxAge: tokenData.refresh_token_expires_in,
316
+ path: "/"
317
+ }
318
+ );
319
+ response.cookies.set(
320
+ namespacedCookie(ECHO_COOKIE.USER_INFO, config.appId),
321
+ JSON.stringify(tokenData.user),
322
+ {
323
+ httpOnly: true,
324
+ secure: process.env.NODE_ENV === "production",
325
+ sameSite: "lax",
326
+ maxAge: tokenData.refresh_token_expires_in,
327
+ path: "/"
328
+ }
329
+ );
330
+ response.cookies.set(
331
+ namespacedCookie(ECHO_COOKIE.REFRESH_TOKEN_EXPIRES, config.appId),
285
332
  String(Math.floor(Date.now() / 1e3) + tokenData.refresh_token_expires_in),
286
333
  {
287
334
  httpOnly: true,
@@ -308,6 +355,18 @@ async function handleRefresh(req, config) {
308
355
  );
309
356
  }
310
357
  }
358
+ async function handleSession(req, config) {
359
+ const jar = await cookies();
360
+ const refreshExp = jar.get(
361
+ namespacedCookie(ECHO_COOKIE.REFRESH_TOKEN_EXPIRES, config.appId)
362
+ )?.value;
363
+ const isAuthenticated = !!refreshExp && Number(refreshExp) > Math.floor(Date.now() / 1e3);
364
+ const userInfo = jar.get(
365
+ namespacedCookie(ECHO_COOKIE.USER_INFO, config.appId)
366
+ )?.value;
367
+ const user = userInfo ? JSON.parse(userInfo) : null;
368
+ return NextResponse2.json({ isAuthenticated, user });
369
+ }
311
370
 
312
371
  // src/index.ts
313
372
  function Echo(config) {
@@ -328,14 +387,18 @@ function Echo(config) {
328
387
  return handleCallback(req, config);
329
388
  case "/refresh":
330
389
  return handleRefresh(req, config);
390
+ case "/session":
391
+ return handleSession(req, config);
331
392
  default:
332
393
  console.error("Unknown path", path);
333
394
  return NextResponse3.error();
334
395
  }
335
396
  };
336
397
  const getUser = async () => {
337
- const cookieStore = await cookies();
338
- const userInfo = cookieStore.get("echo_user_info")?.value;
398
+ const cookieStore = await cookies2();
399
+ const userInfo = cookieStore.get(
400
+ namespacedCookie(ECHO_COOKIE.USER_INFO, config.appId)
401
+ )?.value;
339
402
  if (!userInfo) {
340
403
  return null;
341
404
  }
@@ -343,9 +406,9 @@ function Echo(config) {
343
406
  return user;
344
407
  };
345
408
  const isSignedIn = async () => {
346
- const cookieStore = await cookies();
409
+ const cookieStore = await cookies2();
347
410
  const refreshTokenExpiry = cookieStore.get(
348
- "echo_refresh_token_expires"
411
+ namespacedCookie(ECHO_COOKIE.REFRESH_TOKEN_EXPIRES, config.appId)
349
412
  )?.value;
350
413
  if (!refreshTokenExpiry) {
351
414
  return false;
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/providers/anthropic.ts","../src/auth/token-manager.ts","../src/config.ts","../src/auth/jwt-utils.ts","../src/providers/google.ts","../src/providers/openai.ts","../src/proxy/index.ts","../src/auth/oauth-handlers.ts"],"sourcesContent":["import { cookies } from 'next/headers';\nimport { NextRequest, NextResponse } from 'next/server';\n\nimport { EchoConfig, EchoResult } from './types';\n\nimport { createEchoAnthropic } from 'providers/anthropic';\nimport { createEchoGoogle } from 'providers/google';\nimport { createEchoOpenAI } from 'providers/openai';\n\nimport { RefreshTokenResponse } from 'auth/token-manager';\nimport { handleEchoClientProxy } from 'proxy';\nimport {\n handleCallback,\n handleRefresh,\n handleSignIn,\n handleSignOut,\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 if (path.startsWith('/proxy')) {\n return handleEchoClientProxy(req, config);\n }\n\n switch (path) {\n // all the auth stuff\n case '/signin':\n return handleSignIn(req, config);\n\n case '/signout':\n return handleSignOut(req, config);\n\n case '/callback':\n return handleCallback(req, config);\n\n case '/refresh':\n return handleRefresh(req, config);\n\n default:\n console.error('Unknown path', path);\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 userInfo = cookieStore.get('echo_user_info')?.value;\n if (!userInfo) {\n return null;\n }\n const user = JSON.parse(userInfo) as RefreshTokenResponse['user'];\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 for Next.js API routes\n handlers: {\n GET: httpHandler,\n POST: httpHandler,\n },\n\n // Authentication utilities (server-side only)\n getUser,\n isSignedIn,\n\n // AI provider clients\n openai: createEchoOpenAI(config),\n anthropic: createEchoAnthropic(config),\n google: createEchoGoogle(config),\n };\n}\n","import {\n AnthropicProvider,\n createEchoAnthropic as createEchoAnthropicBase,\n EchoConfig,\n} from '@merit-systems/echo-typescript-sdk';\nimport { getEchoToken } from '../auth/token-manager';\n\nexport function createEchoAnthropic(config: EchoConfig): AnthropicProvider {\n return createEchoAnthropicBase(config, async () => getEchoToken(config));\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 { 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 { 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 {\n createEchoGoogle as createEchoGoogleBase,\n EchoConfig,\n GoogleGenerativeAIProvider,\n} from '@merit-systems/echo-typescript-sdk';\nimport { getEchoToken } from '../auth/token-manager';\n\nexport function createEchoGoogle(\n config: EchoConfig\n): GoogleGenerativeAIProvider {\n return createEchoGoogleBase(config, async () => getEchoToken(config));\n}\n","import { getEchoToken } from '../auth/token-manager';\nimport {\n createEchoOpenAI as createEchoOpenAIBase,\n EchoConfig,\n OpenAIProvider,\n} from '@merit-systems/echo-typescript-sdk';\n\nexport function createEchoOpenAI(config: EchoConfig): OpenAIProvider {\n return createEchoOpenAIBase(config, async () => getEchoToken(config));\n}\n","import { NextRequest, NextResponse } from 'next/server';\nimport { getEchoToken } from '../auth/token-manager';\nimport { EchoConfig } from '../types';\n/**\n * Proxy requests to Echo service with automatic authentication\n */\nexport async function handleEchoClientProxy(\n req: NextRequest,\n config: EchoConfig\n): Promise<Response> {\n const proxyPath = req.nextUrl.pathname.replace(/^.*\\/proxy/, '');\n const baseUrl = config.baseEchoUrl || 'https://echo.merit.systems';\n const targetUrl = `${baseUrl}${proxyPath}`;\n\n const headers = new Headers(req.headers);\n headers.delete('host');\n // Don't ask upstream to compress\n headers.delete('accept-encoding');\n headers.set('accept-encoding', 'identity');\n\n // Optional: also drop conditional headers to avoid weirdness\n headers.delete('if-none-match');\n headers.delete('if-modified-since');\n\n // Get valid token using existing function (handles refresh automatically)\n const accessToken = await getEchoToken(config);\n\n if (!accessToken) {\n return NextResponse.json(\n { error: 'Authentication required' },\n { status: 401 }\n );\n }\n // Inject the valid token\n headers.set('Authorization', `Bearer ${accessToken}`);\n try {\n const response = await fetch(targetUrl, {\n method: req.method,\n headers,\n ...(req.method !== 'GET' &&\n req.method !== 'HEAD' && {\n body: req.body,\n duplex: 'half',\n }),\n });\n\n return response;\n } catch (error) {\n console.error('Proxy request failed:', error);\n return NextResponse.json(\n { error: 'Proxy request failed' },\n { status: 500 }\n );\n }\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 signout request - redirect to home page and clear cookies\n * @param req - The incoming Next.js request\n * @param config - Echo configuration\n * @returns NextResponse with redirect to home page and cookies cleared\n */\nexport async function handleSignOut(\n req: NextRequest,\n config: EchoConfig\n): Promise<NextResponse> {\n const { origin } = req.nextUrl;\n const response = NextResponse.redirect(`${origin}`);\n\n // Clear all authentication cookies to properly sign out the user\n const cookieOptions = {\n httpOnly: true,\n secure: process.env.NODE_ENV === 'production',\n sameSite: 'lax' as const,\n maxAge: 0,\n path: '/',\n };\n\n response.cookies.set('echo_access_token', '', cookieOptions);\n response.cookies.set('echo_refresh_token', '', cookieOptions);\n response.cookies.set('echo_user_info', '', cookieOptions);\n response.cookies.set('echo_refresh_token_expires', '', cookieOptions);\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\n response.cookies.set('echo_access_token', '', {\n httpOnly: true,\n secure: process.env.NODE_ENV === 'production',\n sameSite: 'lax',\n maxAge: 0,\n path: '/',\n });\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 response.cookies.set('echo_user_info', JSON.stringify(tokenData.user), {\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","cookies","NextResponse","NextResponse","NextResponse"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/ai-providers/anthropic.ts","../src/auth/token-manager.ts","../src/config.ts","../src/auth/cookie-names.ts","../src/auth/jwt-utils.ts","../src/ai-providers/google.ts","../src/ai-providers/openai.ts","../src/proxy/index.ts","../src/auth/oauth-handlers.ts"],"sourcesContent":["import { cookies } from 'next/headers';\nimport { NextRequest, NextResponse } from 'next/server';\n\nimport { EchoConfig, EchoResult } from './types';\n\nimport { createEchoAnthropic } from 'ai-providers/anthropic';\nimport { createEchoGoogle } from 'ai-providers/google';\nimport { createEchoOpenAI } from 'ai-providers/openai';\n\nimport { ECHO_COOKIE, namespacedCookie } from 'auth/cookie-names';\nimport { RefreshTokenResponse } from 'auth/token-manager';\nimport { handleEchoClientProxy } from 'proxy';\nimport {\n handleCallback,\n handleRefresh,\n handleSignIn,\n handleSignOut,\n handleSession,\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 if (path.startsWith('/proxy')) {\n return handleEchoClientProxy(req, config);\n }\n\n switch (path) {\n // all the auth stuff\n case '/signin':\n return handleSignIn(req, config);\n\n case '/signout':\n return handleSignOut(req, config);\n\n case '/callback':\n return handleCallback(req, config);\n\n case '/refresh':\n return handleRefresh(req, config);\n\n case '/session':\n return handleSession(req, config);\n\n default:\n console.error('Unknown path', path);\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 userInfo = cookieStore.get(\n namespacedCookie(ECHO_COOKIE.USER_INFO, config.appId)\n )?.value;\n if (!userInfo) {\n return null;\n }\n const user = JSON.parse(userInfo) as RefreshTokenResponse['user'];\n return user;\n };\n\n const isSignedIn = async () => {\n const cookieStore = await cookies();\n const refreshTokenExpiry = cookieStore.get(\n namespacedCookie(ECHO_COOKIE.REFRESH_TOKEN_EXPIRES, config.appId)\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 for Next.js API routes\n handlers: {\n GET: httpHandler,\n POST: httpHandler,\n },\n\n // Authentication utilities (server-side only)\n getUser,\n isSignedIn,\n\n // AI provider clients\n openai: createEchoOpenAI(config),\n anthropic: createEchoAnthropic(config),\n google: createEchoGoogle(config),\n };\n}\n","import {\n AnthropicProvider,\n createEchoAnthropic as createEchoAnthropicBase,\n EchoConfig,\n} from '@merit-systems/echo-typescript-sdk';\nimport { getEchoToken } from '../auth/token-manager';\n\nexport function createEchoAnthropic(config: EchoConfig): AnthropicProvider {\n return createEchoAnthropicBase(config, async () => getEchoToken(config));\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 { ECHO_COOKIE, namespacedCookie } from './cookie-names';\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(\n namespacedCookie(ECHO_COOKIE.ACCESS_TOKEN, config.appId)\n )?.value;\n\n // Check if token needs refresh\n if (!accessToken || shouldRefreshToken(accessToken)) {\n const refreshToken = cookies.get(\n namespacedCookie(ECHO_COOKIE.REFRESH_TOKEN, config.appId)\n )?.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(\n namespacedCookie(ECHO_COOKIE.ACCESS_TOKEN, config.appId),\n refreshResult.access_token,\n {\n httpOnly: true,\n secure: process.env.NODE_ENV === 'production',\n sameSite: 'lax',\n maxAge: refreshResult.expires_in,\n path: '/',\n }\n );\n\n cookies.set(\n namespacedCookie(ECHO_COOKIE.REFRESH_TOKEN, config.appId),\n refreshResult.refresh_token,\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 // Store refresh token expiry time for checking\n cookies.set(\n namespacedCookie(ECHO_COOKIE.REFRESH_TOKEN_EXPIRES, config.appId),\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 { 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","export const ECHO_COOKIE = {\n ACCESS_TOKEN: 'echo_access_token',\n REFRESH_TOKEN: 'echo_refresh_token',\n REFRESH_TOKEN_EXPIRES: 'echo_refresh_token_expires',\n USER_INFO: 'echo_user_info',\n CODE_VERIFIER: 'echo_code_verifier',\n} as const;\n\nexport type EchoCookieName = (typeof ECHO_COOKIE)[keyof typeof ECHO_COOKIE];\n\nexport function namespacedCookie(name: EchoCookieName, appId: string): string {\n // Ensure consistent delimiter and avoid accidental double underscores\n return `${name}_${appId}`;\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 {\n createEchoGoogle as createEchoGoogleBase,\n EchoConfig,\n GoogleGenerativeAIProvider,\n} from '@merit-systems/echo-typescript-sdk';\nimport { getEchoToken } from '../auth/token-manager';\n\nexport function createEchoGoogle(\n config: EchoConfig\n): GoogleGenerativeAIProvider {\n return createEchoGoogleBase(config, async () => getEchoToken(config));\n}\n","import { getEchoToken } from '../auth/token-manager';\nimport {\n createEchoOpenAI as createEchoOpenAIBase,\n EchoConfig,\n OpenAIProvider,\n} from '@merit-systems/echo-typescript-sdk';\n\nexport function createEchoOpenAI(config: EchoConfig): OpenAIProvider {\n return createEchoOpenAIBase(config, async () => getEchoToken(config));\n}\n","import { NextRequest, NextResponse } from 'next/server';\nimport { getEchoToken } from '../auth/token-manager';\nimport { EchoConfig } from '../types';\n/**\n * Proxy requests to Echo service with automatic authentication\n */\nexport async function handleEchoClientProxy(\n req: NextRequest,\n config: EchoConfig\n): Promise<Response> {\n const proxyPath = req.nextUrl.pathname.replace(/^.*\\/proxy/, '');\n const baseUrl = config.baseEchoUrl || 'https://echo.merit.systems';\n const targetUrl = `${baseUrl}${proxyPath}`;\n\n const headers = new Headers(req.headers);\n headers.delete('host');\n // Don't ask upstream to compress\n headers.delete('accept-encoding');\n headers.set('accept-encoding', 'identity');\n\n // Optional: also drop conditional headers to avoid weirdness\n headers.delete('if-none-match');\n headers.delete('if-modified-since');\n\n // Get valid token using existing function (handles refresh automatically)\n const accessToken = await getEchoToken(config);\n\n if (!accessToken) {\n return NextResponse.json(\n { error: 'Authentication required' },\n { status: 401 }\n );\n }\n // Inject the valid token\n headers.set('Authorization', `Bearer ${accessToken}`);\n try {\n const response = await fetch(targetUrl, {\n method: req.method,\n headers,\n ...(req.method !== 'GET' &&\n req.method !== 'HEAD' && {\n body: req.body,\n duplex: 'half',\n }),\n });\n\n return response;\n } catch (error) {\n console.error('Proxy request failed:', error);\n return NextResponse.json(\n { error: 'Proxy request failed' },\n { status: 500 }\n );\n }\n}\n","import { EchoConfig } from '@merit-systems/echo-typescript-sdk';\nimport { cookies } from 'next/headers';\nimport { NextRequest, NextResponse } from 'next/server';\nimport { resolveEchoBaseUrl } from '../config';\nimport { ECHO_COOKIE, namespacedCookie } from './cookie-names';\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(\n namespacedCookie(ECHO_COOKIE.CODE_VERIFIER, config.appId),\n codeVerifier,\n {\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\n return response;\n}\n\n/**\n * Handle OAuth signout request - redirect to home page and clear cookies\n * @param req - The incoming Next.js request\n * @param config - Echo configuration\n * @returns NextResponse with redirect to home page and cookies cleared\n */\nexport async function handleSignOut(\n req: NextRequest,\n config: EchoConfig\n): Promise<NextResponse> {\n const { origin } = req.nextUrl;\n const response = NextResponse.redirect(`${origin}`);\n\n response.cookies.delete(\n namespacedCookie(ECHO_COOKIE.ACCESS_TOKEN, config.appId)\n );\n response.cookies.delete(\n namespacedCookie(ECHO_COOKIE.REFRESH_TOKEN, config.appId)\n );\n response.cookies.delete(\n namespacedCookie(ECHO_COOKIE.USER_INFO, config.appId)\n );\n response.cookies.delete(\n namespacedCookie(ECHO_COOKIE.REFRESH_TOKEN_EXPIRES, config.appId)\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\n response.cookies.set('echo_access_token', '', {\n httpOnly: true,\n secure: process.env.NODE_ENV === 'production',\n sameSite: 'lax',\n maxAge: 0,\n path: '/',\n });\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(\n namespacedCookie(ECHO_COOKIE.CODE_VERIFIER, config.appId)\n )?.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(\n namespacedCookie(ECHO_COOKIE.CODE_VERIFIER, config.appId),\n '',\n {\n httpOnly: true,\n secure: process.env.NODE_ENV === 'production',\n sameSite: 'lax',\n maxAge: 0, // Expire immediately\n path: '/',\n }\n );\n\n // Set the access token as a cookie\n response.cookies.set(\n namespacedCookie(ECHO_COOKIE.ACCESS_TOKEN, config.appId),\n tokenData.access_token,\n {\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\n response.cookies.set(\n namespacedCookie(ECHO_COOKIE.REFRESH_TOKEN, config.appId),\n tokenData.refresh_token,\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 response.cookies.set(\n namespacedCookie(ECHO_COOKIE.USER_INFO, config.appId),\n JSON.stringify(tokenData.user),\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 // Store refresh token expiry time for checking\n response.cookies.set(\n namespacedCookie(ECHO_COOKIE.REFRESH_TOKEN_EXPIRES, config.appId),\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\nexport async function handleSession(req: NextRequest, config: EchoConfig) {\n const jar = await cookies();\n const refreshExp = jar.get(\n namespacedCookie(ECHO_COOKIE.REFRESH_TOKEN_EXPIRES, config.appId)\n )?.value;\n const isAuthenticated =\n !!refreshExp && Number(refreshExp) > Math.floor(Date.now() / 1000);\n const userInfo = jar.get(\n namespacedCookie(ECHO_COOKIE.USER_INFO, config.appId)\n )?.value;\n const user = userInfo ? JSON.parse(userInfo) : null;\n return NextResponse.json({ isAuthenticated, user });\n}\n"],"mappings":"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","names":["cookies","NextResponse","cookies","NextResponse","NextResponse","NextResponse","cookies"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@merit-systems/echo-next-sdk",
3
- "version": "0.0.8",
3
+ "version": "0.0.10",
4
4
  "description": "Next.js SDK for Echo",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -38,11 +38,15 @@
38
38
  "license": "MIT",
39
39
  "dependencies": {
40
40
  "@merit-systems/echo-typescript-sdk": "1.0.14",
41
+ "@merit-systems/echo-react-sdk": "1.0.21",
41
42
  "ai": "^5.0.17",
42
- "jwt-decode": "^4.0.0"
43
+ "jwt-decode": "^4.0.0",
44
+ "swr": "^2.2.4"
43
45
  },
44
46
  "peerDependencies": {
45
- "next": ">=15.0.0"
47
+ "next": ">=15.0.0",
48
+ "react": "^18.0.0 || ^19.0.0",
49
+ "react-dom": "^18.0.0 || ^19.0.0"
46
50
  },
47
51
  "devDependencies": {
48
52
  "@types/node": "^24.0.3",
@@ -52,7 +56,11 @@
52
56
  "next": "^15.1.4",
53
57
  "tsup": "^8.5.0",
54
58
  "typescript": "^5.8.3",
55
- "vitest": "^3.2.3"
59
+ "vitest": "^3.2.3",
60
+ "react": "^18.0.0",
61
+ "react-dom": "^18.0.0",
62
+ "@types/react": "^19.1.10",
63
+ "@types/react-dom": "^19.1.7"
56
64
  },
57
65
  "files": [
58
66
  "dist/**/*"