@friggframework/core 2.0.0-next.63 → 2.0.0-next.65
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/modules/requester/oauth-2.js +150 -11
- package/package.json +5 -5
|
@@ -2,37 +2,117 @@ const { Requester } = require('./requester');
|
|
|
2
2
|
const { get } = require('../../assertions');
|
|
3
3
|
const { ModuleConstants } = require('../ModuleConstants');
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* OAuth 2.0 Requester - Base class for API modules using OAuth 2.0 authentication.
|
|
7
|
+
*
|
|
8
|
+
* Supports multiple OAuth 2.0 grant types:
|
|
9
|
+
* - `authorization_code` (default): Standard OAuth flow with user consent
|
|
10
|
+
* - `client_credentials`: Server-to-server authentication without user
|
|
11
|
+
* - `password`: Resource Owner Password Credentials grant
|
|
12
|
+
*
|
|
13
|
+
* @extends Requester
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* // Authorization Code flow (default)
|
|
17
|
+
* const api = new MyApi({ grant_type: 'authorization_code' });
|
|
18
|
+
* const authUrl = api.getAuthorizationUri();
|
|
19
|
+
* // After user authorizes...
|
|
20
|
+
* await api.getTokenFromCode(code);
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* // Client Credentials flow
|
|
24
|
+
* const api = new MyApi({
|
|
25
|
+
* grant_type: 'client_credentials',
|
|
26
|
+
* client_id: process.env.CLIENT_ID,
|
|
27
|
+
* client_secret: process.env.CLIENT_SECRET,
|
|
28
|
+
* audience: 'https://api.example.com',
|
|
29
|
+
* });
|
|
30
|
+
* await api.getTokenFromClientCredentials();
|
|
31
|
+
*/
|
|
5
32
|
class OAuth2Requester extends Requester {
|
|
6
33
|
|
|
7
34
|
static requesterType = ModuleConstants.authType.oauth2;
|
|
8
35
|
|
|
36
|
+
/**
|
|
37
|
+
* Creates an OAuth2Requester instance.
|
|
38
|
+
*
|
|
39
|
+
* @param {Object} params - Configuration parameters
|
|
40
|
+
* @param {string} [params.grant_type='authorization_code'] - OAuth grant type:
|
|
41
|
+
* 'authorization_code', 'client_credentials', or 'password'
|
|
42
|
+
* @param {string} [params.client_id] - OAuth client ID
|
|
43
|
+
* @param {string} [params.client_secret] - OAuth client secret
|
|
44
|
+
* @param {string} [params.redirect_uri] - OAuth redirect URI for authorization code flow
|
|
45
|
+
* @param {string} [params.scope] - OAuth scopes (space-separated)
|
|
46
|
+
* @param {string} [params.authorizationUri] - Authorization endpoint URL
|
|
47
|
+
* @param {string} [params.tokenUri] - Token endpoint URL for exchanging codes/credentials
|
|
48
|
+
* @param {string} [params.baseURL] - Base URL for API requests
|
|
49
|
+
* @param {string} [params.access_token] - Existing access token
|
|
50
|
+
* @param {string} [params.refresh_token] - Existing refresh token
|
|
51
|
+
* @param {Date} [params.accessTokenExpire] - Access token expiration date
|
|
52
|
+
* @param {Date} [params.refreshTokenExpire] - Refresh token expiration date
|
|
53
|
+
* @param {string} [params.audience] - Token audience (for client_credentials)
|
|
54
|
+
* @param {string} [params.username] - Username (for password grant)
|
|
55
|
+
* @param {string} [params.password] - Password (for password grant)
|
|
56
|
+
* @param {string} [params.state] - OAuth state parameter for CSRF protection
|
|
57
|
+
*/
|
|
9
58
|
constructor(params) {
|
|
10
59
|
super(params);
|
|
60
|
+
/** @type {string} Delegate type for token update notifications */
|
|
11
61
|
this.DLGT_TOKEN_UPDATE = 'TOKEN_UPDATE';
|
|
62
|
+
/** @type {string} Delegate type for token deauthorization notifications */
|
|
12
63
|
this.DLGT_TOKEN_DEAUTHORIZED = 'TOKEN_DEAUTHORIZED';
|
|
13
64
|
|
|
14
65
|
this.delegateTypes.push(this.DLGT_TOKEN_UPDATE);
|
|
15
66
|
this.delegateTypes.push(this.DLGT_TOKEN_DEAUTHORIZED);
|
|
16
67
|
|
|
68
|
+
/** @type {string} OAuth grant type */
|
|
17
69
|
this.grant_type = get(params, 'grant_type', 'authorization_code');
|
|
70
|
+
/** @type {string|null} OAuth client ID */
|
|
18
71
|
this.client_id = get(params, 'client_id', null);
|
|
72
|
+
/** @type {string|null} OAuth client secret */
|
|
19
73
|
this.client_secret = get(params, 'client_secret', null);
|
|
74
|
+
/** @type {string|null} OAuth redirect URI */
|
|
20
75
|
this.redirect_uri = get(params, 'redirect_uri', null);
|
|
76
|
+
/** @type {string|null} OAuth scopes */
|
|
21
77
|
this.scope = get(params, 'scope', null);
|
|
78
|
+
/** @type {string|null} Authorization endpoint URL */
|
|
22
79
|
this.authorizationUri = get(params, 'authorizationUri', null);
|
|
80
|
+
/** @type {string|null} Token endpoint URL */
|
|
81
|
+
this.tokenUri = get(params, 'tokenUri', null);
|
|
82
|
+
/** @type {string|null} Base URL for API requests */
|
|
23
83
|
this.baseURL = get(params, 'baseURL', null);
|
|
84
|
+
/** @type {string|null} Current access token */
|
|
24
85
|
this.access_token = get(params, 'access_token', null);
|
|
86
|
+
/** @type {string|null} Current refresh token */
|
|
25
87
|
this.refresh_token = get(params, 'refresh_token', null);
|
|
88
|
+
/** @type {Date|null} Access token expiration */
|
|
26
89
|
this.accessTokenExpire = get(params, 'accessTokenExpire', null);
|
|
90
|
+
/** @type {Date|null} Refresh token expiration */
|
|
27
91
|
this.refreshTokenExpire = get(params, 'refreshTokenExpire', null);
|
|
92
|
+
/** @type {string|null} Token audience */
|
|
28
93
|
this.audience = get(params, 'audience', null);
|
|
94
|
+
/** @type {string|null} Username for password grant */
|
|
29
95
|
this.username = get(params, 'username', null);
|
|
96
|
+
/** @type {string|null} Password for password grant */
|
|
30
97
|
this.password = get(params, 'password', null);
|
|
98
|
+
/** @type {string|null} OAuth state for CSRF protection */
|
|
31
99
|
this.state = get(params, 'state', null);
|
|
32
100
|
|
|
101
|
+
/** @type {boolean} Whether this requester supports token refresh */
|
|
33
102
|
this.isRefreshable = true;
|
|
34
103
|
}
|
|
35
104
|
|
|
105
|
+
/**
|
|
106
|
+
* Sets OAuth tokens and calculates expiration times.
|
|
107
|
+
* Notifies delegates of token update via DLGT_TOKEN_UPDATE.
|
|
108
|
+
*
|
|
109
|
+
* @param {Object} params - Token response from OAuth server
|
|
110
|
+
* @param {string} params.access_token - The access token
|
|
111
|
+
* @param {string} [params.refresh_token] - The refresh token (if provided)
|
|
112
|
+
* @param {number} [params.expires_in] - Access token lifetime in seconds
|
|
113
|
+
* @param {number} [params.x_refresh_token_expires_in] - Refresh token lifetime in seconds
|
|
114
|
+
* @returns {Promise<void>}
|
|
115
|
+
*/
|
|
36
116
|
async setTokens(params) {
|
|
37
117
|
this.access_token = get(params, 'access_token');
|
|
38
118
|
this.refresh_token = get(params, 'refresh_token', null);
|
|
@@ -49,10 +129,20 @@ class OAuth2Requester extends Requester {
|
|
|
49
129
|
await this.notify(this.DLGT_TOKEN_UPDATE);
|
|
50
130
|
}
|
|
51
131
|
|
|
132
|
+
/**
|
|
133
|
+
* Gets the OAuth authorization URL for initiating the authorization code flow.
|
|
134
|
+
*
|
|
135
|
+
* @returns {string|null} The authorization URL
|
|
136
|
+
*/
|
|
52
137
|
getAuthorizationUri() {
|
|
53
138
|
return this.authorizationUri;
|
|
54
139
|
}
|
|
55
140
|
|
|
141
|
+
/**
|
|
142
|
+
* Returns authorization requirements for this OAuth flow.
|
|
143
|
+
*
|
|
144
|
+
* @returns {{url: string|null, type: string}} Authorization requirements
|
|
145
|
+
*/
|
|
56
146
|
getAuthorizationRequirements() {
|
|
57
147
|
return {
|
|
58
148
|
url: this.getAuthorizationUri(),
|
|
@@ -60,8 +150,13 @@ class OAuth2Requester extends Requester {
|
|
|
60
150
|
};
|
|
61
151
|
}
|
|
62
152
|
|
|
63
|
-
|
|
64
|
-
|
|
153
|
+
/**
|
|
154
|
+
* Exchanges an authorization code for access and refresh tokens.
|
|
155
|
+
* Requires client_id, client_secret, redirect_uri, and tokenUri to be set.
|
|
156
|
+
*
|
|
157
|
+
* @param {string} code - The authorization code from the OAuth callback
|
|
158
|
+
* @returns {Promise<Object>} Token response containing access_token, refresh_token, etc.
|
|
159
|
+
*/
|
|
65
160
|
async getTokenFromCode(code) {
|
|
66
161
|
const params = new URLSearchParams();
|
|
67
162
|
params.append('grant_type', 'authorization_code');
|
|
@@ -82,9 +177,14 @@ class OAuth2Requester extends Requester {
|
|
|
82
177
|
return response;
|
|
83
178
|
}
|
|
84
179
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
180
|
+
/**
|
|
181
|
+
* Exchanges an authorization code for tokens using Basic Auth header.
|
|
182
|
+
* Alternative to getTokenFromCode() for OAuth servers requiring Basic Auth.
|
|
183
|
+
* Override getTokenFromCode() in child class to use this instead.
|
|
184
|
+
*
|
|
185
|
+
* @param {string} code - The authorization code from the OAuth callback
|
|
186
|
+
* @returns {Promise<Object>} Token response containing access_token, refresh_token, etc.
|
|
187
|
+
*/
|
|
88
188
|
async getTokenFromCodeBasicAuthHeader(code) {
|
|
89
189
|
const params = new URLSearchParams();
|
|
90
190
|
params.append('grant_type', 'authorization_code');
|
|
@@ -108,8 +208,14 @@ class OAuth2Requester extends Requester {
|
|
|
108
208
|
return response;
|
|
109
209
|
}
|
|
110
210
|
|
|
111
|
-
|
|
112
|
-
|
|
211
|
+
/**
|
|
212
|
+
* Refreshes the access token using the refresh token.
|
|
213
|
+
* Used for authorization_code and password grant types.
|
|
214
|
+
*
|
|
215
|
+
* @param {Object} refreshTokenObject - Object containing refresh_token
|
|
216
|
+
* @param {string} refreshTokenObject.refresh_token - The refresh token
|
|
217
|
+
* @returns {Promise<Object>} New token response
|
|
218
|
+
*/
|
|
113
219
|
async refreshAccessToken(refreshTokenObject) {
|
|
114
220
|
this.access_token = undefined;
|
|
115
221
|
const params = new URLSearchParams();
|
|
@@ -131,6 +237,12 @@ class OAuth2Requester extends Requester {
|
|
|
131
237
|
return response;
|
|
132
238
|
}
|
|
133
239
|
|
|
240
|
+
/**
|
|
241
|
+
* Adds OAuth Bearer token to request headers.
|
|
242
|
+
*
|
|
243
|
+
* @param {Object} headers - Headers object to modify
|
|
244
|
+
* @returns {Promise<Object>} Headers with Authorization added
|
|
245
|
+
*/
|
|
134
246
|
async addAuthHeaders(headers) {
|
|
135
247
|
if (this.access_token) {
|
|
136
248
|
headers.Authorization = `Bearer ${this.access_token}`;
|
|
@@ -139,18 +251,32 @@ class OAuth2Requester extends Requester {
|
|
|
139
251
|
return headers;
|
|
140
252
|
}
|
|
141
253
|
|
|
254
|
+
/**
|
|
255
|
+
* Checks if the requester has valid authentication.
|
|
256
|
+
*
|
|
257
|
+
* @returns {boolean} True if authenticated with valid tokens
|
|
258
|
+
*/
|
|
142
259
|
isAuthenticated() {
|
|
143
|
-
return (
|
|
144
|
-
this.
|
|
145
|
-
this.
|
|
260
|
+
return !!(
|
|
261
|
+
this.access_token !== null &&
|
|
262
|
+
this.refresh_token !== null &&
|
|
146
263
|
this.accessTokenExpire &&
|
|
147
264
|
this.refreshTokenExpire
|
|
148
265
|
);
|
|
149
266
|
}
|
|
150
267
|
|
|
268
|
+
/**
|
|
269
|
+
* Refreshes authentication based on the configured grant type.
|
|
270
|
+
* - For authorization_code/password: Uses refreshAccessToken() with refresh_token
|
|
271
|
+
* - For client_credentials: Uses getTokenFromClientCredentials() to get new token
|
|
272
|
+
*
|
|
273
|
+
* On failure, notifies delegates via DLGT_INVALID_AUTH.
|
|
274
|
+
*
|
|
275
|
+
* @returns {Promise<void>}
|
|
276
|
+
*/
|
|
151
277
|
async refreshAuth() {
|
|
152
278
|
try {
|
|
153
|
-
if (this.
|
|
279
|
+
if (this.grant_type !== 'client_credentials') {
|
|
154
280
|
await this.refreshAccessToken({
|
|
155
281
|
refresh_token: this.refresh_token,
|
|
156
282
|
});
|
|
@@ -162,6 +288,12 @@ class OAuth2Requester extends Requester {
|
|
|
162
288
|
}
|
|
163
289
|
}
|
|
164
290
|
|
|
291
|
+
/**
|
|
292
|
+
* Obtains tokens using the Resource Owner Password Credentials grant.
|
|
293
|
+
* Requires username and password to be set.
|
|
294
|
+
*
|
|
295
|
+
* @returns {Promise<Object|undefined>} Token response or undefined on error
|
|
296
|
+
*/
|
|
165
297
|
async getTokenFromUsernamePassword() {
|
|
166
298
|
try {
|
|
167
299
|
const url = this.tokenUri;
|
|
@@ -188,6 +320,13 @@ class OAuth2Requester extends Requester {
|
|
|
188
320
|
}
|
|
189
321
|
}
|
|
190
322
|
|
|
323
|
+
/**
|
|
324
|
+
* Obtains tokens using the Client Credentials grant.
|
|
325
|
+
* Used for server-to-server authentication without a user context.
|
|
326
|
+
* Requires client_id, client_secret, and optionally audience to be set.
|
|
327
|
+
*
|
|
328
|
+
* @returns {Promise<Object|undefined>} Token response or undefined on error
|
|
329
|
+
*/
|
|
191
330
|
async getTokenFromClientCredentials() {
|
|
192
331
|
try {
|
|
193
332
|
const url = this.tokenUri;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@friggframework/core",
|
|
3
3
|
"prettier": "@friggframework/prettier-config",
|
|
4
|
-
"version": "2.0.0-next.
|
|
4
|
+
"version": "2.0.0-next.65",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@aws-sdk/client-apigatewaymanagementapi": "^3.588.0",
|
|
7
7
|
"@aws-sdk/client-kms": "^3.588.0",
|
|
@@ -38,9 +38,9 @@
|
|
|
38
38
|
}
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
|
-
"@friggframework/eslint-config": "2.0.0-next.
|
|
42
|
-
"@friggframework/prettier-config": "2.0.0-next.
|
|
43
|
-
"@friggframework/test": "2.0.0-next.
|
|
41
|
+
"@friggframework/eslint-config": "2.0.0-next.65",
|
|
42
|
+
"@friggframework/prettier-config": "2.0.0-next.65",
|
|
43
|
+
"@friggframework/test": "2.0.0-next.65",
|
|
44
44
|
"@prisma/client": "^6.17.0",
|
|
45
45
|
"@types/lodash": "4.17.15",
|
|
46
46
|
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
|
@@ -80,5 +80,5 @@
|
|
|
80
80
|
"publishConfig": {
|
|
81
81
|
"access": "public"
|
|
82
82
|
},
|
|
83
|
-
"gitHead": "
|
|
83
|
+
"gitHead": "a2f6c130dc7c783486a6ef03a262d2ee8dea52f9"
|
|
84
84
|
}
|