locksyk / msteams-bundle
Microsoft Entra ID and Teams for Symfony APIs: Entra token verification, OIDC code-flow web login, Teams tab SSO, and SDK-free Bot Connector inbound authentication with invoke-response helpers.
Package info
github.com/LocksyK/msteams-bundle
Type:symfony-bundle
pkg:composer/locksyk/msteams-bundle
Requires
- php: >=8.4
- symfony/cache-contracts: ^2.5 || ^3.0
- symfony/config: ^7.0 || ^8.0
- symfony/dependency-injection: ^7.0 || ^8.0
- symfony/http-client-contracts: ^3.0
- symfony/http-foundation: ^7.0 || ^8.0
- symfony/http-kernel: ^7.0 || ^8.0
- symfony/security-core: ^7.0 || ^8.0
- web-token/jwt-library: ^4.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.95
- phpstan/phpstan: ^2.2
- phpunit/phpunit: ^13.0
- symfony/cache: ^7.0 || ^8.0
- symfony/http-client: ^7.0 || ^8.0
Suggests
- ext-gd: To generate Teams app icons with TeamsApp\IconSet
- ext-zip: To build Teams app packages with TeamsApp\PackageBuilder
- locksyk/api-session-bundle: Bearer-token sessions whose pre-auth anonymous sessions carry the OIDC login ceremony state
- symfony/http-client: Required to use the LocksyK\MsTeamsBundle\Test fakes (MockHttpClient)
README
Microsoft Entra ID and Teams for Symfony APIs - without the retired Bot Framework SDK.
EntraTokenVerifier- verifies Entra v2.0 tokens against the tenant JWKS (signature, issuer, audience, lifetime + leeway, tenant,ver): web-login id_tokens and Teams tab SSO access tokens alike.OidcLoginService- the OIDC code-flow web-login ceremony:begin()parks state + nonce + PKCE in the request's session and returns the authorization URL;finish()checks state, exchanges the code as a confidential client, verifies the id_token, checks the nonce. Designed for session-establishing logins - withlocksyk/api-session-bundlethe ceremony rides a pre-auth anonymous session exactly like a WebAuthn challenge.Bot\BotTokenVerifier- every check Microsoft documents for SDK-free bots, including theserviceUrlbinding (the forged-activity defence), signing-key channel endorsements, and a tenant pin.Bot\InvokeResponse- the response envelopes Teams expects from message-extension / task-module / card-action invokes, encoding the constraints that otherwise fail silently (previews, the 1.3 card ceiling,Action.Executefallbacks).Bot\BotConnectorClient- the outbound counterpart: replies to plain message activities (conversational "CLI" bots) POSTed to the Connector at the activity's verifiedserviceUrl, authenticated with a cached client-credentials service token. Enabled bybot.client_secret; only ever call it with a serviceUrl from an activityBotTokenVerifieraccepted.Bot\ReplyActivityandBot\Cards- message-CLI plumbing (mention-stripped command text, text/card reply activities with previews stripped) and Adaptive Card envelopes with the hard-won rules baked in (mandatory previews, 1.3 ceiling,Action.ExecutewithOpenUrlfallback).Test\FakeMicrosoft- aMockHttpClientstanding in forlogin.microsoftonline.comandlogin.botframework.com, signing real RS256 tokens (Entra + Bot Connector, scriptable token endpoint) and capturing outbound Connector replies in$botReplies, so consumers functional-test every path with no tenant.
Your application keeps its own user-resolution policy, authenticators, controllers and card content - the bundle owns the protocol.
The browser half lives in frontend/ as
@locksyk/msteams-client: Teams launch detection (the manifest's
?inTeams=1 flag) and the silent tab-SSO token exchange, with
@microsoft/teams-js as a lazily-imported optional peer.
Configuration
# config/packages/ms_teams.yaml ms_teams: tenant_id: '%env(OIDC_TENANT_ID)%' client_id: '%env(OIDC_CLIENT_ID)%' client_secret: '%env(OIDC_CLIENT_SECRET)%' app_id_uri: '%env(OIDC_APP_ID_URI)%' # Teams tab SSO audience redirect_uri: '%env(OIDC_REDIRECT_URI)%' bot: app_id: '%env(TEAMS_BOT_APP_ID)%' # Azure Bot registration client_secret: '%env(OIDC_CLIENT_SECRET)%' # outbound replies; often the same app registration when@test: ms_teams: http_client: LocksyK\MsTeamsBundle\Test\FakeMicrosoft
Empty tenant_id/client_id disable every SSO surface; empty
bot.app_id disables Bot Connector verification. issuer /
*_endpoint / jwks_uri overrides point the machinery at a broker or
a fake without code changes.
Teams app tooling
The bundle also ships the provisioning/packaging toolkit an app's
teams-app/ directory needs, so the mechanics live once:
LocksyK\MsTeamsBundle\TeamsApp\PackageBuilderbuilds the sideloadable zip from the app'smanifest.template.json+ icons, keeping a stable Teams app id and an auto-bumping version in a git-ignoredstate.local.json(Teams caches the definition against the version, so unchanged re-uploads silently do nothing). Bot / compose-extension / tab-SSO capabilities are dropped when their env values are unset - placeholder ids fail far more confusingly than absent capabilities. A thin app-sideteams-app/package.phploads Dotenv and callsPackageBuilder::build(__DIR__, 'myapp', $_ENV).LocksyK\MsTeamsBundle\TeamsApp\IconSetgenerates the requiredcolor.png(192x192 accent tile) andoutline.png(32x32); the app supplies only its glyph-drawing callable.resources/provisioning/provision.ps1provisions - or, with-Check, verifies - the Entra app registration (redirect URI,access_as_userscope, Teams clients pre-authorized in the mandatory second PATCH, v2 tokens) and the Azure Bot resource + Teams channel. App-agnostic: consent strings derive from-DisplayName; apps keep a thin wrapper supplying theirs.
ext-zip and ext-gd are needed only when using this tooling.
License
GPL-2.0-only.