@vunexa/lixa 0.0.1-alpha.8 → 0.1.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 +263 -143
- package/dist/dao/session-cache.d.ts +11 -0
- package/dist/dao/session-cache.d.ts.map +1 -0
- package/dist/dao/state-cache.d.ts +8 -6
- package/dist/dao/state-cache.d.ts.map +1 -1
- package/dist/dao/types.d.ts +376 -3
- package/dist/dao/types.d.ts.map +1 -1
- package/dist/export-types/index.d.ts +1397 -0
- package/dist/export-types/tsdoc-metadata.json +11 -0
- package/dist/index.cjs +1035 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1361 -0
- package/dist/index.d.ts +11 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +992 -9
- package/dist/index.js.map +1 -1
- package/dist/lixa.d.ts +286 -19
- package/dist/lixa.d.ts.map +1 -1
- package/dist/models/session.d.ts +316 -0
- package/dist/models/session.d.ts.map +1 -0
- package/dist/providers/IProvider.d.ts +127 -4
- package/dist/providers/IProvider.d.ts.map +1 -1
- package/dist/providers/index.d.ts +0 -2
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/types.d.ts +195 -24
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/user-info.d.ts +82 -0
- package/dist/utils/user-info.d.ts.map +1 -0
- package/package.json +15 -10
- package/dist/dao/state-cache.js +0 -18
- package/dist/dao/state-cache.js.map +0 -1
- package/dist/dao/types.js +0 -2
- package/dist/dao/types.js.map +0 -1
- package/dist/lixa.js +0 -248
- package/dist/lixa.js.map +0 -1
- package/dist/providers/IProvider.js +0 -2
- package/dist/providers/IProvider.js.map +0 -1
- package/dist/providers/github.d.ts +0 -9
- package/dist/providers/github.d.ts.map +0 -1
- package/dist/providers/github.js +0 -8
- package/dist/providers/github.js.map +0 -1
- package/dist/providers/google.d.ts +0 -9
- package/dist/providers/google.d.ts.map +0 -1
- package/dist/providers/google.js +0 -8
- package/dist/providers/google.js.map +0 -1
- package/dist/providers/index.js +0 -3
- package/dist/providers/index.js.map +0 -1
- package/dist/types.js +0 -2
- package/dist/types.js.map +0 -1
- package/dist/utils/constants.js +0 -4
- package/dist/utils/constants.js.map +0 -1
- package/index.d.ts +0 -227
package/README.md
CHANGED
|
@@ -1,240 +1,360 @@
|
|
|
1
1
|
# @vunexa/lixa
|
|
2
2
|
|
|
3
|
-
> Package is in
|
|
3
|
+
> Package is in active development.
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/@vunexa/lixa)
|
|
6
6
|
[](https://www.npmjs.com/package/@vunexa/lixa)
|
|
7
7
|
[](https://opensource.org/licenses/MIT)
|
|
8
8
|
|
|
9
9
|
A flexible, provider-agnostic OAuth 2.0 and OpenID Connect (OIDC) client library for backend applications.
|
|
10
|
-
|
|
10
|
+
`@vunexa/lixa` simplifies multi-provider authentication (e.g. Google, GitHub, Microsoft), enforces strict **AuthN/AuthZ separation**, enables **multi-SSO account linking**, and provides a dedicated post-login **Resource Connection API**.
|
|
11
11
|
|
|
12
12
|
---
|
|
13
13
|
|
|
14
14
|
## Features
|
|
15
15
|
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
-
-
|
|
21
|
-
-
|
|
22
|
-
-
|
|
16
|
+
- **Strict AuthN / AuthZ Separation**: Primary login is automatically restricted to minimal identity scopes (`openid`, `email`, `profile`). Resource scopes are isolated to post-login connection.
|
|
17
|
+
- **Multi-SSO Account Linking**: Seamlessly merge accounts sharing the same verified email address under a single unified user session.
|
|
18
|
+
- **Post-Login Resource Connection API**: Connect third-party API providers (GitHub Repositories, Google Drive, Slack) post-authentication and manage resource tokens on the user session.
|
|
19
|
+
- **Multi-provider OAuth/OIDC support** with unified API.
|
|
20
|
+
- **Built-in providers** for Google, GitHub, and more via `@vunexa/lixa-providers`.
|
|
21
|
+
- **Custom provider support** with extensible provider interface.
|
|
22
|
+
- **Unified session management** via `SessionHandler` (generation + storage).
|
|
23
|
+
- **Unified state management** via `StateHandler` (PKCE + CSRF protection).
|
|
24
|
+
- **Automatic PKCE** (Proof Key for Code Exchange) for all OAuth flows.
|
|
25
|
+
- **TypeScript-first** with full type safety (zero `any` types).
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Architecture Overview
|
|
30
|
+
|
|
31
|
+
```mermaid
|
|
32
|
+
graph TD
|
|
33
|
+
User([User Client / Browser])
|
|
34
|
+
|
|
35
|
+
subgraph Lixa ["Lixa Core Engine"]
|
|
36
|
+
AuthNGuard["Primary AuthN Guard<br/>(Minimal Identity Scopes Only)"]
|
|
37
|
+
SessionMgr["Session Handler & Unified Session"]
|
|
38
|
+
AccountLinker["Account Linker<br/>(AUTO_LINK_BY_VERIFIED_EMAIL)"]
|
|
39
|
+
ResourceConn["Resource Connection API<br/>(Post-Login AuthZ)"]
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
subgraph IdentityProviders ["Identity Providers (AuthN)"]
|
|
43
|
+
GoogleAuth["Google / OIDC"]
|
|
44
|
+
GitHubAuth["GitHub OAuth"]
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
subgraph ResourceProviders ["Resource APIs (AuthZ)"]
|
|
48
|
+
GitHubAPI["GitHub API (repos, orgs)"]
|
|
49
|
+
GoogleDriveAPI["Google Drive API"]
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
User -->|"1. Primary Login / Link Account"| AuthNGuard
|
|
53
|
+
AuthNGuard -->|"Request Identity"| IdentityProviders
|
|
54
|
+
IdentityProviders -->|"Tokens + UserInfo"| AccountLinker
|
|
55
|
+
AccountLinker -->|"Unified Session (session.accounts)"| SessionMgr
|
|
56
|
+
|
|
57
|
+
User -->|"2. Connect Resource API (Post-Login)"| ResourceConn
|
|
58
|
+
ResourceConn -->|"Request Permissions (repo, drive)"| ResourceProviders
|
|
59
|
+
ResourceProviders -->|"Resource Access Tokens"| SessionMgr
|
|
60
|
+
```
|
|
23
61
|
|
|
24
62
|
---
|
|
25
63
|
|
|
26
64
|
## Installation
|
|
27
65
|
|
|
28
66
|
```bash
|
|
29
|
-
npm install @vunexa/lixa
|
|
67
|
+
npm install @vunexa/lixa @vunexa/lixa-providers
|
|
30
68
|
# or
|
|
31
|
-
yarn add @vunexa/lixa
|
|
69
|
+
yarn add @vunexa/lixa @vunexa/lixa-providers
|
|
32
70
|
```
|
|
33
71
|
|
|
72
|
+
---
|
|
73
|
+
|
|
34
74
|
## Quick Start
|
|
35
75
|
|
|
36
|
-
### 1. Configure
|
|
76
|
+
### 1. Configure Lixa with Account Linking & Providers
|
|
37
77
|
|
|
38
78
|
```typescript
|
|
39
|
-
import { Lixa } from
|
|
40
|
-
|
|
41
|
-
|
|
79
|
+
import { Lixa, AccountLinkingStrategy } from "@vunexa/lixa";
|
|
80
|
+
import { GoogleProvider, GithubProvider } from "@vunexa/lixa-providers";
|
|
81
|
+
|
|
82
|
+
export const lixa = new Lixa({
|
|
83
|
+
// Configure Multi-SSO Account Linking Strategy
|
|
84
|
+
accountLinking: {
|
|
85
|
+
mode: AccountLinkingStrategy.AUTO_LINK_BY_VERIFIED_EMAIL,
|
|
86
|
+
requireVerifiedEmail: true,
|
|
87
|
+
},
|
|
42
88
|
providers: {
|
|
43
89
|
google: {
|
|
90
|
+
provider: new GoogleProvider(),
|
|
44
91
|
clientId: process.env.GOOGLE_CLIENT_ID!,
|
|
45
92
|
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
|
|
46
|
-
redirectUri:
|
|
47
|
-
scopes: [
|
|
48
|
-
extraConfig: {
|
|
49
|
-
prompt: 'consent',
|
|
50
|
-
access_type: 'offline',
|
|
51
|
-
},
|
|
93
|
+
redirectUri: "http://localhost:3000/auth/google/callback",
|
|
94
|
+
scopes: ["openid", "email", "profile"],
|
|
52
95
|
},
|
|
53
96
|
github: {
|
|
97
|
+
provider: new GithubProvider(),
|
|
54
98
|
clientId: process.env.GITHUB_CLIENT_ID!,
|
|
55
99
|
clientSecret: process.env.GITHUB_CLIENT_SECRET!,
|
|
56
|
-
redirectUri:
|
|
57
|
-
scopes: [
|
|
58
|
-
extraConfig: {},
|
|
100
|
+
redirectUri: "http://localhost:3000/auth/github/callback",
|
|
101
|
+
scopes: ["read:user", "user:email"],
|
|
59
102
|
},
|
|
60
103
|
},
|
|
61
|
-
|
|
62
|
-
// Optional: custom session strategy
|
|
63
|
-
sessionStrategy: {
|
|
64
|
-
createSession: async (tokenData) => {
|
|
65
|
-
// Custom session creation logic
|
|
66
|
-
return {
|
|
67
|
-
token: 'custom-session-token',
|
|
68
|
-
raw: tokenData
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
},
|
|
72
104
|
});
|
|
73
105
|
```
|
|
74
106
|
|
|
75
|
-
### 2.
|
|
107
|
+
### 2. Primary Authentication & Callback
|
|
76
108
|
|
|
77
109
|
```typescript
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
// Store state in session for validation
|
|
83
|
-
req.session.oauthState = state;
|
|
84
|
-
|
|
85
|
-
const authUrl = lixa.getAuthUrl(provider.toUpperCase(), state);
|
|
110
|
+
// 1. Redirect to provider authorization URL
|
|
111
|
+
app.get("/auth/:provider", async (req, res) => {
|
|
112
|
+
const authUrl = await lixa.getAuthUrl(req.params.provider);
|
|
86
113
|
res.redirect(authUrl);
|
|
87
114
|
});
|
|
88
|
-
```
|
|
89
115
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
```typescript
|
|
116
|
+
// 2. Handle provider callback
|
|
93
117
|
app.get("/auth/:provider/callback", async (req, res) => {
|
|
94
118
|
const { code, state } = req.query;
|
|
95
119
|
const provider = req.params.provider;
|
|
96
120
|
|
|
97
121
|
try {
|
|
98
|
-
|
|
99
|
-
if (state !== req.session.oauthState) {
|
|
100
|
-
throw new Error('Invalid state parameter');
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
const session = await lixa.handleCallback({
|
|
122
|
+
const sessionId = await lixa.handleCallback({
|
|
104
123
|
provider,
|
|
105
124
|
code: code as string,
|
|
106
125
|
state: state as string,
|
|
107
126
|
});
|
|
108
127
|
|
|
109
|
-
|
|
110
|
-
res.
|
|
111
|
-
httpOnly: true,
|
|
112
|
-
secure: true,
|
|
113
|
-
sameSite: 'strict'
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
res.redirect("/dashboard");
|
|
128
|
+
res.cookie("session_id", sessionId, { httpOnly: true, secure: true });
|
|
129
|
+
res.redirect("/profile");
|
|
117
130
|
} catch (error) {
|
|
118
|
-
|
|
119
|
-
res.status(500).send("Authentication failed");
|
|
131
|
+
res.status(401).send("Authentication failed");
|
|
120
132
|
}
|
|
121
133
|
});
|
|
122
134
|
```
|
|
123
135
|
|
|
124
|
-
|
|
136
|
+
---
|
|
125
137
|
|
|
126
|
-
|
|
138
|
+
## Multi-SSO Account Linking
|
|
139
|
+
|
|
140
|
+
Lixa supports automatic identity merging for users signing in with different SSO providers (e.g. Google and GitHub) that share the same verified email address.
|
|
141
|
+
|
|
142
|
+
### Account Linking Sequence Diagram
|
|
143
|
+
|
|
144
|
+
```mermaid
|
|
145
|
+
sequenceDiagram
|
|
146
|
+
autonumber
|
|
147
|
+
actor User
|
|
148
|
+
participant App as Express Backend
|
|
149
|
+
participant Lixa as Lixa Engine
|
|
150
|
+
participant SessionStore as SessionStorage
|
|
151
|
+
participant Google as Google Identity
|
|
152
|
+
participant GitHub as GitHub Identity
|
|
153
|
+
|
|
154
|
+
Note over User, GitHub: 1. Primary Login with Google
|
|
155
|
+
User->>App: GET /auth/google
|
|
156
|
+
App->>Lixa: getAuthUrl('google')
|
|
157
|
+
Lixa-->>App: Redirect URL (openid, email, profile)
|
|
158
|
+
App-->>User: 302 Redirect to Google Consent
|
|
159
|
+
User->>Google: Grant Identity Consent
|
|
160
|
+
Google-->>App: GET /auth/google/callback?code=xxx
|
|
161
|
+
App->>Lixa: handleCallback({ provider: 'google', code })
|
|
162
|
+
Lixa->>Google: Exchange code for tokens
|
|
163
|
+
Lixa->>Lixa: extractUserInfo() -> alex@example.com (verified)
|
|
164
|
+
Lixa->>SessionStore: Save session (sessionId: 0x123...)
|
|
165
|
+
Lixa-->>App: Returns sessionId: 0x123...
|
|
166
|
+
App-->>User: Set Cookie session_id=0x123...
|
|
167
|
+
|
|
168
|
+
Note over User, GitHub: 2. Subsequent Login with GitHub (Same Email)
|
|
169
|
+
User->>App: GET /auth/github
|
|
170
|
+
App->>Lixa: getAuthUrl('github')
|
|
171
|
+
Lixa-->>App: Redirect URL (read:user, user:email)
|
|
172
|
+
App-->>User: 302 Redirect to GitHub Consent
|
|
173
|
+
User->>GitHub: Grant Identity Consent
|
|
174
|
+
GitHub-->>App: GET /auth/github/callback?code=yyy
|
|
175
|
+
App->>Lixa: handleCallback({ provider: 'github', code })
|
|
176
|
+
Lixa->>GitHub: Exchange code for tokens
|
|
177
|
+
Lixa->>Lixa: extractUserInfo() -> alex@example.com (verified)
|
|
178
|
+
Lixa->>SessionStore: getSessionByEmail("alex@example.com")
|
|
179
|
+
SessionStore-->>Lixa: Existing Session (sessionId: 0x123...)
|
|
180
|
+
Lixa->>Lixa: Merge github into session.accounts['github']
|
|
181
|
+
Lixa->>SessionStore: Save updated session (sessionId: 0x123...)
|
|
182
|
+
Lixa-->>App: Returns SAME sessionId: 0x123...
|
|
183
|
+
```
|
|
127
184
|
|
|
128
|
-
|
|
185
|
+
### Account Linking Configuration Modes
|
|
129
186
|
|
|
130
187
|
```typescript
|
|
131
|
-
import {
|
|
188
|
+
import { AccountLinkingStrategy } from "@vunexa/lixa";
|
|
132
189
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
190
|
+
// Mode 1: Auto-link by verified email (Recommended)
|
|
191
|
+
accountLinking: {
|
|
192
|
+
mode: AccountLinkingStrategy.AUTO_LINK_BY_VERIFIED_EMAIL,
|
|
193
|
+
requireVerifiedEmail: true,
|
|
137
194
|
}
|
|
138
195
|
|
|
139
|
-
//
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
}
|
|
196
|
+
// Mode 2: Treat provider logins as separate accounts
|
|
197
|
+
accountLinking: {
|
|
198
|
+
mode: AccountLinkingStrategy.ISOLATED,
|
|
199
|
+
}
|
|
200
|
+
```
|
|
143
201
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
}
|
|
202
|
+
### Explicit Account Linking & Unlinking APIs
|
|
203
|
+
|
|
204
|
+
```typescript
|
|
205
|
+
// Explicitly link a new provider while authenticated
|
|
206
|
+
await lixa.linkAccount({
|
|
207
|
+
sessionId: req.cookies.session_id,
|
|
208
|
+
provider: "github",
|
|
209
|
+
code: req.query.code as string,
|
|
210
|
+
state: req.query.state as string,
|
|
154
211
|
});
|
|
212
|
+
|
|
213
|
+
// Unlink a provider account
|
|
214
|
+
await lixa.unlinkAccount(req.cookies.session_id, "github");
|
|
155
215
|
```
|
|
156
216
|
|
|
157
|
-
|
|
217
|
+
---
|
|
158
218
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
219
|
+
## Post-Login Resource Connection API (AuthZ)
|
|
220
|
+
|
|
221
|
+
Primary authentication is strictly limited to identity scopes. To request third-party API permissions (such as GitHub Repositories or Google Drive), use Lixa's post-login **Resource Connection API**.
|
|
222
|
+
|
|
223
|
+
### Resource Connection Sequence Diagram
|
|
224
|
+
|
|
225
|
+
```mermaid
|
|
226
|
+
sequenceDiagram
|
|
227
|
+
autonumber
|
|
228
|
+
actor User
|
|
229
|
+
participant App as Express Backend
|
|
230
|
+
participant Lixa as Lixa Engine
|
|
231
|
+
participant SessionStore as SessionStorage
|
|
232
|
+
participant ResourceAPI as GitHub OAuth (Resource Scopes)
|
|
233
|
+
|
|
234
|
+
Note over User, ResourceAPI: Connect Resource API Post-Login
|
|
235
|
+
User->>App: POST /connect/github (Cookie: session_id=0x123...)
|
|
236
|
+
App->>Lixa: getResourceAuthUrl({ sessionId: '0x123...', provider: 'github', scopes: ['repo', 'read:org'] })
|
|
237
|
+
Lixa->>SessionStore: Validate active session
|
|
238
|
+
Lixa-->>App: Resource Authorization URL (scopes: repo read:org)
|
|
239
|
+
App-->>User: 302 Redirect to Resource Consent
|
|
240
|
+
User->>ResourceAPI: Grant Resource Consent (Repo Access)
|
|
241
|
+
ResourceAPI-->>App: GET /connect/github/callback?code=zzz&state=...
|
|
242
|
+
App->>Lixa: handleResourceCallback({ sessionId: '0x123...', provider: 'github', code })
|
|
243
|
+
Lixa->>ResourceAPI: Exchange code for Resource Access Token
|
|
244
|
+
Lixa->>SessionStore: Store under session.resources['github']
|
|
245
|
+
Lixa-->>App: Updated Session
|
|
246
|
+
App-->>User: Render Dashboard (Repo Access Granted)
|
|
164
247
|
```
|
|
165
248
|
|
|
166
|
-
|
|
249
|
+
### Resource Connection Usage Example
|
|
167
250
|
|
|
168
|
-
|
|
251
|
+
```typescript
|
|
252
|
+
// 1. Generate Resource Authorization URL (Requires Active Session)
|
|
253
|
+
app.get("/connect/github", async (req, res) => {
|
|
254
|
+
const sessionId = req.cookies.session_id;
|
|
169
255
|
|
|
170
|
-
|
|
171
|
-
|
|
256
|
+
const resourceAuthUrl = await lixa.getResourceAuthUrl({
|
|
257
|
+
sessionId,
|
|
258
|
+
provider: "github",
|
|
259
|
+
scopes: ["repo", "read:org"], // Resource permissions requested post-login
|
|
260
|
+
});
|
|
172
261
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
- `Lixa.isProviderRegistered(provider: string): boolean` - Check if a provider is registered
|
|
262
|
+
res.redirect(resourceAuthUrl);
|
|
263
|
+
});
|
|
176
264
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
- `handleCallback({ provider, code, state }): Promise<Session>` - Handle OAuth callback and create session
|
|
265
|
+
// 2. Handle Resource Callback
|
|
266
|
+
app.get("/connect/github/callback", async (req, res) => {
|
|
267
|
+
const sessionId = req.cookies.session_id;
|
|
181
268
|
|
|
182
|
-
|
|
269
|
+
const updatedSession = await lixa.handleResourceCallback({
|
|
270
|
+
sessionId,
|
|
271
|
+
provider: "github",
|
|
272
|
+
code: req.query.code as string,
|
|
273
|
+
state: req.query.state as string,
|
|
274
|
+
scopes: ["repo", "read:org"],
|
|
275
|
+
});
|
|
183
276
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
clientId: string;
|
|
187
|
-
clientSecret: string;
|
|
188
|
-
redirectUri: string;
|
|
189
|
-
scopes: string[];
|
|
190
|
-
extraConfig?: Record<string, any>;
|
|
191
|
-
}
|
|
277
|
+
res.redirect("/dashboard");
|
|
278
|
+
});
|
|
192
279
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
}
|
|
280
|
+
// 3. Query Connected Resource Access Token
|
|
281
|
+
app.get("/api/github/repos", async (req, res) => {
|
|
282
|
+
const resource = await lixa.getConnectedResource(req.cookies.session_id, "github");
|
|
197
283
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
}
|
|
284
|
+
if (!resource) {
|
|
285
|
+
return res.status(403).json({ error: "GitHub resource not connected" });
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// Call GitHub API with resource access token
|
|
289
|
+
const response = await fetch("https://api.github.com/user/repos", {
|
|
290
|
+
headers: { Authorization: `Bearer ${resource.accessToken}` },
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
const repos = await response.json();
|
|
294
|
+
res.json(repos);
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
// 4. Disconnect Resource Provider
|
|
298
|
+
app.delete("/connect/github", async (req, res) => {
|
|
299
|
+
await lixa.disconnectResource(req.cookies.session_id, "github");
|
|
300
|
+
res.json({ success: true });
|
|
301
|
+
});
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
---
|
|
201
305
|
|
|
202
|
-
|
|
306
|
+
## Session Interface Structure
|
|
307
|
+
|
|
308
|
+
```typescript
|
|
309
|
+
export interface Session<TRaw = OAuthTokenResponse> {
|
|
310
|
+
/** Unique session ID generated by Lixa */
|
|
311
|
+
id?: string;
|
|
312
|
+
|
|
313
|
+
/** Primary access token or session token */
|
|
203
314
|
token: string;
|
|
204
|
-
raw: any;
|
|
205
|
-
}
|
|
206
315
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
316
|
+
/** Unified user ID across linked accounts */
|
|
317
|
+
userId?: string;
|
|
318
|
+
|
|
319
|
+
/** Primary user email */
|
|
320
|
+
email?: string;
|
|
321
|
+
|
|
322
|
+
/** Current active auth provider */
|
|
323
|
+
provider?: string;
|
|
324
|
+
|
|
325
|
+
/** Linked SSO provider accounts (AuthN) */
|
|
326
|
+
accounts?: Record<string, LinkedAccount>;
|
|
327
|
+
|
|
328
|
+
/** Connected third-party resource provider tokens (AuthZ) */
|
|
329
|
+
resources?: Record<string, ConnectedResource>;
|
|
330
|
+
|
|
331
|
+
/** Full raw token response from provider */
|
|
332
|
+
raw: TRaw;
|
|
211
333
|
}
|
|
212
334
|
```
|
|
213
335
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
- **Google** - OAuth 2.0 and OpenID Connect
|
|
217
|
-
- **GitHub** - OAuth 2.0
|
|
336
|
+
### `LinkedAccount` vs `ConnectedResource`
|
|
218
337
|
|
|
219
|
-
|
|
338
|
+
| Concept | Purpose | Scopes Allowed | Stored Location |
|
|
339
|
+
| :--- | :--- | :--- | :--- |
|
|
340
|
+
| **`LinkedAccount`** | Identity verification & multi-SSO merging (**AuthN**) | Minimal identity scopes (`openid`, `email`, `read:user`) | `session.accounts[provider]` |
|
|
341
|
+
| **`ConnectedResource`** | External API resource access (**AuthZ**) | Resource permissions (`repo`, `drive.readonly`) | `session.resources[provider]` |
|
|
220
342
|
|
|
221
|
-
|
|
222
|
-
# Install dependencies
|
|
223
|
-
npm install
|
|
343
|
+
---
|
|
224
344
|
|
|
225
|
-
|
|
226
|
-
npm test
|
|
345
|
+
## User Info Utilities
|
|
227
346
|
|
|
228
|
-
|
|
229
|
-
npm run test:coverage
|
|
347
|
+
Lixa provides utilities to extract user information from OAuth tokens:
|
|
230
348
|
|
|
231
|
-
|
|
232
|
-
|
|
349
|
+
```typescript
|
|
350
|
+
import { extractUserInfo, type UserInfo } from "@vunexa/lixa";
|
|
233
351
|
|
|
234
|
-
|
|
235
|
-
|
|
352
|
+
const { userInfo } = await extractUserInfo(tokenData, providerMetadata);
|
|
353
|
+
console.log(userInfo.email, userInfo.name);
|
|
236
354
|
```
|
|
237
355
|
|
|
356
|
+
---
|
|
357
|
+
|
|
238
358
|
## License
|
|
239
359
|
|
|
240
|
-
MIT
|
|
360
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { SessionHandler, SessionStorage } from "./types";
|
|
2
|
+
import type { Session, OAuthTokenResponse, ProviderMetadata } from "../models/session";
|
|
3
|
+
declare class LocalSessionHandler implements SessionHandler {
|
|
4
|
+
private cache;
|
|
5
|
+
private emailToSessionMap;
|
|
6
|
+
sessionStorage: SessionStorage;
|
|
7
|
+
constructor(defaultTtlSeconds?: number);
|
|
8
|
+
GenerateSession<T extends Session>(tokenData: OAuthTokenResponse, providerMetadata: ProviderMetadata): Promise<T>;
|
|
9
|
+
}
|
|
10
|
+
export { LocalSessionHandler };
|
|
11
|
+
//# sourceMappingURL=session-cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-cache.d.ts","sourceRoot":"","sources":["../../src/dao/session-cache.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AACzD,OAAO,KAAK,EAAE,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAEvF,cAAM,mBAAoB,YAAW,cAAc;IACjD,OAAO,CAAC,KAAK,CAAY;IACzB,OAAO,CAAC,iBAAiB,CAAkC;IACpD,cAAc,EAAE,cAAc,CAAC;gBAE1B,iBAAiB,GAAE,MAAY;IAuCrC,eAAe,CAAC,CAAC,SAAS,OAAO,EACrC,SAAS,EAAE,kBAAkB,EAC7B,gBAAgB,EAAE,gBAAgB,GACjC,OAAO,CAAC,CAAC,CAAC;CAad;AAED,OAAO,EAAE,mBAAmB,EAAE,CAAC"}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
declare class
|
|
1
|
+
import { StateHandler, StateStorage, StateData } from "./types";
|
|
2
|
+
declare class LocalStateHandler implements StateHandler {
|
|
3
3
|
private cache;
|
|
4
|
+
stateStorage: StateStorage;
|
|
4
5
|
constructor(defaultTtlSeconds?: number);
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
generateState(provider: string): Promise<{
|
|
7
|
+
state: string;
|
|
8
|
+
data: StateData;
|
|
9
|
+
}>;
|
|
8
10
|
}
|
|
9
|
-
export {
|
|
11
|
+
export { LocalStateHandler };
|
|
10
12
|
//# sourceMappingURL=state-cache.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"state-cache.d.ts","sourceRoot":"","sources":["../../src/dao/state-cache.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"state-cache.d.ts","sourceRoot":"","sources":["../../src/dao/state-cache.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEhE,cAAM,iBAAkB,YAAW,YAAY;IAC7C,OAAO,CAAC,KAAK,CAAY;IAClB,YAAY,EAAE,YAAY,CAAC;gBAEtB,iBAAiB,GAAE,MAAY;IAoBrC,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,SAAS,CAAA;KAAE,CAAC;CAanF;AAED,OAAO,EAAE,iBAAiB,EAAE,CAAC"}
|