Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,7 @@ async def process_activity(
use_anonymous_auth_callback = False
if (
not claims_identity.is_authenticated
and activity.channel_id == Channels.emulator
and claims_identity.authentication_type == "Anonymous"
):
use_anonymous_auth_callback = True

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from microsoft.agents.authorization import (
AuthenticationConstants,
AnonymousTokenProvider,
ClaimsIdentity,
Connections,
)
Expand All @@ -15,6 +16,8 @@


class RestChannelServiceClientFactory(ChannelServiceClientFactoryBase):
_ANONYMOUS_TOKEN_PROVIDER = AnonymousTokenProvider()

def __init__(
self,
configuration: Any,
Expand Down Expand Up @@ -43,22 +46,31 @@ async def create_connector_client(
"RestChannelServiceClientFactory.create_connector_client: audience can't be None or Empty"
)

token_provider = (
self._connections.get_token_provider(claims_identity, service_url)
if not use_anonymous
else self._ANONYMOUS_TOKEN_PROVIDER
)

return ConnectorClient(
endpoint=service_url,
credential_token_provider=self._connections.get_token_provider(
claims_identity, service_url
),
credential_token_provider=token_provider,
credential_resource_url=audience,
credential_scopes=scopes,
)

async def create_user_token_client(
self, claims_identity: ClaimsIdentity, use_anonymous: bool = False
) -> UserTokenClient:
return UserTokenClient(
credential_token_provider=self._connections.get_token_provider(
token_provider = (
self._connections.get_token_provider(
claims_identity, self._token_service_endpoint
),
)
if not use_anonymous
else self._ANONYMOUS_TOKEN_PROVIDER
)
return UserTokenClient(
credential_token_provider=token_provider,
credential_resource_url=self._token_service_audience,
credential_scopes=[f"{self._token_service_audience}/.default"],
endpoint=self._token_service_endpoint,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from .access_token_provider_base import AccessTokenProviderBase
from .authentication_constants import AuthenticationConstants
from .anonymous_token_provider import AnonymousTokenProvider
from .connections import Connections
from .agent_auth_configuration import AgentAuthConfiguration
from .claims_identity import ClaimsIdentity
Expand All @@ -8,6 +9,7 @@
__all__ = [
"AccessTokenProviderBase",
"AuthenticationConstants",
"AnonymousTokenProvider",
"Connections",
"AgentAuthConfiguration",
"ClaimsIdentity",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from .access_token_provider_base import AccessTokenProviderBase


class AnonymousTokenProvider(AccessTokenProviderBase):
"""
A class that provides an anonymous token for authentication.
This is used when no authentication is required.
"""

async def get_access_token(
self, resource_url: str, scopes: list[str], force_refresh: bool = False
) -> str:
return ""
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ def validate_token(self, token: str) -> ClaimsIdentity:
# This probably should return a ClaimsIdentity
return ClaimsIdentity(decoded_token, True)

def get_anonymous_claims(self) -> ClaimsIdentity:
return ClaimsIdentity({}, False, authentication_type="Anonymous")

def _get_public_key_or_secret(self, token: str) -> PyJWK:
header = get_unverified_header(token)
unverified_payload: dict = decode(token, options={"verify_signature": False})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ async def jwt_authorization_middleware(request: Request, handler):
except ValueError as e:
return json_response({"error": str(e)}, status=401)
else:
if (not auth_config.CLIENT_ID) and (request.app["env"] == "DEV"):
# TODO: Define anonymous strategy
request["user"] = {"name": "anonymous"}
if not auth_config.CLIENT_ID:
# TODO: Refine anonymous strategy
request["claims_identity"] = token_validator.get_anonymous_claims()
else:
return json_response(
{"error": "Authorization header not found"}, status=401
Expand Down
3 changes: 3 additions & 0 deletions test_samples/agent_to_agent/agent_1/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# Licensed under the MIT License.

from aiohttp.web import Application, Request, Response, run_app
from dotenv import load_dotenv

from microsoft.agents.builder import RestChannelServiceClientFactory
from microsoft.agents.hosting.aiohttp import (
Expand All @@ -25,6 +26,8 @@
from agent1 import Agent1
from config import DefaultConfig

load_dotenv()

AUTH_PROVIDER = MsalAuth(DefaultConfig())


Expand Down
18 changes: 10 additions & 8 deletions test_samples/agent_to_agent/agent_1/config.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from os import environ
from microsoft.agents.authentication.msal import AuthTypes, MsalAuthConfiguration
from microsoft.agents.client import (
ChannelHostConfiguration,
Expand All @@ -9,12 +10,13 @@
class DefaultConfig(MsalAuthConfiguration, ChannelsConfiguration):
"""Agent Configuration"""

AUTH_TYPE = AuthTypes.client_secret
TENANT_ID = ""
CLIENT_ID = ""
CLIENT_SECRET = ""
PORT = 3978
SCOPES = ["https://api.botframework.com/.default"]
def __init__(self) -> None:
self.AUTH_TYPE = AuthTypes.client_secret
self.TENANT_ID = "" or environ.get("TENANT_ID")
self.CLIENT_ID = "" or environ.get("CLIENT_ID")
self.CLIENT_SECRET = "" or environ.get("CLIENT_SECRET")
self.PORT = 3978
self.SCOPES = ["https://api.botframework.com/.default"]

# ChannelHost configuration
@staticmethod
Expand All @@ -23,13 +25,13 @@ def CHANNEL_HOST_CONFIGURATION():
CHANNELS=[
ChannelInfo(
id="EchoAgent",
app_id="", # Target agent's app_id
app_id="" or environ.get("TARGET_APP_ID"), # Target agent's app_id
resource_url="http://localhost:3999/api/messages",
token_provider="ChannelConnection",
channel_factory="HttpAgentClient",
endpoint="http://localhost:3999/api/messages",
)
],
HOST_ENDPOINT="http://localhost:3978/api/botresponse/",
HOST_APP_ID="", # usually the same as CLIENT_ID
HOST_APP_ID="" or environ.get("CLIENT_ID"), # usually the same as CLIENT_ID
)
3 changes: 3 additions & 0 deletions test_samples/agent_to_agent/agent_2/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# Licensed under the MIT License.

from aiohttp.web import Application, Request, Response, run_app
from dotenv import load_dotenv

from microsoft.agents.builder import RestChannelServiceClientFactory
from microsoft.agents.hosting.aiohttp import CloudAdapter, jwt_authorization_middleware
Expand All @@ -15,6 +16,8 @@
from agent2 import Agent2
from config import DefaultConfig

load_dotenv()

AUTH_PROVIDER = MsalAuth(DefaultConfig())


Expand Down
12 changes: 7 additions & 5 deletions test_samples/agent_to_agent/agent_2/config.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
from os import environ
from microsoft.agents.authentication.msal import AuthTypes, MsalAuthConfiguration


class DefaultConfig(MsalAuthConfiguration):
"""Agent Configuration"""

AUTH_TYPE = AuthTypes.client_secret
TENANT_ID = ""
CLIENT_ID = ""
CLIENT_SECRET = ""
PORT = 3999
def __init__(self) -> None:
self.AUTH_TYPE = AuthTypes.client_secret
self.TENANT_ID = "" or environ.get("TENANT_ID")
self.CLIENT_ID = "" or environ.get("CLIENT_ID")
self.CLIENT_SECRET = "" or environ.get("CLIENT_SECRET")
self.PORT = 3999
3 changes: 3 additions & 0 deletions test_samples/echo_agent/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# Licensed under the MIT License.

from aiohttp.web import Application, Request, Response, run_app
from dotenv import load_dotenv

from microsoft.agents.builder import RestChannelServiceClientFactory
from microsoft.agents.hosting.aiohttp import CloudAdapter, jwt_authorization_middleware
Expand All @@ -15,6 +16,8 @@
from echo_agent import EchoAgent
from config import DefaultConfig

load_dotenv()

AUTH_PROVIDER = MsalAuth(DefaultConfig())


Expand Down
12 changes: 7 additions & 5 deletions test_samples/echo_agent/config.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
from os import environ
from microsoft.agents.authentication.msal import AuthTypes, MsalAuthConfiguration


class DefaultConfig(MsalAuthConfiguration):
"""Agent Configuration"""

AUTH_TYPE = AuthTypes.client_secret
TENANT_ID = ""
CLIENT_ID = ""
CLIENT_SECRET = ""
PORT = 3978
def __init__(self) -> None:
self.AUTH_TYPE = AuthTypes.client_secret
self.TENANT_ID = "" or environ.get("TENANT_ID")
self.CLIENT_ID = "" or environ.get("CLIENT_ID")
self.CLIENT_SECRET = "" or environ.get("CLIENT_SECRET")
self.PORT = 3978