@friggframework/core 2.0.0-next.64 → 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.
@@ -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
- // this.client_id, this.client_secret, this.redirect_uri, and this.tokenUri
64
- // will need to be defined in the child class before super(params)
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
- // REPLACE getTokenFromCode IN THE CHILD IF NEEDED
86
- // this.client_id, this.client_secret, this.redirect_uri, and this.tokenUri
87
- // will need to be defined in the child class before super(params)
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
- // this.client_id, this.client_secret, this.redirect_uri, and this.tokenUri
112
- // will need to be defined in the child class before super(params)
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.accessToken !== null &&
145
- this.refreshToken !== null &&
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.grantType !== 'client_credentials') {
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.64",
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.64",
42
- "@friggframework/prettier-config": "2.0.0-next.64",
43
- "@friggframework/test": "2.0.0-next.64",
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": "d7e3b2b756a0a5db13bcc4322de99c29910c4d37"
83
+ "gitHead": "a2f6c130dc7c783486a6ef03a262d2ee8dea52f9"
84
84
  }