Skip to content

Commit

Permalink
Port SSO changes from C# to JS (#1738)
Browse files Browse the repository at this point in the history
* initial commit for sso (most changes)

* add documentation comments

* added null checks. hooked up teams

* started adding tests

* test case passes

* mostly final changes before pr

* merge with master

* address comments

* fix merge conflicts

* addressed comments

* fix some broken tests (2 remaining)

* bugs during e2e testing.Also remove invalid testcase.

* add accidentally reverted change. fix tests. merge with another c# pr

* address merge conflicts

* apply PR feedback and minor cleanup

Co-authored-by: Swagat Mishra <swagatm@microsoft.com>
Co-authored-by: stevengum <14935595+stevengum@users.noreply.github.com>
  • Loading branch information
3 people authored Mar 6, 2020
1 parent 3d05f87 commit 7f6c6df
Show file tree
Hide file tree
Showing 31 changed files with 1,315 additions and 174 deletions.
55 changes: 54 additions & 1 deletion libraries/botbuilder-core/src/activityHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
*/
import { ChannelAccount, MessageReaction, TurnContext } from '.';
import { ActivityHandlerBase } from './activityHandlerBase';
import { verifyStateOperationName, tokenExchangeOperationName, tokenResponseEventName } from './signInConstants';
import { InvokeResponse } from './invokeResponse';

/**
* Describes a bot activity event handler, for use with an [ActivityHandler](xref:botbuilder-core.ActivityHandler) object.
Expand Down Expand Up @@ -250,6 +252,22 @@ export class ActivityHandler extends ActivityHandlerBase {
return this.on('Event', handler);
}

/**
* Registers an activity event handler for the _invoke_ event, emitted for every incoming event activity.
*
* @param handler The event handler.
*
* @remarks
* Returns a reference to the [ActivityHandler](xref:botbuilder-core.ActivityHandler) object.
*
* To handle a `signin/verifyState` invoke event or `signin/tokenExchange`, use the
* [onInvokeActivity](xref:botbuilder-core.ActivityHandler.onInvokeActivity) sub-type
* event handler. To handle other named events, add logic to this handler.
*/
public onInvoke(handler: BotHandler): this {
return this.on('Invoke', handler);
}

/**
* Registers an activity event handler for the _end of conversation_ activity.
*
Expand Down Expand Up @@ -397,6 +415,41 @@ export class ActivityHandler extends ActivityHandlerBase {
await this.handle(context, 'Message', this.defaultNextEvent(context));
}

/*
* Runs all registered _invoke_ handlers and then continues the event emission process.
*
* @param context The context object for the current turn.
*
* @remarks
* Overwrite this method to support channel-specific behavior across multiple channels.
* The default logic is to call any handlers registered via
* [onInvoke](xref:botbuilder-core.ActivityHandler.onInvoke),
* and then continue by calling [defaultNextEvent](xref:botbuilder-core.ActivityHandler.defaultNextEvent).
*/
protected async onInvokeActivity(context: TurnContext): Promise<void|InvokeResponse> {
if(context.activity.name && (context.activity.name === verifyStateOperationName || context.activity.name === tokenExchangeOperationName)) {
await this.onSignInInvoke(context);
}
else {
await this.handle(context, 'Invoke', this.defaultNextEvent(context));
}
}

/*
* Runs all registered _signin invoke activity type_ handlers and then continues the event emission process.
*
* @param context The context object for the current turn.
*
* @remarks
* Overwrite this method to support channel-specific behavior across multiple channels.
* The default logic is to call any handlers registered via
* [onSignInInvoke](xref:botbuilder-core.ActivityHandler.onSignInInvoke),
* and then continue by calling [defaultNextEvent](xref:botbuilder-core.ActivityHandler.defaultNextEvent).
*/
protected async onSignInInvoke(context: TurnContext): Promise<void> {
await this.handle(context, 'Invoke', this.defaultNextEvent(context));
}

/**
* Runs all registered _endOfConversation_ handlers and then continues the event emission process.
*
Expand Down Expand Up @@ -597,7 +650,7 @@ export class ActivityHandler extends ActivityHandlerBase {
* - Continue by calling [defaultNextEvent](xref:botbuilder-core.ActivityHandler.defaultNextEvent).
*/
protected async dispatchEventActivity(context: TurnContext): Promise<void> {
if (context.activity.name === 'tokens/response') {
if (context.activity.name === tokenResponseEventName) {
await this.handle(context, 'TokenResponseEvent', this.defaultNextEvent(context));
} else {
await this.defaultNextEvent(context)();
Expand Down
8 changes: 8 additions & 0 deletions libraries/botbuilder-core/src/activityHandlerBase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export class ActivityHandlerBase {
* - Conversation update activities
* - Message reaction activities
* - Event activities
* - Invoke activities
* - _Unrecognized_ activities, ones that this class has not otherwise defined an _on event_ method for.
*/
protected async onTurnActivity(context: TurnContext): Promise<void> {
Expand All @@ -60,6 +61,9 @@ export class ActivityHandlerBase {
case ActivityTypes.Event:
await this.onEventActivity(context);
break;
case ActivityTypes.Invoke:
await this.onInvokeActivity(context);
break;
case ActivityTypes.EndOfConversation:
await this.onEndOfConversationActivity(context);
break;
Expand Down Expand Up @@ -149,6 +153,10 @@ export class ActivityHandlerBase {
return;
}

protected async onInvokeActivity(context: TurnContext): Promise<void|any> {
return;
}

/**
* Provides a hook for emitting the _end of conversation_ event.
*
Expand Down
7 changes: 4 additions & 3 deletions libraries/botbuilder-core/src/cardFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
import { ActionTypes, AnimationCard, Attachment, AudioCard, CardAction, CardImage, HeroCard, MediaUrl, OAuthCard, O365ConnectorCard, ReceiptCard, SigninCard, ThumbnailCard, VideoCard } from 'botframework-schema';
import { ActionTypes, AnimationCard, Attachment, AudioCard, CardAction, CardImage, HeroCard, MediaUrl, OAuthCard, O365ConnectorCard, ReceiptCard, SigninCard, ThumbnailCard, TokenExchangeResource, VideoCard } from 'botframework-schema';

/**
* Provides methods for formatting the various card types a bot can return.
Expand Down Expand Up @@ -179,12 +179,13 @@ export class CardFactory {
* @remarks
* OAuth cards support the Bot Framework's single sign on (SSO) service.
*/
public static oauthCard(connectionName: string, title: string, text?: string, link?: string): Attachment {
public static oauthCard(connectionName: string, title: string, text?: string, link?: string, tokenExchangeResource?: TokenExchangeResource): Attachment {
const card: Partial<OAuthCard> = {
buttons: [
{ type: ActionTypes.Signin, title: title, value: link, channelData: undefined }
],
connectionName: connectionName
connectionName,
tokenExchangeResource
};
if (text) { card.text = text; }

Expand Down
49 changes: 0 additions & 49 deletions libraries/botbuilder-core/src/credentialTokenProvider.ts

This file was deleted.

95 changes: 95 additions & 0 deletions libraries/botbuilder-core/src/extendedUserTokenProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/**
* @module botbuilder
*/
/**
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/

import { AppCredentials } from './appCredentials';
import { IUserTokenProvider } from './userTokenProvider';
import { TurnContext } from './turnContext';
import { SignInUrlResponse ,TokenResponse, TokenExchangeRequest } from 'botframework-schema';

/**
* Interface for User Token OAuth Single Sign On and Token Exchange APIs for BotAdapters
*/
export interface ExtendedUserTokenProvider extends IUserTokenProvider {
/**
* Retrieves the OAuth token for a user that is in a sign-in flow.
* @param context Context for the current turn of conversation with the user.
* @param connectionName Name of the auth connection to use.
* @param magicCode (Optional) Optional user entered code to validate.
*/
getUserToken(context: TurnContext, connectionName: string, magicCode?: string, appCredentials?: AppCredentials): Promise<TokenResponse>;

/**
* Signs the user out with the token server.
* @param context Context for the current turn of conversation with the user.
* @param connectionName Name of the auth connection to use.
* @param userId User id of user to sign out.
* @param oAuthAppCredentials AppCredentials for OAuth.
*/
signOutUser(context: TurnContext, connectionName: string, userId?: string, appCredentials?: AppCredentials): Promise<void>;

/**
* Gets a signin link from the token server that can be sent as part of a SigninCard.
* @param context Context for the current turn of conversation with the user.
* @param connectionName Name of the auth connection to use.
* @param oAuthAppCredentials AppCredentials for OAuth.
*/
getSignInLink(context: TurnContext, connectionName: string, appCredentials?: AppCredentials): Promise<string>;

/**
* Signs the user out with the token server.
* @param context Context for the current turn of conversation with the user.
* @param connectionName Name of the auth connection to use.
* @param oAuthAppCredentials AppCredentials for OAuth.
*/
getAadTokens(context: TurnContext, connectionName: string, resourceUrls: string[], appCredentials?: AppCredentials): Promise<{
[propertyName: string]: TokenResponse;
}>;

/**
* Get the raw signin resource to be sent to the user for signin for a connection name.
* @param context Context for the current turn of conversation with the user.
* @param connectionName Name of the auth connection to use.
*/
getSignInResource(context: TurnContext, connectionName: string): Promise<SignInUrlResponse>;

/**
* Get the raw signin resource to be sent to the user for signin for a connection name.
* @param context Context for the current turn of conversation with the user.
* @param connectionName Name of the auth connection to use.
* @param userId The user id that will be associated with the token.
* @param finalRedirect The final URL that the OAuth flow will redirect to.
*/
getSignInResource(context: TurnContext, connectionName: string, userId: string, finalRedirect?: string): Promise<SignInUrlResponse>;

/**
* Get the raw signin resource to be sent to the user for signin for a connection name.
* @param context Context for the current turn of conversation with the user.
* @param connectionName Name of the auth connection to use.
* @param userId The user id that will be associated with the token.
* @param finalRedirect The final URL that the OAuth flow will redirect to.
*/
getSignInResource(context: TurnContext, connectionName: string, userId: string, finalRedirect?: string, appCredentials?: AppCredentials): Promise<SignInUrlResponse>;

/**
* Performs a token exchange operation such as for single sign-on.
* @param context Context for the current turn of conversation with the user.
* @param connectionName Name of the auth connection to use.
* @param userId The user id that will be associated with the token.
* @param tokenExchangeRequest The exchange request details, either a token to exchange or a uri to exchange.
*/
exchangeToken(context: TurnContext, connectionName: string, userId: string, tokenExchangeRequest: TokenExchangeRequest): Promise<TokenResponse>;

/**
* Performs a token exchange operation such as for single sign-on.
* @param context Context for the current turn of conversation with the user.
* @param connectionName Name of the auth connection to use.
* @param userId The user id that will be associated with the token.
* @param tokenExchangeRequest The exchange request details, either a token to exchange or a uri to exchange.
*/
exchangeToken(context: TurnContext, connectionName: string, userId: string, tokenExchangeRequest: TokenExchangeRequest, appCredentials: AppCredentials): Promise<TokenResponse>;
}
8 changes: 5 additions & 3 deletions libraries/botbuilder-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
*/

export * from 'botframework-schema';
export * from './appCredentials';
export * from './activityHandler';
export * from './activityHandlerBase';
export * from './autoSaveStateMiddleware';
Expand All @@ -19,6 +20,7 @@ export * from './browserStorage';
export * from './cardFactory';
export * from './conversationState';
export * from './invokeResponse';
export * from './extendedUserTokenProvider';
export * from './memoryStorage';
export * from './memoryTranscriptStore';
export * from './messageFactory';
Expand All @@ -27,16 +29,16 @@ export * from './privateConversationState';
export * from './propertyManager';
export * from './recognizerResult';
export * from './showTypingMiddleware';
export * from './signInConstants';
export { BotFrameworkSkill, BotFrameworkClient, SkillConversationIdFactoryBase, SkillConversationReference,
SkillConversationIdFactoryOptions } from './skills';
export * from './skypeMentionNormalizeMiddleware';
export * from './statusCodes';
export * from './storage';
export * from './telemetryLoggerMiddleware';
export * from './testAdapter';
export * from './transcriptLogger';
export * from './turnContext';
export * from './userState';
export * from './userTokenProvider';
export * from './userTokenSettings';
export * from './appCredentials';
export * from './credentialTokenProvider';
export * from './userTokenSettings';
11 changes: 11 additions & 0 deletions libraries/botbuilder-core/src/signInConstants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/**
* @module botbuilder
*/
/**
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/

export const verifyStateOperationName: string = 'signin/verifyState';
export const tokenExchangeOperationName: string = 'signin/tokenExchange';
export const tokenResponseEventName: string = 'tokens/response';
20 changes: 20 additions & 0 deletions libraries/botbuilder-core/src/statusCodes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/**
* @module botbuilder
*/
/**
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/

export enum StatusCodes {
OK = 200,
BAD_REQUEST = 400,
UNAUTHORIZED = 401,
NOT_FOUND = 404,
METHOD_NOT_ALLOWED = 405,
CONFLICT = 409,
UPGRADE_REQUIRED = 426,
INTERNAL_SERVER_ERROR = 500,
NOT_IMPLEMENTED = 501,
BAD_GATEWAY = 502
}
Loading

0 comments on commit 7f6c6df

Please sign in to comment.