@logto/next 2.4.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,9 +1,14 @@
1
1
  # Logto Next.js SDK
2
+
2
3
  [![Version](https://img.shields.io/npm/v/@logto/next)](https://www.npmjs.com/package/@logto/next)
3
4
  [![Build Status](https://github.com/logto-io/js/actions/workflows/main.yml/badge.svg)](https://github.com/logto-io/js/actions/workflows/main.yml)
4
5
  [![Codecov](https://img.shields.io/codecov/c/github/logto-io/js)](https://app.codecov.io/gh/logto-io/js?branch=master)
5
6
 
6
- The Logto Next.js SDK written in TypeScript. Check out our [integration guide](https://docs.logto.io/docs/recipes/integrate-logto/next-js) or [docs](https://docs.logto.io/sdk/JavaScript/next/) for more information.
7
+ The Logto Next.js SDK written in TypeScript.
8
+
9
+ Check out our [docs](https://docs.logto.io/sdk/next) for more information.
10
+
11
+ If you are using App Router and Server Actions, check out the [docs](https://docs.logto.io/sdk/next-app-router) for more information.
7
12
 
8
13
  ## Installation
9
14
 
@@ -25,18 +30,16 @@ yarn add @logto/next
25
30
  pnpm add @logto/next
26
31
  ```
27
32
 
28
- ## Get sample
29
-
30
- A sample project can be found at [Next.js Sample](https://github.com/logto-io/js/tree/master/packages/next-sample)
33
+ ## Products
31
34
 
32
- Check out the source code and try it with ease.
33
-
34
- ```bash
35
- pnpm i && pnpm start
36
- ```
35
+ | Name | Description |
36
+ | --------------------- | --------------------------------------------- |
37
+ | @logto/next | Traditional Next.js SDK using Page Router |
38
+ | @logto/edge | Next.js SDK running in edge environment |
39
+ | @logto/server-actions | Next.js SDK for App Router and Server Actions |
37
40
 
38
41
  ## Resources
39
42
 
40
43
  [![Website](https://img.shields.io/badge/website-logto.io-8262F8.svg)](https://logto.io/)
41
- [![Docs](https://img.shields.io/badge/docs-logto.io-green.svg)](https://docs.logto.io/sdk/JavaScript/next/)
44
+ [![Docs](https://img.shields.io/badge/docs-logto.io-green.svg)](https://docs.logto.io/)
42
45
  [![Discord](https://img.shields.io/discord/965845662535147551?logo=discord&logoColor=ffffff&color=7389D8&cacheSeconds=600)](https://discord.gg/UEPaF3j5e6)
@@ -3,9 +3,9 @@
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var cookies = require('@edge-runtime/cookies');
6
+ var NodeClient$1 = require('@logto/node');
6
7
  var NodeClient = require('@logto/node/edge');
7
8
  var client = require('../src/client.cjs');
8
- var session = require('../src/session.cjs');
9
9
 
10
10
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
11
11
 
@@ -72,6 +72,7 @@ class LogtoClient extends client.default {
72
72
  this.getLogtoContext = async (request, config = {}) => {
73
73
  const { nodeClient } = await this.createNodeClientFromEdgeRequest(request);
74
74
  const context = await nodeClient.getContext(config);
75
+ await this.storage?.save();
75
76
  return context;
76
77
  };
77
78
  }
@@ -80,7 +81,7 @@ class LogtoClient extends client.default {
80
81
  const cookies$1 = new cookies.RequestCookies(request.headers);
81
82
  const headers = new Headers();
82
83
  const responseCookies = new cookies.ResponseCookies(headers);
83
- const nodeClient = super.createNodeClient(await session.createSession({
84
+ const nodeClient = super.createNodeClient(await NodeClient$1.createSession({
84
85
  secret: this.config.cookieSecret,
85
86
  crypto,
86
87
  }, cookies$1.get(cookieName)?.value ?? '', (value) => {
package/lib/edge/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { RequestCookies, ResponseCookies } from '@edge-runtime/cookies';
2
+ import { createSession } from '@logto/node';
2
3
  import NodeClient from '@logto/node/edge';
3
4
  import LogtoNextBaseClient from '../src/client.js';
4
- import { createSession } from '../src/session.js';
5
5
 
6
6
  class LogtoClient extends LogtoNextBaseClient {
7
7
  constructor(config) {
@@ -64,6 +64,7 @@ class LogtoClient extends LogtoNextBaseClient {
64
64
  this.getLogtoContext = async (request, config = {}) => {
65
65
  const { nodeClient } = await this.createNodeClientFromEdgeRequest(request);
66
66
  const context = await nodeClient.getContext(config);
67
+ await this.storage?.save();
67
68
  return context;
68
69
  };
69
70
  }
@@ -2,9 +2,9 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
+ var NodeClient$1 = require('@logto/node');
5
6
  var NodeClient = require('@logto/node/edge');
6
7
  var client = require('../src/client.cjs');
7
- var session = require('../src/session.cjs');
8
8
 
9
9
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
10
10
 
@@ -77,12 +77,12 @@ class LogtoClient extends client.default {
77
77
  return context;
78
78
  }
79
79
  async createNodeClientFromHeaders(cookie) {
80
- const session$1 = await session.createSession({
80
+ const session = await NodeClient$1.createSession({
81
81
  secret: this.config.cookieSecret,
82
82
  crypto,
83
83
  }, cookie);
84
- const nodeClient = super.createNodeClient(session$1);
85
- return { nodeClient, session: session$1 };
84
+ const nodeClient = super.createNodeClient(session);
85
+ return { nodeClient, session };
86
86
  }
87
87
  }
88
88
 
@@ -42,6 +42,6 @@ export default class LogtoClient extends BaseClient {
42
42
  getLogtoContext(cookie: string, config?: GetContextParameters): Promise<import("@logto/node").LogtoContext>;
43
43
  createNodeClientFromHeaders(cookie: string): Promise<{
44
44
  nodeClient: import("@logto/node").default;
45
- session: import("../src/types.js").Session;
45
+ session: import("@logto/node").Session;
46
46
  }>;
47
47
  }
@@ -1,6 +1,6 @@
1
+ import { createSession } from '@logto/node';
1
2
  import NodeClient from '@logto/node/edge';
2
3
  import LogtoNextBaseClient from '../src/client.js';
3
- import { createSession } from '../src/session.js';
4
4
 
5
5
  class LogtoClient extends LogtoNextBaseClient {
6
6
  constructor(config) {
@@ -1,5 +1,6 @@
1
+ import { type Session } from '@logto/node';
1
2
  import NextStorage from './storage';
2
- import type { Adapters, LogtoNextConfig, Session } from './types';
3
+ import type { Adapters, LogtoNextConfig } from './types';
3
4
  export default class LogtoNextBaseClient {
4
5
  protected readonly config: LogtoNextConfig;
5
6
  protected readonly adapters: Adapters;
package/lib/src/index.cjs CHANGED
@@ -2,32 +2,11 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- var crypto = require('crypto');
6
5
  var NodeClient = require('@logto/node');
7
6
  var client = require('./client.cjs');
8
- var session = require('./session.cjs');
9
7
 
10
8
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
11
9
 
12
- function _interopNamespace(e) {
13
- if (e && e.__esModule) return e;
14
- var n = Object.create(null);
15
- if (e) {
16
- Object.keys(e).forEach(function (k) {
17
- if (k !== 'default') {
18
- var d = Object.getOwnPropertyDescriptor(e, k);
19
- Object.defineProperty(n, k, d.get ? d : {
20
- enumerable: true,
21
- get: function () { return e[k]; }
22
- });
23
- }
24
- });
25
- }
26
- n.default = e;
27
- return Object.freeze(n);
28
- }
29
-
30
- var crypto__namespace = /*#__PURE__*/_interopNamespace(crypto);
31
10
  var NodeClient__default = /*#__PURE__*/_interopDefault(NodeClient);
32
11
 
33
12
  class LogtoClient extends client.default {
@@ -85,6 +64,7 @@ class LogtoClient extends client.default {
85
64
  this.withLogtoApiRoute = (handler, config = {}) => async (request, response) => {
86
65
  const nodeClient = await this.createNodeClientFromNextApi(request, response);
87
66
  const user = await nodeClient.getContext(config);
67
+ await this.storage?.save();
88
68
  // eslint-disable-next-line @silverhand/fp/no-mutating-methods
89
69
  Object.defineProperty(request, 'user', { enumerable: true, get: () => user });
90
70
  return handler(request, response);
@@ -92,6 +72,7 @@ class LogtoClient extends client.default {
92
72
  this.withLogtoSsr = (handler, configs = {}) => async (context) => {
93
73
  const nodeClient = await this.createNodeClientFromNextApi(context.req, context.res);
94
74
  const user = await nodeClient.getContext(configs);
75
+ await this.storage?.save();
95
76
  // eslint-disable-next-line @silverhand/fp/no-mutating-methods
96
77
  Object.defineProperty(context.req, 'user', { enumerable: true, get: () => user });
97
78
  return handler(context);
@@ -99,9 +80,9 @@ class LogtoClient extends client.default {
99
80
  }
100
81
  async createNodeClientFromNextApi(request, response) {
101
82
  const cookieName = `logto:${this.config.appId}`;
102
- return super.createNodeClient(await session.createSession({
83
+ return super.createNodeClient(await NodeClient.createSession({
103
84
  secret: this.config.cookieSecret,
104
- crypto: crypto__namespace,
85
+ crypto,
105
86
  }, request.cookies[cookieName] ?? '', (value) => {
106
87
  const secure = this.config.cookieSecure;
107
88
  const maxAge = 14 * 3600 * 24;
@@ -110,51 +91,51 @@ class LogtoClient extends client.default {
110
91
  }
111
92
  }
112
93
 
113
- Object.defineProperty(exports, 'LogtoClientError', {
94
+ Object.defineProperty(exports, "LogtoClientError", {
114
95
  enumerable: true,
115
96
  get: function () { return NodeClient.LogtoClientError; }
116
97
  });
117
- Object.defineProperty(exports, 'LogtoError', {
98
+ Object.defineProperty(exports, "LogtoError", {
118
99
  enumerable: true,
119
100
  get: function () { return NodeClient.LogtoError; }
120
101
  });
121
- Object.defineProperty(exports, 'LogtoRequestError', {
102
+ Object.defineProperty(exports, "LogtoRequestError", {
122
103
  enumerable: true,
123
104
  get: function () { return NodeClient.LogtoRequestError; }
124
105
  });
125
- Object.defineProperty(exports, 'OidcError', {
106
+ Object.defineProperty(exports, "OidcError", {
126
107
  enumerable: true,
127
108
  get: function () { return NodeClient.OidcError; }
128
109
  });
129
- Object.defineProperty(exports, 'PersistKey', {
110
+ Object.defineProperty(exports, "PersistKey", {
130
111
  enumerable: true,
131
112
  get: function () { return NodeClient.PersistKey; }
132
113
  });
133
- Object.defineProperty(exports, 'Prompt', {
114
+ Object.defineProperty(exports, "Prompt", {
134
115
  enumerable: true,
135
116
  get: function () { return NodeClient.Prompt; }
136
117
  });
137
- Object.defineProperty(exports, 'ReservedResource', {
118
+ Object.defineProperty(exports, "ReservedResource", {
138
119
  enumerable: true,
139
120
  get: function () { return NodeClient.ReservedResource; }
140
121
  });
141
- Object.defineProperty(exports, 'ReservedScope', {
122
+ Object.defineProperty(exports, "ReservedScope", {
142
123
  enumerable: true,
143
124
  get: function () { return NodeClient.ReservedScope; }
144
125
  });
145
- Object.defineProperty(exports, 'UserScope', {
126
+ Object.defineProperty(exports, "UserScope", {
146
127
  enumerable: true,
147
128
  get: function () { return NodeClient.UserScope; }
148
129
  });
149
- Object.defineProperty(exports, 'buildOrganizationUrn', {
130
+ Object.defineProperty(exports, "buildOrganizationUrn", {
150
131
  enumerable: true,
151
132
  get: function () { return NodeClient.buildOrganizationUrn; }
152
133
  });
153
- Object.defineProperty(exports, 'getOrganizationIdFromUrn', {
134
+ Object.defineProperty(exports, "getOrganizationIdFromUrn", {
154
135
  enumerable: true,
155
136
  get: function () { return NodeClient.getOrganizationIdFromUrn; }
156
137
  });
157
- Object.defineProperty(exports, 'organizationUrnPrefix', {
138
+ Object.defineProperty(exports, "organizationUrnPrefix", {
158
139
  enumerable: true,
159
140
  get: function () { return NodeClient.organizationUrnPrefix; }
160
141
  });
@@ -1,4 +1,7 @@
1
1
  /// <reference types="node" />
2
+ /// <reference types="node/http.js" />
3
+ /// <reference types="@logto/node/lib/src/types.js" />
4
+ /// <reference types="@logto/node/src/types.js" />
2
5
  import { type IncomingMessage, type ServerResponse } from 'http';
3
6
  import NodeClient, { type GetContextParameters, type InteractionMode } from '@logto/node';
4
7
  import { type GetServerSidePropsResult, type GetServerSidePropsContext, type NextApiHandler } from 'next';
package/lib/src/index.js CHANGED
@@ -1,8 +1,6 @@
1
- import * as crypto from 'crypto';
2
- import NodeClient from '@logto/node';
1
+ import NodeClient, { createSession } from '@logto/node';
3
2
  export { LogtoClientError, LogtoError, LogtoRequestError, OidcError, PersistKey, Prompt, ReservedResource, ReservedScope, UserScope, buildOrganizationUrn, getOrganizationIdFromUrn, organizationUrnPrefix } from '@logto/node';
4
3
  import LogtoNextBaseClient from './client.js';
5
- import { createSession } from './session.js';
6
4
 
7
5
  class LogtoClient extends LogtoNextBaseClient {
8
6
  constructor(config) {
@@ -59,6 +57,7 @@ class LogtoClient extends LogtoNextBaseClient {
59
57
  this.withLogtoApiRoute = (handler, config = {}) => async (request, response) => {
60
58
  const nodeClient = await this.createNodeClientFromNextApi(request, response);
61
59
  const user = await nodeClient.getContext(config);
60
+ await this.storage?.save();
62
61
  // eslint-disable-next-line @silverhand/fp/no-mutating-methods
63
62
  Object.defineProperty(request, 'user', { enumerable: true, get: () => user });
64
63
  return handler(request, response);
@@ -66,6 +65,7 @@ class LogtoClient extends LogtoNextBaseClient {
66
65
  this.withLogtoSsr = (handler, configs = {}) => async (context) => {
67
66
  const nodeClient = await this.createNodeClientFromNextApi(context.req, context.res);
68
67
  const user = await nodeClient.getContext(configs);
68
+ await this.storage?.save();
69
69
  // eslint-disable-next-line @silverhand/fp/no-mutating-methods
70
70
  Object.defineProperty(context.req, 'user', { enumerable: true, get: () => user });
71
71
  return handler(context);
@@ -1,6 +1,5 @@
1
- import { type Storage } from '@logto/node';
1
+ import { type Session, type Storage } from '@logto/node';
2
2
  import { PersistKey } from '@logto/node/edge';
3
- import { type Session } from './types';
4
3
  export default class NextStorage implements Storage<PersistKey> {
5
4
  private readonly session;
6
5
  private sessionChanged;
@@ -1,15 +1,5 @@
1
- import type { LogtoConfig, PersistKey } from '@logto/node';
1
+ import type { LogtoConfig } from '@logto/node';
2
2
  import type NodeClient from '@logto/node';
3
- export type SessionData = {
4
- [PersistKey.AccessToken]?: string;
5
- [PersistKey.IdToken]?: string;
6
- [PersistKey.SignInSession]?: string;
7
- [PersistKey.RefreshToken]?: string;
8
- };
9
- export type Session = SessionData & {
10
- save: () => Promise<void>;
11
- getValues?: () => Promise<string>;
12
- };
13
3
  export type LogtoNextConfig = LogtoConfig & {
14
4
  cookieSecret: string;
15
5
  cookieSecure: boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@logto/next",
3
- "version": "2.4.0",
3
+ "version": "3.0.0",
4
4
  "type": "module",
5
5
  "main": "./lib/src/index.cjs",
6
6
  "module": "./lib/src/index.js",
@@ -46,7 +46,7 @@
46
46
  },
47
47
  "dependencies": {
48
48
  "@edge-runtime/cookies": "^4.0.0",
49
- "@logto/node": "^2.2.0"
49
+ "@logto/node": "^2.4.0"
50
50
  },
51
51
  "devDependencies": {
52
52
  "@silverhand/eslint-config": "^5.0.0",
@@ -65,7 +65,7 @@
65
65
  "prettier": "^3.0.0",
66
66
  "react": "^18.2.0",
67
67
  "react-dom": "^18.2.0",
68
- "typescript": "^5.0.0"
68
+ "typescript": "^5.3.3"
69
69
  },
70
70
  "peerDependencies": {
71
71
  "next": ">=12"
@@ -1,73 +0,0 @@
1
- 'use strict';
2
-
3
- async function getKeyFromPassword(password, crypto) {
4
- const encoder = new TextEncoder();
5
- const data = encoder.encode(password);
6
- const hash = await crypto.subtle.digest('SHA-256', data);
7
- // Convert the hash to a hex string
8
- return Array.from(new Uint8Array(hash))
9
- .map((byte) => byte.toString(16).padStart(2, '0'))
10
- .join('');
11
- }
12
- async function encrypt(text, password, crypto) {
13
- const iv = crypto.getRandomValues(new Uint8Array(12));
14
- const encodedPlaintext = new TextEncoder().encode(text);
15
- const secretKey = await crypto.subtle.importKey('raw', Buffer.from(await getKeyFromPassword(password, crypto), 'hex'), {
16
- name: 'AES-GCM',
17
- length: 256,
18
- }, true, ['encrypt', 'decrypt']);
19
- const ciphertext = await crypto.subtle.encrypt({
20
- name: 'AES-GCM',
21
- iv,
22
- }, secretKey, encodedPlaintext);
23
- return {
24
- ciphertext: Buffer.from(ciphertext).toString('base64'),
25
- iv: Buffer.from(iv).toString('base64'),
26
- };
27
- }
28
- async function decrypt(ciphertext, iv, password, crypto) {
29
- const secretKey = await crypto.subtle.importKey('raw', Buffer.from(await getKeyFromPassword(password, crypto), 'hex'), {
30
- name: 'AES-GCM',
31
- length: 256,
32
- }, true, ['encrypt', 'decrypt']);
33
- const cleartext = await crypto.subtle.decrypt({
34
- name: 'AES-GCM',
35
- iv: Buffer.from(iv, 'base64'),
36
- }, secretKey, Buffer.from(ciphertext, 'base64'));
37
- return new TextDecoder().decode(cleartext);
38
- }
39
- const unwrapSession = async (cookie, secret, crypto) => {
40
- try {
41
- const [ciphertext, iv] = cookie.split('.');
42
- if (!ciphertext || !iv) {
43
- return {};
44
- }
45
- const decrypted = await decrypt(ciphertext, iv, secret, crypto);
46
- // eslint-disable-next-line no-restricted-syntax
47
- return JSON.parse(decrypted);
48
- }
49
- catch {
50
- // Ignore invalid session
51
- }
52
- return {};
53
- };
54
- const wrapSession = async (session, secret, crypto) => {
55
- const { ciphertext, iv } = await encrypt(JSON.stringify(session), secret, crypto);
56
- return `${ciphertext}.${iv}`;
57
- };
58
- const createSession = async ({ secret, crypto }, cookie, setCookie) => {
59
- const data = await unwrapSession(cookie, secret, crypto);
60
- const getValues = async () => wrapSession(session, secret, crypto);
61
- const session = {
62
- ...data,
63
- save: async () => {
64
- setCookie?.(await getValues());
65
- },
66
- getValues,
67
- };
68
- return session;
69
- };
70
-
71
- exports.createSession = createSession;
72
- exports.unwrapSession = unwrapSession;
73
- exports.wrapSession = wrapSession;
@@ -1,9 +0,0 @@
1
- import { type SessionData, type Session } from './types';
2
- export declare const unwrapSession: (cookie: string, secret: string, crypto: Crypto) => Promise<SessionData>;
3
- export declare const wrapSession: (session: SessionData, secret: string, crypto: Crypto) => Promise<string>;
4
- type SessionConfigs = {
5
- secret: string;
6
- crypto: Crypto;
7
- };
8
- export declare const createSession: ({ secret, crypto }: SessionConfigs, cookie: string, setCookie?: ((value: string) => void) | undefined) => Promise<Session>;
9
- export {};
@@ -1,69 +0,0 @@
1
- async function getKeyFromPassword(password, crypto) {
2
- const encoder = new TextEncoder();
3
- const data = encoder.encode(password);
4
- const hash = await crypto.subtle.digest('SHA-256', data);
5
- // Convert the hash to a hex string
6
- return Array.from(new Uint8Array(hash))
7
- .map((byte) => byte.toString(16).padStart(2, '0'))
8
- .join('');
9
- }
10
- async function encrypt(text, password, crypto) {
11
- const iv = crypto.getRandomValues(new Uint8Array(12));
12
- const encodedPlaintext = new TextEncoder().encode(text);
13
- const secretKey = await crypto.subtle.importKey('raw', Buffer.from(await getKeyFromPassword(password, crypto), 'hex'), {
14
- name: 'AES-GCM',
15
- length: 256,
16
- }, true, ['encrypt', 'decrypt']);
17
- const ciphertext = await crypto.subtle.encrypt({
18
- name: 'AES-GCM',
19
- iv,
20
- }, secretKey, encodedPlaintext);
21
- return {
22
- ciphertext: Buffer.from(ciphertext).toString('base64'),
23
- iv: Buffer.from(iv).toString('base64'),
24
- };
25
- }
26
- async function decrypt(ciphertext, iv, password, crypto) {
27
- const secretKey = await crypto.subtle.importKey('raw', Buffer.from(await getKeyFromPassword(password, crypto), 'hex'), {
28
- name: 'AES-GCM',
29
- length: 256,
30
- }, true, ['encrypt', 'decrypt']);
31
- const cleartext = await crypto.subtle.decrypt({
32
- name: 'AES-GCM',
33
- iv: Buffer.from(iv, 'base64'),
34
- }, secretKey, Buffer.from(ciphertext, 'base64'));
35
- return new TextDecoder().decode(cleartext);
36
- }
37
- const unwrapSession = async (cookie, secret, crypto) => {
38
- try {
39
- const [ciphertext, iv] = cookie.split('.');
40
- if (!ciphertext || !iv) {
41
- return {};
42
- }
43
- const decrypted = await decrypt(ciphertext, iv, secret, crypto);
44
- // eslint-disable-next-line no-restricted-syntax
45
- return JSON.parse(decrypted);
46
- }
47
- catch {
48
- // Ignore invalid session
49
- }
50
- return {};
51
- };
52
- const wrapSession = async (session, secret, crypto) => {
53
- const { ciphertext, iv } = await encrypt(JSON.stringify(session), secret, crypto);
54
- return `${ciphertext}.${iv}`;
55
- };
56
- const createSession = async ({ secret, crypto }, cookie, setCookie) => {
57
- const data = await unwrapSession(cookie, secret, crypto);
58
- const getValues = async () => wrapSession(session, secret, crypto);
59
- const session = {
60
- ...data,
61
- save: async () => {
62
- setCookie?.(await getValues());
63
- },
64
- getValues,
65
- };
66
- return session;
67
- };
68
-
69
- export { createSession, unwrapSession, wrapSession };
@@ -1 +0,0 @@
1
- export {};