Skip to main content
The MABQ BigQuery Agent is designed exclusively for Microsoft Teams. It uses the Teams JavaScript SDK for authentication, user context, and access control.

Why Teams-Only?

The application enforces Teams-only access for several reasons:

SSO Authentication

Teams provides built-in Single Sign-On (SSO) tokens for secure authentication

User Context

Access to user display name, email, and organizational context

Enterprise Security

Leverages Microsoft’s enterprise-grade security infrastructure

Seamless UX

No separate login required - uses existing Teams session

Teams SDK Setup

The frontend uses @microsoft/teams-js v2.48.1 for Teams integration:
{
  "dependencies": {
    "@microsoft/teams-js": "^2.48.1"
  }
}

Initialization Flow

The Teams SDK is initialized in the main page component’s useEffect hook:
import * as microsoftTeams from "@microsoft/teams-js";

export default function Home() {
  const [authToken, setAuthToken] = useState<string>("");
  const [userName, setUserName] = useState<string>("");
  const [isTeams, setIsTeams] = useState<boolean | null>(null); 
  const [tokenReady, setTokenReady] = useState<boolean>(false);

  useEffect(() => {
    microsoftTeams.app.initialize()
      .then(() => {
        setIsTeams(true);
        
        microsoftTeams.app.getContext().then((context) => {
          if (context.user) setUserName(context.user.displayName || "");
        });

        // token
        microsoftTeams.authentication.getAuthToken()
          .then((token) => {
            console.log(" Token seguro recibido, encendiendo el chat..."); 
            setAuthToken(token);
            setTokenReady(true)
          })
          .catch((error) => {
            console.error(" Error obteniendo token:", error);
          });
      })
      .catch(() => {
        console.log(" Modo Web detectado");
        setIsTeams(false);
      });
  }, []);
}

State Management

The component tracks four key states:
StateTypePurpose
authTokenstringStores the Teams SSO token for backend authentication
userNamestringUser’s display name from Teams context
isTeamsboolean | nullnull = checking, true = in Teams, false = not in Teams
tokenReadybooleantrue when authentication token is successfully retrieved

Authentication Token Flow

1

Initialize Teams SDK

microsoftTeams.app.initialize()
Attempts to initialize the Teams SDK. This Promise resolves if running inside Teams, and rejects if running in a browser.
2

Get User Context

microsoftTeams.app.getContext().then((context) => {
  if (context.user) setUserName(context.user.displayName || "");
});
Retrieves the user’s Teams context including:
  • Display name
  • Email address
  • User principal name (UPN)
  • Tenant ID
3

Request Authentication Token

microsoftTeams.authentication.getAuthToken()
  .then((token) => {
    console.log(" Token seguro recibido, encendiendo el chat..."); 
    setAuthToken(token);
    setTokenReady(true)
  })
Requests an SSO token from Teams. This token is a JWT that contains:
  • User identity
  • Tenant information
  • Expiration time
  • Signature for validation
4

Handle Errors

.catch((error) => {
  console.error(" Error obteniendo token:", error);
});
Logs any errors during token retrieval (e.g., user declined consent)
The getAuthToken() method may prompt the user for consent the first time the app is used. Subsequent calls use the cached consent.

Access Control

The app enforces strict access control based on the Teams detection:

Non-Teams Environment

If Teams initialization fails (isTeams === false), the app displays a restriction message:
if (isTeams === false) {
  return (
    <div className="flex h-screen items-center justify-center bg-red-50 text-red-600 font-semibold">
       Acceso Restringido. Esta app solo funciona dentro de Microsoft Teams.
    </div>
  );
}
Users attempting to access the application directly via browser will see this restriction message. The app will NOT function outside of Teams.

Loading State

While checking for Teams or waiting for the authentication token:
if (isTeams === null || !tokenReady) {
  return (
    <div className="flex h-screen flex-col items-center justify-center bg-gray-50">
      <div className="w-8 h-8 border-4 border-blue-600 border-t-transparent rounded-full animate-spin mb-4"></div>
      <p className="text-lg text-gray-600 font-medium">Autenticando de forma segura...</p>
    </div>
  );
}
This loading screen displays:
  • A spinning blue loader (CSS animation)
  • A status message indicating secure authentication

Token Usage

Once the token is retrieved, it’s passed to CopilotKit:
<CopilotKit 
  runtimeUrl="/api/copilotkit" 
  agent="default_agent"
  headers={{
    "Authorization": `Bearer ${authToken}`,
    "X-Visual-Name": userName 
  }}
>
  {/* ... */}
</CopilotKit>

Header Forwarding

1

Authorization Header

"Authorization": `Bearer ${authToken}`
The Teams SSO token is sent as a Bearer token in the Authorization header. The backend validates this token to ensure:
  • The token is not expired
  • The token signature is valid
  • The user has access to the application
2

X-Visual-Name Header

"X-Visual-Name": userName
The user’s display name is sent as a custom header. The backend can use this for:
  • Personalized responses
  • Audit logging
  • User analytics

Token Lifecycle

Token Expiration

Teams SSO tokens typically expire after 1 hour. The frontend does NOT currently implement token refresh. Users may need to reload the app after extended sessions.

Error Handling

Common error scenarios:
ErrorCauseResolution
Teams initialization failsApp opened in browserDisplay access restriction message
getAuthToken() failsUser declined consentLog error, prompt user to retry
Token validation failsExpired or invalid tokenBackend returns 401, user must reload app

Future Enhancement: Token Refresh

// Not currently implemented
const refreshToken = async () => {
  const newToken = await microsoftTeams.authentication.getAuthToken();
  setAuthToken(newToken);
};

// Could be called on 401 response or before token expiration

Teams Context Properties

The getContext() method provides rich user and environment information:
context.user.displayName   // "John Doe"
context.user.userPrincipalName  // "[email protected]"
context.user.id            // User object ID
Currently, the app only uses displayName. Future enhancements could leverage other context properties for localization, theming, or analytics.

Security Considerations

Token Storage: The SSO token is stored in React state (memory only) and is NOT persisted to localStorage or cookies. This prevents token theft via XSS attacks.
HTTPS Required: Teams apps must be served over HTTPS. The Teams SDK will fail to initialize on insecure connections.
Token Validation: The backend MUST validate the token signature and claims. Never trust the token without validation.

Testing Outside Teams

For local development without Teams:
  1. Mock the Teams SDK (not recommended for production):
    if (process.env.NODE_ENV === 'development') {
      // Mock initialization
      setIsTeams(true);
      setAuthToken('mock-token-for-dev');
      setUserName('Dev User');
      setTokenReady(true);
    }
    
  2. Use Teams Toolkit (recommended):
    • Install Teams Toolkit VS Code extension
    • Use “Preview in Teams” to test in real Teams environment
Never deploy mock authentication to production. Always enforce real Teams authentication in deployed environments.

Next Steps

CopilotKit Integration

Learn how the token is used in backend requests

Backend Authentication

Understand how the backend validates Teams tokens

Build docs developers (and LLMs) love