@friggframework/core 2.0.0--canary.540.4653c0a.0 → 2.0.0--canary.540.d7b5b85.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.
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
const { Module } = require('../module');
|
|
2
2
|
const { ModuleConstants } = require('../ModuleConstants');
|
|
3
|
+
const { getLogger } = require('../../logs');
|
|
4
|
+
|
|
5
|
+
const log = getLogger('frigg.modules.authorization_callback');
|
|
3
6
|
|
|
4
7
|
// Statuses considered "broken" for an integration whose credentials have just
|
|
5
8
|
// been successfully re-authorized. Both ERROR (system-driven auth failure) and
|
|
@@ -28,10 +31,12 @@ class ProcessAuthorizationCallback {
|
|
|
28
31
|
}
|
|
29
32
|
|
|
30
33
|
async execute(userId, entityType, params) {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
34
|
+
log.debug('Authorization callback started', {
|
|
35
|
+
eventName: `${log.name}.started`,
|
|
36
|
+
userId,
|
|
37
|
+
entityType,
|
|
38
|
+
hasCode: Boolean(params && params.code),
|
|
39
|
+
});
|
|
35
40
|
|
|
36
41
|
const moduleDefinition = this.moduleDefinitions.find((def) => {
|
|
37
42
|
return entityType === def.moduleName;
|
|
@@ -58,9 +63,11 @@ class ProcessAuthorizationCallback {
|
|
|
58
63
|
: null;
|
|
59
64
|
|
|
60
65
|
if (existingEntity) {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
66
|
+
log.debug('Existing entity found', {
|
|
67
|
+
eventName: `${log.name}.existing_entity_found`,
|
|
68
|
+
entityId: existingEntity.id,
|
|
69
|
+
credentialId: existingEntity.credential?.id,
|
|
70
|
+
});
|
|
64
71
|
}
|
|
65
72
|
|
|
66
73
|
const module = new Module({
|
|
@@ -70,18 +77,25 @@ class ProcessAuthorizationCallback {
|
|
|
70
77
|
});
|
|
71
78
|
|
|
72
79
|
const authType = module.apiClass.requesterType;
|
|
73
|
-
|
|
80
|
+
log.debug('Module created', {
|
|
81
|
+
eventName: `${log.name}.module_created`,
|
|
82
|
+
moduleName: module.getName(),
|
|
83
|
+
authType,
|
|
84
|
+
});
|
|
74
85
|
|
|
75
86
|
let tokenResponse;
|
|
76
87
|
if (authType === ModuleConstants.authType.oauth2) {
|
|
77
|
-
|
|
88
|
+
log.debug('Exchanging authorization code for token', {
|
|
89
|
+
eventName: `${log.name}.token_exchanging`,
|
|
90
|
+
});
|
|
78
91
|
tokenResponse = await moduleDefinition.requiredAuthMethods.getToken(
|
|
79
92
|
module.api,
|
|
80
93
|
params
|
|
81
94
|
);
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
95
|
+
log.debug('Token exchange completed', {
|
|
96
|
+
eventName: `${log.name}.token_exchanged`,
|
|
97
|
+
tokenKeys: Object.keys(tokenResponse || {}),
|
|
98
|
+
});
|
|
85
99
|
// Belt-and-suspenders: persist tokens explicitly here rather than
|
|
86
100
|
// relying solely on the DLGT_TOKEN_UPDATE notification chain
|
|
87
101
|
// inside setTokens. The notification path remains in place but
|
|
@@ -90,29 +104,35 @@ class ProcessAuthorizationCallback {
|
|
|
90
104
|
// OAuth flow appears to succeed.
|
|
91
105
|
await this.onTokenUpdate(module, moduleDefinition, userId);
|
|
92
106
|
} else {
|
|
93
|
-
|
|
107
|
+
log.debug('Setting auth params', {
|
|
108
|
+
eventName: `${log.name}.auth_params_setting`,
|
|
109
|
+
});
|
|
94
110
|
tokenResponse =
|
|
95
111
|
await moduleDefinition.requiredAuthMethods.setAuthParams(
|
|
96
112
|
module.api,
|
|
97
113
|
params
|
|
98
114
|
);
|
|
99
115
|
await this.onTokenUpdate(module, moduleDefinition, userId);
|
|
100
|
-
console.log(`[Frigg][OAuth] Auth params set and credential persisted`);
|
|
101
116
|
}
|
|
102
117
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
118
|
+
log.debug('Credential persisted', {
|
|
119
|
+
eventName: `${log.name}.credential_persisted`,
|
|
120
|
+
credentialId: module.credential?.id,
|
|
121
|
+
authIsValid: module.credential?.authIsValid,
|
|
122
|
+
});
|
|
106
123
|
|
|
107
|
-
|
|
124
|
+
log.debug('Testing auth', { eventName: `${log.name}.auth_testing` });
|
|
108
125
|
const authRes = await module.testAuth();
|
|
109
126
|
if (!authRes) {
|
|
110
|
-
console.error(`[Frigg][OAuth] testAuth() returned false — authorization failed`);
|
|
111
127
|
throw new Error('Authorization failed');
|
|
112
128
|
}
|
|
113
|
-
|
|
129
|
+
log.debug('Auth test passed', {
|
|
130
|
+
eventName: `${log.name}.auth_test_passed`,
|
|
131
|
+
});
|
|
114
132
|
|
|
115
|
-
|
|
133
|
+
log.debug('Fetching entity details', {
|
|
134
|
+
eventName: `${log.name}.entity_details_fetching`,
|
|
135
|
+
});
|
|
116
136
|
const entityDetails =
|
|
117
137
|
await moduleDefinition.requiredAuthMethods.getEntityDetails(
|
|
118
138
|
module.api,
|
|
@@ -120,20 +140,29 @@ class ProcessAuthorizationCallback {
|
|
|
120
140
|
tokenResponse,
|
|
121
141
|
userId
|
|
122
142
|
);
|
|
123
|
-
|
|
143
|
+
log.debug('Entity details received', {
|
|
144
|
+
eventName: `${log.name}.entity_details_received`,
|
|
145
|
+
externalId: entityDetails.identifiers?.externalId,
|
|
146
|
+
});
|
|
124
147
|
|
|
125
148
|
Object.assign(
|
|
126
149
|
entityDetails.details,
|
|
127
150
|
module.apiParamsFromEntity(module.api)
|
|
128
151
|
);
|
|
129
152
|
|
|
130
|
-
|
|
153
|
+
log.debug('Finding or creating entity', {
|
|
154
|
+
eventName: `${log.name}.entity_resolving`,
|
|
155
|
+
});
|
|
131
156
|
const persistedEntity = await this.findOrCreateEntity(
|
|
132
157
|
entityDetails,
|
|
133
158
|
entityType,
|
|
134
159
|
module.credential.id
|
|
135
160
|
);
|
|
136
|
-
|
|
161
|
+
log.debug('Authorization callback completed', {
|
|
162
|
+
eventName: `${log.name}.completed`,
|
|
163
|
+
entityId: persistedEntity.id,
|
|
164
|
+
credentialId: module.credential.id,
|
|
165
|
+
});
|
|
137
166
|
|
|
138
167
|
// Best-effort: a hiccup here must not fail a successful re-auth whose
|
|
139
168
|
// credential + entity are already persisted. Operators can recover
|
|
@@ -142,14 +171,17 @@ class ProcessAuthorizationCallback {
|
|
|
142
171
|
const restoredCount = await this.restoreIntegrationsForEntity(
|
|
143
172
|
persistedEntity.id
|
|
144
173
|
);
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
174
|
+
log.debug('Integrations restored', {
|
|
175
|
+
eventName: `${log.name}.integrations_restored`,
|
|
176
|
+
entityId: persistedEntity.id,
|
|
177
|
+
restoredCount,
|
|
178
|
+
});
|
|
148
179
|
} catch (err) {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
180
|
+
log.error('Failed to restore integrations after re-auth', {
|
|
181
|
+
eventName: `${log.name}.integrations_restore_failed`,
|
|
182
|
+
entityId: persistedEntity.id,
|
|
183
|
+
error: err,
|
|
184
|
+
});
|
|
153
185
|
}
|
|
154
186
|
|
|
155
187
|
return {
|
|
@@ -168,9 +200,12 @@ class ProcessAuthorizationCallback {
|
|
|
168
200
|
let restored = 0;
|
|
169
201
|
for (const integration of integrations) {
|
|
170
202
|
if (STATUSES_RESET_ON_REAUTH.includes(integration.status)) {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
203
|
+
log.info('Integration restored to ENABLED after re-auth', {
|
|
204
|
+
eventName: `${log.name}.integration_restored`,
|
|
205
|
+
integrationId: integration.id,
|
|
206
|
+
previousStatus: integration.status,
|
|
207
|
+
entityId,
|
|
208
|
+
});
|
|
174
209
|
await this.integrationRepository.updateIntegrationStatus(
|
|
175
210
|
integration.id,
|
|
176
211
|
'ENABLED'
|
|
@@ -232,9 +267,12 @@ class ProcessAuthorizationCallback {
|
|
|
232
267
|
credentialId &&
|
|
233
268
|
String(existingCredentialId) !== String(credentialId)
|
|
234
269
|
) {
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
270
|
+
log.info('Entity credential repointed after re-auth', {
|
|
271
|
+
eventName: `${log.name}.entity_credential_repointed`,
|
|
272
|
+
entityId: existingEntity.id,
|
|
273
|
+
previousCredentialId: existingCredentialId,
|
|
274
|
+
credentialId,
|
|
275
|
+
});
|
|
238
276
|
const updated = await this.moduleRepository.updateEntity(
|
|
239
277
|
existingEntity.id,
|
|
240
278
|
{ credential: credentialId }
|
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--canary.540.
|
|
4
|
+
"version": "2.0.0--canary.540.d7b5b85.0",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@aws-sdk/client-apigatewaymanagementapi": "^3.588.0",
|
|
7
7
|
"@aws-sdk/client-kms": "^3.588.0",
|
|
@@ -47,9 +47,9 @@
|
|
|
47
47
|
}
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
|
-
"@friggframework/eslint-config": "2.0.0--canary.540.
|
|
51
|
-
"@friggframework/prettier-config": "2.0.0--canary.540.
|
|
52
|
-
"@friggframework/test": "2.0.0--canary.540.
|
|
50
|
+
"@friggframework/eslint-config": "2.0.0--canary.540.d7b5b85.0",
|
|
51
|
+
"@friggframework/prettier-config": "2.0.0--canary.540.d7b5b85.0",
|
|
52
|
+
"@friggframework/test": "2.0.0--canary.540.d7b5b85.0",
|
|
53
53
|
"@prisma/client": "^6.19.3",
|
|
54
54
|
"@types/lodash": "4.17.15",
|
|
55
55
|
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
|
@@ -89,5 +89,5 @@
|
|
|
89
89
|
"publishConfig": {
|
|
90
90
|
"access": "public"
|
|
91
91
|
},
|
|
92
|
-
"gitHead": "
|
|
92
|
+
"gitHead": "d7b5b8520dfbc052a0bc49e3566f360ee8fe2af5"
|
|
93
93
|
}
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
const Boom = require('@hapi/boom');
|
|
2
|
+
const { getLogger } = require('../../logs');
|
|
3
|
+
|
|
4
|
+
const log = getLogger('frigg.user.authentication');
|
|
2
5
|
|
|
3
6
|
/**
|
|
4
7
|
* Use case for authenticating a user using multiple authentication strategies.
|
|
@@ -50,18 +53,18 @@ class AuthenticateUser {
|
|
|
50
53
|
const authModes = this.userConfig.authModes || { friggToken: true };
|
|
51
54
|
const appUserId = req.headers['x-frigg-appuserid'];
|
|
52
55
|
const appOrgId = req.headers['x-frigg-apporgid'];
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
);
|
|
56
|
+
log.debug('Request headers received', {
|
|
57
|
+
eventName: `${log.name}.headers_received`,
|
|
58
|
+
headerNames: Object.keys(req.headers),
|
|
59
|
+
});
|
|
57
60
|
|
|
58
61
|
// Priority 1: Shared Secret (backend-to-backend with API key)
|
|
59
62
|
if (authModes.sharedSecret !== false) {
|
|
60
63
|
const apiKey = req.headers['x-frigg-api-key'];
|
|
61
64
|
if (apiKey) {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
);
|
|
65
|
+
log.debug('Attempting shared secret authentication', {
|
|
66
|
+
eventName: `${log.name}.shared_secret_attempted`,
|
|
67
|
+
});
|
|
65
68
|
// Validate the API key (authentication)
|
|
66
69
|
await this.authenticateWithSharedSecret.execute(apiKey);
|
|
67
70
|
// Get user from x-frigg headers (authorization)
|
|
@@ -70,9 +73,9 @@ class AuthenticateUser {
|
|
|
70
73
|
appOrgId
|
|
71
74
|
);
|
|
72
75
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
);
|
|
76
|
+
log.debug('No x-frigg-api-key header, skipping shared secret', {
|
|
77
|
+
eventName: `${log.name}.shared_secret_skipped`,
|
|
78
|
+
});
|
|
76
79
|
}
|
|
77
80
|
|
|
78
81
|
// Priority 2: Adopter JWT (if enabled)
|
|
@@ -80,7 +83,9 @@ class AuthenticateUser {
|
|
|
80
83
|
authModes.adopterJwt === true &&
|
|
81
84
|
req.headers.authorization?.startsWith('Bearer ')
|
|
82
85
|
) {
|
|
83
|
-
|
|
86
|
+
log.debug('Attempting adopter JWT authentication', {
|
|
87
|
+
eventName: `${log.name}.adopter_jwt_attempted`,
|
|
88
|
+
});
|
|
84
89
|
const token = req.headers.authorization.split(' ')[1];
|
|
85
90
|
// Detect JWT format (3 parts separated by dots)
|
|
86
91
|
if (token && token.split('.').length === 3) {
|
|
@@ -95,7 +100,9 @@ class AuthenticateUser {
|
|
|
95
100
|
|
|
96
101
|
// Priority 3: Frigg native token (default)
|
|
97
102
|
if (authModes.friggToken !== false && req.headers.authorization) {
|
|
98
|
-
|
|
103
|
+
log.debug('Attempting Frigg native token authentication', {
|
|
104
|
+
eventName: `${log.name}.frigg_token_attempted`,
|
|
105
|
+
});
|
|
99
106
|
const user = await this.getUserFromBearerToken.execute(
|
|
100
107
|
req.headers.authorization
|
|
101
108
|
);
|