@microsoft/teams.apps
Version:
<p> <a href="https://www.npmjs.com/package/@microsoft/teams.apps" target="_blank"> <img src="https://img.shields.io/npm/v/@microsoft/teams.apps/latest" /> </a> <a href="https://www.npmjs.com/package/@microsoft/teams.apps?activeTab=code
137 lines • 12.9 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.OauthHandlers = void 0;
const teams_graph_1 = require("@microsoft/teams.graph");
const constants_1 = require("../diagnostics/constants");
const telemetry_1 = require("./telemetry");
/**
* Default handlers for the SSO sign-in invoke activities
* (`signin/tokenExchange`, `signin/verifyState`, `signin/failure`).
*
* Previously a set of `this: App`-bound free functions; now a collaborator the
* {@link App} constructs and registers as system routes. The handlers are arrow
* class fields so they can be passed directly as route callbacks.
*/
class OauthHandlers {
getFlows;
client;
events;
graphBaseUrl;
usesRegisteredFlows;
constructor(getFlows, client, events, graphBaseUrl, usesRegisteredFlows = () => true) {
this.getFlows = getFlows;
this.client = client;
this.events = events;
this.graphBaseUrl = graphBaseUrl;
this.usesRegisteredFlows = usesRegisteredFlows;
}
onTokenExchange = async (ctx) => {
const { activity } = ctx;
const value = activity.value;
if (!value?.connectionName?.trim()) {
return { status: 400 };
}
const registeredFlow = this.findFlow(value.connectionName);
const flow = registeredFlow ??
(this.usesRegisteredFlows() ? undefined : this.getFlows()[0]);
if (!flow) {
return { status: 400 };
}
const connectionName = registeredFlow?.connectionName ?? value.connectionName;
if (!registeredFlow) {
ctx.log.warn(`default connection name "${flow.connectionName}" does not match activity connection name "${connectionName}"`);
}
const response = await flow.exchangeToken(ctx, value, token => this.completeSignIn(flow, ctx, token, connectionName), error => this.events.emit('error', { error, activity }), connectionName);
return response;
};
onVerifyState = async (ctx) => {
const { activity } = ctx;
const flows = this.getOrderedFlows(ctx, false);
for (const flow of flows) {
const response = await flow.verifyState(ctx, activity.value?.state, token => this.completeSignIn(flow, ctx, token), error => this.events.emit('error', { error, activity }));
if (response) {
return response;
}
}
return { status: 404 };
};
/**
* Default handler for signin/failure invoke activities.
*
* Teams sends a signin/failure invoke when SSO token exchange fails
* (e.g., due to a misconfigured Entra app registration). This handler
* logs the failure details and emits an error event so developers are
* notified rather than having the failure silently swallowed.
*
* Known failure codes (sent by the Teams client):
* - `installappfailed`: Failed to install the app in the user's personal scope (non-silent).
* - `authrequestfailed`: The SSO auth request failed after app installation (non-silent).
* - `installedappnotfound`: The bot app is not installed for the user or group chat.
* - `invokeerror`: A generic error occurred during the SSO invoke flow.
* - `resourcematchfailed`: The token exchange resource URI on the OAuthCard does not
* match the Application ID URI in the Entra app registration's "Expose an API" section.
* - `oauthcardnotvalid`: The bot's OAuthCard could not be parsed.
* - `tokenmissing`: AAD token acquisition failed.
* - `userconsentrequired`: The user needs to consent (handled via OAuth card fallback,
* does not typically reach the bot).
* - `interactionrequired`: User interaction is required (handled via OAuth card fallback,
* does not typically reach the bot).
*/
onSignInFailure = async (ctx) => {
const { log, activity, next } = ctx;
const failure = activity.value ?? {};
const { code, message } = failure;
const pendingFlows = this.getOrderedFlows(ctx, true).filter(flow => flow.getPending(ctx, true) !== undefined);
const flows = pendingFlows.length > 0 ? [pendingFlows[0]] : this.getFlows();
const connectionName = flows[0]?.connectionName ?? '';
return (0, telemetry_1.traceOAuthOperation)(constants_1.APP_SPAN_NAMES.oauth, connectionName, constants_1.APP_OAUTH_OPERATION.signinFailure, async (_span, telemetry) => {
telemetry.failureCode = code;
log.warn(`sign-in failed for user "${activity.from.id}" in conversation "${activity.conversation.id}": ${code ?? 'unknown'} — ${message ?? 'unknown'}. ` +
'If the code is \'resourcematchfailed\', verify that your Entra app registration has \'Expose an API\' configured ' +
'with the correct Application ID URI matching your OAuth connection\'s Token Exchange URL.');
for (const flow of flows) {
flow.clearPending(ctx);
await flow.fail(ctx, failure);
}
this.events.emit('error', {
error: new Error(`Sign-in failure: ${code ?? 'unknown'} — ${message ?? 'unknown'}`),
activity,
});
await next(ctx);
telemetry.result = constants_1.APP_OAUTH_RESULT.notified;
telemetry.responseStatus = 200;
return { status: 200 };
});
};
findFlow(connectionName) {
if (!connectionName) {
return undefined;
}
const normalized = connectionName.trim().toLowerCase();
return this.getFlows().find(flow => flow.connectionName.trim().toLowerCase() === normalized);
}
applyUserToken(ctx, token) {
ctx.userToken = token.token;
ctx.isSignedIn = true;
ctx.userGraph = new teams_graph_1.Client(this.client.clone({
token: token.token,
}), { baseUrlRoot: this.graphBaseUrl });
}
async completeSignIn(flow, ctx, token, connectionName = flow.connectionName) {
this.applyUserToken(ctx, token);
await flow.complete(ctx, token);
await this.events.emitAsync('signin', {
...ctx,
connectionName,
token,
isSignedIn: true,
});
await ctx.next(ctx);
}
getOrderedFlows(ctx, ssoOnly) {
return [...this.getFlows()].sort((left, right) => (right.getPending(ctx, ssoOnly) ?? 0) -
(left.getPending(ctx, ssoOnly) ?? 0));
}
}
exports.OauthHandlers = OauthHandlers;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaGFuZGxlcnMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvb2F1dGgvaGFuZGxlcnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBUUEsd0RBQStEO0FBRy9ELHdEQUlrQztBQUlsQywyQ0FBa0Q7QUFJbEQ7Ozs7Ozs7R0FPRztBQUNILE1BQWEsYUFBYTtJQUVMO0lBQ0E7SUFDQTtJQUNBO0lBQ0E7SUFMbkIsWUFDbUIsUUFBNkMsRUFDN0MsTUFBa0IsRUFDbEIsTUFBd0MsRUFDeEMsWUFBcUIsRUFDckIsc0JBQXFDLEdBQUcsRUFBRSxDQUFDLElBQUk7UUFKL0MsYUFBUSxHQUFSLFFBQVEsQ0FBcUM7UUFDN0MsV0FBTSxHQUFOLE1BQU0sQ0FBWTtRQUNsQixXQUFNLEdBQU4sTUFBTSxDQUFrQztRQUN4QyxpQkFBWSxHQUFaLFlBQVksQ0FBUztRQUNyQix3QkFBbUIsR0FBbkIsbUJBQW1CLENBQTRCO0lBQzlELENBQUM7SUFFTCxlQUFlLEdBQUcsS0FBSyxFQUNyQixHQUFvRyxFQUNwRyxFQUFFO1FBQ0YsTUFBTSxFQUFFLFFBQVEsRUFBRSxHQUFHLEdBQUcsQ0FBQztRQUN6QixNQUFNLEtBQUssR0FBRyxRQUFRLENBQUMsS0FBSyxDQUFDO1FBRTdCLElBQUksQ0FBQyxLQUFLLEVBQUUsY0FBYyxFQUFFLElBQUksRUFBRSxFQUFFLENBQUM7WUFDbkMsT0FBTyxFQUFFLE1BQU0sRUFBRSxHQUFHLEVBQUUsQ0FBQztRQUN6QixDQUFDO1FBRUQsTUFBTSxjQUFjLEdBQUcsSUFBSSxDQUFDLFFBQVEsQ0FBQyxLQUFLLENBQUMsY0FBYyxDQUFDLENBQUM7UUFDM0QsTUFBTSxJQUFJLEdBQUcsY0FBYztZQUN6QixDQUFDLElBQUksQ0FBQyxtQkFBbUIsRUFBRSxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxRQUFRLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQ2hFLElBQUksQ0FB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