@vunexa/lixa 0.0.1-alpha.9 → 0.1.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.
Files changed (84) hide show
  1. package/README.md +173 -143
  2. package/README.template.md +360 -0
  3. package/dist/dao/session-cache.d.ts +11 -0
  4. package/dist/dao/session-cache.d.ts.map +1 -0
  5. package/dist/dao/state-cache.d.ts +8 -6
  6. package/dist/dao/state-cache.d.ts.map +1 -1
  7. package/dist/dao/types.d.ts +376 -3
  8. package/dist/dao/types.d.ts.map +1 -1
  9. package/dist/export-types/index.d.ts +1397 -0
  10. package/dist/export-types/tsdoc-metadata.json +11 -0
  11. package/dist/index.cjs +1035 -0
  12. package/dist/index.cjs.map +1 -0
  13. package/dist/index.d.cts +1361 -0
  14. package/dist/index.d.ts +11 -2
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +992 -9
  17. package/dist/index.js.map +1 -1
  18. package/dist/lixa.d.ts +280 -15
  19. package/dist/lixa.d.ts.map +1 -1
  20. package/dist/models/session.d.ts +316 -0
  21. package/dist/models/session.d.ts.map +1 -0
  22. package/dist/providers/IProvider.d.ts +127 -4
  23. package/dist/providers/IProvider.d.ts.map +1 -1
  24. package/dist/providers/index.d.ts +0 -2
  25. package/dist/providers/index.d.ts.map +1 -1
  26. package/dist/types.d.ts +195 -24
  27. package/dist/types.d.ts.map +1 -1
  28. package/dist/utils/user-info.d.ts +82 -0
  29. package/dist/utils/user-info.d.ts.map +1 -0
  30. package/docs/api/index.md +32 -0
  31. package/docs/api/lixa.iprovider.authorizationendpoint.md +13 -0
  32. package/docs/api/lixa.iprovider.md +100 -0
  33. package/docs/api/lixa.iprovider.tokenendpoint.md +13 -0
  34. package/docs/api/lixa.iprovider.userinfoendpoint.md +13 -0
  35. package/docs/api/lixa.lixa._constructor_.md +50 -0
  36. package/docs/api/lixa.lixa.generaterandomstate.md +23 -0
  37. package/docs/api/lixa.lixa.getauthurl.md +85 -0
  38. package/docs/api/lixa.lixa.handlecallback.md +73 -0
  39. package/docs/api/lixa.lixa.isproviderregistered.md +65 -0
  40. package/docs/api/lixa.lixa.md +164 -0
  41. package/docs/api/lixa.lixa.registerprovider.md +69 -0
  42. package/docs/api/lixa.lixaconfig.md +96 -0
  43. package/docs/api/lixa.lixaconfig.providers.md +13 -0
  44. package/docs/api/lixa.lixaconfig.sessionstrategy.md +13 -0
  45. package/docs/api/lixa.lixaconfig.statedao.md +13 -0
  46. package/docs/api/lixa.md +108 -0
  47. package/docs/api/lixa.providerconfig.clientid.md +13 -0
  48. package/docs/api/lixa.providerconfig.clientsecret.md +13 -0
  49. package/docs/api/lixa.providerconfig.extraconfig.md +13 -0
  50. package/docs/api/lixa.providerconfig.md +134 -0
  51. package/docs/api/lixa.providerconfig.redirecturi.md +13 -0
  52. package/docs/api/lixa.providerconfig.scopes.md +13 -0
  53. package/docs/api/lixa.session.md +77 -0
  54. package/docs/api/lixa.session.raw.md +13 -0
  55. package/docs/api/lixa.session.token.md +13 -0
  56. package/docs/api/lixa.sessionstrategy.createsession.md +56 -0
  57. package/docs/api/lixa.sessionstrategy.md +40 -0
  58. package/docs/images/account-linking.svg +1 -0
  59. package/docs/images/architecture.svg +1 -0
  60. package/docs/images/resource-connection.svg +1 -0
  61. package/package.json +18 -10
  62. package/dist/dao/state-cache.js +0 -18
  63. package/dist/dao/state-cache.js.map +0 -1
  64. package/dist/dao/types.js +0 -2
  65. package/dist/dao/types.js.map +0 -1
  66. package/dist/lixa.js +0 -244
  67. package/dist/lixa.js.map +0 -1
  68. package/dist/providers/IProvider.js +0 -2
  69. package/dist/providers/IProvider.js.map +0 -1
  70. package/dist/providers/github.d.ts +0 -9
  71. package/dist/providers/github.d.ts.map +0 -1
  72. package/dist/providers/github.js +0 -8
  73. package/dist/providers/github.js.map +0 -1
  74. package/dist/providers/google.d.ts +0 -9
  75. package/dist/providers/google.d.ts.map +0 -1
  76. package/dist/providers/google.js +0 -8
  77. package/dist/providers/google.js.map +0 -1
  78. package/dist/providers/index.js +0 -3
  79. package/dist/providers/index.js.map +0 -1
  80. package/dist/types.js +0 -2
  81. package/dist/types.js.map +0 -1
  82. package/dist/utils/constants.js +0 -4
  83. package/dist/utils/constants.js.map +0 -1
  84. package/index.d.ts +0 -229
package/README.md CHANGED
@@ -1,240 +1,270 @@
1
1
  # @vunexa/lixa
2
2
 
3
- > Package is in very early stage and can have frequent breaking changes. DO NOT USE it for production
3
+ > Package is in active development.
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/@vunexa/lixa.svg)](https://www.npmjs.com/package/@vunexa/lixa)
6
6
  [![npm downloads](https://img.shields.io/npm/dm/@vunexa/lixa.svg)](https://www.npmjs.com/package/@vunexa/lixa)
7
7
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](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
- @vunexa/lixa simplifies multi-provider authentication flows (e.g., Google, GitHub), supports extensible session management, and enables custom provider registration.
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
- - Multi-provider OAuth/OIDC support with unified API
17
- - Built-in support for popular providers like Google and GitHub
18
- - Custom provider registration with extensible provider interface
19
- - Extensible session management via pluggable strategies
20
- - PKCE (Proof Key for Code Exchange) support
21
- - TypeScript-first with strong typing and async/await support
22
- - 100% test coverage with comprehensive error handling
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
+ ![Architecture Overview](./docs/images/architecture.svg)
23
32
 
24
33
  ---
25
34
 
26
35
  ## Installation
27
36
 
28
37
  ```bash
29
- npm install @vunexa/lixa
38
+ npm install @vunexa/lixa @vunexa/lixa-providers
30
39
  # or
31
- yarn add @vunexa/lixa
40
+ yarn add @vunexa/lixa @vunexa/lixa-providers
32
41
  ```
33
42
 
43
+ ---
44
+
34
45
  ## Quick Start
35
46
 
36
- ### 1. Configure lixa with multiple providers
47
+ ### 1. Configure Lixa with Account Linking & Providers
37
48
 
38
49
  ```typescript
39
- import { Lixa } from '@vunexa/lixa';
40
-
41
- const lixa = new Lixa({
50
+ import { Lixa, AccountLinkingStrategy } from "@vunexa/lixa";
51
+ import { GoogleProvider, GithubProvider } from "@vunexa/lixa-providers";
52
+
53
+ export const lixa = new Lixa({
54
+ // Configure Multi-SSO Account Linking Strategy
55
+ accountLinking: {
56
+ mode: AccountLinkingStrategy.AUTO_LINK_BY_VERIFIED_EMAIL,
57
+ requireVerifiedEmail: true,
58
+ },
42
59
  providers: {
43
60
  google: {
61
+ provider: new GoogleProvider(),
44
62
  clientId: process.env.GOOGLE_CLIENT_ID!,
45
63
  clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
46
- redirectUri: 'https://yourapp.com/auth/google/callback',
47
- scopes: ['openid', 'email', 'profile'],
48
- extraConfig: {
49
- prompt: 'consent',
50
- access_type: 'offline',
51
- },
64
+ redirectUri: "http://localhost:3000/auth/google/callback",
65
+ scopes: ["openid", "email", "profile"],
52
66
  },
53
67
  github: {
68
+ provider: new GithubProvider(),
54
69
  clientId: process.env.GITHUB_CLIENT_ID!,
55
70
  clientSecret: process.env.GITHUB_CLIENT_SECRET!,
56
- redirectUri: 'https://yourapp.com/auth/github/callback',
57
- scopes: ['read:user', 'user:email'],
58
- extraConfig: {},
71
+ redirectUri: "http://localhost:3000/auth/github/callback",
72
+ scopes: ["read:user", "user:email"],
59
73
  },
60
74
  },
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
75
  });
73
76
  ```
74
77
 
75
- ### 2. Redirect users to the provider's authorization URL
78
+ ### 2. Primary Authentication & Callback
76
79
 
77
80
  ```typescript
78
- app.get("/login", (req, res) => {
79
- const provider = req.query.provider as string; // 'google' or 'github'
80
- const state = lixa.generateRandomState();
81
-
82
- // Store state in session for validation
83
- req.session.oauthState = state;
84
-
85
- const authUrl = lixa.getAuthUrl(provider.toUpperCase(), state);
81
+ // 1. Redirect to provider authorization URL
82
+ app.get("/auth/:provider", async (req, res) => {
83
+ const authUrl = await lixa.getAuthUrl(req.params.provider);
86
84
  res.redirect(authUrl);
87
85
  });
88
- ```
89
-
90
- ### 3. Handle the provider callback and establish a session
91
86
 
92
- ```typescript
87
+ // 2. Handle provider callback
93
88
  app.get("/auth/:provider/callback", async (req, res) => {
94
89
  const { code, state } = req.query;
95
90
  const provider = req.params.provider;
96
91
 
97
92
  try {
98
- // Validate state parameter
99
- if (state !== req.session.oauthState) {
100
- throw new Error('Invalid state parameter');
101
- }
102
-
103
- const session = await lixa.handleCallback({
93
+ const sessionId = await lixa.handleCallback({
104
94
  provider,
105
95
  code: code as string,
106
96
  state: state as string,
107
97
  });
108
98
 
109
- // Session established
110
- res.cookie("session_token", session.token, {
111
- httpOnly: true,
112
- secure: true,
113
- sameSite: 'strict'
114
- });
115
-
116
- res.redirect("/dashboard");
99
+ res.cookie("session_id", sessionId, { httpOnly: true, secure: true });
100
+ res.redirect("/profile");
117
101
  } catch (error) {
118
- console.error("Authentication error:", error);
119
- res.status(500).send("Authentication failed");
102
+ res.status(401).send("Authentication failed");
120
103
  }
121
104
  });
122
105
  ```
123
106
 
124
- ## Advanced Usage
107
+ ---
108
+
109
+ ## Multi-SSO Account Linking
110
+
111
+ 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.
125
112
 
126
- ### Custom Provider Registration
113
+ ### Account Linking Sequence Diagram
127
114
 
128
- You can register custom OAuth providers by implementing the `IProvider` interface:
115
+ ![Account Linking Sequence](./docs/images/account-linking.svg)
116
+
117
+ ### Account Linking Configuration Modes
129
118
 
130
119
  ```typescript
131
- import { Lixa, IProvider } from '@vunexa/lixa';
120
+ import { AccountLinkingStrategy } from "@vunexa/lixa";
132
121
 
133
- class CustomProvider implements IProvider {
134
- authorizationEndpoint = 'https://custom-provider.com/oauth/authorize';
135
- tokenEndpoint = 'https://custom-provider.com/oauth/token';
136
- userInfoEndpoint = 'https://custom-provider.com/api/user';
122
+ // Mode 1: Auto-link by verified email (Recommended)
123
+ accountLinking: {
124
+ mode: AccountLinkingStrategy.AUTO_LINK_BY_VERIFIED_EMAIL,
125
+ requireVerifiedEmail: true,
137
126
  }
138
127
 
139
- // Register the custom provider
140
- Lixa.registerProvider({
141
- custom: new CustomProvider()
142
- });
143
-
144
- // Use it in your configuration
145
- const lixa = new Lixa({
146
- providers: {
147
- custom: {
148
- clientId: 'your-client-id',
149
- clientSecret: 'your-client-secret',
150
- redirectUri: 'https://yourapp.com/auth/custom/callback',
151
- scopes: ['read:user'],
152
- }
153
- }
154
- });
128
+ // Mode 2: Treat provider logins as separate accounts
129
+ accountLinking: {
130
+ mode: AccountLinkingStrategy.ISOLATED,
131
+ }
155
132
  ```
156
133
 
157
- ### Check Provider Registration
134
+ ### Explicit Account Linking & Unlinking APIs
158
135
 
159
136
  ```typescript
160
- // Check if a provider is registered
161
- if (Lixa.isProviderRegistered('google')) {
162
- console.log('Google provider is available');
163
- }
137
+ // Explicitly link a new provider while authenticated
138
+ await lixa.linkAccount({
139
+ sessionId: req.cookies.session_id,
140
+ provider: "github",
141
+ code: req.query.code as string,
142
+ state: req.query.state as string,
143
+ });
144
+
145
+ // Unlink a provider account
146
+ await lixa.unlinkAccount(req.cookies.session_id, "github");
164
147
  ```
165
148
 
166
- ## API Reference
149
+ ---
167
150
 
168
- ### `Lixa` Class
151
+ ## Post-Login Resource Connection API (AuthZ)
169
152
 
170
- #### Constructor
171
- - `new Lixa(config: LixaConfig)` - Creates a new Lixa instance
153
+ 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**.
172
154
 
173
- #### Static Methods
174
- - `Lixa.registerProvider(providerMap: { [key: string]: IProvider })` - Register custom providers
175
- - `Lixa.isProviderRegistered(provider: string): boolean` - Check if a provider is registered
155
+ ### Resource Connection Sequence Diagram
176
156
 
177
- #### Instance Methods
178
- - `generateRandomState(): string` - Generate a random state parameter for OAuth flow
179
- - `getAuthUrl(provider: string, state: string): string` - Get authorization URL for a provider
180
- - `handleCallback({ provider, code, state }): Promise<Session>` - Handle OAuth callback and create session
157
+ ![Resource Connection Sequence](./docs/images/resource-connection.svg)
181
158
 
182
- ### Types
159
+ ### Resource Connection Usage Example
183
160
 
184
161
  ```typescript
185
- interface ProviderConfig {
186
- clientId: string;
187
- clientSecret: string;
188
- redirectUri: string;
189
- scopes: string[];
190
- extraConfig?: Record<string, any>;
191
- }
162
+ // 1. Generate Resource Authorization URL (Requires Active Session)
163
+ app.get("/connect/github", async (req, res) => {
164
+ const sessionId = req.cookies.session_id;
192
165
 
193
- interface LixaConfig {
194
- providers: Record<string, ProviderConfig>;
195
- sessionStrategy?: SessionStrategy;
196
- }
166
+ const resourceAuthUrl = await lixa.getResourceAuthUrl({
167
+ sessionId,
168
+ provider: "github",
169
+ scopes: ["repo", "read:org"], // Resource permissions requested post-login
170
+ });
197
171
 
198
- interface SessionStrategy {
199
- createSession(userInfo: any): Promise<Session>;
200
- }
172
+ res.redirect(resourceAuthUrl);
173
+ });
174
+
175
+ // 2. Handle Resource Callback
176
+ app.get("/connect/github/callback", async (req, res) => {
177
+ const sessionId = req.cookies.session_id;
178
+
179
+ const updatedSession = await lixa.handleResourceCallback({
180
+ sessionId,
181
+ provider: "github",
182
+ code: req.query.code as string,
183
+ state: req.query.state as string,
184
+ scopes: ["repo", "read:org"],
185
+ });
186
+
187
+ res.redirect("/dashboard");
188
+ });
189
+
190
+ // 3. Query Connected Resource Access Token
191
+ app.get("/api/github/repos", async (req, res) => {
192
+ const resource = await lixa.getConnectedResource(req.cookies.session_id, "github");
193
+
194
+ if (!resource) {
195
+ return res.status(403).json({ error: "GitHub resource not connected" });
196
+ }
197
+
198
+ // Call GitHub API with resource access token
199
+ const response = await fetch("https://api.github.com/user/repos", {
200
+ headers: { Authorization: `Bearer ${resource.accessToken}` },
201
+ });
202
+
203
+ const repos = await response.json();
204
+ res.json(repos);
205
+ });
206
+
207
+ // 4. Disconnect Resource Provider
208
+ app.delete("/connect/github", async (req, res) => {
209
+ await lixa.disconnectResource(req.cookies.session_id, "github");
210
+ res.json({ success: true });
211
+ });
212
+ ```
213
+
214
+ ---
201
215
 
202
- interface Session {
216
+ ## Session Interface Structure
217
+
218
+ ```typescript
219
+ export interface Session<TRaw = OAuthTokenResponse> {
220
+ /** Unique session ID generated by Lixa */
221
+ id?: string;
222
+
223
+ /** Primary access token or session token */
203
224
  token: string;
204
- raw: any;
205
- }
206
225
 
207
- interface IProvider {
208
- authorizationEndpoint: string;
209
- tokenEndpoint: string;
210
- userInfoEndpoint: string;
226
+ /** Unified user ID across linked accounts */
227
+ userId?: string;
228
+
229
+ /** Primary user email */
230
+ email?: string;
231
+
232
+ /** Current active auth provider */
233
+ provider?: string;
234
+
235
+ /** Linked SSO provider accounts (AuthN) */
236
+ accounts?: Record<string, LinkedAccount>;
237
+
238
+ /** Connected third-party resource provider tokens (AuthZ) */
239
+ resources?: Record<string, ConnectedResource>;
240
+
241
+ /** Full raw token response from provider */
242
+ raw: TRaw;
211
243
  }
212
244
  ```
213
245
 
214
- ## Built-in Providers
215
-
216
- - **Google** - OAuth 2.0 and OpenID Connect
217
- - **GitHub** - OAuth 2.0
246
+ ### `LinkedAccount` vs `ConnectedResource`
218
247
 
219
- ## Development
248
+ | Concept | Purpose | Scopes Allowed | Stored Location |
249
+ | :--- | :--- | :--- | :--- |
250
+ | **`LinkedAccount`** | Identity verification & multi-SSO merging (**AuthN**) | Minimal identity scopes (`openid`, `email`, `read:user`) | `session.accounts[provider]` |
251
+ | **`ConnectedResource`** | External API resource access (**AuthZ**) | Resource permissions (`repo`, `drive.readonly`) | `session.resources[provider]` |
220
252
 
221
- ```bash
222
- # Install dependencies
223
- npm install
253
+ ---
224
254
 
225
- # Run tests
226
- npm test
255
+ ## User Info Utilities
227
256
 
228
- # Run tests with coverage
229
- npm run test:coverage
257
+ Lixa provides utilities to extract user information from OAuth tokens:
230
258
 
231
- # Build the library
232
- npm run build
259
+ ```typescript
260
+ import { extractUserInfo, type UserInfo } from "@vunexa/lixa";
233
261
 
234
- # Lint code
235
- npm run lint
262
+ const { userInfo } = await extractUserInfo(tokenData, providerMetadata);
263
+ console.log(userInfo.email, userInfo.name);
236
264
  ```
237
265
 
266
+ ---
267
+
238
268
  ## License
239
269
 
240
- MIT
270
+ [MIT](LICENSE)