@logto/next 3.5.0 → 3.6.1

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.
@@ -19,7 +19,6 @@ class LogtoClient extends client.default {
19
19
  this.handleSignIn = (redirectUri = `${this.config.baseUrl}/api/logto/sign-in-callback`, interactionMode) => async (request) => {
20
20
  const { nodeClient, headers } = await this.createNodeClientFromEdgeRequest(request);
21
21
  await nodeClient.signIn(redirectUri, interactionMode);
22
- await this.storage?.save();
23
22
  const response = new Response(null, {
24
23
  headers,
25
24
  status: 307,
@@ -33,7 +32,6 @@ class LogtoClient extends client.default {
33
32
  const { nodeClient, headers } = await this.createNodeClientFromEdgeRequest(request);
34
33
  await nodeClient.signOut(redirectUri);
35
34
  await this.storage?.destroy();
36
- await this.storage?.save();
37
35
  const response = new Response(null, {
38
36
  headers,
39
37
  status: 307,
@@ -51,7 +49,6 @@ class LogtoClient extends client.default {
51
49
  const requestUrl = new URL(request.url);
52
50
  const callbackUrl = new URL(`${requestUrl.pathname}${requestUrl.search}${requestUrl.hash}`, this.config.baseUrl);
53
51
  await nodeClient.handleSignInCallback(callbackUrl.toString());
54
- await this.storage?.save();
55
52
  }
56
53
  const response = new Response(null, {
57
54
  status: 307,
@@ -72,25 +69,31 @@ class LogtoClient extends client.default {
72
69
  this.getLogtoContext = async (request, config = {}) => {
73
70
  const { nodeClient } = await this.createNodeClientFromEdgeRequest(request);
74
71
  const context = await nodeClient.getContext(config);
75
- await this.storage?.save();
76
72
  return context;
77
73
  };
78
74
  }
79
75
  async createNodeClientFromEdgeRequest(request) {
80
- const cookieName = `logto:${this.config.appId}`;
81
76
  const cookies$1 = new cookies.RequestCookies(request.headers);
82
77
  const headers = new Headers();
83
78
  const responseCookies = new cookies.ResponseCookies(headers);
84
- const nodeClient = super.createNodeClient(await NodeClient$1.createSession({
85
- secret: this.config.cookieSecret,
86
- crypto,
87
- }, cookies$1.get(cookieName)?.value ?? '', (value) => {
88
- responseCookies.set(cookieName, value, {
89
- maxAge: 14 * 3600 * 24,
90
- secure: this.config.cookieSecure,
91
- sameSite: this.config.cookieSecure ? 'lax' : undefined,
92
- });
93
- }));
79
+ this.storage = new NodeClient$1.CookieStorage({
80
+ encryptionKey: this.config.cookieSecret,
81
+ cookieKey: `logto:${this.config.appId}`,
82
+ isSecure: this.config.cookieSecure,
83
+ getCookie: (name) => {
84
+ return cookies$1.get(name)?.value ?? '';
85
+ },
86
+ setCookie: (name, value, options) => {
87
+ responseCookies.set(name, value, options);
88
+ },
89
+ });
90
+ await this.storage.init();
91
+ const nodeClient = new this.adapters.NodeClient(this.config, {
92
+ storage: this.storage,
93
+ navigate: (url) => {
94
+ this.navigateUrl = url;
95
+ },
96
+ });
94
97
  return { nodeClient, headers };
95
98
  }
96
99
  }
package/lib/edge/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { RequestCookies, ResponseCookies } from '@edge-runtime/cookies';
2
- import { createSession } from '@logto/node';
2
+ import { CookieStorage } from '@logto/node';
3
3
  import NodeClient from '@logto/node/edge';
4
4
  import LogtoNextBaseClient from '../src/client.js';
5
5
 
@@ -11,7 +11,6 @@ class LogtoClient extends LogtoNextBaseClient {
11
11
  this.handleSignIn = (redirectUri = `${this.config.baseUrl}/api/logto/sign-in-callback`, interactionMode) => async (request) => {
12
12
  const { nodeClient, headers } = await this.createNodeClientFromEdgeRequest(request);
13
13
  await nodeClient.signIn(redirectUri, interactionMode);
14
- await this.storage?.save();
15
14
  const response = new Response(null, {
16
15
  headers,
17
16
  status: 307,
@@ -25,7 +24,6 @@ class LogtoClient extends LogtoNextBaseClient {
25
24
  const { nodeClient, headers } = await this.createNodeClientFromEdgeRequest(request);
26
25
  await nodeClient.signOut(redirectUri);
27
26
  await this.storage?.destroy();
28
- await this.storage?.save();
29
27
  const response = new Response(null, {
30
28
  headers,
31
29
  status: 307,
@@ -43,7 +41,6 @@ class LogtoClient extends LogtoNextBaseClient {
43
41
  const requestUrl = new URL(request.url);
44
42
  const callbackUrl = new URL(`${requestUrl.pathname}${requestUrl.search}${requestUrl.hash}`, this.config.baseUrl);
45
43
  await nodeClient.handleSignInCallback(callbackUrl.toString());
46
- await this.storage?.save();
47
44
  }
48
45
  const response = new Response(null, {
49
46
  status: 307,
@@ -64,25 +61,31 @@ class LogtoClient extends LogtoNextBaseClient {
64
61
  this.getLogtoContext = async (request, config = {}) => {
65
62
  const { nodeClient } = await this.createNodeClientFromEdgeRequest(request);
66
63
  const context = await nodeClient.getContext(config);
67
- await this.storage?.save();
68
64
  return context;
69
65
  };
70
66
  }
71
67
  async createNodeClientFromEdgeRequest(request) {
72
- const cookieName = `logto:${this.config.appId}`;
73
68
  const cookies = new RequestCookies(request.headers);
74
69
  const headers = new Headers();
75
70
  const responseCookies = new ResponseCookies(headers);
76
- const nodeClient = super.createNodeClient(await createSession({
77
- secret: this.config.cookieSecret,
78
- crypto,
79
- }, cookies.get(cookieName)?.value ?? '', (value) => {
80
- responseCookies.set(cookieName, value, {
81
- maxAge: 14 * 3600 * 24,
82
- secure: this.config.cookieSecure,
83
- sameSite: this.config.cookieSecure ? 'lax' : undefined,
84
- });
85
- }));
71
+ this.storage = new CookieStorage({
72
+ encryptionKey: this.config.cookieSecret,
73
+ cookieKey: `logto:${this.config.appId}`,
74
+ isSecure: this.config.cookieSecure,
75
+ getCookie: (name) => {
76
+ return cookies.get(name)?.value ?? '';
77
+ },
78
+ setCookie: (name, value, options) => {
79
+ responseCookies.set(name, value, options);
80
+ },
81
+ });
82
+ await this.storage.init();
83
+ const nodeClient = new this.adapters.NodeClient(this.config, {
84
+ storage: this.storage,
85
+ navigate: (url) => {
86
+ this.navigateUrl = url;
87
+ },
88
+ });
86
89
  return { nodeClient, headers };
87
90
  }
88
91
  }
@@ -19,13 +19,12 @@ class LogtoClient extends client.default {
19
19
  /**
20
20
  * Init sign-in and return the url to redirect to Logto.
21
21
  *
22
- * @param cookie the raw cookie string
23
22
  * @param redirectUri the uri (callbackUri) to redirect to after sign in
24
23
  * @param interactionMode OIDC interaction mode
25
- * @returns the url to redirect to and new cookie if any
24
+ * @returns the url to redirect
26
25
  */
27
- async handleSignIn(cookie, redirectUri, interactionMode) {
28
- const { nodeClient, session } = await this.createNodeClientFromHeaders(cookie);
26
+ async handleSignIn(redirectUri, interactionMode) {
27
+ const nodeClient = await this.createNodeClient();
29
28
  await nodeClient.signIn(redirectUri, interactionMode);
30
29
  if (!this.navigateUrl) {
31
30
  // Not expected to happen
@@ -33,19 +32,18 @@ class LogtoClient extends client.default {
33
32
  }
34
33
  return {
35
34
  url: this.navigateUrl,
36
- newCookie: await session.getValues?.(),
37
35
  };
38
36
  }
39
37
  /**
40
38
  * Init sign-out and return the url to redirect to Logto.
41
39
  *
42
- * @param cookie the raw cookie string
43
40
  * @param redirectUri the uri (postSignOutUri) to redirect to after sign out
44
41
  * @returns the url to redirect to
45
42
  */
46
- async handleSignOut(cookie, redirectUri = this.config.baseUrl) {
47
- const { nodeClient } = await this.createNodeClientFromHeaders(cookie);
43
+ async handleSignOut(redirectUri = this.config.baseUrl) {
44
+ const nodeClient = await this.createNodeClient();
48
45
  await nodeClient.signOut(redirectUri);
46
+ await this.storage?.destroy();
49
47
  if (!this.navigateUrl) {
50
48
  // Not expected to happen
51
49
  throw new Error('navigateUrl is not set');
@@ -55,34 +53,46 @@ class LogtoClient extends client.default {
55
53
  /**
56
54
  * Handle sign-in callback from Logto.
57
55
  *
58
- * @param cookie the raw cookie string
59
56
  * @param callbackUrl the uri (callbackUri) to redirect to after sign in, should match the one used in handleSignIn
60
- * @returns new cookie if any
61
57
  */
62
- async handleSignInCallback(cookie, callbackUrl) {
63
- const { nodeClient, session } = await this.createNodeClientFromHeaders(cookie);
58
+ async handleSignInCallback(callbackUrl) {
59
+ const nodeClient = await this.createNodeClient();
64
60
  await nodeClient.handleSignInCallback(callbackUrl);
65
- return session.getValues?.();
66
61
  }
67
62
  /**
68
63
  * Get Logto context from cookies.
69
64
  *
70
- * @param cookie the raw cookie string
71
65
  * @param config additional configs of GetContextParameters
72
66
  * @returns LogtoContext
73
67
  */
74
- async getLogtoContext(cookie, config = {}) {
75
- const { nodeClient } = await this.createNodeClientFromHeaders(cookie);
68
+ async getLogtoContext(config = {}) {
69
+ const nodeClient = await this.createNodeClient({ ignoreCookieChange: true });
76
70
  const context = await nodeClient.getContext(config);
77
71
  return context;
78
72
  }
79
- async createNodeClientFromHeaders(cookie) {
80
- const session = await NodeClient$1.createSession({
81
- secret: this.config.cookieSecret,
82
- crypto,
83
- }, cookie);
84
- const nodeClient = super.createNodeClient(session);
85
- return { nodeClient, session };
73
+ async createNodeClient({ ignoreCookieChange } = {}) {
74
+ const { cookies } = await import('next/headers');
75
+ this.storage = new NodeClient$1.CookieStorage({
76
+ encryptionKey: this.config.cookieSecret,
77
+ cookieKey: `logto:${this.config.appId}`,
78
+ isSecure: this.config.cookieSecure,
79
+ getCookie: (...args) => {
80
+ return cookies().get(...args)?.value ?? '';
81
+ },
82
+ setCookie: (...args) => {
83
+ // In server component (RSC), it is not allowed to modify cookies, see https://nextjs.org/docs/app/api-reference/functions/cookies#cookiessetname-value-options.
84
+ if (!ignoreCookieChange) {
85
+ cookies().set(...args);
86
+ }
87
+ },
88
+ });
89
+ await this.storage.init();
90
+ return new this.adapters.NodeClient(this.config, {
91
+ storage: this.storage,
92
+ navigate: (url) => {
93
+ this.navigateUrl = url;
94
+ },
95
+ });
86
96
  }
87
97
  }
88
98
 
@@ -7,41 +7,35 @@ export default class LogtoClient extends BaseClient {
7
7
  /**
8
8
  * Init sign-in and return the url to redirect to Logto.
9
9
  *
10
- * @param cookie the raw cookie string
11
10
  * @param redirectUri the uri (callbackUri) to redirect to after sign in
12
11
  * @param interactionMode OIDC interaction mode
13
- * @returns the url to redirect to and new cookie if any
12
+ * @returns the url to redirect
14
13
  */
15
- handleSignIn(cookie: string, redirectUri: string, interactionMode?: InteractionMode): Promise<{
14
+ handleSignIn(redirectUri: string, interactionMode?: InteractionMode): Promise<{
16
15
  url: string;
17
16
  newCookie?: string;
18
17
  }>;
19
18
  /**
20
19
  * Init sign-out and return the url to redirect to Logto.
21
20
  *
22
- * @param cookie the raw cookie string
23
21
  * @param redirectUri the uri (postSignOutUri) to redirect to after sign out
24
22
  * @returns the url to redirect to
25
23
  */
26
- handleSignOut(cookie: string, redirectUri?: string): Promise<string>;
24
+ handleSignOut(redirectUri?: string): Promise<string>;
27
25
  /**
28
26
  * Handle sign-in callback from Logto.
29
27
  *
30
- * @param cookie the raw cookie string
31
28
  * @param callbackUrl the uri (callbackUri) to redirect to after sign in, should match the one used in handleSignIn
32
- * @returns new cookie if any
33
29
  */
34
- handleSignInCallback(cookie: string, callbackUrl: string): Promise<string | undefined>;
30
+ handleSignInCallback(callbackUrl: string): Promise<void>;
35
31
  /**
36
32
  * Get Logto context from cookies.
37
33
  *
38
- * @param cookie the raw cookie string
39
34
  * @param config additional configs of GetContextParameters
40
35
  * @returns LogtoContext
41
36
  */
42
- getLogtoContext(cookie: string, config?: GetContextParameters): Promise<import("@logto/node").LogtoContext>;
43
- createNodeClientFromHeaders(cookie: string): Promise<{
44
- nodeClient: import("@logto/node").default;
45
- session: import("@logto/node").Session;
46
- }>;
37
+ getLogtoContext(config?: GetContextParameters): Promise<import("@logto/node").LogtoContext>;
38
+ createNodeClient({ ignoreCookieChange }?: {
39
+ ignoreCookieChange?: boolean;
40
+ }): Promise<import("@logto/node").default>;
47
41
  }
@@ -1,4 +1,4 @@
1
- import { createSession } from '@logto/node';
1
+ import { CookieStorage } from '@logto/node';
2
2
  import NodeClient from '@logto/node/edge';
3
3
  import LogtoNextBaseClient from '../src/client.js';
4
4
 
@@ -11,13 +11,12 @@ class LogtoClient extends LogtoNextBaseClient {
11
11
  /**
12
12
  * Init sign-in and return the url to redirect to Logto.
13
13
  *
14
- * @param cookie the raw cookie string
15
14
  * @param redirectUri the uri (callbackUri) to redirect to after sign in
16
15
  * @param interactionMode OIDC interaction mode
17
- * @returns the url to redirect to and new cookie if any
16
+ * @returns the url to redirect
18
17
  */
19
- async handleSignIn(cookie, redirectUri, interactionMode) {
20
- const { nodeClient, session } = await this.createNodeClientFromHeaders(cookie);
18
+ async handleSignIn(redirectUri, interactionMode) {
19
+ const nodeClient = await this.createNodeClient();
21
20
  await nodeClient.signIn(redirectUri, interactionMode);
22
21
  if (!this.navigateUrl) {
23
22
  // Not expected to happen
@@ -25,19 +24,18 @@ class LogtoClient extends LogtoNextBaseClient {
25
24
  }
26
25
  return {
27
26
  url: this.navigateUrl,
28
- newCookie: await session.getValues?.(),
29
27
  };
30
28
  }
31
29
  /**
32
30
  * Init sign-out and return the url to redirect to Logto.
33
31
  *
34
- * @param cookie the raw cookie string
35
32
  * @param redirectUri the uri (postSignOutUri) to redirect to after sign out
36
33
  * @returns the url to redirect to
37
34
  */
38
- async handleSignOut(cookie, redirectUri = this.config.baseUrl) {
39
- const { nodeClient } = await this.createNodeClientFromHeaders(cookie);
35
+ async handleSignOut(redirectUri = this.config.baseUrl) {
36
+ const nodeClient = await this.createNodeClient();
40
37
  await nodeClient.signOut(redirectUri);
38
+ await this.storage?.destroy();
41
39
  if (!this.navigateUrl) {
42
40
  // Not expected to happen
43
41
  throw new Error('navigateUrl is not set');
@@ -47,34 +45,46 @@ class LogtoClient extends LogtoNextBaseClient {
47
45
  /**
48
46
  * Handle sign-in callback from Logto.
49
47
  *
50
- * @param cookie the raw cookie string
51
48
  * @param callbackUrl the uri (callbackUri) to redirect to after sign in, should match the one used in handleSignIn
52
- * @returns new cookie if any
53
49
  */
54
- async handleSignInCallback(cookie, callbackUrl) {
55
- const { nodeClient, session } = await this.createNodeClientFromHeaders(cookie);
50
+ async handleSignInCallback(callbackUrl) {
51
+ const nodeClient = await this.createNodeClient();
56
52
  await nodeClient.handleSignInCallback(callbackUrl);
57
- return session.getValues?.();
58
53
  }
59
54
  /**
60
55
  * Get Logto context from cookies.
61
56
  *
62
- * @param cookie the raw cookie string
63
57
  * @param config additional configs of GetContextParameters
64
58
  * @returns LogtoContext
65
59
  */
66
- async getLogtoContext(cookie, config = {}) {
67
- const { nodeClient } = await this.createNodeClientFromHeaders(cookie);
60
+ async getLogtoContext(config = {}) {
61
+ const nodeClient = await this.createNodeClient({ ignoreCookieChange: true });
68
62
  const context = await nodeClient.getContext(config);
69
63
  return context;
70
64
  }
71
- async createNodeClientFromHeaders(cookie) {
72
- const session = await createSession({
73
- secret: this.config.cookieSecret,
74
- crypto,
75
- }, cookie);
76
- const nodeClient = super.createNodeClient(session);
77
- return { nodeClient, session };
65
+ async createNodeClient({ ignoreCookieChange } = {}) {
66
+ const { cookies } = await import('next/headers');
67
+ this.storage = new CookieStorage({
68
+ encryptionKey: this.config.cookieSecret,
69
+ cookieKey: `logto:${this.config.appId}`,
70
+ isSecure: this.config.cookieSecure,
71
+ getCookie: (...args) => {
72
+ return cookies().get(...args)?.value ?? '';
73
+ },
74
+ setCookie: (...args) => {
75
+ // In server component (RSC), it is not allowed to modify cookies, see https://nextjs.org/docs/app/api-reference/functions/cookies#cookiessetname-value-options.
76
+ if (!ignoreCookieChange) {
77
+ cookies().set(...args);
78
+ }
79
+ },
80
+ });
81
+ await this.storage.init();
82
+ return new this.adapters.NodeClient(this.config, {
83
+ storage: this.storage,
84
+ navigate: (url) => {
85
+ this.navigateUrl = url;
86
+ },
87
+ });
78
88
  }
79
89
  }
80
90
 
@@ -4,17 +4,13 @@ Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var navigation = require('next/navigation');
6
6
  var client = require('./client.cjs');
7
- var cookie = require('./cookie.cjs');
8
7
 
9
8
  /**
10
9
  * Init sign in process and redirect to the Logto sign-in page
11
10
  */
12
11
  const signIn = async (config, redirectUri, interactionMode) => {
13
12
  const client$1 = new client.default(config);
14
- const { url, newCookie } = await client$1.handleSignIn(await cookie.getCookies(config), redirectUri ?? `${config.baseUrl}/callback`, interactionMode);
15
- if (newCookie) {
16
- await cookie.setCookies(newCookie, config);
17
- }
13
+ const { url } = await client$1.handleSignIn(redirectUri ?? `${config.baseUrl}/callback`, interactionMode);
18
14
  navigation.redirect(url);
19
15
  };
20
16
  /**
@@ -22,20 +18,16 @@ const signIn = async (config, redirectUri, interactionMode) => {
22
18
  */
23
19
  async function handleSignIn(config, searchParamsOrUrl) {
24
20
  const client$1 = new client.default(config);
25
- const newCookie = await client$1.handleSignInCallback(await cookie.getCookies(config), searchParamsOrUrl instanceof URL
21
+ await client$1.handleSignInCallback(searchParamsOrUrl instanceof URL
26
22
  ? searchParamsOrUrl.toString()
27
23
  : `${config.baseUrl}/callback?${searchParamsOrUrl.toString()}`);
28
- if (newCookie) {
29
- await cookie.setCookies(newCookie, config);
30
- }
31
24
  }
32
25
  /**
33
26
  * Init sign out process, clear session, and redirect to the Logto sign-out page
34
27
  */
35
28
  const signOut = async (config, redirectUri) => {
36
29
  const client$1 = new client.default(config);
37
- const url = await client$1.handleSignOut(await cookie.getCookies(config), redirectUri);
38
- await cookie.setCookies('', config);
30
+ const url = await client$1.handleSignOut(redirectUri);
39
31
  navigation.redirect(url);
40
32
  };
41
33
  /**
@@ -43,7 +35,7 @@ const signOut = async (config, redirectUri) => {
43
35
  */
44
36
  const getLogtoContext = async (config, getContextParameters) => {
45
37
  const client$1 = new client.default(config);
46
- return client$1.getLogtoContext(await cookie.getCookies(config), getContextParameters);
38
+ return client$1.getLogtoContext(getContextParameters);
47
39
  };
48
40
  /**
49
41
  * Get organization tokens from session
@@ -56,7 +48,7 @@ const getOrganizationTokens = async (config) => {
56
48
  return [];
57
49
  }
58
50
  const client$1 = new client.default(config);
59
- const { nodeClient } = await client$1.createNodeClientFromHeaders(await cookie.getCookies(config));
51
+ const nodeClient = await client$1.createNodeClient();
60
52
  const { organizations = [] } = await nodeClient.getIdTokenClaims();
61
53
  return Promise.all(organizations.map(async (organizationId) => ({
62
54
  id: organizationId,
@@ -69,13 +61,8 @@ const getOrganizationTokens = async (config) => {
69
61
  */
70
62
  const getAccessToken = async (config, resource, organizationId) => {
71
63
  const client$1 = new client.default(config);
72
- const { nodeClient, session } = await client$1.createNodeClientFromHeaders(await cookie.getCookies(config));
64
+ const nodeClient = await client$1.createNodeClient();
73
65
  const accessToken = await nodeClient.getAccessToken(resource, organizationId);
74
- // Update access token cache
75
- const newCookie = await session.getValues?.();
76
- if (newCookie) {
77
- await cookie.setCookies(newCookie, config);
78
- }
79
66
  return accessToken;
80
67
  };
81
68
  /**
@@ -93,7 +80,7 @@ const getOrganizationToken = async (config, organizationId) => {
93
80
  */
94
81
  const getAccessTokenRSC = async (config, resource, organizationId) => {
95
82
  const client$1 = new client.default(config);
96
- const { nodeClient } = await client$1.createNodeClientFromHeaders(await cookie.getCookies(config));
83
+ const nodeClient = await client$1.createNodeClient({ ignoreCookieChange: true });
97
84
  return nodeClient.getAccessToken(resource, organizationId);
98
85
  };
99
86
  /**
@@ -1,16 +1,12 @@
1
1
  import { redirect } from 'next/navigation';
2
2
  import LogtoClient from './client.js';
3
- import { getCookies, setCookies } from './cookie.js';
4
3
 
5
4
  /**
6
5
  * Init sign in process and redirect to the Logto sign-in page
7
6
  */
8
7
  const signIn = async (config, redirectUri, interactionMode) => {
9
8
  const client = new LogtoClient(config);
10
- const { url, newCookie } = await client.handleSignIn(await getCookies(config), redirectUri ?? `${config.baseUrl}/callback`, interactionMode);
11
- if (newCookie) {
12
- await setCookies(newCookie, config);
13
- }
9
+ const { url } = await client.handleSignIn(redirectUri ?? `${config.baseUrl}/callback`, interactionMode);
14
10
  redirect(url);
15
11
  };
16
12
  /**
@@ -18,20 +14,16 @@ const signIn = async (config, redirectUri, interactionMode) => {
18
14
  */
19
15
  async function handleSignIn(config, searchParamsOrUrl) {
20
16
  const client = new LogtoClient(config);
21
- const newCookie = await client.handleSignInCallback(await getCookies(config), searchParamsOrUrl instanceof URL
17
+ await client.handleSignInCallback(searchParamsOrUrl instanceof URL
22
18
  ? searchParamsOrUrl.toString()
23
19
  : `${config.baseUrl}/callback?${searchParamsOrUrl.toString()}`);
24
- if (newCookie) {
25
- await setCookies(newCookie, config);
26
- }
27
20
  }
28
21
  /**
29
22
  * Init sign out process, clear session, and redirect to the Logto sign-out page
30
23
  */
31
24
  const signOut = async (config, redirectUri) => {
32
25
  const client = new LogtoClient(config);
33
- const url = await client.handleSignOut(await getCookies(config), redirectUri);
34
- await setCookies('', config);
26
+ const url = await client.handleSignOut(redirectUri);
35
27
  redirect(url);
36
28
  };
37
29
  /**
@@ -39,7 +31,7 @@ const signOut = async (config, redirectUri) => {
39
31
  */
40
32
  const getLogtoContext = async (config, getContextParameters) => {
41
33
  const client = new LogtoClient(config);
42
- return client.getLogtoContext(await getCookies(config), getContextParameters);
34
+ return client.getLogtoContext(getContextParameters);
43
35
  };
44
36
  /**
45
37
  * Get organization tokens from session
@@ -52,7 +44,7 @@ const getOrganizationTokens = async (config) => {
52
44
  return [];
53
45
  }
54
46
  const client = new LogtoClient(config);
55
- const { nodeClient } = await client.createNodeClientFromHeaders(await getCookies(config));
47
+ const nodeClient = await client.createNodeClient();
56
48
  const { organizations = [] } = await nodeClient.getIdTokenClaims();
57
49
  return Promise.all(organizations.map(async (organizationId) => ({
58
50
  id: organizationId,
@@ -65,13 +57,8 @@ const getOrganizationTokens = async (config) => {
65
57
  */
66
58
  const getAccessToken = async (config, resource, organizationId) => {
67
59
  const client = new LogtoClient(config);
68
- const { nodeClient, session } = await client.createNodeClientFromHeaders(await getCookies(config));
60
+ const nodeClient = await client.createNodeClient();
69
61
  const accessToken = await nodeClient.getAccessToken(resource, organizationId);
70
- // Update access token cache
71
- const newCookie = await session.getValues?.();
72
- if (newCookie) {
73
- await setCookies(newCookie, config);
74
- }
75
62
  return accessToken;
76
63
  };
77
64
  /**
@@ -89,7 +76,7 @@ const getOrganizationToken = async (config, organizationId) => {
89
76
  */
90
77
  const getAccessTokenRSC = async (config, resource, organizationId) => {
91
78
  const client = new LogtoClient(config);
92
- const { nodeClient } = await client.createNodeClientFromHeaders(await getCookies(config));
79
+ const nodeClient = await client.createNodeClient({ ignoreCookieChange: true });
93
80
  return nodeClient.getAccessToken(resource, organizationId);
94
81
  };
95
82
  /**
@@ -2,22 +2,11 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- var storage = require('./storage.cjs');
6
-
7
5
  class LogtoNextBaseClient {
8
6
  constructor(config, adapters) {
9
7
  this.config = config;
10
8
  this.adapters = adapters;
11
9
  }
12
- createNodeClient(session) {
13
- this.storage = new storage.default(session);
14
- return new this.adapters.NodeClient(this.config, {
15
- storage: this.storage,
16
- navigate: (url) => {
17
- this.navigateUrl = url;
18
- },
19
- });
20
- }
21
10
  }
22
11
 
23
12
  exports.default = LogtoNextBaseClient;
@@ -1,11 +1,9 @@
1
- import { type Session } from '@logto/node';
2
- import NextStorage from './storage';
1
+ import { type CookieStorage } from '@logto/node';
3
2
  import type { Adapters, LogtoNextConfig } from './types';
4
3
  export default class LogtoNextBaseClient {
5
4
  protected readonly config: LogtoNextConfig;
6
5
  protected readonly adapters: Adapters;
7
6
  protected navigateUrl?: string;
8
- protected storage?: NextStorage;
7
+ protected storage?: CookieStorage;
9
8
  constructor(config: LogtoNextConfig, adapters: Adapters);
10
- protected createNodeClient(session: Session): import("@logto/node").default;
11
9
  }
package/lib/src/client.js CHANGED
@@ -1,19 +1,8 @@
1
- import NextStorage from './storage.js';
2
-
3
1
  class LogtoNextBaseClient {
4
2
  constructor(config, adapters) {
5
3
  this.config = config;
6
4
  this.adapters = adapters;
7
5
  }
8
- createNodeClient(session) {
9
- this.storage = new NextStorage(session);
10
- return new this.adapters.NodeClient(this.config, {
11
- storage: this.storage,
12
- navigate: (url) => {
13
- this.navigateUrl = url;
14
- },
15
- });
16
- }
17
6
  }
18
7
 
19
8
  export { LogtoNextBaseClient as default };
package/lib/src/index.cjs CHANGED
@@ -3,6 +3,7 @@
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var NodeClient = require('@logto/node');
6
+ var cookie = require('cookie');
6
7
  var client = require('./client.cjs');
7
8
  var utils = require('./utils.cjs');
8
9
 
@@ -18,7 +19,6 @@ class LogtoClient extends client.default {
18
19
  this.handleSignIn = (redirectUri = `${this.config.baseUrl}/api/logto/sign-in-callback`, interactionMode, onError) => utils.buildHandler(async (request, response) => {
19
20
  const nodeClient = await this.createNodeClientFromNextApi(request, response);
20
21
  await nodeClient.signIn(redirectUri, interactionMode);
21
- await this.storage?.save();
22
22
  if (this.navigateUrl) {
23
23
  response.redirect(this.navigateUrl);
24
24
  }
@@ -27,7 +27,6 @@ class LogtoClient extends client.default {
27
27
  const nodeClient = await this.createNodeClientFromNextApi(request, response);
28
28
  if (request.url) {
29
29
  await nodeClient.handleSignInCallback(`${this.config.baseUrl}${request.url}`);
30
- await this.storage?.save();
31
30
  response.redirect(redirectTo);
32
31
  }
33
32
  }, onError);
@@ -35,7 +34,6 @@ class LogtoClient extends client.default {
35
34
  const nodeClient = await this.createNodeClientFromNextApi(request, response);
36
35
  await nodeClient.signOut(redirectUri);
37
36
  await this.storage?.destroy();
38
- await this.storage?.save();
39
37
  if (this.navigateUrl) {
40
38
  response.redirect(this.navigateUrl);
41
39
  }
@@ -62,10 +60,17 @@ class LogtoClient extends client.default {
62
60
  }
63
61
  response.status(404).end();
64
62
  };
63
+ this.getAccessToken = async (request, response, resource) => {
64
+ const nodeClient = await this.createNodeClientFromNextApi(request, response);
65
+ return nodeClient.getAccessToken(resource);
66
+ };
67
+ this.getOrganizationToken = async (request, response, organizationId) => {
68
+ const nodeClient = await this.createNodeClientFromNextApi(request, response);
69
+ return nodeClient.getOrganizationToken(organizationId);
70
+ };
65
71
  this.withLogtoApiRoute = (handler, config = {}, onError) => utils.buildHandler(async (request, response) => {
66
72
  const nodeClient = await this.createNodeClientFromNextApi(request, response);
67
73
  const user = await nodeClient.getContext(config);
68
- await this.storage?.save();
69
74
  // eslint-disable-next-line @silverhand/fp/no-mutating-methods
70
75
  Object.defineProperty(request, 'user', { enumerable: true, get: () => user });
71
76
  return handler(request, response);
@@ -77,15 +82,24 @@ class LogtoClient extends client.default {
77
82
  };
78
83
  }
79
84
  async createNodeClientFromNextApi(request, response) {
80
- const cookieName = `logto:${this.config.appId}`;
81
- return super.createNodeClient(await NodeClient.createSession({
82
- secret: this.config.cookieSecret,
83
- crypto,
84
- }, request.cookies[cookieName] ?? '', (value) => {
85
- const secure = this.config.cookieSecure;
86
- const maxAge = 14 * 3600 * 24;
87
- response.setHeader('Set-Cookie', `${cookieName}=${value}; Path=/; Max-Age=${maxAge}; ${secure ? 'Secure; SameSite=Lax' : ''}`);
88
- }));
85
+ this.storage = new NodeClient.CookieStorage({
86
+ encryptionKey: this.config.cookieSecret,
87
+ cookieKey: `logto:${this.config.appId}`,
88
+ isSecure: this.config.cookieSecure,
89
+ getCookie: (name) => {
90
+ return request.cookies[name] ?? '';
91
+ },
92
+ setCookie: (name, value, options) => {
93
+ response.setHeader('Set-Cookie', cookie.serialize(name, value, options));
94
+ },
95
+ });
96
+ await this.storage.init();
97
+ return new this.adapters.NodeClient(this.config, {
98
+ storage: this.storage,
99
+ navigate: (url) => {
100
+ this.navigateUrl = url;
101
+ },
102
+ });
89
103
  }
90
104
  }
91
105
 
@@ -1,7 +1,7 @@
1
1
  /// <reference types="node" />
2
2
  import { type IncomingMessage, type ServerResponse } from 'node:http';
3
3
  import NodeClient, { type GetContextParameters, type InteractionMode } from '@logto/node';
4
- import { type GetServerSidePropsResult, type GetServerSidePropsContext, type NextApiHandler } from 'next';
4
+ import { type GetServerSidePropsResult, type GetServerSidePropsContext, type NextApiHandler, type NextApiRequest, type NextApiResponse } from 'next';
5
5
  import { type NextApiRequestCookies } from 'next/dist/server/api-utils/index.js';
6
6
  import LogtoNextBaseClient from './client.js';
7
7
  import type { ErrorHandler, LogtoNextConfig } from './types.js';
@@ -15,6 +15,8 @@ export default class LogtoClient extends LogtoNextBaseClient {
15
15
  handleSignOut: (redirectUri?: string, onError?: ErrorHandler) => NextApiHandler;
16
16
  handleUser: (configs?: GetContextParameters, onError?: ErrorHandler) => NextApiHandler;
17
17
  handleAuthRoutes: (configs?: GetContextParameters, onError?: ErrorHandler) => NextApiHandler;
18
+ getAccessToken: (request: NextApiRequest, response: NextApiResponse, resource: string) => Promise<string>;
19
+ getOrganizationToken: (request: NextApiRequest, response: NextApiResponse, organizationId: string) => Promise<string>;
18
20
  withLogtoApiRoute: (handler: NextApiHandler, config?: GetContextParameters, onError?: ErrorHandler) => NextApiHandler;
19
21
  withLogtoSsr: <P extends Record<string, unknown> = Record<string, unknown>>(handler: (context: GetServerSidePropsContext) => GetServerSidePropsResult<P> | Promise<GetServerSidePropsResult<P>>, configs?: GetContextParameters, onError?: ((error: unknown) => unknown) | undefined) => (context: GetServerSidePropsContext) => Promise<NextApiHandler>;
20
22
  createNodeClientFromNextApi(request: IncomingMessage & {
package/lib/src/index.js CHANGED
@@ -1,5 +1,6 @@
1
- import NodeClient, { createSession } from '@logto/node';
1
+ import NodeClient, { CookieStorage } from '@logto/node';
2
2
  export { LogtoClientError, LogtoError, LogtoRequestError, OidcError, PersistKey, Prompt, ReservedResource, ReservedScope, UserScope, buildOrganizationUrn, getOrganizationIdFromUrn, organizationUrnPrefix } from '@logto/node';
3
+ import { serialize } from 'cookie';
3
4
  import LogtoNextBaseClient from './client.js';
4
5
  import { buildHandler } from './utils.js';
5
6
 
@@ -11,7 +12,6 @@ class LogtoClient extends LogtoNextBaseClient {
11
12
  this.handleSignIn = (redirectUri = `${this.config.baseUrl}/api/logto/sign-in-callback`, interactionMode, onError) => buildHandler(async (request, response) => {
12
13
  const nodeClient = await this.createNodeClientFromNextApi(request, response);
13
14
  await nodeClient.signIn(redirectUri, interactionMode);
14
- await this.storage?.save();
15
15
  if (this.navigateUrl) {
16
16
  response.redirect(this.navigateUrl);
17
17
  }
@@ -20,7 +20,6 @@ class LogtoClient extends LogtoNextBaseClient {
20
20
  const nodeClient = await this.createNodeClientFromNextApi(request, response);
21
21
  if (request.url) {
22
22
  await nodeClient.handleSignInCallback(`${this.config.baseUrl}${request.url}`);
23
- await this.storage?.save();
24
23
  response.redirect(redirectTo);
25
24
  }
26
25
  }, onError);
@@ -28,7 +27,6 @@ class LogtoClient extends LogtoNextBaseClient {
28
27
  const nodeClient = await this.createNodeClientFromNextApi(request, response);
29
28
  await nodeClient.signOut(redirectUri);
30
29
  await this.storage?.destroy();
31
- await this.storage?.save();
32
30
  if (this.navigateUrl) {
33
31
  response.redirect(this.navigateUrl);
34
32
  }
@@ -55,10 +53,17 @@ class LogtoClient extends LogtoNextBaseClient {
55
53
  }
56
54
  response.status(404).end();
57
55
  };
56
+ this.getAccessToken = async (request, response, resource) => {
57
+ const nodeClient = await this.createNodeClientFromNextApi(request, response);
58
+ return nodeClient.getAccessToken(resource);
59
+ };
60
+ this.getOrganizationToken = async (request, response, organizationId) => {
61
+ const nodeClient = await this.createNodeClientFromNextApi(request, response);
62
+ return nodeClient.getOrganizationToken(organizationId);
63
+ };
58
64
  this.withLogtoApiRoute = (handler, config = {}, onError) => buildHandler(async (request, response) => {
59
65
  const nodeClient = await this.createNodeClientFromNextApi(request, response);
60
66
  const user = await nodeClient.getContext(config);
61
- await this.storage?.save();
62
67
  // eslint-disable-next-line @silverhand/fp/no-mutating-methods
63
68
  Object.defineProperty(request, 'user', { enumerable: true, get: () => user });
64
69
  return handler(request, response);
@@ -70,15 +75,24 @@ class LogtoClient extends LogtoNextBaseClient {
70
75
  };
71
76
  }
72
77
  async createNodeClientFromNextApi(request, response) {
73
- const cookieName = `logto:${this.config.appId}`;
74
- return super.createNodeClient(await createSession({
75
- secret: this.config.cookieSecret,
76
- crypto,
77
- }, request.cookies[cookieName] ?? '', (value) => {
78
- const secure = this.config.cookieSecure;
79
- const maxAge = 14 * 3600 * 24;
80
- response.setHeader('Set-Cookie', `${cookieName}=${value}; Path=/; Max-Age=${maxAge}; ${secure ? 'Secure; SameSite=Lax' : ''}`);
81
- }));
78
+ this.storage = new CookieStorage({
79
+ encryptionKey: this.config.cookieSecret,
80
+ cookieKey: `logto:${this.config.appId}`,
81
+ isSecure: this.config.cookieSecure,
82
+ getCookie: (name) => {
83
+ return request.cookies[name] ?? '';
84
+ },
85
+ setCookie: (name, value, options) => {
86
+ response.setHeader('Set-Cookie', serialize(name, value, options));
87
+ },
88
+ });
89
+ await this.storage.init();
90
+ return new this.adapters.NodeClient(this.config, {
91
+ storage: this.storage,
92
+ navigate: (url) => {
93
+ this.navigateUrl = url;
94
+ },
95
+ });
82
96
  }
83
97
  }
84
98
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@logto/next",
3
- "version": "3.5.0",
3
+ "version": "3.6.1",
4
4
  "type": "module",
5
5
  "main": "./lib/src/index.cjs",
6
6
  "module": "./lib/src/index.js",
@@ -46,12 +46,14 @@
46
46
  },
47
47
  "dependencies": {
48
48
  "@edge-runtime/cookies": "^4.0.0",
49
- "@logto/node": "^2.5.5"
49
+ "cookie": "^0.6.0",
50
+ "@logto/node": "^2.5.7"
50
51
  },
51
52
  "devDependencies": {
52
53
  "@silverhand/eslint-config": "^6.0.1",
53
54
  "@silverhand/ts-config": "^6.0.0",
54
55
  "@silverhand/ts-config-react": "^6.0.0",
56
+ "@types/cookie": "^0.6.0",
55
57
  "@vitest/coverage-v8": "^1.6.0",
56
58
  "eslint": "^8.57.0",
57
59
  "lint-staged": "^15.0.0",
@@ -1,17 +0,0 @@
1
- 'use strict';
2
-
3
- const getCookies = async (config) => {
4
- const { cookies } = await import('next/headers');
5
- return cookies().get(`logto:${config.appId}`)?.value ?? '';
6
- };
7
- const setCookies = async (newCookie, config) => {
8
- const { cookies } = await import('next/headers');
9
- cookies().set(`logto:${config.appId}`, newCookie, {
10
- maxAge: 14 * 3600 * 24,
11
- secure: config.cookieSecure,
12
- sameSite: config.cookieSecure ? 'lax' : undefined,
13
- });
14
- };
15
-
16
- exports.getCookies = getCookies;
17
- exports.setCookies = setCookies;
@@ -1,3 +0,0 @@
1
- import { type LogtoNextConfig } from '../src/types';
2
- export declare const getCookies: (config: LogtoNextConfig) => Promise<string>;
3
- export declare const setCookies: (newCookie: string, config: LogtoNextConfig) => Promise<void>;
@@ -1,14 +0,0 @@
1
- const getCookies = async (config) => {
2
- const { cookies } = await import('next/headers');
3
- return cookies().get(`logto:${config.appId}`)?.value ?? '';
4
- };
5
- const setCookies = async (newCookie, config) => {
6
- const { cookies } = await import('next/headers');
7
- cookies().set(`logto:${config.appId}`, newCookie, {
8
- maxAge: 14 * 3600 * 24,
9
- secure: config.cookieSecure,
10
- sameSite: config.cookieSecure ? 'lax' : undefined,
11
- });
12
- };
13
-
14
- export { getCookies, setCookies };
@@ -1,43 +0,0 @@
1
- 'use strict';
2
-
3
- Object.defineProperty(exports, '__esModule', { value: true });
4
-
5
- var NodeClient = require('@logto/node/edge');
6
-
7
- class NextStorage {
8
- constructor(session) {
9
- this.session = session;
10
- this.sessionChanged = false;
11
- }
12
- async setItem(key, value) {
13
- this.session[key] = value;
14
- this.sessionChanged = true;
15
- }
16
- async getItem(key) {
17
- const value = this.session[key];
18
- if (value === undefined) {
19
- return null;
20
- }
21
- return String(value);
22
- }
23
- async removeItem(key) {
24
- this.session[key] = undefined;
25
- this.sessionChanged = true;
26
- }
27
- async destroy() {
28
- this.session[NodeClient.PersistKey.AccessToken] = undefined;
29
- this.session[NodeClient.PersistKey.IdToken] = undefined;
30
- this.session[NodeClient.PersistKey.SignInSession] = undefined;
31
- this.session[NodeClient.PersistKey.RefreshToken] = undefined;
32
- this.sessionChanged = true;
33
- }
34
- async save() {
35
- if (!this.sessionChanged) {
36
- return;
37
- }
38
- await this.session.save();
39
- this.sessionChanged = false;
40
- }
41
- }
42
-
43
- exports.default = NextStorage;
@@ -1,14 +0,0 @@
1
- import { type Session, type Storage } from '@logto/node';
2
- import { PersistKey } from '@logto/node/edge';
3
- export default class NextStorage implements Storage<PersistKey> {
4
- private readonly session;
5
- private sessionChanged;
6
- constructor(session: Session & {
7
- save: () => Promise<void>;
8
- });
9
- setItem(key: PersistKey, value: string): Promise<void>;
10
- getItem(key: PersistKey): Promise<string | null>;
11
- removeItem(key: PersistKey): Promise<void>;
12
- destroy(): Promise<void>;
13
- save(): Promise<void>;
14
- }
@@ -1,39 +0,0 @@
1
- import { PersistKey } from '@logto/node/edge';
2
-
3
- class NextStorage {
4
- constructor(session) {
5
- this.session = session;
6
- this.sessionChanged = false;
7
- }
8
- async setItem(key, value) {
9
- this.session[key] = value;
10
- this.sessionChanged = true;
11
- }
12
- async getItem(key) {
13
- const value = this.session[key];
14
- if (value === undefined) {
15
- return null;
16
- }
17
- return String(value);
18
- }
19
- async removeItem(key) {
20
- this.session[key] = undefined;
21
- this.sessionChanged = true;
22
- }
23
- async destroy() {
24
- this.session[PersistKey.AccessToken] = undefined;
25
- this.session[PersistKey.IdToken] = undefined;
26
- this.session[PersistKey.SignInSession] = undefined;
27
- this.session[PersistKey.RefreshToken] = undefined;
28
- this.sessionChanged = true;
29
- }
30
- async save() {
31
- if (!this.sessionChanged) {
32
- return;
33
- }
34
- await this.session.save();
35
- this.sessionChanged = false;
36
- }
37
- }
38
-
39
- export { NextStorage as default };
@@ -1 +0,0 @@
1
- export {};