@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
507 lines • 43.7 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.DEFAULT_OAUTH_SETTINGS = exports.OAuthFlow = void 0;
exports.startOAuthSignIn = startOAuthSignIn;
const axios_1 = require("axios");
const teams_api_1 = require("@microsoft/teams.api");
const constants_1 = require("../diagnostics/constants");
const telemetry_1 = require("./telemetry");
/** @internal Gets a cached token or emits an OAuth sign-in card. */
async function startOAuthSignIn(context, connectionName, options = {}, onSignInInitiated) {
const oauthCardText = options.oauthCardText ?? 'Please Sign In...';
const signInButtonText = options.signInButtonText ?? 'Sign In';
try {
const response = await context.api.users.getToken({
channelId: context.activity.channelId,
userId: context.activity.from.id,
connectionName,
});
if (response.token) {
return response.token;
}
}
catch (error) {
if (!isTokenNotFoundError(error)) {
throw error;
}
}
const conversation = { ...context.ref };
const tokenExchangeState = {
connectionName,
conversation,
relatesTo: context.activity.relatesTo,
msAppId: context.appId,
};
const state = Buffer.from(JSON.stringify(tokenExchangeState)).toString('base64');
const resource = await context.api.bots.signIn.getResource({ state });
const conversationType = context.activity.conversation.conversationType;
const isChannel = conversationType === 'channel';
const isChannelOrGroup = isChannel || conversationType === 'groupChat';
const recipient = isChannelOrGroup
? { ...context.activity.from, isTargeted: true }
: context.activity.from;
const tokenExchangeResource = isChannel
? undefined
: resource.tokenExchangeResource;
await context.send(options.overrideSignInActivity?.(tokenExchangeResource, resource.tokenPostResource, resource.signInLink) ?? {
type: 'message',
recipient,
attachments: [
(0, teams_api_1.cardAttachment)('oauth', {
text: oauthCardText,
connectionName,
tokenExchangeResource,
tokenPostResource: resource.tokenPostResource,
buttons: [
{
type: 'signin',
title: signInButtonText,
value: options.signInLink || resource.signInLink,
},
],
}),
],
}, conversation);
await onSignInInitiated?.(context, connectionName, tokenExchangeResource !== undefined);
return undefined;
}
/**
* High-level OAuth lifecycle for one Bot Framework OAuth connection.
*
* Flows are registered through `AppOptions.oauthFlows` or
* `app.addOAuthFlow(...)` so inbound sign-in invokes can be dispatched to the
* correct connection. Apps without registered flows retain their legacy
* default connection as the same flow type for backward compatibility.
*/
class OAuthFlow {
connectionName;
options;
static PENDING_TTL_MS = 5 * 60 * 1000;
static MAX_PENDING_ENTRIES = 1_000;
static EXCHANGE_TTL_MS = 5 * 60 * 1000;
static MAX_EXCHANGE_ENTRIES = 1_000;
static EXCHANGE_STATE_KEY = '__oauth:exchanges';
signInCompleteHandler;
signInFailureHandler;
pendingSignIns = new Map();
pendingSsoSignIns = new Map();
// Process-local only: this map collapses concurrent duplicates that reach the
// same instance; persisted conversation state covers cross-instance retries.
tokenExchangeLocks = new Map();
// Retains completed exchanges when state is disabled or unavailable.
processedExchanges = new Map();
/**
* Creates an OAuth flow for a connection.
*
* Applications should use `AppOptions.oauthFlows` or
* `app.addOAuthFlow(...)` so the flow is registered for inbound invokes.
* @param connectionName OAuth connection name configured on the bot.
* @param options Optional default card options.
*/
constructor(connectionName, options = {}) {
this.connectionName = connectionName;
this.options = options;
if (!connectionName.trim()) {
throw new Error('OAuth connection name is required.');
}
}
/**
* Registers the callback invoked after interactive sign-in completes.
*
* Calling this method again replaces the previous callback.
*/
onSignInComplete(handler) {
this.signInCompleteHandler = handler;
return this;
}
/**
* Registers the callback invoked when interactive sign-in fails.
*
* Calling this method again replaces the previous callback.
*/
onSignInFailure(handler) {
this.signInFailureHandler = handler;
return this;
}
/**
* Silently gets the current user's token for this connection.
*
* Returns undefined when the token service returns no token or reports `404`.
* Other transport and service failures, including `400` and `412`, propagate.
*/
async getToken(context) {
return (0, telemetry_1.traceOAuthOperation)(constants_1.APP_SPAN_NAMES.oauth, this.connectionName, constants_1.APP_OAUTH_OPERATION.getToken, async (_span, telemetry) => {
try {
const response = await context.api.users.getToken({
channelId: context.activity.channelId,
userId: context.activity.from.id,
connectionName: this.connectionName,
});
const token = response.token || undefined;
telemetry.result = token
? constants_1.APP_OAUTH_RESULT.hit
: constants_1.APP_OAUTH_RESULT.miss;
return token;
}
catch (error) {
if (isTokenNotFoundError(error)) {
telemetry.result = constants_1.APP_OAUTH_RESULT.miss;
return undefined;
}
throw error;
}
});
}
/**
* Gets a cached token or sends an OAuth card to start interactive sign-in.
*
* Returns the cached token string when already signed in. Returns undefined
* after sending a card; completion is then delivered to the registered
* callback and the existing app `signin` event. Cached-token lookup errors
* other than `404` propagate without sending a card.
*/
signIn(context, options = {}) {
return (0, telemetry_1.traceOAuthOperation)(constants_1.APP_SPAN_NAMES.oauth, this.connectionName, constants_1.APP_OAUTH_OPERATION.signIn, async (_span, telemetry) => {
const token = await startOAuthSignIn(context, this.connectionName, {
...this.options,
...options,
connectionName: this.connectionName,
}, (signInContext, _connectionName, supportsSso) => {
this.recordPending(signInContext, supportsSso);
});
telemetry.result = token
? constants_1.APP_OAUTH_RESULT.cached
: constants_1.APP_OAUTH_RESULT.cardSent;
return token;
});
}
/**
* Revokes the current user's token for this connection.
*/
signOut(context) {
return (0, telemetry_1.traceOAuthOperation)(constants_1.APP_SPAN_NAMES.oauth, this.connectionName, constants_1.APP_OAUTH_OPERATION.signOut, async (_span, telemetry) => {
await context.api.users.signOut({
channelId: context.activity.channelId,
userId: context.activity.from.id,
connectionName: this.connectionName,
});
telemetry.result = constants_1.APP_OAUTH_RESULT.success;
});
}
/**
* Returns whether a token is currently available for this connection.
*/
async isSignedIn(context) {
return (await this.getToken(context)) !== undefined;
}
/** @internal Invokes the registered completion callback. */
async complete(context, token) {
await this.signInCompleteHandler?.(context, token);
}
/** @internal Invokes the registered failure callback. */
async fail(context, failure) {
await this.signInFailureHandler?.(context, failure);
}
/** @internal Exchanges an SSO token for this flow. */
async exchangeToken(context, value, onSuccess, onError, connectionName = this.connectionName) {
return (0, telemetry_1.traceOAuthOperation)(constants_1.APP_SPAN_NAMES.oauth, connectionName, constants_1.APP_OAUTH_OPERATION.tokenExchange, async (span, telemetry) => {
const exchangeId = value.id;
if (exchangeId) {
if (this.isExchangeProcessed(context, exchangeId)) {
telemetry.result = constants_1.APP_OAUTH_RESULT.duplicate;
telemetry.responseStatus = 200;
return { status: 200 };
}
const existingLock = this.tokenExchangeLocks.get(exchangeId);
if (existingLock) {
const result = await existingLock;
telemetry.result = result.status === 200
? constants_1.APP_OAUTH_RESULT.duplicate
: constants_1.APP_OAUTH_RESULT.failure;
telemetry.responseStatus = result.status;
return result;
}
}
const performExchange = async () => {
let token;
try {
token = await context.api.users.exchangeToken({
channelId: context.activity.channelId,
userId: context.activity.from.id,
connectionName,
exchangeRequest: {
token: value.token,
},
});
}
catch (error) {
if (!(error instanceof axios_1.AxiosError)) {
// Token-service failures use AxiosError. Let anything outside
// that contract follow the app's normal error handling.
throw error;
}
if (isExpectedOAuthInvokeError(error)) {
// 412 tells Teams that silent SSO could not complete and it
// should fall back to the interactive OAuth card.
await this.fail(context);
telemetry.callbackInvoked =
this.signInFailureHandler !== undefined;
telemetry.result = constants_1.APP_OAUTH_RESULT.failure;
telemetry.responseStatus = 412;
return this.tokenExchangeFailure(value.id, connectionName);
}
// Unexpected service statuses are operational errors, not a
// request for interactive fallback, so preserve their status.
this.clearPending(context);
await this.fail(context);
telemetry.callbackInvoked =
this.signInFailureHandler !== undefined;
telemetry.result = constants_1.APP_OAUTH_RESULT.failure;
const status = error.status || 500;
telemetry.responseStatus = status;
(0, telemetry_1.recordUnexpectedOAuthError)(span, telemetry, error, constants_1.APP_OAUTH_ERROR_TYPE.httpError);
onError(error);
return { status };
}
if (!token?.token) {
this.clearPending(context);
await this.fail(context);
telemetry.callbackInvoked = this.signInFailureHandler !== undefined;
telemetry.result = constants_1.APP_OAUTH_RESULT.failure;
telemetry.responseStatus = 412;
return this.tokenExchangeFailure(value.id, connectionName);
}
if (exchangeId) {
this.markExchangeProcessed(context, exchangeId);
}
this.clearPending(context);
await onSuccess(token);
telemetry.callbackInvoked = this.signInCompleteHandler !== undefined;
telemetry.result = constants_1.APP_OAUTH_RESULT.success;
telemetry.responseStatus = 200;
return { status: 200 };
};
if (!exchangeId) {
return performExchange();
}
const lock = performExchange();
this.tokenExchangeLocks.set(exchangeId, lock);
try {
return await lock;
}
finally {
this.tokenExchangeLocks.delete(exchangeId);
}
});
}
/** @internal Attempts to redeem a verify-state code for this flow. */
async verifyState(context, state, onSuccess, onError) {
return (0, telemetry_1.traceOAuthOperation)(constants_1.APP_SPAN_NAMES.oauth, this.connectionName, constants_1.APP_OAUTH_OPERATION.verifyState, async (span, telemetry) => {
if (!state) {
context.log.warn(`auth state not found for conversation "${context.activity.conversation.id}" and user "${context.activity.from.id}"`);
telemetry.result = constants_1.APP_OAUTH_RESULT.noToken;
telemetry.responseStatus = 404;
return undefined;
}
let token;
try {
token = await context.api.users.getToken({
channelId: context.activity.channelId,
userId: context.activity.from.id,
connectionName: this.connectionName,
code: state,
});
}
catch (error) {
if (!(error instanceof axios_1.AxiosError)) {
throw error;
}
if (isExpectedOAuthInvokeError(error)) {
telemetry.result = constants_1.APP_OAUTH_RESULT.noToken;
telemetry.responseStatus = error.status;
return undefined;
}
this.clearPending(context);
await this.fail(context);
telemetry.callbackInvoked = this.signInFailureHandler !== undefined;
telemetry.result = constants_1.APP_OAUTH_RESULT.failure;
const status = error.status || 500;
telemetry.responseStatus = status;
(0, telemetry_1.recordUnexpectedOAuthError)(span, telemetry, error, constants_1.APP_OAUTH_ERROR_TYPE.httpError);
onError(error);
return { status };
}
if (!token?.token) {
telemetry.result = constants_1.APP_OAUTH_RESULT.noToken;
telemetry.responseStatus = 404;
return undefined;
}
this.clearPending(context);
await onSuccess(token);
telemetry.callbackInvoked = this.signInCompleteHandler !== undefined;
telemetry.result = constants_1.APP_OAUTH_RESULT.success;
telemetry.responseStatus = 200;
return { status: 200 };
});
}
/** @internal Records this flow as the source of an interactive sign-in. */
recordPending(context, supportsSso) {
const startedAt = Date.now();
this.setPendingValue(context, false, startedAt);
if (supportsSso) {
this.setPendingValue(context, true, startedAt);
}
else {
this.deletePendingValue(context, true);
}
}
/** @internal Gets when this flow most recently initiated sign-in. */
getPending(context, ssoOnly) {
const now = Date.now();
const stateKey = this.pendingStateKey(ssoOnly);
const userState = context.state?.user;
if (userState) {
const stateValue = userState.get(stateKey);
if (stateValue !== undefined) {
if (now - stateValue < OAuthFlow.PENDING_TTL_MS) {
return stateValue;
}
userState.delete(stateKey);
}
return undefined;
}
const pending = this.pendingMap(ssoOnly);
this.prunePending(pending, now);
return pending.get(this.pendingContextKey(context));
}
/** @internal Clears pending sign-in attribution for this flow. */
clearPending(context) {
this.deletePendingValue(context, false);
this.deletePendingValue(context, true);
}
setPendingValue(context, ssoOnly, value) {
const userState = context.state?.user;
if (userState) {
userState.set(this.pendingStateKey(ssoOnly), value);
return;
}
const pending = this.pendingMap(ssoOnly);
pending.set(this.pendingContextKey(context), value);
this.prunePending(pending, value);
}
deletePendingValue(context, ssoOnly) {
context.state?.user?.delete(this.pendingStateKey(ssoOnly));
this.pendingMap(ssoOnly).delete(this.pendingContextKey(context));
}
pendingStateKey(ssoOnly) {
return `__oauth:pending:${ssoOnly ? 'sso:' : ''}${this.connectionName.toLowerCase()}`;
}
pendingMap(ssoOnly) {
return ssoOnly ? this.pendingSsoSignIns : this.pendingSignIns;
}
pendingContextKey(context) {
return JSON.stringify([
context.activity.conversation.id,
context.activity.from.id,
]);
}
prunePending(pending, now) {
for (const [contextKey, timestamp] of pending) {
if (now - timestamp >= OAuthFlow.PENDING_TTL_MS) {
pending.delete(contextKey);
}
}
if (pending.size <= OAuthFlow.MAX_PENDING_ENTRIES) {
return;
}
for (const [contextKey] of [...pending.entries()]
.sort(([, left], [, right]) => right - left)
.slice(OAuthFlow.MAX_PENDING_ENTRIES)) {
pending.delete(contextKey);
}
}
tokenExchangeFailure(id, connectionName = this.connectionName) {
return {
status: 412,
body: {
id,
connectionName,
failureDetail: 'unable to exchange token...',
},
};
}
isExchangeProcessed(context, exchangeId) {
const now = Date.now();
const stateTimestamp = this.getExchangeState(context, now)[exchangeId];
if (stateTimestamp !== undefined &&
now - stateTimestamp < OAuthFlow.EXCHANGE_TTL_MS) {
return true;
}
this.pruneProcessedExchanges(now);
return this.processedExchanges.has(exchangeId);
}
markExchangeProcessed(context, exchangeId) {
const timestamp = Date.now();
this.processedExchanges.set(exchangeId, timestamp);
this.pruneProcessedExchanges(timestamp);
const conversationState = context.state?.conversation;
if (!conversationState) {
return;
}
const exchanges = this.getExchangeState(context, timestamp);
exchanges[exchangeId] = timestamp;
const bounded = Object.fromEntries(Object.entries(exchanges)
.sort(([, left], [, right]) => right - left)
.slice(0, OAuthFlow.MAX_EXCHANGE_ENTRIES));
conversationState.set(OAuthFlow.EXCHANGE_STATE_KEY, bounded);
}
pruneProcessedExchanges(now) {
for (const [exchangeId, timestamp] of this.processedExchanges) {
if (now - timestamp >= OAuthFlow.EXCHANGE_TTL_MS) {
this.processedExchanges.delete(exchangeId);
}
}
if (this.processedExchanges.size <= OAuthFlow.MAX_EXCHANGE_ENTRIES) {
return;
}
for (const [exchangeId] of [...this.processedExchanges.entries()]
.sort(([, left], [, right]) => right - left)
.slice(OAuthFlow.MAX_EXCHANGE_ENTRIES)) {
this.processedExchanges.delete(exchangeId);
}
}
getExchangeState(context, now) {
const conversationState = context.state?.conversation;
if (!conversationState) {
return {};
}
const stored = conversationState.get(OAuthFlow.EXCHANGE_STATE_KEY) ?? {};
const current = Object.fromEntries(Object.entries(stored)
.filter(([, timestamp]) => Number.isFinite(timestamp) &&
now - timestamp < OAuthFlow.EXCHANGE_TTL_MS)
.sort(([, left], [, right]) => right - left)
.slice(0, OAuthFlow.MAX_EXCHANGE_ENTRIES));
if (Object.keys(current).length !== Object.keys(stored).length) {
if (Object.keys(current).length === 0) {
conversationState.delete(OAuthFlow.EXCHANGE_STATE_KEY);
}
else {
conversationState.set(OAuthFlow.EXCHANGE_STATE_KEY, current);
}
}
return current;
}
}
exports.OAuthFlow = OAuthFlow;
/** @internal Defaults retained for deprecated context OAuth behavior. */
exports.DEFAULT_OAUTH_SETTINGS = {
defaultConnectionName: 'graph'
};
function isTokenNotFoundError(error) {
return error instanceof axios_1.AxiosError && error.status === 404;
}
function isExpectedOAuthInvokeError(error) {
return error instanceof axios_1.AxiosError &&
(error.status === 400 || error.status === 404 || error.status === 412);
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvb2F1dGgvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBNkZBLDRDQTZFQztBQTFLRCxpQ0FBbUM7QUFFbkMsb0RBWThCO0FBRzlCLHdEQUtrQztBQUlsQywyQ0FHcUI7QUErRHJCLG9FQUFvRTtBQUM3RCxLQUFLLFVBQVUsZ0JBQWdCLENBQ3BDLE9BQXlCLEVBQ3pCLGNBQXNCLEVBQ3RCLFVBQThCLEVBQUUsRUFDaEMsaUJBQStDO0lBRS9DLE1BQU0sYUFBYSxHQUFHLE9BQU8sQ0FBQyxhQUFhLElBQUksbUJBQW1CLENBQUM7SUFDbkUsTUFBTSxnQkFBZ0IsR0FBRyxPQUFPLENBQUMsZ0JBQWdCLElBQUksU0FBUyxDQUFDO0lBRS9ELElBQUksQ0FBQztRQUNILE1BQU0sUUFBUSxHQUFHLE1BQU0sT0FBTyxDQUFDLEdBQUcsQ0FBQyxLQUFLLENBQUMsUUFBUSxDQUFDO1lBQ2hELFNBQVMsRUFBRSxPQUFPLENBQUMsUUFBUSxDQUFDLFNBQVM7WUFDckMsTUFBTSxFQUFFLE9BQU8sQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLEVBQUU7WUFDaEMsY0FBYztTQUNmLENBQUMsQ0FBQztRQUNILElBQUksUUFBUSxDQUFDLEtBQUssRUFBRSxDQUFDO1lBQ25CLE9BQU8sUUFBUSxDQUFDLEtBQUssQ0FBQztRQUN4QixDQUFDO0lBQ0gsQ0FBQztJQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7UUFDZixJQUFJLENBQUMsb0JBQW9CLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQztZQUNqQyxNQUFNLEtBQUssQ0FBQztRQUNkLENBQUM7SUFDSCxDQUFDO0lBRUQsTUFBTSxZQUFZLEdBQUcsRUFBRSxHQUFHLE9BQU8sQ0FBQyxHQUFHLEVBQUUsQ0FBQztJQUN4QyxNQUFNLGtCQUFrQixHQUF1QjtRQUM3QyxjQUFjO1FBQ2QsWUFBWTtRQUNaLFNBQVMsRUFBRSxPQUFPLENBQUMs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