@knsdev330/node-utils 1.0.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.
- package/LICENSE +21 -0
- package/README.md +85 -0
- package/dist/Cookies.d.ts +33 -0
- package/dist/Cookies.d.ts.map +1 -0
- package/dist/Cookies.js +54 -0
- package/dist/Cookies.js.map +1 -0
- package/dist/EApiCodes.d.ts +2 -0
- package/dist/EApiCodes.d.ts.map +1 -0
- package/dist/EApiCodes.js +2 -0
- package/dist/EApiCodes.js.map +1 -0
- package/dist/ErrorClasses.d.ts +78 -0
- package/dist/ErrorClasses.d.ts.map +1 -0
- package/dist/ErrorClasses.js +118 -0
- package/dist/ErrorClasses.js.map +1 -0
- package/dist/FBUtils.d.ts +22 -0
- package/dist/FBUtils.d.ts.map +1 -0
- package/dist/FBUtils.js +68 -0
- package/dist/FBUtils.js.map +1 -0
- package/dist/GoogleUtils.d.ts +43 -0
- package/dist/GoogleUtils.d.ts.map +1 -0
- package/dist/GoogleUtils.js +80 -0
- package/dist/GoogleUtils.js.map +1 -0
- package/dist/HMACUtils.d.ts +39 -0
- package/dist/HMACUtils.d.ts.map +1 -0
- package/dist/HMACUtils.js +61 -0
- package/dist/HMACUtils.js.map +1 -0
- package/dist/HttpCodes.d.ts +26 -0
- package/dist/HttpCodes.d.ts.map +1 -0
- package/dist/HttpCodes.js +25 -0
- package/dist/HttpCodes.js.map +1 -0
- package/dist/IDUtils.d.ts +28 -0
- package/dist/IDUtils.d.ts.map +1 -0
- package/dist/IDUtils.js +47 -0
- package/dist/IDUtils.js.map +1 -0
- package/dist/JwtUtils.d.ts +79 -0
- package/dist/JwtUtils.d.ts.map +1 -0
- package/dist/JwtUtils.js +94 -0
- package/dist/JwtUtils.js.map +1 -0
- package/dist/Nums.d.ts +101 -0
- package/dist/Nums.d.ts.map +1 -0
- package/dist/Nums.js +141 -0
- package/dist/Nums.js.map +1 -0
- package/dist/PasswordUtils.d.ts +58 -0
- package/dist/PasswordUtils.d.ts.map +1 -0
- package/dist/PasswordUtils.js +82 -0
- package/dist/PasswordUtils.js.map +1 -0
- package/dist/Rand.d.ts +44 -0
- package/dist/Rand.d.ts.map +1 -0
- package/dist/Rand.js +72 -0
- package/dist/Rand.js.map +1 -0
- package/dist/TurnstileValidator.d.ts +57 -0
- package/dist/TurnstileValidator.d.ts.map +1 -0
- package/dist/TurnstileValidator.js +99 -0
- package/dist/TurnstileValidator.js.map +1 -0
- package/dist/bnUtils.d.ts +15 -0
- package/dist/bnUtils.d.ts.map +1 -0
- package/dist/bnUtils.js +67 -0
- package/dist/bnUtils.js.map +1 -0
- package/dist/getEnvv.d.ts +21 -0
- package/dist/getEnvv.d.ts.map +1 -0
- package/dist/getEnvv.js +68 -0
- package/dist/getEnvv.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/dist/tryCatch.d.ts +23 -0
- package/dist/tryCatch.d.ts.map +1 -0
- package/dist/tryCatch.js +29 -0
- package/dist/tryCatch.js.map +1 -0
- package/dist/types.d.ts +8 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/validators.d.ts +7 -0
- package/dist/validators.d.ts.map +1 -0
- package/dist/validators.js +11 -0
- package/dist/validators.js.map +1 -0
- package/package.json +80 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Khandaker Sajjat
|
|
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
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# @knsdev330/node-utils
|
|
2
|
+
|
|
3
|
+
TypeScript utilities for Node.js backends: passwords, JWT, cookies, OAuth helpers, env parsing, IDs, and small shared primitives.
|
|
4
|
+
|
|
5
|
+
This library is built for reuse across projects. It is somewhat opinionated and not optimized for minimal install size—review modules before production use.
|
|
6
|
+
|
|
7
|
+
## Disclaimer
|
|
8
|
+
|
|
9
|
+
- Utilities reflect personal conventions and may include niche helpers.
|
|
10
|
+
- **Security-sensitive code** (password hashing, JWT, OAuth, Turnstile) should be reviewed and tested in your environment.
|
|
11
|
+
- No stability guarantee for semver until you explicitly commit to a versioning policy.
|
|
12
|
+
|
|
13
|
+
## Requirements
|
|
14
|
+
|
|
15
|
+
- **Node.js** with ESM (`"type": "module"` in consuming packages, or a bundler that handles ESM).
|
|
16
|
+
- **Express** is a **peer dependency** (v4 or v5). Install it in your app; it is required for cookie helpers that use `express` response types.
|
|
17
|
+
|
|
18
|
+
Runtime dependencies installed with this package include `bcrypt`, `jsonwebtoken`, and `jose`.
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm install @knsdev330/node-utils express
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
pnpm add @knsdev330/node-utils express
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Releases (CI)
|
|
31
|
+
|
|
32
|
+
Pushes to `main` run [.github/workflows/publish.yml](.github/workflows/publish.yml), which publishes with **[npm trusted publishing (OIDC)](https://docs.npmjs.com/trusted-publishers)**—no long-lived **`NPM_TOKEN`** for publish. On npmjs.com, add this repo as the package’s trusted publisher (GitHub org/user, repository, workflow file **`publish.yml`**). The [`repository`](https://docs.npmjs.com/trusted-publishers#troubleshooting) field in `package.json` must match this GitHub repo exactly. **Bump `version` in `package.json`** before each release; npm rejects duplicate versions.
|
|
33
|
+
|
|
34
|
+
## Usage
|
|
35
|
+
|
|
36
|
+
Default entry re-exports the public API:
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
import {
|
|
40
|
+
PasswordUtils,
|
|
41
|
+
JwtUtils,
|
|
42
|
+
CookieUtils,
|
|
43
|
+
getEnvv,
|
|
44
|
+
tryCatch,
|
|
45
|
+
} from "@knsdev330/node-utils";
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Subpath imports map to built files under `dist/` (see `package.json` `"exports"`):
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
import { PasswordUtils } from "@knsdev330/node-utils/PasswordUtils.js";
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Use the `.js` extension in import specifiers to match Node ESM resolution.
|
|
55
|
+
|
|
56
|
+
## What is included
|
|
57
|
+
|
|
58
|
+
| Area | Exports (examples) |
|
|
59
|
+
|------|---------------------|
|
|
60
|
+
| Passwords | `PasswordUtils` — bcrypt hash/compare, strength bounds |
|
|
61
|
+
| JWT | `JwtUtils` — sign/verify via `jsonwebtoken` |
|
|
62
|
+
| Cookies | `CookieUtils` — set/get/delete on Express `Response` |
|
|
63
|
+
| OAuth | `GoogleUtils`, `FacebookUtils` — token / user helpers |
|
|
64
|
+
| Crypto / integrity | `HMACUtils` |
|
|
65
|
+
| HTTP / API | `HttpCodes`, `EApiCodes`, `ErrorClasses` |
|
|
66
|
+
| Env | `getEnvv` — typed env from a schema |
|
|
67
|
+
| IDs / random | `IDUtils`, `Rand` |
|
|
68
|
+
| Numbers / bigint | `Nums`, `bnUtils` |
|
|
69
|
+
| Validation | `validateUUID`, `TurnstileValidator` |
|
|
70
|
+
| Control flow | `tryCatch`, `tryCatch2` |
|
|
71
|
+
|
|
72
|
+
See `src/index.ts` for the full export list and each module for behavior and parameters.
|
|
73
|
+
|
|
74
|
+
## Development
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
pnpm install
|
|
78
|
+
pnpm run build
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
`prepublishOnly` runs `pnpm run build` so `dist/` is fresh before publish.
|
|
82
|
+
|
|
83
|
+
## License
|
|
84
|
+
|
|
85
|
+
MIT — see [LICENSE](./LICENSE).
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { type Response } from "express";
|
|
2
|
+
declare class cookieUtils {
|
|
3
|
+
/**
|
|
4
|
+
* Set an HTTP-only cookie with common security defaults.
|
|
5
|
+
* @param res - Express response object.
|
|
6
|
+
* @param name - Cookie name.
|
|
7
|
+
* @param value - Cookie value.
|
|
8
|
+
* @param maxAgeMs - Cookie max age in milliseconds (Express `maxAge`).
|
|
9
|
+
* @param domain - Cookie domain.
|
|
10
|
+
* @param APP_ENV - Current application environment.
|
|
11
|
+
* @param path - Cookie path.
|
|
12
|
+
* @param sameSite - SameSite policy.
|
|
13
|
+
* @returns Nothing.
|
|
14
|
+
*/
|
|
15
|
+
readonly setCookie: (res: Response, name: string, value: string, maxAgeMs: number, domain: string, APP_ENV: "dev" | "test" | "prod", path?: string, sameSite?: "lax" | "strict" | "none") => void;
|
|
16
|
+
/**
|
|
17
|
+
* Clear an HTTP-only cookie by setting it expired immediately.
|
|
18
|
+
* @param res - Express response object.
|
|
19
|
+
* @param name - Cookie name.
|
|
20
|
+
* @param domain - Cookie domain.
|
|
21
|
+
* @param APP_ENV - Current application environment.
|
|
22
|
+
* @param path - Cookie path.
|
|
23
|
+
* @param sameSite - SameSite policy.
|
|
24
|
+
* @returns Nothing.
|
|
25
|
+
*/
|
|
26
|
+
readonly clearCookie: (res: Response, name: string, domain: string, APP_ENV: "dev" | "test" | "prod", path?: string, sameSite?: "lax" | "strict" | "none") => void;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Singleton instance of cookie helper utilities.
|
|
30
|
+
*/
|
|
31
|
+
export declare const CookieUtils: cookieUtils;
|
|
32
|
+
export {};
|
|
33
|
+
//# sourceMappingURL=Cookies.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Cookies.d.ts","sourceRoot":"","sources":["../src/Cookies.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,SAAS,CAAC;AAExC,cAAM,WAAW;IAChB;;;;;;;;;;;OAWG;IACH,QAAQ,CAAC,SAAS,GAAI,KAAK,QAAQ,EAAE,MAAM,MAAM,EAAE,OAAO,MAAM,EAAE,UAAU,MAAM,EAAE,QAAQ,MAAM,EAAE,SAAS,KAAK,GAAG,MAAM,GAAG,MAAM,EAAE,OAAM,MAAY,EAAE,WAAU,KAAK,GAAG,QAAQ,GAAG,MAAc,UAYpM;IAED;;;;;;;;;OASG;IACH,QAAQ,CAAC,WAAW,GAAI,KAAK,QAAQ,EAAE,MAAM,MAAM,EAAE,QAAQ,MAAM,EAAE,SAAS,KAAK,GAAG,MAAM,GAAG,MAAM,EAAE,OAAM,MAAY,EAAE,WAAU,KAAK,GAAG,QAAQ,GAAG,MAAc,UAYrK;CACD;AAED;;GAEG;AACH,eAAO,MAAM,WAAW,aAAoB,CAAC"}
|
package/dist/Cookies.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import {} from "express";
|
|
2
|
+
class cookieUtils {
|
|
3
|
+
/**
|
|
4
|
+
* Set an HTTP-only cookie with common security defaults.
|
|
5
|
+
* @param res - Express response object.
|
|
6
|
+
* @param name - Cookie name.
|
|
7
|
+
* @param value - Cookie value.
|
|
8
|
+
* @param maxAgeMs - Cookie max age in milliseconds (Express `maxAge`).
|
|
9
|
+
* @param domain - Cookie domain.
|
|
10
|
+
* @param APP_ENV - Current application environment.
|
|
11
|
+
* @param path - Cookie path.
|
|
12
|
+
* @param sameSite - SameSite policy.
|
|
13
|
+
* @returns Nothing.
|
|
14
|
+
*/
|
|
15
|
+
setCookie = (res, name, value, maxAgeMs, domain, APP_ENV, path = '/', sameSite = 'lax') => {
|
|
16
|
+
// If sameSite is 'none', secure must be true for most modern browsers
|
|
17
|
+
const isSecure = sameSite === 'none' ? true : APP_ENV !== 'dev';
|
|
18
|
+
res.cookie(name, value, {
|
|
19
|
+
httpOnly: true,
|
|
20
|
+
secure: isSecure,
|
|
21
|
+
maxAge: maxAgeMs,
|
|
22
|
+
path: path,
|
|
23
|
+
sameSite: sameSite,
|
|
24
|
+
domain: domain
|
|
25
|
+
});
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Clear an HTTP-only cookie by setting it expired immediately.
|
|
29
|
+
* @param res - Express response object.
|
|
30
|
+
* @param name - Cookie name.
|
|
31
|
+
* @param domain - Cookie domain.
|
|
32
|
+
* @param APP_ENV - Current application environment.
|
|
33
|
+
* @param path - Cookie path.
|
|
34
|
+
* @param sameSite - SameSite policy.
|
|
35
|
+
* @returns Nothing.
|
|
36
|
+
*/
|
|
37
|
+
clearCookie = (res, name, domain, APP_ENV, path = '/', sameSite = 'lax') => {
|
|
38
|
+
const isSecure = sameSite === 'none' ? true : APP_ENV !== 'dev';
|
|
39
|
+
res.cookie(name, 'invalid', {
|
|
40
|
+
httpOnly: true,
|
|
41
|
+
secure: isSecure,
|
|
42
|
+
maxAge: 0,
|
|
43
|
+
expires: new Date(0),
|
|
44
|
+
path: path,
|
|
45
|
+
sameSite: sameSite,
|
|
46
|
+
domain: domain
|
|
47
|
+
});
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Singleton instance of cookie helper utilities.
|
|
52
|
+
*/
|
|
53
|
+
export const CookieUtils = new cookieUtils();
|
|
54
|
+
//# sourceMappingURL=Cookies.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Cookies.js","sourceRoot":"","sources":["../src/Cookies.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,MAAM,SAAS,CAAC;AAExC,MAAM,WAAW;IAChB;;;;;;;;;;;OAWG;IACM,SAAS,GAAG,CAAC,GAAa,EAAE,IAAY,EAAE,KAAa,EAAE,QAAgB,EAAE,MAAc,EAAE,OAAgC,EAAE,OAAe,GAAG,EAAE,WAAsC,KAAK,EAAE,EAAE;QACxM,sEAAsE;QACtE,MAAM,QAAQ,GAAG,QAAQ,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,KAAK,KAAK,CAAC;QAEhE,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE;YACvB,QAAQ,EAAE,IAAI;YACd,MAAM,EAAE,QAAQ;YAChB,MAAM,EAAE,QAAQ;YAChB,IAAI,EAAE,IAAI;YACV,QAAQ,EAAE,QAAQ;YAClB,MAAM,EAAE,MAAM;SACd,CAAC,CAAC;IACJ,CAAC,CAAA;IAED;;;;;;;;;OASG;IACM,WAAW,GAAG,CAAC,GAAa,EAAE,IAAY,EAAE,MAAc,EAAE,OAAgC,EAAE,OAAe,GAAG,EAAE,WAAsC,KAAK,EAAE,EAAE;QACzK,MAAM,QAAQ,GAAG,QAAQ,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,KAAK,KAAK,CAAC;QAEhE,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,SAAS,EAAE;YAC3B,QAAQ,EAAE,IAAI;YACd,MAAM,EAAE,QAAQ;YAChB,MAAM,EAAE,CAAC;YACT,OAAO,EAAE,IAAI,IAAI,CAAC,CAAC,CAAC;YACpB,IAAI,EAAE,IAAI;YACV,QAAQ,EAAE,QAAQ;YAClB,MAAM,EAAE,MAAM;SACd,CAAC,CAAC;IACJ,CAAC,CAAA;CACD;AAED;;GAEG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,IAAI,WAAW,EAAE,CAAC"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export type EApiCodes = `API_ERROR` | `SUCCESS` | `BAD_REQUEST` | `UNAUTHORIZED` | `FORBIDDEN` | `NOT_FOUND` | `INTERNAL_SERVER_ERROR` | `TOO_MANY_REQUESTS` | `JWT__ENCRYPTION_FAILED` | `JWT__INVALID_OR_EXPIRED` | `CAPTCHA__VALIDATION_FAILED` | `ID__INVALID`;
|
|
2
|
+
//# sourceMappingURL=EApiCodes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EApiCodes.d.ts","sourceRoot":"","sources":["../src/EApiCodes.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GAAG,WAAW,GAClC,SAAS,GACT,aAAa,GACb,cAAc,GACd,WAAW,GACX,WAAW,GACX,uBAAuB,GACvB,mBAAmB,GACnB,wBAAwB,GACxB,yBAAyB,GACzB,4BAA4B,GAC5B,aAAa,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EApiCodes.js","sourceRoot":"","sources":["../src/EApiCodes.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import type { EApiCodes } from "./EApiCodes.js";
|
|
2
|
+
/**
|
|
3
|
+
* Base error type for API responses.
|
|
4
|
+
* Extends {@link Error} with an HTTP status code and an API error code.
|
|
5
|
+
*/
|
|
6
|
+
export declare class ApiError extends Error {
|
|
7
|
+
status: number;
|
|
8
|
+
message: EApiCodes;
|
|
9
|
+
retryAfterDate: Date | undefined;
|
|
10
|
+
/**
|
|
11
|
+
* @param status - HTTP status code associated with the error.
|
|
12
|
+
* @param message - API error code (defaults to `API_ERROR`).
|
|
13
|
+
* @param stack - Optional stack trace override (empty string means "use default").
|
|
14
|
+
* @param retryAfterDate - Optional date used for rate-limit responses (e.g. `429`).
|
|
15
|
+
*/
|
|
16
|
+
constructor(status: number, message?: EApiCodes, stack?: string, retryAfterDate?: Date);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* 500 Internal Server Error.
|
|
20
|
+
*/
|
|
21
|
+
export declare class InternalServerError extends ApiError {
|
|
22
|
+
/**
|
|
23
|
+
* @param message - API error code (defaults to `INTERNAL_SERVER_ERROR`).
|
|
24
|
+
* @param stack - Optional stack trace override (empty string means "use default").
|
|
25
|
+
*/
|
|
26
|
+
constructor(message?: EApiCodes, stack?: string);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* 429 Too Many Requests.
|
|
30
|
+
*/
|
|
31
|
+
export declare class TooManyRequests extends ApiError {
|
|
32
|
+
/**
|
|
33
|
+
* @param message - API error code (defaults to `TOO_MANY_REQUESTS`).
|
|
34
|
+
* @param retryAfterDate - Optional date when clients may retry.
|
|
35
|
+
*/
|
|
36
|
+
constructor(message?: EApiCodes, retryAfterDate?: Date);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* 400 Bad Request.
|
|
40
|
+
*/
|
|
41
|
+
export declare class BadRequest extends ApiError {
|
|
42
|
+
/**
|
|
43
|
+
* @param message - API error code (defaults to `BAD_REQUEST`).
|
|
44
|
+
* @param stack - Optional stack trace override (empty string means "use default").
|
|
45
|
+
*/
|
|
46
|
+
constructor(message?: EApiCodes, stack?: string);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* 403 Forbidden.
|
|
50
|
+
*/
|
|
51
|
+
export declare class Forbidden extends ApiError {
|
|
52
|
+
/**
|
|
53
|
+
* @param message - API error code (defaults to `FORBIDDEN`).
|
|
54
|
+
* @param stack - Optional stack trace override (empty string means "use default").
|
|
55
|
+
*/
|
|
56
|
+
constructor(message?: EApiCodes, stack?: string);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* 401 Unauthorized.
|
|
60
|
+
*/
|
|
61
|
+
export declare class Unauthorized extends ApiError {
|
|
62
|
+
/**
|
|
63
|
+
* @param message - API error code (defaults to `UNAUTHORIZED`).
|
|
64
|
+
* @param stack - Optional stack trace override (empty string means "use default").
|
|
65
|
+
*/
|
|
66
|
+
constructor(message?: EApiCodes, stack?: string);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* 404 Not Found.
|
|
70
|
+
*/
|
|
71
|
+
export declare class NotFound extends ApiError {
|
|
72
|
+
/**
|
|
73
|
+
* @param message - API error code (defaults to `NOT_FOUND`).
|
|
74
|
+
* @param stack - Optional stack trace override (empty string means "use default").
|
|
75
|
+
*/
|
|
76
|
+
constructor(message?: EApiCodes, stack?: string);
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=ErrorClasses.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ErrorClasses.d.ts","sourceRoot":"","sources":["../src/ErrorClasses.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEhD;;;GAGG;AACH,qBAAa,QAAS,SAAQ,KAAK;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,SAAS,CAAC;IACnB,cAAc,EAAE,IAAI,GAAG,SAAS,CAAC;IAEjC;;;;;OAKG;gBACS,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,EAAE,KAAK,SAAK,EAAE,cAAc,CAAC,EAAE,IAAI;CAQlF;AAED;;GAEG;AACH,qBAAa,mBAAoB,SAAQ,QAAQ;IAChD;;;OAGG;gBACS,OAAO,GAAE,SAAmC,EAAE,KAAK,SAAK;CAKpE;AAED;;GAEG;AACH,qBAAa,eAAgB,SAAQ,QAAQ;IAC5C;;;OAGG;gBACS,OAAO,GAAE,SAA+B,EAAE,cAAc,CAAC,EAAE,IAAI;CAG3E;AAED;;GAEG;AACH,qBAAa,UAAW,SAAQ,QAAQ;IACvC;;;OAGG;gBACS,OAAO,GAAE,SAAyB,EAAE,KAAK,SAAK;CAK1D;AAED;;GAEG;AACH,qBAAa,SAAU,SAAQ,QAAQ;IACtC;;;OAGG;gBACS,OAAO,GAAE,SAAuB,EAAE,KAAK,SAAK;CAKxD;AAED;;GAEG;AACH,qBAAa,YAAa,SAAQ,QAAQ;IACzC;;;OAGG;gBACS,OAAO,GAAE,SAA0B,EAAE,KAAK,SAAK;CAK3D;AAED;;GAEG;AACH,qBAAa,QAAS,SAAQ,QAAQ;IACrC;;;OAGG;gBACS,OAAO,GAAE,SAAuB,EAAE,KAAK,SAAK;CAKxD"}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base error type for API responses.
|
|
3
|
+
* Extends {@link Error} with an HTTP status code and an API error code.
|
|
4
|
+
*/
|
|
5
|
+
export class ApiError extends Error {
|
|
6
|
+
status;
|
|
7
|
+
message;
|
|
8
|
+
retryAfterDate;
|
|
9
|
+
/**
|
|
10
|
+
* @param status - HTTP status code associated with the error.
|
|
11
|
+
* @param message - API error code (defaults to `API_ERROR`).
|
|
12
|
+
* @param stack - Optional stack trace override (empty string means "use default").
|
|
13
|
+
* @param retryAfterDate - Optional date used for rate-limit responses (e.g. `429`).
|
|
14
|
+
*/
|
|
15
|
+
constructor(status, message, stack = "", retryAfterDate) {
|
|
16
|
+
super(message || 'API_ERROR');
|
|
17
|
+
this.status = status;
|
|
18
|
+
this.message = message || 'API_ERROR';
|
|
19
|
+
this.retryAfterDate = retryAfterDate;
|
|
20
|
+
if (stack)
|
|
21
|
+
this.stack = stack;
|
|
22
|
+
else
|
|
23
|
+
Error.captureStackTrace(this, this.constructor);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* 500 Internal Server Error.
|
|
28
|
+
*/
|
|
29
|
+
export class InternalServerError extends ApiError {
|
|
30
|
+
/**
|
|
31
|
+
* @param message - API error code (defaults to `INTERNAL_SERVER_ERROR`).
|
|
32
|
+
* @param stack - Optional stack trace override (empty string means "use default").
|
|
33
|
+
*/
|
|
34
|
+
constructor(message = 'INTERNAL_SERVER_ERROR', stack = "") {
|
|
35
|
+
super(500, message);
|
|
36
|
+
if (stack)
|
|
37
|
+
this.stack = stack;
|
|
38
|
+
else
|
|
39
|
+
Error.captureStackTrace(this, this.constructor);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* 429 Too Many Requests.
|
|
44
|
+
*/
|
|
45
|
+
export class TooManyRequests extends ApiError {
|
|
46
|
+
/**
|
|
47
|
+
* @param message - API error code (defaults to `TOO_MANY_REQUESTS`).
|
|
48
|
+
* @param retryAfterDate - Optional date when clients may retry.
|
|
49
|
+
*/
|
|
50
|
+
constructor(message = 'TOO_MANY_REQUESTS', retryAfterDate) {
|
|
51
|
+
super(429, message, undefined, retryAfterDate);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* 400 Bad Request.
|
|
56
|
+
*/
|
|
57
|
+
export class BadRequest extends ApiError {
|
|
58
|
+
/**
|
|
59
|
+
* @param message - API error code (defaults to `BAD_REQUEST`).
|
|
60
|
+
* @param stack - Optional stack trace override (empty string means "use default").
|
|
61
|
+
*/
|
|
62
|
+
constructor(message = 'BAD_REQUEST', stack = "") {
|
|
63
|
+
super(400, message);
|
|
64
|
+
if (stack)
|
|
65
|
+
this.stack = stack;
|
|
66
|
+
else
|
|
67
|
+
Error.captureStackTrace(this, this.constructor);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* 403 Forbidden.
|
|
72
|
+
*/
|
|
73
|
+
export class Forbidden extends ApiError {
|
|
74
|
+
/**
|
|
75
|
+
* @param message - API error code (defaults to `FORBIDDEN`).
|
|
76
|
+
* @param stack - Optional stack trace override (empty string means "use default").
|
|
77
|
+
*/
|
|
78
|
+
constructor(message = 'FORBIDDEN', stack = "") {
|
|
79
|
+
super(403, message);
|
|
80
|
+
if (stack)
|
|
81
|
+
this.stack = stack;
|
|
82
|
+
else
|
|
83
|
+
Error.captureStackTrace(this, this.constructor);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* 401 Unauthorized.
|
|
88
|
+
*/
|
|
89
|
+
export class Unauthorized extends ApiError {
|
|
90
|
+
/**
|
|
91
|
+
* @param message - API error code (defaults to `UNAUTHORIZED`).
|
|
92
|
+
* @param stack - Optional stack trace override (empty string means "use default").
|
|
93
|
+
*/
|
|
94
|
+
constructor(message = 'UNAUTHORIZED', stack = "") {
|
|
95
|
+
super(401, message);
|
|
96
|
+
if (stack)
|
|
97
|
+
this.stack = stack;
|
|
98
|
+
else
|
|
99
|
+
Error.captureStackTrace(this, this.constructor);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* 404 Not Found.
|
|
104
|
+
*/
|
|
105
|
+
export class NotFound extends ApiError {
|
|
106
|
+
/**
|
|
107
|
+
* @param message - API error code (defaults to `NOT_FOUND`).
|
|
108
|
+
* @param stack - Optional stack trace override (empty string means "use default").
|
|
109
|
+
*/
|
|
110
|
+
constructor(message = 'NOT_FOUND', stack = "") {
|
|
111
|
+
super(404, message);
|
|
112
|
+
if (stack)
|
|
113
|
+
this.stack = stack;
|
|
114
|
+
else
|
|
115
|
+
Error.captureStackTrace(this, this.constructor);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=ErrorClasses.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ErrorClasses.js","sourceRoot":"","sources":["../src/ErrorClasses.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,OAAO,QAAS,SAAQ,KAAK;IAClC,MAAM,CAAS;IACf,OAAO,CAAY;IACnB,cAAc,CAAmB;IAEjC;;;;;OAKG;IACH,YAAY,MAAc,EAAE,OAAmB,EAAE,KAAK,GAAG,EAAE,EAAE,cAAqB;QACjF,KAAK,CAAC,OAAO,IAAI,WAAW,CAAC,CAAC;QAC9B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,OAAO,GAAG,OAAO,IAAI,WAAW,CAAC;QACtC,IAAI,CAAC,cAAc,GAAG,cAAc,CAAC;QACrC,IAAI,KAAK;YAAE,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;;YACzB,KAAK,CAAC,iBAAiB,CAAC,IAAI,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;IACtD,CAAC;CACD;AAED;;GAEG;AACH,MAAM,OAAO,mBAAoB,SAAQ,QAAQ;IAChD;;;OAGG;IACH,YAAY,UAAqB,uBAAuB,EAAE,KAAK,GAAG,EAAE;QACnE,KAAK,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QACpB,IAAI,KAAK;YAAE,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;;YACzB,KAAK,CAAC,iBAAiB,CAAC,IAAI,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;IACtD,CAAC;CACD;AAED;;GAEG;AACH,MAAM,OAAO,eAAgB,SAAQ,QAAQ;IAC5C;;;OAGG;IACH,YAAY,UAAqB,mBAAmB,EAAE,cAAqB;QAC1E,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,SAAS,EAAE,cAAc,CAAC,CAAC;IAChD,CAAC;CACD;AAED;;GAEG;AACH,MAAM,OAAO,UAAW,SAAQ,QAAQ;IACvC;;;OAGG;IACH,YAAY,UAAqB,aAAa,EAAE,KAAK,GAAG,EAAE;QACzD,KAAK,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QACpB,IAAI,KAAK;YAAE,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;;YACzB,KAAK,CAAC,iBAAiB,CAAC,IAAI,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;IACtD,CAAC;CACD;AAED;;GAEG;AACH,MAAM,OAAO,SAAU,SAAQ,QAAQ;IACtC;;;OAGG;IACH,YAAY,UAAqB,WAAW,EAAE,KAAK,GAAG,EAAE;QACvD,KAAK,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QACpB,IAAI,KAAK;YAAE,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;;YACzB,KAAK,CAAC,iBAAiB,CAAC,IAAI,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;IACtD,CAAC;CACD;AAED;;GAEG;AACH,MAAM,OAAO,YAAa,SAAQ,QAAQ;IACzC;;;OAGG;IACH,YAAY,UAAqB,cAAc,EAAE,KAAK,GAAG,EAAE;QAC1D,KAAK,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QACpB,IAAI,KAAK;YAAE,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;;YACzB,KAAK,CAAC,iBAAiB,CAAC,IAAI,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;IACtD,CAAC;CACD;AAED;;GAEG;AACH,MAAM,OAAO,QAAS,SAAQ,QAAQ;IACrC;;;OAGG;IACH,YAAY,UAAqB,WAAW,EAAE,KAAK,GAAG,EAAE;QACvD,KAAK,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QACpB,IAAI,KAAK;YAAE,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;;YACzB,KAAK,CAAC,iBAAiB,CAAC,IAAI,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;IACtD,CAAC;CACD"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
declare class facebookUtils {
|
|
2
|
+
/**
|
|
3
|
+
* Validates a Facebook access token and fetches user profile details.
|
|
4
|
+
* Uses POST bodies so tokens and app secrets are not placed in request URLs (fewer access-log leaks).
|
|
5
|
+
* @param token - Facebook user access token.
|
|
6
|
+
* @param appId - Facebook application ID.
|
|
7
|
+
* @param appSecret - Facebook application secret.
|
|
8
|
+
* @returns Normalized user profile or `null` if token/profile lookup fails.
|
|
9
|
+
*/
|
|
10
|
+
readonly getUserFromJwt: (token: string, appId: string, appSecret: string) => Promise<{
|
|
11
|
+
name: string | undefined;
|
|
12
|
+
email: string | undefined;
|
|
13
|
+
picture: string | undefined;
|
|
14
|
+
oauth_id: string;
|
|
15
|
+
} | null>;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Singleton instance of Facebook OAuth utilities.
|
|
19
|
+
*/
|
|
20
|
+
export declare const FacebookUtils: facebookUtils;
|
|
21
|
+
export {};
|
|
22
|
+
//# sourceMappingURL=FBUtils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FBUtils.d.ts","sourceRoot":"","sources":["../src/FBUtils.ts"],"names":[],"mappings":"AAWA,cAAM,aAAa;IAClB;;;;;;;OAOG;IACH,QAAQ,CAAC,cAAc,GAAU,OAAO,MAAM,EAAE,OAAO,MAAM,EAAE,WAAW,MAAM,KAAG,OAAO,CAAC;QAC1F,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;QACzB,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;QAC1B,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;QAC5B,QAAQ,EAAE,MAAM,CAAC;KACjB,GAAG,IAAI,CAAC,CA4DR;CACD;AAED;;GAEG;AACH,eAAO,MAAM,aAAa,eAAsB,CAAC"}
|
package/dist/FBUtils.js
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { tryCatch } from "./tryCatch.js";
|
|
2
|
+
const FORM_URLENCODED = "application/x-www-form-urlencoded";
|
|
3
|
+
class facebookUtils {
|
|
4
|
+
/**
|
|
5
|
+
* Validates a Facebook access token and fetches user profile details.
|
|
6
|
+
* Uses POST bodies so tokens and app secrets are not placed in request URLs (fewer access-log leaks).
|
|
7
|
+
* @param token - Facebook user access token.
|
|
8
|
+
* @param appId - Facebook application ID.
|
|
9
|
+
* @param appSecret - Facebook application secret.
|
|
10
|
+
* @returns Normalized user profile or `null` if token/profile lookup fails.
|
|
11
|
+
*/
|
|
12
|
+
getUserFromJwt = async (token, appId, appSecret) => {
|
|
13
|
+
const debugBody = new URLSearchParams({
|
|
14
|
+
input_token: token,
|
|
15
|
+
access_token: `${appId}|${appSecret}`,
|
|
16
|
+
});
|
|
17
|
+
const [response, error] = await tryCatch(fetch("https://graph.facebook.com/v19.0/debug_token", {
|
|
18
|
+
method: "POST",
|
|
19
|
+
headers: { "Content-Type": FORM_URLENCODED },
|
|
20
|
+
body: debugBody.toString(),
|
|
21
|
+
}));
|
|
22
|
+
if (error) {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
if (!response.ok) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
const data = (await response.json());
|
|
29
|
+
if (!data.data?.is_valid || data.data.app_id !== appId) {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
// `expires_at === 0` is treated as non-expiring (avoid rejecting valid long-lived tokens).
|
|
33
|
+
const nowSec = Date.now() / 1000;
|
|
34
|
+
if (data.data.expires_at !== 0 && data.data.expires_at < nowSec) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
const meBody = new URLSearchParams({
|
|
38
|
+
fields: "id,name,email,picture.type(large)",
|
|
39
|
+
access_token: token,
|
|
40
|
+
});
|
|
41
|
+
const [user, userError] = await tryCatch(fetch("https://graph.facebook.com/v19.0/me", {
|
|
42
|
+
method: "POST",
|
|
43
|
+
headers: { "Content-Type": FORM_URLENCODED },
|
|
44
|
+
body: meBody.toString(),
|
|
45
|
+
}));
|
|
46
|
+
if (userError) {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
if (!user.ok) {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
const userData = (await user.json());
|
|
53
|
+
if (!userData.id) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
oauth_id: userData.id,
|
|
58
|
+
name: userData.name ?? 'Anonymous',
|
|
59
|
+
email: userData.email,
|
|
60
|
+
picture: userData.picture?.data?.url ?? ``,
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Singleton instance of Facebook OAuth utilities.
|
|
66
|
+
*/
|
|
67
|
+
export const FacebookUtils = new facebookUtils();
|
|
68
|
+
//# sourceMappingURL=FBUtils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FBUtils.js","sourceRoot":"","sources":["../src/FBUtils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AASzC,MAAM,eAAe,GAAG,mCAAmC,CAAC;AAE5D,MAAM,aAAa;IAClB;;;;;;;OAOG;IACM,cAAc,GAAG,KAAK,EAAE,KAAa,EAAE,KAAa,EAAE,SAAiB,EAKtE,EAAE;QAEX,MAAM,SAAS,GAAG,IAAI,eAAe,CAAC;YACrC,WAAW,EAAE,KAAK;YAClB,YAAY,EAAE,GAAG,KAAK,IAAI,SAAS,EAAE;SACrC,CAAC,CAAC;QAEH,MAAM,CAAC,QAAQ,EAAE,KAAK,CAAC,GAAG,MAAM,QAAQ,CAAC,KAAK,CAAC,8CAA8C,EAAE;YAC9F,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,cAAc,EAAE,eAAe,EAAE;YAC5C,IAAI,EAAE,SAAS,CAAC,QAAQ,EAAE;SAC1B,CAAC,CAAC,CAAC;QACJ,IAAI,KAAK,EAAE,CAAC;YACX,OAAO,IAAI,CAAC;QACb,CAAC;QAED,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YAClB,OAAO,IAAI,CAAC;QACb,CAAC;QAED,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAyE,CAAC;QAC7G,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,KAAK,KAAK,EAAE,CAAC;YACxD,OAAO,IAAI,CAAC;QACb,CAAC;QAED,2FAA2F;QAC3F,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;QACjC,IAAI,IAAI,CAAC,IAAI,CAAC,UAAU,KAAK,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,UAAU,GAAG,MAAM,EAAE,CAAC;YACjE,OAAO,IAAI,CAAC;QACb,CAAC;QAED,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC;YAClC,MAAM,EAAE,mCAAmC;YAC3C,YAAY,EAAE,KAAK;SACnB,CAAC,CAAC;QAEH,MAAM,CAAC,IAAI,EAAE,SAAS,CAAC,GAAG,MAAM,QAAQ,CAAC,KAAK,CAAC,qCAAqC,EAAE;YACrF,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,cAAc,EAAE,eAAe,EAAE;YAC5C,IAAI,EAAE,MAAM,CAAC,QAAQ,EAAE;SACvB,CAAC,CAAC,CAAC;QACJ,IAAI,SAAS,EAAE,CAAC;YACf,OAAO,IAAI,CAAC;QACb,CAAC;QAED,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;YACd,OAAO,IAAI,CAAC;QACb,CAAC;QAED,MAAM,QAAQ,GAAG,CAAC,MAAM,IAAI,CAAC,IAAI,EAAE,CAAkB,CAAC;QACtD,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YAClB,OAAO,IAAI,CAAC;QACb,CAAC;QAED,OAAO;YACN,QAAQ,EAAE,QAAQ,CAAC,EAAE;YACrB,IAAI,EAAE,QAAQ,CAAC,IAAI,IAAI,WAAW;YAClC,KAAK,EAAE,QAAQ,CAAC,KAAK;YACrB,OAAO,EAAE,QAAQ,CAAC,OAAO,EAAE,IAAI,EAAE,GAAG,IAAI,EAAE;SAC1C,CAAC;IACH,CAAC,CAAA;CACD;AAED;;GAEG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,aAAa,EAAE,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
declare class googleUtils {
|
|
2
|
+
/**
|
|
3
|
+
* Verifies a Google ID token and returns normalized user info.
|
|
4
|
+
* @param jwtToken - Google ID token.
|
|
5
|
+
* @param client_id - OAuth client ID expected in token audience.
|
|
6
|
+
* @returns Normalized user object, or `null` when verification fails.
|
|
7
|
+
*/
|
|
8
|
+
readonly getUserFromJwt: (jwtToken: string, client_id: string) => Promise<{
|
|
9
|
+
name: string;
|
|
10
|
+
email: string | undefined;
|
|
11
|
+
picture: string;
|
|
12
|
+
oauth_id: string;
|
|
13
|
+
} | null>;
|
|
14
|
+
/**
|
|
15
|
+
* Exchanges an authorization code for an ID token, then resolves user info.
|
|
16
|
+
* @param code - Google OAuth authorization code.
|
|
17
|
+
* @param client_id - OAuth client ID.
|
|
18
|
+
* @param client_secret - OAuth client secret.
|
|
19
|
+
* @param redirect_uri - Redirect URI used for the OAuth flow.
|
|
20
|
+
* @returns Normalized user object, or `null` on failure.
|
|
21
|
+
*/
|
|
22
|
+
readonly getUserFromCode: (code: string, client_id: string, client_secret: string, redirect_uri: string) => Promise<{
|
|
23
|
+
name: string | undefined;
|
|
24
|
+
email: string | undefined;
|
|
25
|
+
picture: string | undefined;
|
|
26
|
+
oauth_id: string;
|
|
27
|
+
} | null>;
|
|
28
|
+
/**
|
|
29
|
+
* Exchange Google OAuth authorization code for an ID token.
|
|
30
|
+
* @param code - Google OAuth authorization code.
|
|
31
|
+
* @param client_id - OAuth client ID.
|
|
32
|
+
* @param client_secret - OAuth client secret.
|
|
33
|
+
* @param redirect_uri - Redirect URI used for the OAuth flow.
|
|
34
|
+
* @returns ID token string, or `null` on failure.
|
|
35
|
+
*/
|
|
36
|
+
private readonly jwtFromCode;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Singleton instance of Google OAuth utilities.
|
|
40
|
+
*/
|
|
41
|
+
export declare const GoogleUtils: googleUtils;
|
|
42
|
+
export {};
|
|
43
|
+
//# sourceMappingURL=GoogleUtils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GoogleUtils.d.ts","sourceRoot":"","sources":["../src/GoogleUtils.ts"],"names":[],"mappings":"AA8BA,cAAM,WAAW;IAChB;;;;;OAKG;IACH,QAAQ,CAAC,cAAc,GAAU,UAAU,MAAM,EAAE,WAAW,MAAM,KAAG,OAAO,CAAC;QAC9E,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;QAC1B,OAAO,EAAE,MAAM,CAAC;QAChB,QAAQ,EAAE,MAAM,CAAC;KACjB,GAAG,IAAI,CAAC,CAeR;IAED;;;;;;;OAOG;IACH,QAAQ,CAAC,eAAe,GAAU,MAAM,MAAM,EAAE,WAAW,MAAM,EAAE,eAAe,MAAM,EAAE,cAAc,MAAM,KAAG,OAAO,CAAC;QACxH,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;QACzB,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;QAC1B,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;QAC5B,QAAQ,EAAE,MAAM,CAAC;KACjB,GAAG,IAAI,CAAC,CAIR;IAGD;;;;;;;OAOG;IACH,OAAO,CAAC,QAAQ,CAAC,WAAW,CAqB3B;CACD;AAED;;GAEG;AACH,eAAO,MAAM,WAAW,aAAoB,CAAC"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { createRemoteJWKSet } from "jose/jwks/remote";
|
|
2
|
+
import { tryCatch } from "./tryCatch.js";
|
|
3
|
+
import { jwtVerify } from "jose";
|
|
4
|
+
import { randInt } from "./Rand.js";
|
|
5
|
+
const Google_JWK = createRemoteJWKSet(new URL("https://www.googleapis.com/oauth2/v3/certs"));
|
|
6
|
+
class googleUtils {
|
|
7
|
+
/**
|
|
8
|
+
* Verifies a Google ID token and returns normalized user info.
|
|
9
|
+
* @param jwtToken - Google ID token.
|
|
10
|
+
* @param client_id - OAuth client ID expected in token audience.
|
|
11
|
+
* @returns Normalized user object, or `null` when verification fails.
|
|
12
|
+
*/
|
|
13
|
+
getUserFromJwt = async (jwtToken, client_id) => {
|
|
14
|
+
if (!Google_JWK)
|
|
15
|
+
return null;
|
|
16
|
+
const [data, error] = await tryCatch(jwtVerify(jwtToken, Google_JWK, {
|
|
17
|
+
issuer: "https://accounts.google.com",
|
|
18
|
+
audience: client_id,
|
|
19
|
+
}));
|
|
20
|
+
if (error)
|
|
21
|
+
return null;
|
|
22
|
+
return {
|
|
23
|
+
name: data.payload.name ?? 'Anonymous',
|
|
24
|
+
email: data.payload.email,
|
|
25
|
+
picture: data.payload.picture ?? `/images/avatars/default_${randInt(1, 10)}.svg`,
|
|
26
|
+
oauth_id: data.payload.sub,
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Exchanges an authorization code for an ID token, then resolves user info.
|
|
31
|
+
* @param code - Google OAuth authorization code.
|
|
32
|
+
* @param client_id - OAuth client ID.
|
|
33
|
+
* @param client_secret - OAuth client secret.
|
|
34
|
+
* @param redirect_uri - Redirect URI used for the OAuth flow.
|
|
35
|
+
* @returns Normalized user object, or `null` on failure.
|
|
36
|
+
*/
|
|
37
|
+
getUserFromCode = async (code, client_id, client_secret, redirect_uri) => {
|
|
38
|
+
const id_token = await this.jwtFromCode(code, client_id, client_secret, redirect_uri);
|
|
39
|
+
if (!id_token)
|
|
40
|
+
return null;
|
|
41
|
+
return await this.getUserFromJwt(id_token, client_id);
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Exchange Google OAuth authorization code for an ID token.
|
|
45
|
+
* @param code - Google OAuth authorization code.
|
|
46
|
+
* @param client_id - OAuth client ID.
|
|
47
|
+
* @param client_secret - OAuth client secret.
|
|
48
|
+
* @param redirect_uri - Redirect URI used for the OAuth flow.
|
|
49
|
+
* @returns ID token string, or `null` on failure.
|
|
50
|
+
*/
|
|
51
|
+
jwtFromCode = async (code, client_id, client_secret, redirect_uri) => {
|
|
52
|
+
const [resData, error] = await tryCatch(fetch(`https://oauth2.googleapis.com/token`, {
|
|
53
|
+
method: 'POST',
|
|
54
|
+
headers: {
|
|
55
|
+
'Content-Type': 'application/json',
|
|
56
|
+
'Accept': 'application/json',
|
|
57
|
+
},
|
|
58
|
+
body: JSON.stringify({
|
|
59
|
+
code,
|
|
60
|
+
client_id,
|
|
61
|
+
client_secret,
|
|
62
|
+
redirect_uri,
|
|
63
|
+
grant_type: 'authorization_code'
|
|
64
|
+
})
|
|
65
|
+
}));
|
|
66
|
+
if (error)
|
|
67
|
+
return null;
|
|
68
|
+
if (!resData.ok)
|
|
69
|
+
return null;
|
|
70
|
+
const data = await resData.json();
|
|
71
|
+
if (!data.id_token)
|
|
72
|
+
return null;
|
|
73
|
+
return data.id_token;
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Singleton instance of Google OAuth utilities.
|
|
78
|
+
*/
|
|
79
|
+
export const GoogleUtils = new googleUtils();
|
|
80
|
+
//# sourceMappingURL=GoogleUtils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GoogleUtils.js","sourceRoot":"","sources":["../src/GoogleUtils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,MAAM,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAuBpC,MAAM,UAAU,GAAG,kBAAkB,CACpC,IAAI,GAAG,CAAC,4CAA4C,CAAC,CACrD,CAAC;AAEF,MAAM,WAAW;IAChB;;;;;OAKG;IACM,cAAc,GAAG,KAAK,EAAE,QAAgB,EAAE,SAAiB,EAK1D,EAAE;QACX,IAAI,CAAC,UAAU;YAAE,OAAO,IAAI,CAAC;QAE7B,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,GAAG,MAAM,QAAQ,CAAC,SAAS,CAAoB,QAAQ,EAAE,UAAU,EAAE;YACvF,MAAM,EAAE,6BAA6B;YACrC,QAAQ,EAAE,SAAS;SACnB,CAAC,CAAC,CAAC;QAEJ,IAAI,KAAK;YAAE,OAAO,IAAI,CAAC;QACvB,OAAO;YACN,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,IAAI,IAAI,WAAW;YACtC,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,KAAK;YACzB,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,OAAO,IAAI,2BAA2B,OAAO,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM;YAChF,QAAQ,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG;SAC1B,CAAC;IACH,CAAC,CAAA;IAED;;;;;;;OAOG;IACM,eAAe,GAAG,KAAK,EAAE,IAAY,EAAE,SAAiB,EAAE,aAAqB,EAAE,YAAoB,EAKpG,EAAE;QACX,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,SAAS,EAAE,aAAa,EAAE,YAAY,CAAC,CAAC;QACtF,IAAI,CAAC,QAAQ;YAAE,OAAO,IAAI,CAAC;QAC3B,OAAO,MAAM,IAAI,CAAC,cAAc,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;IACvD,CAAC,CAAA;IAGD;;;;;;;OAOG;IACc,WAAW,GAAG,KAAK,EAAE,IAAY,EAAE,SAAiB,EAAE,aAAqB,EAAE,YAAoB,EAA0B,EAAE;QAC7I,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,GAAG,MAAM,QAAQ,CAAC,KAAK,CAAC,qCAAqC,EAAE;YACpF,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACR,cAAc,EAAE,kBAAkB;gBAClC,QAAQ,EAAE,kBAAkB;aAC5B;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gBACpB,IAAI;gBACJ,SAAS;gBACT,aAAa;gBACb,YAAY;gBACZ,UAAU,EAAE,oBAAoB;aAChC,CAAC;SACF,CAAC,CAAC,CAAC;QAEJ,IAAI,KAAK;YAAE,OAAO,IAAI,CAAC;QACvB,IAAI,CAAC,OAAO,CAAC,EAAE;YAAE,OAAO,IAAI,CAAC;QAC7B,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,IAAI,EAAgC,CAAC;QAChE,IAAI,CAAC,IAAI,CAAC,QAAQ;YAAE,OAAO,IAAI,CAAC;QAChC,OAAO,IAAI,CAAC,QAAQ,CAAC;IACtB,CAAC,CAAA;CACD;AAED;;GAEG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,IAAI,WAAW,EAAE,CAAC"}
|