@o2js/core 0.0.0-stage → 0.2.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Gökhan Altun
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,413 @@
1
- # Temporary Holding Version
1
+ # o2js
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ **A lightweight, provider-agnostic OAuth2 and OpenID Connect library for Node.js.**
4
+
5
+ o2js provides the core OAuth2 Authorization Code and Refresh Token flows, along with OpenID Connect ID Token validation.
6
+
7
+ It does not manage users, sessions, token storage, or database operations. It handles protocol mechanics and leaves application state and lifecycle management to your application.
8
+
9
+ ## Installation
10
+
11
+ ```bash
12
+ npm install @o2js/core
13
+ ```
14
+
15
+ ## Quick Start
16
+
17
+ First, define a provider:
18
+
19
+ ```typescript
20
+ import type { Provider } from "@o2js/core";
21
+
22
+ const provider: Provider = {
23
+ authURL: "https://example.com/oauth/authorize",
24
+ tokenURL: "https://example.com/oauth/token",
25
+ };
26
+ ```
27
+
28
+ Create an OAuth2 client:
29
+
30
+ ```typescript
31
+ import { OAuth2 } from "@o2js/core";
32
+
33
+ const oauth = new OAuth2(provider, {
34
+ clientID: "your-client-id",
35
+ clientSecret: "your-client-secret",
36
+ redirectURL: "https://yourapp.com/callback",
37
+ });
38
+ ```
39
+
40
+ Generate an authorization URL:
41
+
42
+ ```typescript
43
+ const state = "your-random-state";
44
+ const url = oauth.authCodeURL(state);
45
+ ```
46
+
47
+ After the user authorizes your application, exchange the authorization code:
48
+
49
+ ```typescript
50
+ const response = await oauth.exchangeAuthCode(code);
51
+
52
+ console.log(response.data);
53
+ ```
54
+
55
+ Refresh an access token:
56
+
57
+ ```typescript
58
+ const response = await oauth.exchangeRefreshToken(refreshToken);
59
+ ```
60
+
61
+ ## Configuration
62
+
63
+ ```typescript
64
+ type ClientAuthMethod =
65
+ | "client_secret_basic"
66
+ | "client_secret_post";
67
+
68
+ type Config = {
69
+ clientID: string;
70
+ clientSecret: string;
71
+ redirectURL: string;
72
+ clientAuthMethod?: ClientAuthMethod;
73
+ };
74
+ ```
75
+
76
+ - `clientID`: OAuth application client ID.
77
+ - `clientSecret`: OAuth application client secret.
78
+ - `redirectURL`: Registered callback URL.
79
+ - `clientAuthMethod`: Client authentication method used at the token endpoint.
80
+
81
+ Supported client authentication methods:
82
+
83
+ - `client_secret_basic`: Sends client credentials using HTTP Basic Authentication. This is the default.
84
+ - `client_secret_post`: Sends client credentials in the request body.
85
+
86
+ Example using `client_secret_post`:
87
+
88
+ ```typescript
89
+ const oauth = new OAuth2(provider, {
90
+ clientID: "your-client-id",
91
+ clientSecret: "your-client-secret",
92
+ redirectURL: "https://yourapp.com/callback",
93
+ clientAuthMethod: "client_secret_post",
94
+ });
95
+ ```
96
+
97
+ If `clientAuthMethod` is omitted, o2js uses `client_secret_basic`.
98
+
99
+ ## Custom Parameters
100
+
101
+ OAuth providers may require additional parameters for scopes, PKCE, or other features.
102
+
103
+ You can pass additional parameters directly to the relevant methods.
104
+
105
+ Authorization URL parameters:
106
+
107
+ ```typescript
108
+ const url = oauth.authCodeURL(state, {
109
+ scope: "profile email",
110
+ prompt: "consent",
111
+ });
112
+ ```
113
+
114
+ Token request parameters:
115
+
116
+ ```typescript
117
+ const response = await oauth.exchangeAuthCode(code, {
118
+ code_verifier: "your-code-verifier",
119
+ });
120
+ ```
121
+
122
+ Refresh token requests also accept additional parameters:
123
+
124
+ ```typescript
125
+ const response = await oauth.exchangeRefreshToken(refreshToken, {
126
+ custom_parameter: "value",
127
+ });
128
+ ```
129
+
130
+ Check your provider's documentation for supported parameters and their expected values.
131
+
132
+ ## State, Nonce and PKCE
133
+
134
+ o2js provides helpers for generating cryptographically secure random state, nonce, and PKCE values.
135
+
136
+ ### State
137
+
138
+ Generate a random state value:
139
+
140
+ ```typescript
141
+ import { generateRandomState } from "@o2js/core";
142
+
143
+ const state = generateRandomState();
144
+ ```
145
+
146
+ Pass it to the authorization URL:
147
+
148
+ ```typescript
149
+ const url = oauth.authCodeURL(state);
150
+ ```
151
+
152
+ Your application is responsible for storing the state and verifying the returned value during the callback.
153
+
154
+ ### Nonce
155
+
156
+ Generate a random nonce:
157
+
158
+ ```typescript
159
+ import { generateRandomNonce } from "@o2js/core";
160
+
161
+ const nonce = generateRandomNonce();
162
+ ```
163
+
164
+ Include it in the authorization request:
165
+
166
+ ```typescript
167
+ const url = oauth.authCodeURL(state, {
168
+ nonce,
169
+ });
170
+ ```
171
+
172
+ After receiving the ID Token, pass the expected nonce to `parseClaims`:
173
+
174
+ ```typescript
175
+ const claims = await oauth.parseClaims(idToken, {
176
+ nonce,
177
+ });
178
+ ```
179
+
180
+ o2js verifies that the nonce in the ID Token matches the expected value.
181
+
182
+ Your application is responsible for storing and managing the nonce throughout the authorization flow.
183
+
184
+ ### PKCE
185
+
186
+ Generate PKCE parameters:
187
+
188
+ ```typescript
189
+ import { generateRandomPKCE } from "@o2js/core";
190
+
191
+ const pkce = generateRandomPKCE();
192
+ ```
193
+
194
+ Use the generated values in the authorization URL:
195
+
196
+ ```typescript
197
+ const url = oauth.authCodeURL(state, {
198
+ code_challenge: pkce.codeChallenge,
199
+ code_challenge_method: "S256",
200
+ });
201
+ ```
202
+
203
+ Send the code verifier when exchanging the authorization code:
204
+
205
+ ```typescript
206
+ const response = await oauth.exchangeAuthCode(code, {
207
+ code_verifier: pkce.codeVerifier,
208
+ });
209
+ ```
210
+
211
+ State, nonce, and PKCE values must be stored and managed by your application. Your application is also responsible for verifying the returned state and managing the lifecycle of these values.
212
+
213
+ o2js does not store or manage these values.
214
+
215
+ ## OpenID Connect
216
+
217
+ o2js supports OpenID Connect ID Token validation through the `parseClaims` method.
218
+
219
+ To enable ID Token validation, provide the issuer and JWKS URLs in your provider configuration:
220
+
221
+ ```typescript
222
+ import type { Provider } from "@o2js/core";
223
+
224
+ const provider: Provider = {
225
+ authURL: "https://example.com/oauth/authorize",
226
+ tokenURL: "https://example.com/oauth/token",
227
+ issuerURL: "https://example.com",
228
+ jwksURL: "https://example.com/.well-known/jwks.json",
229
+ };
230
+ ```
231
+
232
+ The `issuerURL` and `jwksURL` properties are optional for OAuth2-only providers. They are required when using `parseClaims`.
233
+
234
+ ### ID Token Validation
235
+
236
+ After exchanging the authorization code, retrieve the ID Token from the token response and validate it:
237
+
238
+ ```typescript
239
+ const response = await oauth.exchangeAuthCode(code);
240
+
241
+ const idToken = response.data.id_token;
242
+
243
+ if (typeof idToken !== "string") {
244
+ throw new Error("ID Token is missing");
245
+ }
246
+
247
+ const claims = await oauth.parseClaims(idToken);
248
+
249
+ console.log(claims.sub);
250
+ ```
251
+
252
+ `parseClaims` verifies the ID Token signature using the provider's JWKS and validates its issuer, audience, expiration, and required claims.
253
+
254
+ ### Validation Options
255
+
256
+ The `parseClaims` method accepts an optional options object:
257
+
258
+ ```typescript
259
+ type ParseClaimsOptions = {
260
+ nonce?: string;
261
+ maxAge?: number;
262
+ trustedAudiences?: string[];
263
+ algorithms?: (
264
+ | "RS256"
265
+ | "PS256"
266
+ | "ES256"
267
+ | "ES384"
268
+ | "ES512"
269
+ )[];
270
+ };
271
+ ```
272
+
273
+ **Nonce**
274
+
275
+ If a nonce was included in the authorization request, pass the expected value when validating the ID Token:
276
+
277
+ ```typescript
278
+ const claims = await oauth.parseClaims(idToken, {
279
+ nonce: savedNonce,
280
+ });
281
+ ```
282
+
283
+ The nonce must match the value in the ID Token.
284
+
285
+ **Maximum Authentication Age**
286
+
287
+ If you request a maximum authentication age, include `max_age` in the authorization request:
288
+
289
+ ```typescript
290
+ const url = oauth.authCodeURL(state, {
291
+ max_age: "300",
292
+ });
293
+ ```
294
+
295
+ Pass the same value to `parseClaims`:
296
+
297
+ ```typescript
298
+ const claims = await oauth.parseClaims(idToken, {
299
+ maxAge: 300,
300
+ });
301
+ ```
302
+
303
+ When `maxAge` is provided, `auth_time` is required and checked against the requested maximum age.
304
+
305
+ **Trusted Audiences**
306
+
307
+ By default, o2js accepts the configured client ID as a trusted audience.
308
+
309
+ If your application expects additional audiences, specify them explicitly:
310
+
311
+ ```typescript
312
+ const claims = await oauth.parseClaims(idToken, {
313
+ trustedAudiences: [
314
+ "trusted-service",
315
+ ],
316
+ });
317
+ ```
318
+
319
+ Any audience not included in the client ID or the trusted audience list is rejected.
320
+
321
+ **Signing Algorithms**
322
+
323
+ The default signing algorithm is `RS256`.
324
+
325
+ You can specify additional supported algorithms:
326
+
327
+ ```typescript
328
+ const claims = await oauth.parseClaims(idToken, {
329
+ algorithms: ["RS256", "PS256", "ES256"],
330
+ });
331
+ ```
332
+
333
+ Only use algorithms supported and expected by your identity provider.
334
+
335
+ ### ID Token Claims
336
+
337
+ The `parseClaims` method returns an `IDTokenClaims` object:
338
+
339
+ ```typescript
340
+ import type { IDTokenClaims } from "@o2js/core";
341
+ ```
342
+
343
+ The type extends `JWTPayload` and includes the standard claims:
344
+
345
+ - `iss`: Issuer identifier.
346
+ - `sub`: Subject identifier.
347
+ - `aud`: Intended audience.
348
+ - `exp`: Expiration time.
349
+ - `iat`: Issued-at time.
350
+
351
+ Other claims provided by the identity provider are also available through the returned payload.
352
+
353
+ ## Response
354
+
355
+ Token exchange methods return an `ExchangeResponse`:
356
+
357
+ ```typescript
358
+ type ExchangeResponse = {
359
+ raw: string;
360
+ data: Record<string, unknown>;
361
+ };
362
+ ```
363
+
364
+ - `raw`: Raw response body.
365
+ - `data`: Parsed JSON response.
366
+
367
+ ## Error Handling
368
+
369
+ HTTP and token response parsing errors are represented by `HTTPError`.
370
+
371
+ ```typescript
372
+ import { HTTPError } from "@o2js/core";
373
+
374
+ try {
375
+ const response = await oauth.exchangeAuthCode(code);
376
+ } catch (error) {
377
+ if (error instanceof HTTPError) {
378
+ console.log("Message:", error.message);
379
+ console.log("Status:", error.status);
380
+ console.log("Body:", error.body);
381
+ }
382
+ }
383
+ ```
384
+
385
+ `body` contains the raw response body when available.
386
+
387
+ ID Token validation errors and JWT verification errors are also thrown by `parseClaims`. These may originate from the validation logic or the underlying `jose` library.
388
+
389
+ ## Providers
390
+
391
+ o2js does not include provider implementations.
392
+
393
+ You can define your own provider using the `Provider` interface:
394
+
395
+ ```typescript
396
+ export interface Provider {
397
+ authURL: string;
398
+ tokenURL: string;
399
+ issuerURL?: string;
400
+ jwksURL?: string;
401
+ }
402
+ ```
403
+
404
+ - `authURL`: Authorization endpoint URL.
405
+ - `tokenURL`: Token endpoint URL.
406
+ - `issuerURL`: OpenID Connect issuer URL. Required for ID Token validation.
407
+ - `jwksURL`: JSON Web Key Set endpoint URL. Required for ID Token validation.
408
+
409
+ OAuth2-only providers do not need to define `issuerURL` or `jwksURL`.
410
+
411
+ ## License
412
+
413
+ MIT
@@ -0,0 +1,8 @@
1
+ export type ClientAuthMethod = "client_secret_basic" | "client_secret_post";
2
+ export type Config = {
3
+ clientID: string;
4
+ clientSecret: string;
5
+ redirectURL: string;
6
+ clientAuthMethod?: ClientAuthMethod;
7
+ };
8
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,gBAAgB,GACtB,qBAAqB,GACrB,oBAAoB,CAAC;AAE3B,MAAM,MAAM,MAAM,GAAG;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;CACvC,CAAC"}
package/dist/config.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":""}
@@ -0,0 +1,8 @@
1
+ export type PKCE = {
2
+ codeVerifier: string;
3
+ codeChallenge: string;
4
+ };
5
+ export declare function generateRandomState(): string;
6
+ export declare function generateRandomNonce(): string;
7
+ export declare function generateRandomPKCE(): PKCE;
8
+ //# sourceMappingURL=crypto.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../src/crypto.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,IAAI,GAAG;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,wBAAgB,mBAAmB,IAAI,MAAM,CAE5C;AAED,wBAAgB,mBAAmB,IAAI,MAAM,CAE5C;AAED,wBAAgB,kBAAkB,IAAI,IAAI,CAWzC"}
package/dist/crypto.js ADDED
@@ -0,0 +1,18 @@
1
+ import { randomBytes, createHash } from "node:crypto";
2
+ export function generateRandomState() {
3
+ return randomBytes(32).toString("hex");
4
+ }
5
+ export function generateRandomNonce() {
6
+ return generateRandomState();
7
+ }
8
+ export function generateRandomPKCE() {
9
+ const codeVerifier = randomBytes(32).toString("base64url");
10
+ const codeChallenge = createHash("sha256")
11
+ .update(codeVerifier)
12
+ .digest("base64url");
13
+ return {
14
+ codeVerifier,
15
+ codeChallenge,
16
+ };
17
+ }
18
+ //# sourceMappingURL=crypto.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crypto.js","sourceRoot":"","sources":["../src/crypto.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAOtD,MAAM,UAAU,mBAAmB;IAC/B,OAAO,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AAC3C,CAAC;AAED,MAAM,UAAU,mBAAmB;IAC/B,OAAO,mBAAmB,EAAE,CAAC;AACjC,CAAC;AAED,MAAM,UAAU,kBAAkB;IAC9B,MAAM,YAAY,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;IAE3D,MAAM,aAAa,GAAG,UAAU,CAAC,QAAQ,CAAC;SACrC,MAAM,CAAC,YAAY,CAAC;SACpB,MAAM,CAAC,WAAW,CAAC,CAAC;IAEzB,OAAO;QACH,YAAY;QACZ,aAAa;KAChB,CAAC;AACN,CAAC"}
@@ -0,0 +1,6 @@
1
+ export declare class HTTPError extends Error {
2
+ readonly status?: number | undefined;
3
+ readonly body?: string | undefined;
4
+ constructor(message: string, status?: number | undefined, body?: string | undefined, options?: ErrorOptions);
5
+ }
6
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,qBAAa,SAAU,SAAQ,KAAK;aAGZ,MAAM,CAAC,EAAE,MAAM;aACf,IAAI,CAAC,EAAE,MAAM;IAHjC,YACI,OAAO,EAAE,MAAM,EACC,MAAM,CAAC,EAAE,MAAM,YAAA,EACf,IAAI,CAAC,EAAE,MAAM,YAAA,EAC7B,OAAO,CAAC,EAAE,YAAY,EAIzB;CACJ"}
package/dist/errors.js ADDED
@@ -0,0 +1,11 @@
1
+ export class HTTPError extends Error {
2
+ status;
3
+ body;
4
+ constructor(message, status, body, options) {
5
+ super(message, options);
6
+ this.status = status;
7
+ this.body = body;
8
+ this.name = "HTTPError";
9
+ }
10
+ }
11
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,SAAU,SAAQ,KAAK;IAGZ,MAAM;IACN,IAAI;IAHxB,YACI,OAAe,EACC,MAAe,EACf,IAAa,EAC7B,OAAsB;QAEtB,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;sBAJR,MAAM;oBACN,IAAI;QAIpB,IAAI,CAAC,IAAI,GAAG,WAAW,CAAC;IAC5B,CAAC;CACJ"}
@@ -0,0 +1,7 @@
1
+ export { OAuth2, type IDTokenClaims } from "./oauth2.js";
2
+ export { HTTPError } from "./errors.js";
3
+ export type { Provider } from "./provider.js";
4
+ export type { Config, ClientAuthMethod } from "./config.js";
5
+ export type { ExchangeResponse } from "./response.js";
6
+ export { generateRandomState, generateRandomNonce, generateRandomPKCE, type PKCE } from "./crypto.js";
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,YAAY,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC9C,YAAY,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC5D,YAAY,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,EACH,mBAAmB,EACnB,mBAAmB,EACnB,kBAAkB,EAClB,KAAK,IAAI,EACZ,MAAM,aAAa,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export { OAuth2 } from "./oauth2.js";
2
+ export { HTTPError } from "./errors.js";
3
+ export { generateRandomState, generateRandomNonce, generateRandomPKCE } from "./crypto.js";
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAsB,MAAM,aAAa,CAAC;AACzD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAIxC,OAAO,EACH,mBAAmB,EACnB,mBAAmB,EACnB,kBAAkB,EAErB,MAAM,aAAa,CAAC"}
@@ -0,0 +1,29 @@
1
+ import type { Provider } from "./provider.js";
2
+ import type { Config } from "./config.js";
3
+ import type { ExchangeResponse } from "./response.js";
4
+ import { type JWTPayload } from "jose";
5
+ export type IDTokenClaims = JWTPayload & {
6
+ iss: string;
7
+ sub: string;
8
+ aud: string | string[];
9
+ exp: number;
10
+ iat: number;
11
+ };
12
+ export declare class OAuth2 {
13
+ private provider;
14
+ private config;
15
+ private jwks?;
16
+ constructor(provider: Provider, config: Config);
17
+ authCodeURL(state: string, urlParams?: Record<string, string>): string;
18
+ exchangeAuthCode(code: string, tokenParams?: Record<string, string>): Promise<ExchangeResponse>;
19
+ exchangeRefreshToken(refreshToken: string, tokenParams?: Record<string, string>): Promise<ExchangeResponse>;
20
+ private exchange;
21
+ parseClaims(idToken: string, options?: {
22
+ nonce?: string;
23
+ maxAge?: number;
24
+ trustedAudiences?: string[];
25
+ algorithms?: ("RS256" | "PS256" | "ES256" | "ES384" | "ES512")[];
26
+ }): Promise<IDTokenClaims>;
27
+ private validate;
28
+ }
29
+ //# sourceMappingURL=oauth2.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth2.d.ts","sourceRoot":"","sources":["../src/oauth2.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAQtD,OAAO,EAGH,KAAK,UAAU,EAClB,MAAM,MAAM,CAAC;AAEd,MAAM,MAAM,aAAa,GAAG,UAAU,GAAG;IACrC,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;CACf,CAAC;AAQF,qBAAa,MAAM;IAIX,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,MAAM;IAJlB,OAAO,CAAC,IAAI,CAAC,CAAwC;IAErD,YACY,QAAQ,EAAE,QAAQ,EAClB,MAAM,EAAE,MAAM,EAGzB;IAED,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CA0BrE;IAEK,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAYpG;IAEK,oBAAoB,CAAC,YAAY,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAehH;YAEa,QAAQ;IAqGhB,WAAW,CACb,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE;QACN,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;QAC5B,UAAU,CAAC,EAAE,CAAC,OAAO,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC,EAAE,CAAC;KACpE,GACF,OAAO,CAAC,aAAa,CAAC,CAmKxB;IAED,OAAO,CAAC,QAAQ;CAoCnB"}
package/dist/oauth2.js ADDED
@@ -0,0 +1,240 @@
1
+ import { HTTPError } from "./errors.js";
2
+ import { parseParams, baseReservedParams, } from "./params.js";
3
+ import { createRemoteJWKSet, jwtVerify, } from "jose";
4
+ function encodeFormValue(value) {
5
+ return new URLSearchParams([["value", value]])
6
+ .toString()
7
+ .slice("value=".length);
8
+ }
9
+ export class OAuth2 {
10
+ provider;
11
+ config;
12
+ jwks;
13
+ constructor(provider, config) {
14
+ this.provider = provider;
15
+ this.config = config;
16
+ this.validate();
17
+ }
18
+ authCodeURL(state, urlParams) {
19
+ const params = new URLSearchParams();
20
+ params.set("client_id", this.config.clientID);
21
+ params.set("redirect_uri", this.config.redirectURL);
22
+ params.set("response_type", "code");
23
+ parseParams(baseReservedParams(), urlParams ?? {}, (key, value) => {
24
+ params.set(key, value);
25
+ });
26
+ if (state !== "") {
27
+ params.set("state", state);
28
+ }
29
+ const url = new URL(this.provider.authURL);
30
+ params.forEach((value, key) => {
31
+ url.searchParams.set(key, value);
32
+ });
33
+ return url.toString();
34
+ }
35
+ async exchangeAuthCode(code, tokenParams) {
36
+ if (!code.trim()) {
37
+ throw new Error("Code cannot be empty");
38
+ }
39
+ const params = new URLSearchParams();
40
+ params.set("code", code);
41
+ params.set("redirect_uri", this.config.redirectURL);
42
+ params.set("grant_type", "authorization_code");
43
+ return this.exchange(params, baseReservedParams(), tokenParams);
44
+ }
45
+ async exchangeRefreshToken(refreshToken, tokenParams) {
46
+ if (!refreshToken.trim()) {
47
+ throw new Error("Refresh token cannot be empty");
48
+ }
49
+ const params = new URLSearchParams();
50
+ params.set("refresh_token", refreshToken);
51
+ params.set("grant_type", "refresh_token");
52
+ return this.exchange(params, baseReservedParams(), tokenParams);
53
+ }
54
+ async exchange(params, reservedParams, tokenParams) {
55
+ parseParams(reservedParams, tokenParams ?? {}, (key, value) => {
56
+ params.set(key, value);
57
+ });
58
+ const authMethod = this.config.clientAuthMethod ?? "client_secret_basic";
59
+ const headers = {
60
+ "Content-Type": "application/x-www-form-urlencoded",
61
+ };
62
+ if (authMethod === "client_secret_basic") {
63
+ const credentials = Buffer.from(`${encodeFormValue(this.config.clientID)}:${encodeFormValue(this.config.clientSecret)}`, "utf8").toString("base64");
64
+ headers["Authorization"] = `Basic ${credentials}`;
65
+ }
66
+ else {
67
+ params.set("client_id", this.config.clientID);
68
+ params.set("client_secret", this.config.clientSecret);
69
+ }
70
+ let response;
71
+ try {
72
+ response = await fetch(this.provider.tokenURL, {
73
+ method: "POST",
74
+ headers,
75
+ body: params.toString(),
76
+ });
77
+ }
78
+ catch (error) {
79
+ throw new HTTPError("Exchange: failed to post token request", undefined, undefined, { cause: error });
80
+ }
81
+ let raw;
82
+ try {
83
+ raw = await response.text();
84
+ }
85
+ catch (error) {
86
+ throw new HTTPError("Exchange: failed to read token response", response.status, undefined, { cause: error });
87
+ }
88
+ if (!response.ok) {
89
+ throw new HTTPError(`Exchange: token request failed with status ${response.status}`, response.status, raw);
90
+ }
91
+ let parsed;
92
+ try {
93
+ parsed = JSON.parse(raw);
94
+ }
95
+ catch (error) {
96
+ throw new HTTPError("Exchange: failed to parse token response", response.status, raw, { cause: error });
97
+ }
98
+ if (typeof parsed !== "object" ||
99
+ parsed === null ||
100
+ Array.isArray(parsed)) {
101
+ throw new HTTPError("Exchange: token response must be a JSON object", response.status, raw);
102
+ }
103
+ return {
104
+ raw,
105
+ data: parsed,
106
+ };
107
+ }
108
+ async parseClaims(idToken, options) {
109
+ if (!idToken.trim()) {
110
+ throw new Error("ID Token cannot be empty");
111
+ }
112
+ const issuer = this.provider.issuerURL;
113
+ const jwksURL = this.provider.jwksURL;
114
+ if (!issuer?.trim()) {
115
+ throw new Error("Provider issuer URL is required");
116
+ }
117
+ if (!jwksURL?.trim()) {
118
+ throw new Error("Provider JWKS URL is required");
119
+ }
120
+ if (options?.nonce !== undefined &&
121
+ !options.nonce.trim()) {
122
+ throw new Error("Nonce cannot be empty");
123
+ }
124
+ if (options?.maxAge !== undefined &&
125
+ (!Number.isFinite(options.maxAge) || options.maxAge < 0)) {
126
+ throw new Error("maxAge must be a non-negative number");
127
+ }
128
+ if (options?.trustedAudiences?.some((audience) => !audience.trim())) {
129
+ throw new Error("Trusted audiences cannot contain empty values");
130
+ }
131
+ const algorithms = options?.algorithms ?? ["RS256"];
132
+ if (algorithms.length === 0) {
133
+ throw new Error("At least one signing algorithm is required");
134
+ }
135
+ if (!this.jwks) {
136
+ this.jwks = createRemoteJWKSet(new URL(jwksURL));
137
+ }
138
+ const { payload } = await jwtVerify(idToken, this.jwks, {
139
+ issuer,
140
+ audience: this.config.clientID,
141
+ algorithms,
142
+ clockTolerance: 5,
143
+ });
144
+ // Subject
145
+ if (typeof payload.sub !== "string" ||
146
+ !payload.sub) {
147
+ throw new Error("Invalid ID Token: missing subject");
148
+ }
149
+ // Audience
150
+ const audiences = typeof payload.aud === "string"
151
+ ? [payload.aud]
152
+ : payload.aud;
153
+ if (!Array.isArray(audiences) ||
154
+ audiences.length === 0 ||
155
+ audiences.some((audience) => typeof audience !== "string" ||
156
+ !audience)) {
157
+ throw new Error("Invalid ID Token: invalid audience");
158
+ }
159
+ const trustedAudiences = new Set([
160
+ this.config.clientID,
161
+ ...(options?.trustedAudiences ?? []),
162
+ ]);
163
+ if (audiences.some((audience) => !trustedAudiences.has(audience))) {
164
+ throw new Error("Invalid ID Token: untrusted audience");
165
+ }
166
+ // Expiration
167
+ if (typeof payload.exp !== "number" ||
168
+ !Number.isFinite(payload.exp)) {
169
+ throw new Error("Invalid ID Token: invalid expiration");
170
+ }
171
+ // Issued at
172
+ if (typeof payload.iat !== "number" ||
173
+ !Number.isFinite(payload.iat) ||
174
+ payload.iat < 0) {
175
+ throw new Error("Invalid ID Token: invalid issued-at claim");
176
+ }
177
+ // Nonce
178
+ if (options?.nonce !== undefined) {
179
+ if (typeof payload.nonce !== "string" ||
180
+ payload.nonce !== options.nonce) {
181
+ throw new Error("Invalid ID Token: nonce mismatch");
182
+ }
183
+ }
184
+ // Authorized party
185
+ if (audiences.length > 1 && typeof payload.azp !== "string") {
186
+ throw new Error("Invalid ID Token: missing authorized party");
187
+ }
188
+ if (payload.azp !== undefined &&
189
+ (typeof payload.azp !== "string" ||
190
+ payload.azp !== this.config.clientID)) {
191
+ throw new Error("Invalid ID Token: authorized party mismatch");
192
+ }
193
+ // Authentication time
194
+ if (options?.maxAge !== undefined) {
195
+ if (typeof payload.auth_time !== "number") {
196
+ throw new Error("Invalid ID Token: missing authentication time");
197
+ }
198
+ const now = Math.floor(Date.now() / 1000);
199
+ const authTime = payload.auth_time;
200
+ if (!Number.isFinite(authTime) || authTime < 0) {
201
+ throw new Error("Invalid ID Token: invalid authentication time");
202
+ }
203
+ if (authTime > now + 5) {
204
+ throw new Error("Invalid ID Token: authentication time is in the future");
205
+ }
206
+ if (now - authTime > options.maxAge + 5) {
207
+ throw new Error("Invalid ID Token: authentication is too old");
208
+ }
209
+ }
210
+ return payload;
211
+ }
212
+ validate() {
213
+ if (!this.provider) {
214
+ throw new Error("Provider cannot be null or undefined");
215
+ }
216
+ if (!this.config) {
217
+ throw new Error("Config cannot be null or undefined");
218
+ }
219
+ if (!this.provider.authURL.trim()) {
220
+ throw new Error("Provider authorization URL cannot be empty");
221
+ }
222
+ if (!this.provider.tokenURL.trim()) {
223
+ throw new Error("Provider token URL cannot be empty");
224
+ }
225
+ if (!this.config.clientID.trim()) {
226
+ throw new Error("Client ID cannot be empty");
227
+ }
228
+ if (!this.config.clientSecret.trim()) {
229
+ throw new Error("Client secret cannot be empty");
230
+ }
231
+ if (!this.config.redirectURL.trim()) {
232
+ throw new Error("Redirect URL cannot be empty");
233
+ }
234
+ if (this.config.clientAuthMethod !== undefined &&
235
+ !["client_secret_basic", "client_secret_post"].includes(this.config.clientAuthMethod)) {
236
+ throw new Error("Unsupported client authentication method");
237
+ }
238
+ }
239
+ }
240
+ //# sourceMappingURL=oauth2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth2.js","sourceRoot":"","sources":["../src/oauth2.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ type ParseParamsCallback = (key: string, value: string) => void;
2
+ export declare function baseReservedParams(): Set<string>;
3
+ export declare function extendReservedParams(extraReservedParams?: string[]): Set<string>;
4
+ export declare function parseParams(reservedParams: Set<string>, paramsData: Record<string, string>, callback: ParseParamsCallback): void;
5
+ export {};
6
+ //# sourceMappingURL=params.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"params.d.ts","sourceRoot":"","sources":["../src/params.ts"],"names":[],"mappings":"AAAA,KAAK,mBAAmB,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;AAEhE,wBAAgB,kBAAkB,IAAI,GAAG,CAAC,MAAM,CAAC,CAWhD;AAED,wBAAgB,oBAAoB,CAAC,mBAAmB,GAAE,MAAM,EAAO,GAAG,GAAG,CAAC,MAAM,CAAC,CAQpF;AAED,wBAAgB,WAAW,CACvB,cAAc,EAAE,GAAG,CAAC,MAAM,CAAC,EAC3B,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAClC,QAAQ,EAAE,mBAAmB,GAC9B,IAAI,CAQN"}
package/dist/params.js ADDED
@@ -0,0 +1,28 @@
1
+ export function baseReservedParams() {
2
+ return new Set([
3
+ "client_id",
4
+ "client_secret",
5
+ "redirect_uri",
6
+ "code",
7
+ "refresh_token",
8
+ "state",
9
+ "response_type",
10
+ "grant_type"
11
+ ]);
12
+ }
13
+ export function extendReservedParams(extraReservedParams = []) {
14
+ const params = baseReservedParams();
15
+ for (const param of extraReservedParams) {
16
+ params.add(param);
17
+ }
18
+ return params;
19
+ }
20
+ export function parseParams(reservedParams, paramsData, callback) {
21
+ for (const [key, value] of Object.entries(paramsData)) {
22
+ if (reservedParams.has(key)) {
23
+ continue;
24
+ }
25
+ callback(key, value);
26
+ }
27
+ }
28
+ //# sourceMappingURL=params.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"params.js","sourceRoot":"","sources":["../src/params.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,kBAAkB;IAC9B,OAAO,IAAI,GAAG,CAAC;QACX,WAAW;QACX,eAAe;QACf,cAAc;QACd,MAAM;QACN,eAAe;QACf,OAAO;QACP,eAAe;QACf,YAAY;KACf,CAAC,CAAC;AACP,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,mBAAmB,GAAa,EAAE;IACnE,MAAM,MAAM,GAAG,kBAAkB,EAAE,CAAC;IAEpC,KAAK,MAAM,KAAK,IAAI,mBAAmB,EAAE,CAAC;QACtC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACtB,CAAC;IAED,OAAO,MAAM,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,WAAW,CACvB,cAA2B,EAC3B,UAAkC,EAClC,QAA6B;IAE7B,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC;QACpD,IAAI,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YAC1B,SAAS;QACb,CAAC;QAED,QAAQ,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IACzB,CAAC;AACL,CAAC"}
@@ -0,0 +1,7 @@
1
+ export interface Provider {
2
+ authURL: string;
3
+ tokenURL: string;
4
+ issuerURL?: string;
5
+ jwksURL?: string;
6
+ }
7
+ //# sourceMappingURL=provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,QAAQ;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CACpB"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.js","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":""}
@@ -0,0 +1,5 @@
1
+ export type ExchangeResponse = {
2
+ raw: string;
3
+ data: Record<string, unknown>;
4
+ };
5
+ //# sourceMappingURL=response.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"response.d.ts","sourceRoot":"","sources":["../src/response.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,gBAAgB,GAAG;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=response.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"response.js","sourceRoot":"","sources":["../src/response.ts"],"names":[],"mappings":""}
package/package.json CHANGED
@@ -1,6 +1,49 @@
1
1
  {
2
2
  "name": "@o2js/core",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.2.0",
4
+ "description": "A minimal, provider-agnostic OAuth 2.0 client for Node.js.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "main": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "sideEffects": false,
19
+ "engines": {
20
+ "node": ">=18"
21
+ },
22
+ "keywords": [
23
+ "oauth",
24
+ "oauth2",
25
+ "oauth-2.0",
26
+ "openid",
27
+ "openid-connect",
28
+ "oidc",
29
+ "pkce",
30
+ "authorization-code",
31
+ "refresh-token",
32
+ "authentication",
33
+ "authorization",
34
+ "node",
35
+ "nodejs",
36
+ "typescript"
37
+ ],
38
+ "scripts": {
39
+ "build": "tsc -p tsconfig.json",
40
+ "test": "npm run build && node --test"
41
+ },
42
+ "devDependencies": {
43
+ "@types/node": "^26.6.6",
44
+ "typescript": "^7.0.2"
45
+ },
46
+ "dependencies": {
47
+ "jose": "^6.2.15"
48
+ }
6
49
  }