@nya-account/node-sdk 2.0.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) 2025 Neko Along
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,172 @@
1
+ # @nya-account/node-sdk
2
+
3
+ Official Node.js SDK for [Nya Account](https://github.com/alongw/sso) SSO system.
4
+
5
+ Provides a complete OAuth 2.1 / OIDC client with PKCE, JWT verification, and Express middleware.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npm install @nya-account/node-sdk
11
+ # or
12
+ pnpm add @nya-account/node-sdk
13
+ # or
14
+ yarn add @nya-account/node-sdk
15
+ ```
16
+
17
+ ## Quick Start
18
+
19
+ ```typescript
20
+ import { NyaAccountClient } from '@nya-account/node-sdk'
21
+
22
+ const client = new NyaAccountClient({
23
+ issuer: 'https://account.example.com',
24
+ clientId: 'my-app',
25
+ clientSecret: 'my-secret',
26
+ })
27
+
28
+ // Create authorization URL (with PKCE)
29
+ const { url, codeVerifier, state } = await client.createAuthorizationUrl({
30
+ redirectUri: 'https://myapp.com/callback',
31
+ scope: 'openid profile email',
32
+ })
33
+
34
+ // Exchange code for tokens
35
+ const tokens = await client.exchangeCode({
36
+ code: callbackCode,
37
+ redirectUri: 'https://myapp.com/callback',
38
+ codeVerifier,
39
+ })
40
+
41
+ // Get user info
42
+ const userInfo = await client.getUserInfo(tokens.accessToken)
43
+ ```
44
+
45
+ ## Express Middleware
46
+
47
+ ```typescript
48
+ import express from 'express'
49
+ import { NyaAccountClient } from '@nya-account/node-sdk'
50
+ import { getAuth } from '@nya-account/node-sdk/express'
51
+
52
+ const app = express()
53
+ const client = new NyaAccountClient({
54
+ issuer: 'https://account.example.com',
55
+ clientId: 'my-app',
56
+ clientSecret: 'my-secret',
57
+ })
58
+
59
+ // Protect all /api routes
60
+ app.use('/api', client.authenticate())
61
+
62
+ app.get('/api/me', (req, res) => {
63
+ const auth = getAuth(req)
64
+ res.json({ userId: auth?.sub, scopes: auth?.scope })
65
+ })
66
+
67
+ // Require specific scopes
68
+ app.get('/api/profile',
69
+ client.authenticate(),
70
+ client.requireScopes('profile'),
71
+ (req, res) => {
72
+ const auth = getAuth(req)
73
+ res.json({ name: auth?.sub })
74
+ }
75
+ )
76
+
77
+ // Use introspection for sensitive operations
78
+ app.post('/api/sensitive',
79
+ client.authenticate({ strategy: 'introspection' }),
80
+ handler
81
+ )
82
+ ```
83
+
84
+ ## Configuration
85
+
86
+ | Option | Type | Default | Description |
87
+ |---|---|---|---|
88
+ | `issuer` | `string` | *required* | SSO service URL (Issuer URL) |
89
+ | `clientId` | `string` | *required* | OAuth client ID |
90
+ | `clientSecret` | `string` | *required* | OAuth client secret |
91
+ | `timeout` | `number` | `10000` | HTTP request timeout in milliseconds |
92
+ | `discoveryCacheTtl` | `number` | `3600000` | Discovery document cache TTL in milliseconds (default: 1 hour) |
93
+ | `endpoints` | `EndpointConfig` | — | Explicitly specify endpoint URLs (auto-discovered via OIDC Discovery if omitted) |
94
+
95
+ ## API Reference
96
+
97
+ ### `NyaAccountClient`
98
+
99
+ #### Authorization
100
+
101
+ - **`createAuthorizationUrl(options)`** — Create an OAuth authorization URL with PKCE
102
+
103
+ #### Token Operations
104
+
105
+ - **`exchangeCode(options)`** — Exchange an authorization code for tokens
106
+ - **`refreshToken(refreshToken)`** — Refresh an Access Token
107
+ - **`revokeToken(token)`** — Revoke a token (RFC 7009)
108
+ - **`introspectToken(token)`** — Token introspection (RFC 7662)
109
+
110
+ #### User Info
111
+
112
+ - **`getUserInfo(accessToken)`** — Get user info via OIDC UserInfo endpoint
113
+
114
+ #### JWT Verification
115
+
116
+ - **`verifyAccessToken(token, options?)`** — Locally verify a JWT Access Token (RFC 9068)
117
+ - **`verifyIdToken(token, options?)`** — Locally verify an OIDC ID Token
118
+
119
+ #### Express Middleware
120
+
121
+ - **`authenticate(options?)`** — Middleware to verify Bearer Token (`local` or `introspection` strategy)
122
+ - **`requireScopes(...scopes)`** — Middleware to validate token scopes
123
+
124
+ #### Cache
125
+
126
+ - **`discover()`** — Fetch OIDC Discovery document (cached with TTL)
127
+ - **`clearCache()`** — Clear Discovery and JWT verifier cache
128
+
129
+ ### Express Helpers
130
+
131
+ Available from `@nya-account/node-sdk/express`:
132
+
133
+ - **`getAuth(req)`** — Retrieve the verified Access Token payload from a request
134
+ - **`extractBearerToken(req)`** — Extract Bearer token from the Authorization header
135
+ - **`sendOAuthError(res, statusCode, error, errorDescription)`** — Send an OAuth-standard error response
136
+
137
+ ### PKCE Utilities
138
+
139
+ - **`generatePkce()`** — Generate a code_verifier and code_challenge pair
140
+ - **`generateCodeVerifier()`** — Generate a PKCE code_verifier
141
+ - **`generateCodeChallenge(codeVerifier)`** — Generate an S256 code_challenge
142
+
143
+ ## Error Handling
144
+
145
+ The SDK provides typed error classes:
146
+
147
+ ```typescript
148
+ import {
149
+ NyaAccountError, // Base error class
150
+ OAuthError, // OAuth protocol errors from the server
151
+ TokenVerificationError, // JWT verification failures
152
+ DiscoveryError, // OIDC Discovery failures
153
+ } from '@nya-account/node-sdk'
154
+
155
+ try {
156
+ await client.verifyAccessToken(token)
157
+ } catch (error) {
158
+ if (error instanceof TokenVerificationError) {
159
+ console.log(error.code) // 'token_verification_failed'
160
+ console.log(error.description) // Human-readable description
161
+ }
162
+ }
163
+ ```
164
+
165
+ ## Requirements
166
+
167
+ - Node.js >= 20.0.0
168
+ - Express 4.x or 5.x (optional, for middleware features)
169
+
170
+ ## License
171
+
172
+ [MIT](./LICENSE)
@@ -0,0 +1,83 @@
1
+ //#region src/middleware/express.ts
2
+ /**
3
+
4
+ * Global symbol key for storing auth payload on request objects.
5
+
6
+ * Using `Symbol.for()` ensures the same symbol is shared across
7
+
8
+ * multiple bundle entry points (index.js and express.js).
9
+
10
+ */
11
+ const AUTH_KEY = Symbol.for("@nya-account/auth-payload");
12
+ /**
13
+
14
+ * Retrieve the verified Access Token payload from a request object.
15
+
16
+ *
17
+
18
+ * Must be used together with `client.authenticate()` middleware.
19
+
20
+ *
21
+
22
+ * @example
23
+
24
+ * ```typescript
25
+
26
+ * app.get('/api/me', (req, res) => {
27
+
28
+ * const auth = getAuth(req)
29
+
30
+ * if (auth) {
31
+
32
+ * res.json({ userId: auth.sub })
33
+
34
+ * }
35
+
36
+ * })
37
+
38
+ * ```
39
+
40
+ */
41
+ function getAuth(req) {
42
+ return Object.getOwnPropertyDescriptor(req, AUTH_KEY)?.value;
43
+ }
44
+ /**
45
+
46
+ * Store the auth payload on a request object (SDK internal use).
47
+
48
+ */
49
+ function setAuth(req, payload) {
50
+ Object.defineProperty(req, AUTH_KEY, {
51
+ value: payload,
52
+ configurable: true,
53
+ enumerable: false,
54
+ writable: false
55
+ });
56
+ }
57
+ /**
58
+
59
+ * Extract Bearer token from the Authorization request header.
60
+
61
+ */
62
+ function extractBearerToken(req) {
63
+ const header = req.headers.authorization;
64
+ if (!header) return null;
65
+ const parts = header.split(" ");
66
+ if (parts.length !== 2 || parts[0] !== "Bearer") return null;
67
+ return parts[1] ?? null;
68
+ }
69
+ /**
70
+
71
+ * Send an OAuth-standard error response.
72
+
73
+ */
74
+ function sendOAuthError(res, statusCode, error, errorDescription) {
75
+ res.status(statusCode).json({
76
+ error,
77
+ error_description: errorDescription
78
+ });
79
+ }
80
+
81
+ //#endregion
82
+ export { extractBearerToken, getAuth, sendOAuthError, setAuth };
83
+ //# sourceMappingURL=express-BHHzodXb.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"express-BHHzodXb.js","names":["req: object","payload: AccessTokenPayload","req: Request","res: Response","statusCode: number","error: string","errorDescription: string"],"sources":["../src/middleware/express.ts"],"sourcesContent":["import type { Request, Response } from 'express'\r\nimport type { AccessTokenPayload } from '@/core/schemas'\r\n\r\n/**\r\n * Global symbol key for storing auth payload on request objects.\r\n * Using `Symbol.for()` ensures the same symbol is shared across\r\n * multiple bundle entry points (index.js and express.js).\r\n */\r\nconst AUTH_KEY = Symbol.for('@nya-account/auth-payload')\r\n\r\n/**\r\n * Retrieve the verified Access Token payload from a request object.\r\n *\r\n * Must be used together with `client.authenticate()` middleware.\r\n *\r\n * @example\r\n * ```typescript\r\n * app.get('/api/me', (req, res) => {\r\n * const auth = getAuth(req)\r\n * if (auth) {\r\n * res.json({ userId: auth.sub })\r\n * }\r\n * })\r\n * ```\r\n */\r\nexport function getAuth(req: object): AccessTokenPayload | undefined {\r\n return Object.getOwnPropertyDescriptor(req, AUTH_KEY)?.value\r\n}\r\n\r\n/**\r\n * Store the auth payload on a request object (SDK internal use).\r\n */\r\nexport function setAuth(req: object, payload: AccessTokenPayload): void {\r\n Object.defineProperty(req, AUTH_KEY, {\r\n value: payload,\r\n configurable: true,\r\n enumerable: false,\r\n writable: false,\r\n })\r\n}\r\n\r\n/**\r\n * Extract Bearer token from the Authorization request header.\r\n */\r\nexport function extractBearerToken(req: Request): string | null {\r\n const header = req.headers.authorization\r\n if (!header) return null\r\n\r\n const parts = header.split(' ')\r\n if (parts.length !== 2 || parts[0] !== 'Bearer') return null\r\n\r\n return parts[1] ?? null\r\n}\r\n\r\n/**\r\n * Send an OAuth-standard error response.\r\n */\r\nexport function sendOAuthError(\r\n res: Response,\r\n statusCode: number,\r\n error: string,\r\n errorDescription: string,\r\n): void {\r\n res.status(statusCode).json({\r\n error,\r\n error_description: errorDescription,\r\n })\r\n}\r\n"],"mappings":";;;;;;;;;;AAQA,MAAM,WAAW,OAAO,IAAI,4BAA4B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiBxD,SAAgB,QAAQA,KAA6C;AACjE,QAAO,OAAO,yBAAyB,KAAK,SAAS,EAAE;AAC1D;;;;;;AAKD,SAAgB,QAAQA,KAAaC,SAAmC;AACpE,QAAO,eAAe,KAAK,UAAU;EACjC,OAAO;EACP,cAAc;EACd,YAAY;EACZ,UAAU;CACb,EAAC;AACL;;;;;;AAKD,SAAgB,mBAAmBC,KAA6B;CAC5D,MAAM,SAAS,IAAI,QAAQ;AAC3B,MAAK,OAAQ,QAAO;CAEpB,MAAM,QAAQ,OAAO,MAAM,IAAI;AAC/B,KAAI,MAAM,WAAW,KAAK,MAAM,OAAO,SAAU,QAAO;AAExD,QAAO,MAAM,MAAM;AACtB;;;;;;AAKD,SAAgB,eACZC,KACAC,YACAC,OACAC,kBACI;AACJ,KAAI,OAAO,WAAW,CAAC,KAAK;EACxB;EACA,mBAAmB;CACtB,EAAC;AACL"}
@@ -0,0 +1,118 @@
1
+ import { z } from "zod";
2
+ import { Request, Response } from "express";
3
+
4
+ //#region src/core/schemas.d.ts
5
+
6
+ declare const AccessTokenPayloadSchema: z.ZodObject<{
7
+ iss: z.ZodString;
8
+ sub: z.ZodString;
9
+ aud: z.ZodString;
10
+ scope: z.ZodString;
11
+ ver: z.ZodString;
12
+ iat: z.ZodNumber;
13
+ exp: z.ZodNumber;
14
+ jti: z.ZodString;
15
+ cnf: z.ZodOptional<z.ZodObject<{
16
+ jkt: z.ZodString;
17
+ }, "strip", z.ZodTypeAny, {
18
+ jkt: string;
19
+ }, {
20
+ jkt: string;
21
+ }>>;
22
+ }, "strip", z.ZodTypeAny, {
23
+ scope: string;
24
+ exp: number;
25
+ iat: number;
26
+ sub: string;
27
+ aud: string;
28
+ iss: string;
29
+ jti: string;
30
+ ver: string;
31
+ cnf?: {
32
+ jkt: string;
33
+ } | undefined;
34
+ }, {
35
+ scope: string;
36
+ exp: number;
37
+ iat: number;
38
+ sub: string;
39
+ aud: string;
40
+ iss: string;
41
+ jti: string;
42
+ ver: string;
43
+ cnf?: {
44
+ jkt: string;
45
+ } | undefined;
46
+ }>;
47
+ type AccessTokenPayload = z.infer<typeof AccessTokenPayloadSchema>;
48
+ declare const IdTokenPayloadSchema: z.ZodObject<{
49
+ iss: z.ZodString;
50
+ sub: z.ZodString;
51
+ aud: z.ZodString;
52
+ iat: z.ZodNumber;
53
+ exp: z.ZodNumber;
54
+ nonce: z.ZodOptional<z.ZodString>;
55
+ name: z.ZodOptional<z.ZodString>;
56
+ preferred_username: z.ZodOptional<z.ZodString>;
57
+ email: z.ZodOptional<z.ZodString>;
58
+ email_verified: z.ZodOptional<z.ZodBoolean>;
59
+ updated_at: z.ZodOptional<z.ZodNumber>;
60
+ }, "strip", z.ZodTypeAny, {
61
+ exp: number;
62
+ iat: number;
63
+ sub: string;
64
+ aud: string;
65
+ iss: string;
66
+ name?: string | undefined;
67
+ preferred_username?: string | undefined;
68
+ email?: string | undefined;
69
+ email_verified?: boolean | undefined;
70
+ updated_at?: number | undefined;
71
+ nonce?: string | undefined;
72
+ }, {
73
+ exp: number;
74
+ iat: number;
75
+ sub: string;
76
+ aud: string;
77
+ iss: string;
78
+ name?: string | undefined;
79
+ preferred_username?: string | undefined;
80
+ email?: string | undefined;
81
+ email_verified?: boolean | undefined;
82
+ updated_at?: number | undefined;
83
+ nonce?: string | undefined;
84
+ }>;
85
+ type IdTokenPayload = z.infer<typeof IdTokenPayloadSchema>; //#endregion
86
+ //#region src/middleware/express.d.ts
87
+ /**
88
+ * Retrieve the verified Access Token payload from a request object.
89
+ *
90
+ * Must be used together with `client.authenticate()` middleware.
91
+ *
92
+ * @example
93
+ * ```typescript
94
+ * app.get('/api/me', (req, res) => {
95
+ * const auth = getAuth(req)
96
+ * if (auth) {
97
+ * res.json({ userId: auth.sub })
98
+ * }
99
+ * })
100
+ * ```
101
+ */
102
+ declare function getAuth(req: object): AccessTokenPayload | undefined;
103
+ /**
104
+ * Store the auth payload on a request object (SDK internal use).
105
+ */
106
+
107
+ /**
108
+ * Extract Bearer token from the Authorization request header.
109
+ */
110
+ declare function extractBearerToken(req: Request): string | null;
111
+ /**
112
+ * Send an OAuth-standard error response.
113
+ */
114
+ declare function sendOAuthError(res: Response, statusCode: number, error: string, errorDescription: string): void;
115
+
116
+ //#endregion
117
+ export { AccessTokenPayload, IdTokenPayload, extractBearerToken as extractBearerToken$1, getAuth as getAuth$1, sendOAuthError as sendOAuthError$1 };
118
+ //# sourceMappingURL=express-yO7hxKKd.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"express-yO7hxKKd.d.ts","names":[],"sources":["../src/core/schemas.d.ts","../src/middleware/express.d.ts"],"sourcesContent":null,"mappings":";;;;AAWA,IAAW,2BAAW;CAAA;CAAA,MAAA,EAAA;CAAA,MAAA,EAAA;CAAA,MAAA,EAAA;CAAA,MAAA,EAAA;CAAA,MAAA,EAAA;CAAA,MAAA,EAAA;CAAA,MAAA,EAAA;CAAA,MAAA,EAAA;CAAA,MAAA,EAAA;CAAA,MAAA,EAAA;CAAA,MAAA,EAAA;CAAA,MAAA,EAAA;CAAA,MAAA,EAAA;CAAA,MAAA,EAAA;AAAA;AACtB,IAAW,qBAAc;CAAA;CAAA,MAAA;CAAA,MAAA,EAAA;AAAA;AACzB,IAAW,uBAAM;CAAA;CAAA,MAAA,EAAA;CAAA,MAAA,EAAA;CAAA,MAAA,EAAA;CAAA,MAAA,EAAA;CAAA,MAAA,EAAA;CAAA,MAAA,EAAA;CAAA,MAAA,EAAA;CAAA,MAAA,EAAA;CAAA,MAAA,EAAA;CAAA,MAAA,EAAA;CAAA,MAAA,EAAA;CAAA,MAAA,EAAA;CAAA,MAAA,EAAA;CAAA,MAAA,EAAA;CAAA,MAAA,EAAA;CAAA,MAAA,EAAA;CAAA,MAAA,EAAA;CAAA,MAAA,EAAA;CAAA,MAAA,EAAA;AAAA;AACjB,IAAW,iBAAiB;CAAC;CAAI,MAAA;CAAA,MAAA,EAAA;AAAA;;;;;;;;;;;;;;;;;;;ACGjC,IAAW,UAAU,CAAC,GAAG,MAAM,kBAAmB;;;;AAQlD,IAAW,qBAAqB,CAAC,GAAG,MAAM,OAAQ;;;;AAIlD,IAAW,iBAAiB,CAAC,GAAG,MAAM,QAAS"}
@@ -0,0 +1,2 @@
1
+ import { extractBearerToken$1 as extractBearerToken, getAuth$1 as getAuth, sendOAuthError$1 as sendOAuthError } from "./express-yO7hxKKd.js";
2
+ export { extractBearerToken, getAuth, sendOAuthError };
@@ -0,0 +1,3 @@
1
+ import { extractBearerToken, getAuth, sendOAuthError } from "./express-BHHzodXb.js";
2
+
3
+ export { extractBearerToken, getAuth, sendOAuthError };