@microsoft/agents-hosting 1.5.0-beta.5.g18f3420031 → 1.5.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.
Files changed (157) hide show
  1. package/dist/package.json +10 -9
  2. package/dist/src/activityHandler.js +2 -2
  3. package/dist/src/activityHandler.js.map +1 -1
  4. package/dist/src/agent-client/agentClient.js +49 -40
  5. package/dist/src/agent-client/agentClient.js.map +1 -1
  6. package/dist/src/agent-client/agentResponseHandler.js +2 -2
  7. package/dist/src/agent-client/agentResponseHandler.js.map +1 -1
  8. package/dist/src/app/agentApplication.d.ts +36 -10
  9. package/dist/src/app/agentApplication.js +169 -99
  10. package/dist/src/app/agentApplication.js.map +1 -1
  11. package/dist/src/app/agentApplicationBuilder.d.ts +15 -0
  12. package/dist/src/app/agentApplicationBuilder.js +22 -4
  13. package/dist/src/app/agentApplicationBuilder.js.map +1 -1
  14. package/dist/src/app/agentApplicationOptions.d.ts +38 -0
  15. package/dist/src/app/attachmentDownloader.js +2 -2
  16. package/dist/src/app/attachmentDownloader.js.map +1 -1
  17. package/dist/src/app/auth/authorization.js +12 -9
  18. package/dist/src/app/auth/authorization.js.map +1 -1
  19. package/dist/src/app/auth/authorizationManager.d.ts +18 -5
  20. package/dist/src/app/auth/authorizationManager.js +258 -45
  21. package/dist/src/app/auth/authorizationManager.js.map +1 -1
  22. package/dist/src/app/auth/handlerStorage.js +3 -1
  23. package/dist/src/app/auth/handlerStorage.js.map +1 -1
  24. package/dist/src/app/auth/handlers/agenticAuthorization.d.ts +19 -16
  25. package/dist/src/app/auth/handlers/agenticAuthorization.js +46 -52
  26. package/dist/src/app/auth/handlers/agenticAuthorization.js.map +1 -1
  27. package/dist/src/app/auth/handlers/azureBotAuthorization.d.ts +51 -75
  28. package/dist/src/app/auth/handlers/azureBotAuthorization.js +217 -192
  29. package/dist/src/app/auth/handlers/azureBotAuthorization.js.map +1 -1
  30. package/dist/src/app/auth/types.d.ts +100 -1
  31. package/dist/src/app/auth/utils.d.ts +10 -0
  32. package/dist/src/app/auth/utils.js +21 -0
  33. package/dist/src/app/auth/utils.js.map +1 -0
  34. package/dist/src/app/index.d.ts +1 -0
  35. package/dist/src/app/index.js +1 -0
  36. package/dist/src/app/index.js.map +1 -1
  37. package/dist/src/app/proactive/conversation.d.ts +43 -0
  38. package/dist/src/app/proactive/conversation.js +67 -0
  39. package/dist/src/app/proactive/conversation.js.map +1 -0
  40. package/dist/src/app/proactive/conversationBuilder.d.ts +54 -0
  41. package/dist/src/app/proactive/conversationBuilder.js +110 -0
  42. package/dist/src/app/proactive/conversationBuilder.js.map +1 -0
  43. package/dist/src/app/proactive/conversationReferenceBuilder.d.ts +68 -0
  44. package/dist/src/app/proactive/conversationReferenceBuilder.js +125 -0
  45. package/dist/src/app/proactive/conversationReferenceBuilder.js.map +1 -0
  46. package/dist/src/app/proactive/createConversationOptions.d.ts +30 -0
  47. package/dist/src/app/proactive/createConversationOptions.js +10 -0
  48. package/dist/src/app/proactive/createConversationOptions.js.map +1 -0
  49. package/dist/src/app/proactive/createConversationOptionsBuilder.d.ts +69 -0
  50. package/dist/src/app/proactive/createConversationOptionsBuilder.js +141 -0
  51. package/dist/src/app/proactive/createConversationOptionsBuilder.js.map +1 -0
  52. package/dist/src/app/proactive/index.d.ts +7 -0
  53. package/dist/src/app/proactive/index.js +26 -0
  54. package/dist/src/app/proactive/index.js.map +1 -0
  55. package/dist/src/app/proactive/proactive.d.ts +248 -0
  56. package/dist/src/app/proactive/proactive.js +310 -0
  57. package/dist/src/app/proactive/proactive.js.map +1 -0
  58. package/dist/src/app/proactive/proactiveOptions.d.ts +19 -0
  59. package/dist/src/app/proactive/proactiveOptions.js +5 -0
  60. package/dist/src/app/proactive/proactiveOptions.js.map +1 -0
  61. package/dist/src/app/streaming/streamingResponse.js +2 -2
  62. package/dist/src/app/streaming/streamingResponse.js.map +1 -1
  63. package/dist/src/app/teamsAttachmentDownloader.js +2 -2
  64. package/dist/src/app/teamsAttachmentDownloader.js.map +1 -1
  65. package/dist/src/app/turnState.js +2 -2
  66. package/dist/src/app/turnState.js.map +1 -1
  67. package/dist/src/auth/authConfiguration.d.ts +61 -0
  68. package/dist/src/auth/authConfiguration.js +52 -3
  69. package/dist/src/auth/authConfiguration.js.map +1 -1
  70. package/dist/src/auth/jwt-middleware.js +2 -2
  71. package/dist/src/auth/jwt-middleware.js.map +1 -1
  72. package/dist/src/auth/msalConnectionManager.js +20 -0
  73. package/dist/src/auth/msalConnectionManager.js.map +1 -1
  74. package/dist/src/auth/msalTokenCredential.js +3 -0
  75. package/dist/src/auth/msalTokenCredential.js.map +1 -1
  76. package/dist/src/auth/msalTokenProvider.js +136 -110
  77. package/dist/src/auth/msalTokenProvider.js.map +1 -1
  78. package/dist/src/baseAdapter.js +2 -2
  79. package/dist/src/baseAdapter.js.map +1 -1
  80. package/dist/src/cloudAdapter.js +201 -154
  81. package/dist/src/cloudAdapter.js.map +1 -1
  82. package/dist/src/connector-client/connectorClient.js +176 -127
  83. package/dist/src/connector-client/connectorClient.js.map +1 -1
  84. package/dist/src/errorHelper.js +108 -0
  85. package/dist/src/errorHelper.js.map +1 -1
  86. package/dist/src/middlewareSet.js +2 -2
  87. package/dist/src/middlewareSet.js.map +1 -1
  88. package/dist/src/oauth/userTokenClient.js +78 -48
  89. package/dist/src/oauth/userTokenClient.js.map +1 -1
  90. package/dist/src/observability/index.d.ts +2 -0
  91. package/dist/src/observability/index.js +21 -0
  92. package/dist/src/observability/index.js.map +1 -0
  93. package/dist/src/observability/metrics.d.ts +21 -0
  94. package/dist/src/observability/metrics.js +87 -0
  95. package/dist/src/observability/metrics.js.map +1 -0
  96. package/dist/src/observability/traces.d.ts +234 -0
  97. package/dist/src/observability/traces.js +962 -0
  98. package/dist/src/observability/traces.js.map +1 -0
  99. package/dist/src/state/agentState.js +2 -2
  100. package/dist/src/state/agentState.js.map +1 -1
  101. package/dist/src/storage/fileStorage.js +38 -28
  102. package/dist/src/storage/fileStorage.js.map +1 -1
  103. package/dist/src/storage/memoryStorage.js +41 -30
  104. package/dist/src/storage/memoryStorage.js.map +1 -1
  105. package/dist/src/transcript/fileTranscriptLogger.js +2 -2
  106. package/dist/src/transcript/fileTranscriptLogger.js.map +1 -1
  107. package/dist/src/transcript/transcriptLoggerMiddleware.js +2 -2
  108. package/dist/src/transcript/transcriptLoggerMiddleware.js.map +1 -1
  109. package/dist/src/turnContext.js +48 -42
  110. package/dist/src/turnContext.js.map +1 -1
  111. package/package.json +10 -9
  112. package/src/activityHandler.ts +1 -1
  113. package/src/agent-client/agentClient.ts +53 -42
  114. package/src/agent-client/agentResponseHandler.ts +1 -1
  115. package/src/app/agentApplication.ts +212 -86
  116. package/src/app/agentApplicationBuilder.ts +26 -4
  117. package/src/app/agentApplicationOptions.ts +43 -0
  118. package/src/app/attachmentDownloader.ts +1 -1
  119. package/src/app/auth/authorization.ts +11 -8
  120. package/src/app/auth/authorizationManager.ts +297 -45
  121. package/src/app/auth/handlerStorage.ts +3 -1
  122. package/src/app/auth/handlers/agenticAuthorization.ts +68 -72
  123. package/src/app/auth/handlers/azureBotAuthorization.ts +260 -264
  124. package/src/app/auth/types.ts +102 -1
  125. package/src/app/auth/utils.ts +22 -0
  126. package/src/app/index.ts +1 -0
  127. package/src/app/proactive/conversation.ts +87 -0
  128. package/src/app/proactive/conversationBuilder.ts +139 -0
  129. package/src/app/proactive/conversationReferenceBuilder.ts +161 -0
  130. package/src/app/proactive/createConversationOptions.ts +35 -0
  131. package/src/app/proactive/createConversationOptionsBuilder.ts +181 -0
  132. package/src/app/proactive/index.ts +10 -0
  133. package/src/app/proactive/proactive.ts +524 -0
  134. package/src/app/proactive/proactiveOptions.ts +24 -0
  135. package/src/app/streaming/streamingResponse.ts +1 -1
  136. package/src/app/teamsAttachmentDownloader.ts +1 -1
  137. package/src/app/turnState.ts +1 -1
  138. package/src/auth/authConfiguration.ts +58 -1
  139. package/src/auth/jwt-middleware.ts +1 -1
  140. package/src/auth/msalConnectionManager.ts +22 -0
  141. package/src/auth/msalTokenCredential.ts +4 -0
  142. package/src/auth/msalTokenProvider.ts +138 -107
  143. package/src/baseAdapter.ts +1 -1
  144. package/src/cloudAdapter.ts +239 -184
  145. package/src/connector-client/connectorClient.ts +169 -126
  146. package/src/errorHelper.ts +124 -0
  147. package/src/middlewareSet.ts +1 -1
  148. package/src/oauth/userTokenClient.ts +70 -46
  149. package/src/observability/index.ts +5 -0
  150. package/src/observability/metrics.ts +103 -0
  151. package/src/observability/traces.ts +988 -0
  152. package/src/state/agentState.ts +1 -1
  153. package/src/storage/fileStorage.ts +36 -26
  154. package/src/storage/memoryStorage.ts +40 -29
  155. package/src/transcript/fileTranscriptLogger.ts +1 -1
  156. package/src/transcript/transcriptLoggerMiddleware.ts +1 -1
  157. package/src/turnContext.ts +47 -41
@@ -8,14 +8,17 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
8
8
  };
9
9
  Object.defineProperty(exports, "__esModule", { value: true });
10
10
  exports.AzureBotAuthorization = void 0;
11
- const logger_1 = require("@microsoft/agents-activity/logger");
12
11
  const types_1 = require("../types");
13
12
  const messageFactory_1 = require("../../../messageFactory");
14
13
  const cards_1 = require("../../../cards");
15
14
  const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
16
15
  const handlerStorage_1 = require("../handlerStorage");
16
+ const utils_1 = require("../utils");
17
17
  const agents_activity_1 = require("@microsoft/agents-activity");
18
- const logger = (0, logger_1.debug)('agents:authorization:azurebot');
18
+ const agents_telemetry_1 = require("@microsoft/agents-telemetry");
19
+ const observability_1 = require("../../../observability");
20
+ const errorHelper_1 = require("../../../errorHelper");
21
+ const logger = (0, agents_telemetry_1.debug)('agents:authorization:azurebot');
19
22
  const DEFAULT_SIGN_IN_ATTEMPTS = 2;
20
23
  var Category;
21
24
  (function (Category) {
@@ -29,72 +32,56 @@ class AzureBotAuthorization {
29
32
  /**
30
33
  * Creates an instance of the AzureBotAuthorization.
31
34
  * @param id The unique identifier for the handler.
32
- * @param options The settings for the handler.
33
- * @param app The agent application instance.
35
+ * @param options The settings for the handler (must be fully resolved).
36
+ * @param settings The authorization handler settings.
34
37
  */
35
38
  constructor(id, options, settings) {
36
39
  this.id = id;
40
+ this.options = options;
37
41
  this.settings = settings;
42
+ this.type = 'azurebot';
38
43
  this._key = `${AzureBotAuthorization.name}/${this.id}`;
39
44
  /**
40
45
  * Predefined messages with dynamic placeholders.
41
46
  */
42
47
  this.messages = {
43
48
  invalidCode: (code) => {
44
- var _a, _b;
45
- const message = (_b = (_a = this._options.messages) === null || _a === void 0 ? void 0 : _a.invalidCode) !== null && _b !== void 0 ? _b : 'Invalid **{code}** code entered. Please try again with a new sign-in request.';
49
+ var _a;
50
+ const message = (_a = this.options.invalidSignInRetryMessage) !== null && _a !== void 0 ? _a : 'Invalid **{code}** code entered. Please try again with a new sign-in request.';
46
51
  return message.replaceAll('{code}', code);
47
52
  },
48
53
  invalidCodeFormat: (attemptsLeft) => {
49
- var _a, _b;
50
- const message = (_b = (_a = this._options.messages) === null || _a === void 0 ? void 0 : _a.invalidCodeFormat) !== null && _b !== void 0 ? _b : 'Please enter a valid **6-digit** code format (_e.g. 123456_).\r\n**{attemptsLeft} attempt(s) left...**';
54
+ var _a;
55
+ const message = (_a = this.options.invalidSignInRetryMessageFormat) !== null && _a !== void 0 ? _a : 'Please enter a valid **6-digit** code format (_e.g. 123456_).\r\n**{attemptsLeft} attempt(s) left...**';
51
56
  return message.replaceAll('{attemptsLeft}', attemptsLeft.toString());
52
57
  },
53
58
  maxAttemptsExceeded: (maxAttempts) => {
54
- var _a, _b;
55
- const message = (_b = (_a = this._options.messages) === null || _a === void 0 ? void 0 : _a.maxAttemptsExceeded) !== null && _b !== void 0 ? _b : 'You have exceeded the maximum number of sign-in attempts ({maxAttempts}). Please try again with a new sign-in request.';
59
+ var _a;
60
+ const message = (_a = this.options.invalidSignInRetryMaxExceededMessage) !== null && _a !== void 0 ? _a : 'You have exceeded the maximum number of sign-in attempts ({maxAttempts}). Please try again with a new sign-in request.';
56
61
  return message.replaceAll('{maxAttempts}', maxAttempts.toString());
57
62
  },
58
63
  };
59
64
  if (!this.settings.storage) {
60
- throw new Error(this.prefix('The \'storage\' option is not available in the app options. Ensure that the app is properly configured.'));
65
+ throw agents_activity_1.ExceptionHelper.generateException(Error, errorHelper_1.Errors.StorageOptionNotAvailable);
61
66
  }
62
67
  if (!this.settings.connections) {
63
- throw new Error(this.prefix('The \'connections\' option is not available in the app options. Ensure that the app is properly configured.'));
68
+ throw agents_activity_1.ExceptionHelper.generateException(Error, errorHelper_1.Errors.ConnectionsOptionNotAvailable);
69
+ }
70
+ if (!options.azureBotOAuthConnectionName) {
71
+ throw agents_activity_1.ExceptionHelper.generateException(Error, errorHelper_1.Errors.AzureBotOAuthConnectionNameRequired);
64
72
  }
65
- this._options = this.loadOptions(options);
66
73
  }
67
74
  /**
68
- * Loads and validates the authorization handler options.
75
+ * The OBO scopes configured for this handler.
69
76
  */
70
- loadOptions(settings) {
71
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r;
72
- const result = {
73
- name: (_a = settings.name) !== null && _a !== void 0 ? _a : (process.env[`${this.id}_connectionName`]),
74
- title: (_c = (_b = settings.title) !== null && _b !== void 0 ? _b : (process.env[`${this.id}_connectionTitle`])) !== null && _c !== void 0 ? _c : 'Sign-in',
75
- text: (_e = (_d = settings.text) !== null && _d !== void 0 ? _d : (process.env[`${this.id}_connectionText`])) !== null && _e !== void 0 ? _e : 'Please sign-in to continue',
76
- maxAttempts: (_f = settings.maxAttempts) !== null && _f !== void 0 ? _f : parseInt(process.env[`${this.id}_maxAttempts`]),
77
- messages: {
78
- invalidCode: (_h = (_g = settings.messages) === null || _g === void 0 ? void 0 : _g.invalidCode) !== null && _h !== void 0 ? _h : process.env[`${this.id}_messages_invalidCode`],
79
- invalidCodeFormat: (_k = (_j = settings.messages) === null || _j === void 0 ? void 0 : _j.invalidCodeFormat) !== null && _k !== void 0 ? _k : process.env[`${this.id}_messages_invalidCodeFormat`],
80
- maxAttemptsExceeded: (_m = (_l = settings.messages) === null || _l === void 0 ? void 0 : _l.maxAttemptsExceeded) !== null && _m !== void 0 ? _m : process.env[`${this.id}_messages_maxAttemptsExceeded`],
81
- },
82
- obo: {
83
- connection: (_p = (_o = settings.obo) === null || _o === void 0 ? void 0 : _o.connection) !== null && _p !== void 0 ? _p : process.env[`${this.id}_obo_connection`],
84
- scopes: (_r = (_q = settings.obo) === null || _q === void 0 ? void 0 : _q.scopes) !== null && _r !== void 0 ? _r : this.loadScopes(process.env[`${this.id}_obo_scopes`]),
85
- },
86
- enableSso: process.env[`${this.id}_enableSso`] !== 'false' // default value is true
87
- };
88
- if (!result.name) {
89
- throw new Error(this.prefix(`The 'name' property or '${this.id}_connectionName' env variable is required to initialize the handler.`));
90
- }
91
- return result;
77
+ get scopes() {
78
+ return this.options.oboScopes;
92
79
  }
93
80
  /**
94
81
  * Maximum number of attempts for magic code entry.
95
82
  */
96
83
  get maxAttempts() {
97
- const attempts = this._options.maxAttempts;
84
+ const attempts = this.options.invalidSignInRetryMax;
98
85
  const result = typeof attempts === 'number' && Number.isFinite(attempts) ? Math.round(attempts) : NaN;
99
86
  return result > 0 ? result : DEFAULT_SIGN_IN_ATTEMPTS;
100
87
  }
@@ -120,18 +107,19 @@ class AzureBotAuthorization {
120
107
  */
121
108
  async token(context, options) {
122
109
  var _a;
123
- let { token } = this.getContext(context);
124
- if (!(token === null || token === void 0 ? void 0 : token.trim())) {
125
- const { activity } = context;
126
- const userTokenClient = await this.getUserTokenClient(context);
127
- // Using getTokenOrSignInResource instead of getUserToken to avoid HTTP 404 errors.
128
- const { tokenResponse } = await userTokenClient.getTokenOrSignInResource((_a = activity.from) === null || _a === void 0 ? void 0 : _a.id, this._options.name, activity.channelId, activity.getConversationReference(), activity.relatesTo, '');
129
- token = tokenResponse === null || tokenResponse === void 0 ? void 0 : tokenResponse.token;
130
- }
131
- if (!(token === null || token === void 0 ? void 0 : token.trim())) {
132
- return { token: undefined };
110
+ const oboScopes = (options === null || options === void 0 ? void 0 : options.scopes) && options.scopes.length > 0 ? options.scopes : this.options.oboScopes;
111
+ // OBO token acquisition
112
+ if (oboScopes && oboScopes.length > 0) {
113
+ const oboConnection = (_a = options === null || options === void 0 ? void 0 : options.connection) !== null && _a !== void 0 ? _a : this.options.oboConnectionName;
114
+ const token = await this.getOBOToken(context, oboConnection, oboScopes);
115
+ return { token };
133
116
  }
134
- return await this.handleOBO(token, options);
117
+ // Regular token acquisition
118
+ return (0, agents_telemetry_1.trace)(observability_1.AuthorizationTraceDefinitions.azureBotToken, async ({ record }) => {
119
+ record({ handlerId: this.id, connectionName: this.options.azureBotOAuthConnectionName });
120
+ const token = await this.getBaseToken(context);
121
+ return { token };
122
+ });
135
123
  }
136
124
  /**
137
125
  * Signs out the user from the service.
@@ -139,17 +127,20 @@ class AzureBotAuthorization {
139
127
  * @returns True if the signout was successful, false otherwise.
140
128
  */
141
129
  async signout(context) {
142
- var _a;
143
- const user = (_a = context.activity.from) === null || _a === void 0 ? void 0 : _a.id;
144
- const channel = context.activity.channelId;
145
- const connection = this._options.name;
146
- if (!channel || !user) {
147
- throw new Error(this.prefix('Both \'activity.channelId\' and \'activity.from.id\' are required to perform signout.'));
148
- }
149
- logger.debug(this.prefix(`Signing out User '${user}' from => Channel: '${channel}', Connection: '${connection}'`), context.activity);
150
- const userTokenClient = await this.getUserTokenClient(context);
151
- await userTokenClient.signOut(user, connection, channel);
152
- return true;
130
+ return (0, agents_telemetry_1.trace)(observability_1.AuthorizationTraceDefinitions.azureBotSignout, async ({ record }) => {
131
+ var _a;
132
+ const user = (_a = context.activity.from) === null || _a === void 0 ? void 0 : _a.id;
133
+ const channel = context.activity.channelId;
134
+ const connection = this.options.azureBotOAuthConnectionName;
135
+ record({ handlerId: this.id, connectionName: connection, channelId: channel !== null && channel !== void 0 ? channel : 'unknown' });
136
+ if (!channel || !user) {
137
+ throw agents_activity_1.ExceptionHelper.generateException(Error, errorHelper_1.Errors.ChannelIdAndFromIdRequiredForSignout);
138
+ }
139
+ logger.debug(this.prefix(`Signing out User '${user}' from => Channel: '${channel}', Connection: '${connection}'`), context.activity);
140
+ const userTokenClient = await this.getUserTokenClient(context);
141
+ await userTokenClient.signOut(user, connection, channel);
142
+ return true;
143
+ });
153
144
  }
154
145
  /**
155
146
  * Initiates the sign-in process for the handler.
@@ -158,118 +149,172 @@ class AzureBotAuthorization {
158
149
  * @returns The status of the sign-in attempt.
159
150
  */
160
151
  async signin(context, active) {
161
- var _a, _b, _c;
162
- const { activity } = context;
163
- const [category] = (_b = (_a = activity.name) === null || _a === void 0 ? void 0 : _a.split('/')) !== null && _b !== void 0 ? _b : [Category.UNKNOWN];
164
- const storage = new handlerStorage_1.HandlerStorage(this.settings.storage, context);
165
- if (!active) {
166
- return this.setToken(storage, context);
167
- }
168
- logger.debug(this.prefix('Sign-in active session detected'), active.activity);
169
- if (active.attemptsLeft <= 0) {
170
- logger.warn(this.prefix('Maximum sign-in attempts exceeded'), activity);
171
- await context.sendActivity(messageFactory_1.MessageFactory.text(this.messages.maxAttemptsExceeded(this.maxAttempts)));
172
- return types_1.AuthorizationHandlerStatus.REJECTED;
173
- }
174
- if (category === Category.SIGNIN) {
175
- await storage.write({ ...active, category });
176
- const status = await this.handleSignInActivities(context);
177
- if (status !== types_1.AuthorizationHandlerStatus.IGNORED) {
178
- return status;
179
- }
180
- }
181
- else if (active.category === Category.SIGNIN && activity.channelId === agents_activity_1.Channels.Msteams) {
182
- // Specific to MS Teams, M365 does not send signin/verifyState when user consent is required.
183
- // This is only for safety in case of unexpected behaviors during the MS Teams sign-in process,
184
- // e.g., user interrupts the flow by clicking the Consent Cancel button.
185
- logger.warn(this.prefix('The incoming activity will be revalidated due to a change in the sign-in flow'), activity);
186
- return types_1.AuthorizationHandlerStatus.REVALIDATE;
187
- }
188
- const { status, code } = await this.codeVerification(storage, context, active);
189
- if (status !== types_1.AuthorizationHandlerStatus.APPROVED) {
190
- return status;
191
- }
192
- try {
193
- const result = await this.setToken(storage, context, active, code);
194
- if (result !== types_1.AuthorizationHandlerStatus.APPROVED) {
195
- await this.sendInvokeResponse(context, { status: 404 });
196
- return result;
152
+ return (0, agents_telemetry_1.trace)(observability_1.AuthorizationTraceDefinitions.azureBotSignin, async ({ record, actions }) => {
153
+ var _a, _b, _c, _d;
154
+ const reason = { message: '' };
155
+ let status;
156
+ const { activity } = context;
157
+ const [category] = (_b = (_a = activity.name) === null || _a === void 0 ? void 0 : _a.split('/')) !== null && _b !== void 0 ? _b : [Category.UNKNOWN];
158
+ const storage = new handlerStorage_1.HandlerStorage(this.settings.storage, context);
159
+ try {
160
+ if (!active) {
161
+ status = await this.setToken(storage, context, undefined, undefined, reason);
162
+ return status;
163
+ }
164
+ logger.debug(this.prefix('Sign-in active session detected'), active.activity);
165
+ if (active.attemptsLeft <= 0) {
166
+ reason.message = 'Maximum sign-in attempts exceeded';
167
+ logger.warn(this.prefix(reason.message), activity);
168
+ await context.sendActivity(messageFactory_1.MessageFactory.text(this.messages.maxAttemptsExceeded(this.maxAttempts)));
169
+ status = types_1.AuthorizationHandlerStatus.REJECTED;
170
+ return status;
171
+ }
172
+ if (category === Category.SIGNIN) {
173
+ await storage.write({ ...active, category });
174
+ status = await this.handleSignInActivities(context, reason);
175
+ if (status !== types_1.AuthorizationHandlerStatus.IGNORED) {
176
+ return status;
177
+ }
178
+ }
179
+ else if (active.category === Category.SIGNIN && activity.channelId === agents_activity_1.Channels.Msteams) {
180
+ // Specific to MS Teams, M365 does not send signin/verifyState when user consent is required.
181
+ // This is only for safety in case of unexpected behaviors during the MS Teams sign-in process,
182
+ // e.g., user interrupts the flow by clicking the Consent Cancel button.
183
+ reason.message = 'The incoming activity will be revalidated due to a change in the sign-in flow';
184
+ logger.warn(this.prefix(reason.message), activity);
185
+ status = types_1.AuthorizationHandlerStatus.REVALIDATE;
186
+ return status;
187
+ }
188
+ const verification = await this.codeVerification(storage, context, active, reason);
189
+ status = verification.status;
190
+ if (status !== types_1.AuthorizationHandlerStatus.APPROVED) {
191
+ return status;
192
+ }
193
+ try {
194
+ const result = await this.setToken(storage, context, active, verification.code, reason);
195
+ status = result;
196
+ if (result !== types_1.AuthorizationHandlerStatus.APPROVED) {
197
+ await (0, utils_1.sendInvokeResponse)(context, { status: 404 });
198
+ return result;
199
+ }
200
+ await (0, utils_1.sendInvokeResponse)(context, { status: 200 });
201
+ await ((_c = this._onSuccess) === null || _c === void 0 ? void 0 : _c.call(this, context));
202
+ return result;
203
+ }
204
+ catch (error) {
205
+ await (0, utils_1.sendInvokeResponse)(context, { status: 500 });
206
+ if (error instanceof Error) {
207
+ error.message = this.prefix(error.message);
208
+ }
209
+ throw error;
210
+ }
197
211
  }
198
- await this.sendInvokeResponse(context, { status: 200 });
199
- await ((_c = this._onSuccess) === null || _c === void 0 ? void 0 : _c.call(this, context));
200
- return result;
201
- }
202
- catch (error) {
203
- await this.sendInvokeResponse(context, { status: 500 });
204
- if (error instanceof Error) {
205
- error.message = this.prefix(error.message);
212
+ finally {
213
+ await actions.link(storage);
214
+ record({
215
+ handlerId: this.id,
216
+ status: status !== null && status !== void 0 ? status : 'unknown',
217
+ statusReason: reason.message,
218
+ connectionName: (_d = this.options.azureBotOAuthConnectionName) !== null && _d !== void 0 ? _d : 'unknown'
219
+ });
206
220
  }
207
- throw error;
208
- }
221
+ });
209
222
  }
210
223
  /**
211
- * Handles on-behalf-of token acquisition.
224
+ * Retrieves the base token from the turn state or the user token client.
225
+ * @param context The turn context.
226
+ * @returns The token string or undefined if not available.
212
227
  */
213
- async handleOBO(token, options) {
214
- var _a, _b, _c;
215
- const oboConnection = (_a = options === null || options === void 0 ? void 0 : options.connection) !== null && _a !== void 0 ? _a : (_b = this._options.obo) === null || _b === void 0 ? void 0 : _b.connection;
216
- const oboScopes = (options === null || options === void 0 ? void 0 : options.scopes) && options.scopes.length > 0 ? options.scopes : (_c = this._options.obo) === null || _c === void 0 ? void 0 : _c.scopes;
217
- if (!oboScopes || oboScopes.length === 0) {
218
- return { token };
219
- }
220
- if (!this.isExchangeable(token)) {
221
- throw new Error(this.prefix('The current token is not exchangeable for an on-behalf-of flow. Ensure the token audience starts with \'api://\'.'));
222
- }
223
- try {
224
- const provider = oboConnection ? this.settings.connections.getConnection(oboConnection) : this.settings.connections.getDefaultConnection();
225
- const newToken = await provider.acquireTokenOnBehalfOf(oboScopes, token);
226
- logger.debug(this.prefix('Successfully acquired on-behalf-of token'), { connection: oboConnection, scopes: oboScopes });
227
- return { token: newToken };
228
- }
229
- catch (error) {
230
- logger.error(this.prefix('Failed to exchange on-behalf-of token'), { connection: oboConnection, scopes: oboScopes }, error);
231
- return { token: undefined };
228
+ async getBaseToken(context) {
229
+ var _a;
230
+ const { token } = this.getContext(context);
231
+ if (!(token === null || token === void 0 ? void 0 : token.trim())) {
232
+ const { activity } = context;
233
+ const userTokenClient = await this.getUserTokenClient(context);
234
+ // Using getTokenOrSignInResource instead of getUserToken to avoid HTTP 404 errors.
235
+ const { tokenResponse } = await userTokenClient.getTokenOrSignInResource((_a = activity.from) === null || _a === void 0 ? void 0 : _a.id, this.options.azureBotOAuthConnectionName, activity.channelId, activity.getConversationReference(), activity.relatesTo, '');
236
+ return tokenResponse === null || tokenResponse === void 0 ? void 0 : tokenResponse.token;
232
237
  }
238
+ return token;
239
+ }
240
+ /**
241
+ * Acquires an on-behalf-of token for the user based on the provided scopes and connection.
242
+ */
243
+ async getOBOToken(context, oboConnection, oboScopes) {
244
+ return (0, agents_telemetry_1.trace)(observability_1.AuthorizationTraceDefinitions.azureBotOBOToken, async ({ record }) => {
245
+ var _a, _b;
246
+ record({ handlerId: this.id, connectionName: oboConnection, authScopes: oboScopes });
247
+ const token = await this.getBaseToken(context);
248
+ if (!token) {
249
+ return;
250
+ }
251
+ if (!this.isExchangeable(token)) {
252
+ throw agents_activity_1.ExceptionHelper.generateException(Error, errorHelper_1.Errors.AzureBotConnectionTokenNotExchangeable, undefined, { connectionName: this.options.azureBotOAuthConnectionName });
253
+ }
254
+ try {
255
+ const provider = oboConnection ? this.settings.connections.getConnection(oboConnection) : this.settings.connections.getDefaultConnection();
256
+ // Record the connection name again in case it changes.
257
+ record({ connectionName: (_b = (_a = provider === null || provider === void 0 ? void 0 : provider.connectionSettings) === null || _a === void 0 ? void 0 : _a.connectionName) !== null && _b !== void 0 ? _b : oboConnection });
258
+ const newToken = await provider.acquireTokenOnBehalfOf(oboScopes, token);
259
+ logger.debug(this.prefix('Successfully acquired on-behalf-of token'), { connection: oboConnection, scopes: oboScopes });
260
+ return newToken;
261
+ }
262
+ catch (error) {
263
+ logger.error(this.prefix('Failed to exchange on-behalf-of token'), { connection: oboConnection, scopes: oboScopes }, error);
264
+ throw error;
265
+ }
266
+ });
233
267
  }
234
268
  /**
235
269
  * Checks if a token is exchangeable for an on-behalf-of flow.
236
270
  */
237
271
  isExchangeable(token) {
272
+ var _a;
238
273
  if (!token || typeof token !== 'string') {
239
274
  return false;
240
275
  }
241
276
  const payload = jsonwebtoken_1.default.decode(token);
277
+ if (!payload || typeof payload === 'string') {
278
+ return false;
279
+ }
280
+ const appid = (_a = payload.azp) !== null && _a !== void 0 ? _a : payload.appid;
281
+ if (typeof appid !== 'string' || appid.length === 0) {
282
+ return false;
283
+ }
242
284
  const audiences = Array.isArray(payload.aud) ? payload.aud : [payload.aud];
243
- return audiences.some(aud => typeof aud === 'string' && aud.startsWith('api://'));
285
+ return audiences.some(aud => typeof aud === 'string' && aud.includes(appid));
244
286
  }
245
287
  /**
246
288
  * Sets the token from the token response or initiates the sign-in flow.
247
289
  */
248
- async setToken(storage, context, active, code) {
290
+ async setToken(storage, context, active, code, reason = { message: '' }) {
249
291
  var _a;
250
292
  const { activity } = context;
251
293
  const userTokenClient = await this.getUserTokenClient(context);
252
- const { tokenResponse, signInResource } = await userTokenClient.getTokenOrSignInResource((_a = activity.from) === null || _a === void 0 ? void 0 : _a.id, this._options.name, activity.channelId, activity.getConversationReference(), activity.relatesTo, code !== null && code !== void 0 ? code : '');
253
- if (!tokenResponse && active) {
254
- logger.warn(this.prefix('Invalid code entered. Restarting sign-in flow'), activity);
255
- await context.sendActivity(messageFactory_1.MessageFactory.text(this.messages.invalidCode(code !== null && code !== void 0 ? code : '')));
294
+ const { tokenResponse, signInResource } = await userTokenClient.getTokenOrSignInResource((_a = activity.from) === null || _a === void 0 ? void 0 : _a.id, this.options.azureBotOAuthConnectionName, activity.channelId, activity.getConversationReference(), activity.relatesTo, code !== null && code !== void 0 ? code : '');
295
+ if (!tokenResponse && active && code) {
296
+ reason.message = 'Invalid code entered. Restarting sign-in flow';
297
+ logger.warn(this.prefix(reason.message), activity);
298
+ await context.sendActivity(messageFactory_1.MessageFactory.text(this.messages.invalidCode(code)));
256
299
  return types_1.AuthorizationHandlerStatus.REJECTED;
257
300
  }
258
301
  if (!tokenResponse) {
259
- logger.debug(this.prefix('Cannot find token. Sending sign-in card'), activity);
260
- const oCard = cards_1.CardFactory.oauthCard(this._options.name, this._options.title, this._options.text, signInResource, this._options.enableSso);
302
+ reason.message = 'Cannot find token. Sending sign-in card';
303
+ logger.debug(this.prefix(reason.message), activity);
304
+ const oCard = cards_1.CardFactory.oauthCard(this.options.azureBotOAuthConnectionName, this.options.title, this.options.text, signInResource, this.options.enableSso);
261
305
  await context.sendActivity(messageFactory_1.MessageFactory.attachment(oCard));
262
306
  await storage.write({ activity, id: this.id, ...(active !== null && active !== void 0 ? active : {}), attemptsLeft: this.maxAttempts });
263
307
  return types_1.AuthorizationHandlerStatus.PENDING;
264
308
  }
265
- logger.debug(this.prefix('Successfully acquired token'), activity);
309
+ reason.message = 'Successfully acquired token';
310
+ logger.debug(this.prefix(reason.message), activity);
266
311
  this.setContext(context, { token: tokenResponse.token });
267
312
  return types_1.AuthorizationHandlerStatus.APPROVED;
268
313
  }
269
314
  /**
270
315
  * Handles sign-in related activities.
271
316
  */
272
- async handleSignInActivities(context) {
317
+ async handleSignInActivities(context, reason) {
273
318
  var _a, _b, _c, _d;
274
319
  const { activity } = context;
275
320
  // Ignore signin/verifyState here (handled in codeVerification).
@@ -281,64 +326,66 @@ class AzureBotAuthorization {
281
326
  const tokenExchangeInvokeRequest = activity.value;
282
327
  const tokenExchangeRequest = { token: tokenExchangeInvokeRequest === null || tokenExchangeInvokeRequest === void 0 ? void 0 : tokenExchangeInvokeRequest.token };
283
328
  if (!(tokenExchangeRequest === null || tokenExchangeRequest === void 0 ? void 0 : tokenExchangeRequest.token)) {
284
- const reason = 'The Agent received an InvokeActivity that is missing a TokenExchangeInvokeRequest value. This is required to be sent with the InvokeActivity.';
285
- await this.sendInvokeResponse(context, {
329
+ reason.message = 'The Agent received an InvokeActivity that is missing a TokenExchangeInvokeRequest value. This is required to be sent with the InvokeActivity.';
330
+ await (0, utils_1.sendInvokeResponse)(context, {
286
331
  status: 400,
287
- body: { connectionName: this._options.name, failureDetail: reason }
332
+ body: { connectionName: this.options.azureBotOAuthConnectionName, failureDetail: reason.message }
288
333
  });
289
- logger.error(this.prefix(reason));
290
- await ((_a = this._onFailure) === null || _a === void 0 ? void 0 : _a.call(this, context, reason));
334
+ logger.error(this.prefix(reason.message));
335
+ await ((_a = this._onFailure) === null || _a === void 0 ? void 0 : _a.call(this, context, reason.message));
291
336
  return types_1.AuthorizationHandlerStatus.REJECTED;
292
337
  }
293
- if (tokenExchangeInvokeRequest.connectionName !== this._options.name) {
294
- const reason = `The Agent received an InvokeActivity with a TokenExchangeInvokeRequest for a different connection name ('${tokenExchangeInvokeRequest.connectionName}') than expected ('${this._options.name}').`;
295
- await this.sendInvokeResponse(context, {
338
+ if (tokenExchangeInvokeRequest.connectionName !== this.options.azureBotOAuthConnectionName) {
339
+ reason.message = `The Agent received an InvokeActivity with a TokenExchangeInvokeRequest for a different connection name ('${tokenExchangeInvokeRequest.connectionName}') than expected ('${this.options.azureBotOAuthConnectionName}').`;
340
+ await (0, utils_1.sendInvokeResponse)(context, {
296
341
  status: 400,
297
- body: { id: tokenExchangeInvokeRequest.id, connectionName: this._options.name, failureDetail: reason }
342
+ body: { id: tokenExchangeInvokeRequest.id, connectionName: this.options.azureBotOAuthConnectionName, failureDetail: reason.message }
298
343
  });
299
- logger.error(this.prefix(reason));
300
- await ((_b = this._onFailure) === null || _b === void 0 ? void 0 : _b.call(this, context, reason));
344
+ logger.error(this.prefix(reason.message));
345
+ await ((_b = this._onFailure) === null || _b === void 0 ? void 0 : _b.call(this, context, reason.message));
301
346
  return types_1.AuthorizationHandlerStatus.REJECTED;
302
347
  }
303
- const { token } = await userTokenClient.exchangeTokenAsync((_c = activity.from) === null || _c === void 0 ? void 0 : _c.id, this._options.name, activity.channelId, tokenExchangeRequest);
348
+ const { token } = await userTokenClient.exchangeTokenAsync((_c = activity.from) === null || _c === void 0 ? void 0 : _c.id, this.options.azureBotOAuthConnectionName, activity.channelId, tokenExchangeRequest);
304
349
  if (!token) {
305
- const reason = 'The MS Teams token service didn\'t send back the exchanged token. Waiting for MS Teams to send another signin/tokenExchange request. After multiple failed attempts, the user will be asked to enter the magic code.';
306
- await this.sendInvokeResponse(context, {
350
+ reason.message = 'The MS Teams token service didn\'t send back the exchanged token. Waiting for MS Teams to send another signin/tokenExchange request. After multiple failed attempts, the user will be asked to enter the magic code.';
351
+ await (0, utils_1.sendInvokeResponse)(context, {
307
352
  status: 412,
308
- body: { id: tokenExchangeInvokeRequest.id, connectionName: this._options.name, failureDetail: reason }
353
+ body: { id: tokenExchangeInvokeRequest.id, connectionName: this.options.azureBotOAuthConnectionName, failureDetail: reason.message }
309
354
  });
310
- logger.debug(this.prefix(reason));
355
+ logger.debug(this.prefix(reason.message));
311
356
  return types_1.AuthorizationHandlerStatus.PENDING;
312
357
  }
313
- await this.sendInvokeResponse(context, {
358
+ await (0, utils_1.sendInvokeResponse)(context, {
314
359
  status: 200,
315
- body: { id: tokenExchangeInvokeRequest.id, connectionName: this._options.name }
360
+ body: { id: tokenExchangeInvokeRequest.id, connectionName: this.options.azureBotOAuthConnectionName }
316
361
  });
317
- logger.debug(this.prefix('Successfully exchanged token'));
362
+ reason.message = 'Successfully exchanged token';
363
+ logger.debug(this.prefix(reason.message));
318
364
  this.setContext(context, { token });
319
365
  await ((_d = this._onSuccess) === null || _d === void 0 ? void 0 : _d.call(this, context));
320
366
  return types_1.AuthorizationHandlerStatus.APPROVED;
321
367
  }
322
368
  if (activity.name === 'signin/failure') {
323
- await this.sendInvokeResponse(context, { status: 200 });
324
- const reason = 'Failed to sign-in';
369
+ await (0, utils_1.sendInvokeResponse)(context, { status: 200 });
370
+ reason.message = 'Failed to sign-in';
325
371
  const value = activity.value;
326
- logger.error(this.prefix(reason), value, activity);
372
+ logger.error(this.prefix(reason.message), value, activity);
327
373
  if (this._onFailure) {
328
- await this._onFailure(context, value.message || reason);
374
+ await this._onFailure(context, value.message || reason.message);
329
375
  }
330
376
  else {
331
- await context.sendActivity(messageFactory_1.MessageFactory.text(`${reason}. Please try again.`));
377
+ await context.sendActivity(messageFactory_1.MessageFactory.text(`${reason.message}. Please try again.`));
332
378
  }
333
379
  return types_1.AuthorizationHandlerStatus.REJECTED;
334
380
  }
335
- logger.error(this.prefix(`Unknown sign-in activity name: ${activity.name}`), activity);
381
+ reason.message = `Unknown sign-in activity name: ${activity.name}`;
382
+ logger.error(this.prefix(reason.message), activity);
336
383
  return types_1.AuthorizationHandlerStatus.REJECTED;
337
384
  }
338
385
  /**
339
386
  * Verifies the magic code provided by the user.
340
387
  */
341
- async codeVerification(storage, context, active) {
388
+ async codeVerification(storage, context, active, reason = { message: '' }) {
342
389
  if (!active) {
343
390
  logger.debug(this.prefix('No active session found. Skipping code verification.'), context.activity);
344
391
  return { status: types_1.AuthorizationHandlerStatus.IGNORED };
@@ -351,18 +398,21 @@ class AzureBotAuthorization {
351
398
  state = teamsState;
352
399
  }
353
400
  if (state === 'CancelledByUser') {
354
- await this.sendInvokeResponse(context, { status: 200 });
355
- logger.warn(this.prefix('Sign-in process was cancelled by the user'), activity);
401
+ await (0, utils_1.sendInvokeResponse)(context, { status: 200 });
402
+ reason.message = 'Sign-in process was cancelled by the user';
403
+ logger.warn(this.prefix(reason.message), activity);
356
404
  return { status: types_1.AuthorizationHandlerStatus.REJECTED };
357
405
  }
358
406
  if (!(state === null || state === void 0 ? void 0 : state.match(/^\d{6}$/))) {
359
- logger.warn(this.prefix(`Invalid magic code entered. Attempts left: ${active.attemptsLeft}`), activity);
407
+ reason.message = `Invalid magic code entered. Attempts left: ${active.attemptsLeft}`;
408
+ logger.warn(this.prefix(reason.message), activity);
360
409
  await context.sendActivity(messageFactory_1.MessageFactory.text(this.messages.invalidCodeFormat(active.attemptsLeft)));
361
410
  await storage.write({ ...active, attemptsLeft: active.attemptsLeft - 1 });
362
411
  return { status: types_1.AuthorizationHandlerStatus.PENDING };
363
412
  }
364
- await this.sendInvokeResponse(context, { status: 200 });
365
- logger.debug(this.prefix('Code verification successful'), activity);
413
+ await (0, utils_1.sendInvokeResponse)(context, { status: 200 });
414
+ reason.message = 'Code verification successful';
415
+ logger.debug(this.prefix(reason.message), activity);
366
416
  return { status: types_1.AuthorizationHandlerStatus.APPROVED, code: state };
367
417
  }
368
418
  /**
@@ -385,41 +435,16 @@ class AzureBotAuthorization {
385
435
  async getUserTokenClient(context) {
386
436
  const userTokenClient = context.turnState.get(context.adapter.UserTokenClientKey);
387
437
  if (!userTokenClient) {
388
- throw new Error(this.prefix('The \'userTokenClient\' is not available in the adapter. Ensure that the adapter supports user token operations.'));
438
+ throw agents_activity_1.ExceptionHelper.generateException(Error, errorHelper_1.Errors.UserTokenClientNotAvailable);
389
439
  }
390
440
  return userTokenClient;
391
441
  }
392
- /**
393
- * Sends an InvokeResponse activity if the channel is Microsoft Teams, including Copilot within MS Teams.
394
- */
395
- sendInvokeResponse(context, response) {
396
- if (context.activity.channelIdChannel !== agents_activity_1.Channels.Msteams) {
397
- return Promise.resolve();
398
- }
399
- return context.sendActivity(agents_activity_1.Activity.fromObject({
400
- type: agents_activity_1.ActivityTypes.InvokeResponse,
401
- value: response
402
- }));
403
- }
404
442
  /**
405
443
  * Prefixes a message with the handler ID.
406
444
  */
407
445
  prefix(message) {
408
446
  return `[handler:${this.id}] ${message}`;
409
447
  }
410
- /**
411
- * Loads the OAuth scopes from the environment variables.
412
- */
413
- loadScopes(value) {
414
- var _a;
415
- return (_a = value === null || value === void 0 ? void 0 : value.split(',').reduce((acc, scope) => {
416
- const trimmed = scope.trim();
417
- if (trimmed) {
418
- acc.push(trimmed);
419
- }
420
- return acc;
421
- }, [])) !== null && _a !== void 0 ? _a : [];
422
- }
423
448
  }
424
449
  exports.AzureBotAuthorization = AzureBotAuthorization;
425
450
  //# sourceMappingURL=azureBotAuthorization.js.map