Skip to content

Commit 58f706d

Browse files
authored
docs(node): update README with a general guide of implementing SDKs (#961)
1 parent 3f8d42f commit 58f706d

File tree

1 file changed

+167
-2
lines changed

1 file changed

+167
-2
lines changed

packages/node/README.md

Lines changed: 167 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,13 @@
44
[![Build Status](https://github.com/logto-io/js/actions/workflows/main.yml/badge.svg)](https://github.com/logto-io/js/actions/workflows/main.yml)
55
[![Codecov](https://img.shields.io/codecov/c/github/logto-io/js)](https://app.codecov.io/gh/logto-io/js?branch=master)
66

7-
The Logto Node.js SDK written in TypeScript. Check out our [docs](https://docs.logto.io/sdk/JavaScript/node/) for more information.
7+
The Logto Node.js SDK written in TypeScript.
8+
9+
## Table of Contents
10+
- [Installation](#installation)
11+
- [What is this and how does it work?](#what-is-this-and-how-does-it-work)
12+
- [How to create your own SDK from scratch?](#how-to-create-your-own-sdk-from-scratch)
13+
- [Resources](#resources)
814

915
## Installation
1016

@@ -33,10 +39,169 @@ As the name suggests, Logto Node.js SDK is the foundation of all Logto SDKs that
3339
- Implements `requester` by using package `node-fetch`.
3440
- Implements `generateCodeChallenge`, `generateCodeVerifier`, `generateState` methods by using `crypto`.
3541

36-
Usually you are not expected to use it directly in your application, but instead choosing a framework specific SDK that built on top of it. We have already released a set of official SDKs to accelerate your integration. [Check this out](https://docs.logto.io/docs/recipes/integrate-logto/) and get started!
42+
Usually, you are not expected to use it directly in your application, but instead choosing a framework specific SDK that built on top of it. We have already released a set of official SDKs to accelerate your integration. [Check this out](https://docs.logto.io/integrate-logto) and get started!
43+
44+
## How to create your own SDK from scratch?
3745

3846
If Logto does not support your traditional web framework and you want to create your own SDK from scratch, we recommend checking out the SDK specification first. You can also refer to our [Express SDK](https://github.com/logto-io/js/tree/master/packages/express) and [Next.js SDK](https://github.com/logto-io/js/tree/master/packages/next) to learn more about the implementation details.
3947

48+
### Prerequisites
49+
- Basic understanding of Node.js and TypeScript
50+
- Familiarity with your target web framework
51+
- Understanding of OAuth 2.0 and OpenID Connect concepts
52+
53+
### Step 1: Setup the project
54+
55+
Prepare your project by installing `@logto/node` as the dependency.
56+
57+
### Step 2: Implement store adapter
58+
59+
The store adapter is used to store the Logto session information. In most cases, we recommend using cookie-based storage to store the session information.
60+
61+
```typescript
62+
// storage.ts
63+
import { CookieStorage } from '@logto/node';
64+
65+
export const storage = new CookieStorage({
66+
encryptionKey: '<your-encryption-key>',
67+
cookieKey: `<logto_app_xxx>`,
68+
isSecure: false, // Set to true if you are using HTTPS
69+
getCookie: (name) => {
70+
// Example usage, get cookie from the request, depends on your framework
71+
return request.cookies[name] ?? '';
72+
},
73+
setCookie: (name, value, options) => {
74+
// Example usage, set cookie to the response, depends on your framework
75+
response.setHeader('Set-Cookie', serialize(name, value, options));
76+
},
77+
});
78+
```
79+
80+
This will wrap the session data, encrypt it with the encryption key, and store it in the cookie directly.
81+
82+
But the cookie size is limited, so you may need to use external storage like Redis or memory cache to store the session information. Especially when you are using organization features, the session information will be quite large.
83+
84+
```typescript
85+
// storage.ts
86+
import { CookieStorage } from '@logto/node';
87+
88+
class RedisSessionWrapper implements SessionWrapper {
89+
constructor(private readonly redis: Redis) {}
90+
91+
async wrap(data: unknown, _key: string): Promise<string> {
92+
const sessionId = randomUUID();
93+
await this.redis.set(`logto_session_${sessionId}`, JSON.stringify(data));
94+
return sessionId;
95+
}
96+
97+
async unwrap(value: string, _key: string): Promise<SessionData> {
98+
if (!value) {
99+
return {};
100+
}
101+
102+
const data = await this.redis.get(`logto_session_${value}`);
103+
return JSON.parse(data);
104+
}
105+
}
106+
107+
export const storage = new CookieStorage({
108+
cookieKey: `<logto_app_xxx>`,
109+
sessionWrapper: new RedisSessionWrapper(redis),
110+
isSecure: false, // Set to true if you are using HTTPS
111+
getCookie: (name) => {
112+
// Example usage, get cookie from the request, depends on your framework
113+
return request.cookies[name] ?? '';
114+
},
115+
setCookie: (name, value, options) => {
116+
// Example usage, set cookie to the response, depends on your framework
117+
response.setHeader('Set-Cookie', serialize(name, value, options));
118+
},
119+
});
120+
```
121+
122+
### Step 3: Implement navigation adapter and create the client
123+
124+
Continue to prepare the navigation adapter and create the client. You need to implement the "redirect" function to redirect the user to the Logto sign-in page.
125+
126+
```typescript
127+
// client.ts
128+
import { NodeClient } from '@logto/node';
129+
import { config } from './config';
130+
import { storage } from './storage';
131+
132+
export const client = new NodeClient({
133+
...config,
134+
storage,
135+
navigate: (url) => {
136+
// Example usage, navigate to the url, depends on your framework
137+
response.redirect(url);
138+
},
139+
});
140+
```
141+
142+
### Step 4: Use the client to sign in
143+
144+
You can now trigger the sign-in flow by calling the `signIn` method.
145+
146+
```typescript
147+
// app.ts
148+
import { client } from './client';
149+
150+
await client.signIn({
151+
redirectUri: 'http://localhost:3000/callback',
152+
});
153+
```
154+
155+
### Step 5: Handle the sign-in callback
156+
157+
You need to handle the sign-in callback by calling the `signInCallback` method.
158+
159+
```typescript
160+
// app.ts
161+
import { client } from './client';
162+
163+
// You'll need to pass the full url to the callback handler,
164+
// it depends on your framework to get the url.
165+
await client.handleSignInCallback(`${request.url}`);
166+
167+
// After handling the callback, the user is signed in,
168+
// information stored in the storage adapter we created earlier,
169+
// you can now redirect the user to the home page.
170+
response.redirect('/');
171+
```
172+
173+
### Step 6: Implement sign out and other methods
174+
175+
You can now implement the sign out and other methods by referring to our example implementation in other frameworks.
176+
177+
### Step 7: Retrieve user information and protect resources
178+
179+
You can call the `getContext` method to retrieve the user information and protect your resources based on the context.
180+
181+
```typescript
182+
// app.ts
183+
import { client } from './client';
184+
185+
const { isAuthenticated, claims } = await client.getContext();
186+
187+
if (!isAuthenticated) {
188+
// The user is not signed in, redirect to the sign-in page.
189+
// protect the resource from being accessed by unauthenticated users.
190+
response.redirect('/sign-in');
191+
}
192+
193+
// The user is signed in, you can now do something with the user information.
194+
console.log(claims);
195+
```
196+
197+
### Awesome unofficial SDKs
198+
199+
We have a list of awesome implementations of Logto SDK:
200+
201+
- [Astro SDK](https://github.com/RyzeKit/astro-logto-auth-example)
202+
203+
We are grateful for the amazing Logto community!
204+
40205
## Resources
41206

42207
[![Website](https://img.shields.io/badge/website-logto.io-8262F8.svg)](https://logto.io/)

0 commit comments

Comments
 (0)