-
Notifications
You must be signed in to change notification settings - Fork 102
Description
Describe the bug
When trying to delete a user from a Team's private channel, we always get a 502 error. It doesn't matter the team, channel or user.
To Reproduce
Run this PHP code from the Microsoft Website.
`<?php
require 'vendor/autoload.php';
use Microsoft\Kiota\Authentication\Oauth\ClientCredentialContext;
use Microsoft\Graph\Core\Authentication\GraphPhpLeagueAuthenticationProvider;
use Microsoft\Graph\Core\Authentication\GraphPhpLeagueAccessTokenProvider;
use Microsoft\Graph\GraphServiceClient;
use Microsoft\Kiota\Abstractions\ApiException;
use Microsoft\Kiota\Authentication\Oauth\AuthorizationCodeContext;
use Microsoft\Graph\Generated\Models;
use Microsoft\Graph\Generated\Users\UsersRequestBuilderGetQueryParameters;
use Microsoft\Graph\Generated\Users\UsersRequestBuilderGetRequestConfiguration;
use Microsoft\Graph\Generated\Groups\GroupsRequestBuilderGetRequestConfiguration;
use Microsoft\Graph\Generated\Teams\Item\Channels\ChannelsRequestBuilderGetRequestConfiguration;
use Microsoft\Graph\Generated\Models\Channel;
use Microsoft\Graph\Generated\Teams\Item\Channels\Item\ChannelItemRequestBuilderGetRequestConfiguration;
$tenantId = "";
//We are using an application for permissions and not delegation by a certain user
//We have checked all permissions required via the graph explorer and running test commands
//All other commands we have run succeed, just the delete does not.
$applicationId = "";
$clientSecret = "";
$targetTeamName = "";
$targetChannelName = ""; // The name of the channel you are looking for
$targetUserEmail = '';
$tokenContext = new ClientCredentialContext($tenantId, $applicationId, $clientSecret);
$serviceClient = new GraphServiceClient($tokenContext, ['https://graph.microsoft.com/.default']);
$tokenProvider = new GraphPhpLeagueAccessTokenProvider($tokenContext);
try
{
$accessToken = $tokenProvider->getAuthorizationTokenAsync('https://graph.microsoft.com')->wait();
}
catch (Exception $e)
{
print('Error getting access token: '.$e->getMessage()."
"."
");
}
try {
$team = $serviceClient->groups()->get()->wait();
$requestConfig = new GroupsRequestBuilderGetRequestConfiguration();
$requestConfig->queryParameters = GroupsRequestBuilderGetRequestConfiguration::createQueryParameters();
$requestConfig->queryParameters->filter = "displayName eq '$targetTeamName'";
$groups = $serviceClient->groups()->get($requestConfig)->wait();
foreach ($groups->getValue() as $group) {
// The Group ID is the Team ID
$teamId = $group->getId();
}
if ($teamId) {
print "Found team ID for '$targetTeamName': " . $teamId."<br>"."<br>";
} else {
print "Team '$targetTeamName' not found."."<br>"."<br>";
}
$requestConfiguration = new ChannelsRequestBuilderGetRequestConfiguration();
$requestConfiguration->queryParameters = $requestConfiguration->createQueryParameters();
$requestConfiguration->queryParameters->select = ["id", "displayName"];
// Make the API call to list the channels
$channelsCollectionResponse = $serviceClient->teams()->byTeamId($teamId)->channels()->get($requestConfiguration)->wait();
$channels = $channelsCollectionResponse->getValue();
$channelId = null;
// Iterate through the channels to find the ID of the target channel
foreach ($channels as $channel) {
if ($channel->getDisplayName() === $targetChannelName) {
$channelId = $channel->getId();
break; // Exit the loop once found
}
}
if ($channelId) {
print "Found channel ID for '$targetChannelName': " . $channelId."<br>"."<br>";
} else {
print "Channel '$targetChannelName' not found."."<br>"."<br>";
}
$membersCollectionResponse = $serviceClient->teams()->byTeamId($teamId)->channels()->byChannelId($channelId)->members()->get()->wait();
$members = $membersCollectionResponse->getValue();
// Iterate through the members to find a match by email
foreach ($members as $member)
{
$memberEmail = $member->getEmail();
if ($memberEmail == $targetUserEmail)
{
$foundMember = $member;
break;
}
}
if ($foundMember)
{
try
{
$result = removeChannelMember($accessToken, $teamId, $channelId, $targetUserEmail);
if ($result === true) {
print "Member removed successfully."."<br>"."<br>";
} else {
print "Error: $result"."<br>"."<br>";
}
}
catch (Exception $e)
{
print $e->message ."<br>"."<br>";
}
}
}
catch (ApiException $ex) {
print $ex->getError()->getMessage()."
"."
";
}
/**
-
Remove a user from a Microsoft Teams private/shared channel using Microsoft Graph API.
-
Includes automatic retry for transient 502 errors.
-
@param string $accessToken Microsoft Graph OAuth 2.0 access token
-
@param string $teamId The Team ID
-
@param string $channelId The Channel ID (must be private/shared)
-
@param string $userIdOrEmail Azure AD Object ID or user principal name (email)
-
@param int $maxRetries Number of retries for transient errors
-
@return bool|string True on success, error message on failure
*/
function removeChannelMember($accessToken, $teamId, $channelId, $userIdOrEmail, $maxRetries = 3) {
$baseUrl = "https://graph.microsoft.com/v1.0";// Step 1: Get channel members
$url = "$baseUrl/teams/$teamId/channels/$channelId/members";
$members = graphApiRequest($url, $accessToken);if (!isset($members['body']['value'])) {
return "Failed to fetch members: " . json_encode($members);
}// Step 2: Find membership ID for the given user
$membershipId = null;
foreach ($members['body']['value'] as $member) {
if (
(isset($member['userId']) && strtolower($member['userId']) === strtolower($userIdOrEmail)) ||
(isset($member['email']) && strtolower($member['email']) === strtolower($userIdOrEmail)) ||
(isset($member['roles']) && isset($member['displayName']) && strtolower($member['displayName']) === strtolower($userIdOrEmail))
) {
$membershipId = $member['id'];
break;
}
// Also check inside 'user' object if present
if (isset($member['user']) && (
strtolower($member['user']['id']) === strtolower($userIdOrEmail) ||
strtolower($member['user']['email']) === strtolower($userIdOrEmail) ||
strtolower($member['user']['userPrincipalName']) === strtolower($userIdOrEmail)
)) {
$membershipId = $member['id'];
break;
}
}if (!$membershipId) {
return "User not found in channel members.";
}// Step 3: Delete member with retry logic
$deleteUrl = "$baseUrl/teams/$teamId/channels/$channelId/members/$membershipId";
$attempt = 0;while ($attempt < $maxRetries) {
$attempt++;
$response = graphApiRequest($deleteUrl, $accessToken, "DELETE");
$httpCode = $response['http_code'] ?? 0;if ($httpCode === 204) { return true; // Success } if ($httpCode === 502) { // Wait before retrying (exponential backoff) sleep(pow(2, $attempt)); continue; } return "Failed to remove member. HTTP $httpCode: " . json_encode($response['body']);}
return "Failed after $maxRetries retries due to repeated 502 errors.";
}
/**
-
Helper function to make Microsoft Graph API requests.
*/
function graphApiRequest($url, $accessToken, $method = "GET", $body = null) {
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Authorization: Bearer $accessToken",
"Content-Type: application/json"
]);if ($body !== null) {
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body));
}$result = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);if (curl_errno($ch)) {
$error = curl_error($ch);
curl_close($ch);
return ["http_code" => 0, "body" => ["error" => $error]];
}curl_close($ch);
$decoded = json_decode($result, true);
return ["http_code" => $httpCode, "body" => $decoded];
}
?>
`
Expected behavior
We expect it to successfully remove the user from the channel. We have tried all sorts of calls. The fail in the graph explorer in addition to this code.
Desktop (please complete the following information):
- OS: Windows 10
- Browser: MS Edge
- Version: 144.0.3719.115 (Official build) (64-bit)