async def exchange_token_from_credentials( self, turn_context: TurnContext, oauth_app_credentials: AppCredentials, connection_name: str, user_id: str, exchange_request: TokenExchangeRequest, ) -> TokenResponse: exchangeable_value = exchange_request.token or exchange_request.uri key = ExchangeableToken( channel_id=turn_context.activity.channel_id, connection_name=connection_name, exchangeable_item=exchangeable_value, user_id=user_id, ) token_exchange_response = self.exchangeable_tokens.get(key.to_key()) if token_exchange_response: return TokenResponse( channel_id=key.channel_id, connection_name=key.connection_name, token=token_exchange_response.token, expiration=None, ) return None
async def exchange_token_from_credentials( self, turn_context: TurnContext, oauth_app_credentials: AppCredentials, connection_name: str, user_id: str, exchange_request: TokenExchangeRequest, ) -> TokenResponse: exchangeable_value = exchange_request.token or exchange_request.uri key = ExchangeableToken( channel_id=turn_context.activity.channel_id, connection_name=connection_name, exchangeable_item=exchangeable_value, user_id=user_id, ) token_exchange_response = self.exchangeable_tokens.get(key.to_key()) if token_exchange_response: if token_exchange_response.token == TestAdapter.__EXCEPTION_EXPECTED: raise Exception("Exception occurred during exchanging tokens") return TokenResponse( channel_id=key.channel_id, connection_name=key.connection_name, token=token_exchange_response.token, expiration=None, ) return None
async def inspector(activity: Activity, description: str = None): # pylint: disable=unused-argument self.assertTrue(len(activity.attachments) == 1) self.assertTrue( activity.attachments[0].content_type == CardFactory.content_types.oauth_card ) # send a mock EventActivity back to the bot with the token adapter.add_user_token( connection_name, activity.channel_id, activity.recipient.id, token ) event_activity = create_reply(activity) event_activity.type = ActivityTypes.event event_activity.from_property, event_activity.recipient = ( event_activity.recipient, event_activity.from_property, ) event_activity.name = "tokens/response" event_activity.value = TokenResponse( connection_name=connection_name, token=token ) context = TurnContext(adapter, event_activity) await callback_handler(context)
async def get_user_token(self, context: TurnContext, connection_name: str, magic_code: str = None) -> TokenResponse: key = UserToken() key.channel_id = context.activity.channel_id key.connection_name = connection_name key.user_id = context.activity.from_property.id if magic_code: magic_code_record = list(filter(lambda x: key.equals_key(x.key), self._magic_codes)) if magic_code_record and len(magic_code_record) > 0 and magic_code_record[0].magic_code == magic_code: # Move the token to long term dictionary. self.add_user_token(connection_name, key.channel_id, key.user_id, magic_code_record[0].key.token) # Remove from the magic code list. idx = self._magic_codes.index(magic_code_record[0]) self._magic_codes = [self._magic_codes.pop(idx)] match = list(filter(lambda x: key.equals_key(x), self._user_tokens)) if match and len(match) > 0: return TokenResponse( connection_name=match[0].connection_name, token=match[0].token, expiration=None ) else: # Not found. return None
async def get_user_token( self, context: TurnContext, connection_name: str, magic_code: str = None, oauth_app_credentials: AppCredentials = None, # pylint: disable=unused-argument ) -> TokenResponse: key = UserToken() key.channel_id = context.activity.channel_id key.connection_name = connection_name key.user_id = context.activity.from_property.id if magic_code: magic_code_record = list( filter(lambda x: key.equals_key(x.key), self._magic_codes)) if magic_code_record and magic_code_record[ 0].magic_code == magic_code: # Move the token to long term dictionary. self.add_user_token( connection_name, key.channel_id, key.user_id, magic_code_record[0].key.token, ) # Remove from the magic code list. idx = self._magic_codes.index(magic_code_record[0]) self._magic_codes = [self._magic_codes.pop(idx)] match = [token for token in self._user_tokens if key.equals_key(token)] if match: return TokenResponse( connection_name=match[0].connection_name, token=match[0].token, expiration=None, ) # Not found. return None
async def _recognize_token( self, dialog_context: DialogContext) -> PromptRecognizerResult: context = dialog_context.context token = None if OAuthPrompt._is_token_response_event(context): token = context.activity.value # fixup the turnContext's state context if this was received from a skill host caller state: CallerInfo = dialog_context.active_dialog.state[ OAuthPrompt.PERSISTED_CALLER] if state: # set the ServiceUrl to the skill host's Url dialog_context.context.activity.service_url = state.caller_service_url # recreate a ConnectorClient and set it in TurnState so replies use the correct one if not isinstance(context.adapter, ConnectorClientBuilder): raise TypeError( "OAuthPrompt: IConnectorClientProvider interface not implemented by the current adapter" ) connector_client_builder: ConnectorClientBuilder = context.adapter claims_identity = context.turn_state.get( BotAdapter.BOT_IDENTITY_KEY) connector_client = await connector_client_builder.create_connector_client( dialog_context.context.activity.service_url, claims_identity, state.scope, ) context.turn_state[ BotAdapter.BOT_CONNECTOR_CLIENT_KEY] = connector_client elif OAuthPrompt._is_teams_verification_invoke(context): code = context.activity.value["state"] try: token = await self.get_user_token(context, code) if token is not None: await context.send_activity( Activity( type="invokeResponse", value=InvokeResponse(int(HTTPStatus.OK)), )) else: await context.send_activity( Activity( type="invokeResponse", value=InvokeResponse(int(HTTPStatus.NOT_FOUND)), )) except Exception: await context.send_activity( Activity( type="invokeResponse", value=InvokeResponse( int(HTTPStatus.INTERNAL_SERVER_ERROR)), )) elif self._is_token_exchange_request_invoke(context): if isinstance(context.activity.value, dict): context.activity.value = TokenExchangeInvokeRequest( ).from_dict(context.activity.value) if not (context.activity.value and self._is_token_exchange_request( context.activity.value)): # Received activity is not a token exchange request. await context.send_activity( self._get_token_exchange_invoke_response( int(HTTPStatus.BAD_REQUEST), "The bot received an InvokeActivity that is missing a TokenExchangeInvokeRequest value." " This is required to be sent with the InvokeActivity.", )) elif (context.activity.value.connection_name != self._settings.connection_name): # Connection name on activity does not match that of setting. await context.send_activity( self._get_token_exchange_invoke_response( int(HTTPStatus.BAD_REQUEST), "The bot received an InvokeActivity with a TokenExchangeInvokeRequest containing a" " ConnectionName that does not match the ConnectionName expected by the bots active" " OAuthPrompt. Ensure these names match when sending the InvokeActivityInvalid" " ConnectionName in the TokenExchangeInvokeRequest", )) elif not getattr(context.adapter, "exchange_token"): # Token Exchange not supported in the adapter. await context.send_activity( self._get_token_exchange_invoke_response( int(HTTPStatus.BAD_GATEWAY), "The bot's BotAdapter does not support token exchange operations." " Ensure the bot's Adapter supports the ExtendedUserTokenProvider interface.", )) raise AttributeError( "OAuthPrompt._recognize_token(): not supported by the current adapter." ) else: # No errors. Proceed with token exchange. extended_user_token_provider: ExtendedUserTokenProvider = context.adapter token_exchange_response = None try: token_exchange_response = await extended_user_token_provider.exchange_token_from_credentials( context, self._settings.oath_app_credentials, self._settings.connection_name, context.activity.from_property.id, TokenExchangeRequest( token=context.activity.value.token), ) except: # Ignore Exceptions # If token exchange failed for any reason, tokenExchangeResponse above stays null, and # hence we send back a failure invoke response to the caller. pass if not token_exchange_response or not token_exchange_response.token: await context.send_activity( self._get_token_exchange_invoke_response( int(HTTPStatus.PRECONDITION_FAILED), "The bot is unable to exchange token. Proceed with regular login.", )) else: await context.send_activity( self._get_token_exchange_invoke_response( int(HTTPStatus.OK), None, context.activity.value.id)) token = TokenResponse( channel_id=token_exchange_response.channel_id, connection_name=token_exchange_response. connection_name, token=token_exchange_response.token, expiration=None, ) elif context.activity.type == ActivityTypes.message and context.activity.text: match = re.match(r"(?<!\d)\d{6}(?!\d)", context.activity.text) if match: token = await self.get_user_token(context, match[0]) return (PromptRecognizerResult(True, token) if token is not None else PromptRecognizerResult())
async def _recognize_token(self, context: TurnContext) -> PromptRecognizerResult: token = None if OAuthPrompt._is_token_response_event(context): token = context.activity.value elif OAuthPrompt._is_teams_verification_invoke(context): code = context.activity.value["state"] try: token = await self.get_user_token(context, code) if token is not None: await context.send_activity( Activity( type="invokeResponse", value=InvokeResponse(int(HTTPStatus.OK)), )) else: await context.send_activity( Activity( type="invokeResponse", value=InvokeResponse(int(HTTPStatus.NOT_FOUND)), )) except Exception: await context.send_activity( Activity( type="invokeResponse", value=InvokeResponse( int(HTTPStatus.INTERNAL_SERVER_ERROR)), )) elif self._is_token_exchange_request_invoke(context): if isinstance(context.activity.value, dict): context.activity.value = TokenExchangeInvokeRequest( ).from_dict(context.activity.value) if not (context.activity.value and self._is_token_exchange_request( context.activity.value)): # Received activity is not a token exchange request. await context.send_activity( self._get_token_exchange_invoke_response( int(HTTPStatus.BAD_REQUEST), "The bot received an InvokeActivity that is missing a TokenExchangeInvokeRequest value." " This is required to be sent with the InvokeActivity.", )) elif (context.activity.value.connection_name != self._settings.connection_name): # Connection name on activity does not match that of setting. await context.send_activity( self._get_token_exchange_invoke_response( int(HTTPStatus.BAD_REQUEST), "The bot received an InvokeActivity with a TokenExchangeInvokeRequest containing a" " ConnectionName that does not match the ConnectionName expected by the bots active" " OAuthPrompt. Ensure these names match when sending the InvokeActivityInvalid" " ConnectionName in the TokenExchangeInvokeRequest", )) elif not getattr(context.adapter, "exchange_token"): # Token Exchange not supported in the adapter. await context.send_activity( self._get_token_exchange_invoke_response( int(HTTPStatus.BAD_GATEWAY), "The bot's BotAdapter does not support token exchange operations." " Ensure the bot's Adapter supports the ITokenExchangeProvider interface.", )) raise AttributeError( "OAuthPrompt.recognize(): not supported by the current adapter." ) else: # No errors. Proceed with token exchange. extended_user_token_provider: ExtendedUserTokenProvider = context.adapter token_exchange_response = await extended_user_token_provider.exchange_token_from_credentials( context, self._settings.oath_app_credentials, self._settings.connection_name, context.activity.from_property.id, TokenExchangeRequest(token=context.activity.value.token), ) if not token_exchange_response or not token_exchange_response.token: await context.send_activity( self._get_token_exchange_invoke_response( int(HTTPStatus.CONFLICT), "The bot is unable to exchange token. Proceed with regular login.", )) else: await context.send_activity( self._get_token_exchange_invoke_response( int(HTTPStatus.OK), None, context.activity.value.id)) token = TokenResponse( channel_id=token_exchange_response.channel_id, connection_name=token_exchange_response. connection_name, token=token_exchange_response.token, expiration=None, ) elif context.activity.type == ActivityTypes.message and context.activity.text: match = re.match(r"(?<!\d)\d{6}(?!\d)", context.activity.text) if match: token = await self.get_user_token(context, match[0]) return (PromptRecognizerResult(True, token) if token is not None else PromptRecognizerResult())