Thursday 18 May 2023

Issue creating a call in Microsoft Graph

I am creating a POC for a razor app to make calls to a local phone number using Azure/Teams/Graph or whatever mechanism. My app authenticates right, but for any reason when it tries to start the call, it gives me a forbidden. the user has admin privilege of the account, and I don't know if the problem is related to the kind of Azure account or maybe there's something bad in my implementation.

In my front, this is the code.

@model CallRequest

<form asp-controller="Home" asp-action="CreateOnlineMeeting" method="post">
    <div class="form-group">
        <label for="PhoneNumber">Phone</label>
        <input type="text" class="form-control" id="PhoneNumber" name="PhoneNumber" value="+18499999999" />
    </div>
    <div class="form-group">
        <label for="DurationInMinutes">Duration</label>
        <input type="number" class="form-control" id="DurationInMinutes" name="DurationInMinutes" value="30" />
    </div>
    <div class="form-group">
        <label for="DisplayName">Name</label>
        <input type="text" class="form-control" id="DisplayName" name="DisplayName" value="User Name From the other people" />
    </div> 
</form>

<button id="callButton" onclick="initiateCall()">Call</button>
 


<script src="https://alcdn.msauth.net/browser/2.22.1/js/msal-browser.min.js" integrity="sha384-nCTmWvEOevLDR1A0WzHvi1PbktdL8pPPACO2UYs9NPp+TCEz0hE0c8JmMxRlNSjh" crossorigin="anonymous"></script>



<script>
    const msalConfig = {
        auth: {
            clientId: "CLIENTAPPID",
            authority: "https://login.microsoftonline.com/TENANTID",
            redirectUri: window.location.href,
        },
        cache: {
            cacheLocation: "sessionStorage",
            storeAuthStateInCookie: false,
        },
    };
    const msalInstance = new msal.PublicClientApplication(msalConfig);
    async function getTokenWithClientCredentials() {
    const response = await fetch('/Home/GetToken', {
        method: 'POST',
    });

    if (response.ok) {
        return await response.text();
    } else {
        throw new Error('Error getting the token');
    }
}



    //async function signInAndGetToken() {
    //    const loginRequest = {
    //        scopes: ["https://graph.microsoft.com/.default"],
    //    };

    //    try {
    //        const silentRequest = {
    //            ...loginRequest,
    //            account: msalInstance.getActiveAccount(),
    //        };
    //        const silentResult = await msalInstance.acquireTokenSilent(silentRequest);
    //        return silentResult.accessToken;
    //    } catch (error) {
    //        if (error instanceof msal.InteractionRequiredAuthError) {
    //            const interactiveResult = await msalInstance.acquireTokenPopup(loginRequest);
    //            return interactiveResult.accessToken;
    //        } 
    //        else {
    //            console.error("Error to get the token:", error);
    //            throw error;
    //        }
    //    }
    //}

    async function getMicrophonePermission() {
        try {
            const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
            stream.getTracks().forEach(track => track.stop());
            return true;
        } catch (error) {
            console.error("Error to get mic permissions:", error);
            return false;
        }
    }

    async function signIn() {
        const loginRequest = {
            scopes: ["openid", "profile", "https://graph.microsoft.com/.default"],
        };

        try {
            const loginResponse = await msalInstance.loginPopup(loginRequest);
            const account = msalInstance.getAccountByHomeId(loginResponse.account.homeAccountId);
            msalInstance.setActiveAccount(account);
        } catch (error) {
            console.error("Error during login:", error);
        }
    }

//    async function signIn() {
//    const loginRequest = {
//        scopes: ["openid", "profile", "https://graph.microsoft.com/.default"],
//    };

//    try {
//        const loginResponse = await msalInstance.loginPopup(loginRequest);
//        const account = msalInstance.getAccountByHomeId(loginResponse.account.homeAccountId);
//        msalInstance.setActiveAccount(account);
//    } catch (error) {
//        console.error("Error during login:", error);
//    }
//}
    async function getUserId(token) {
        const response = await fetch('https://graph.microsoft.com/v1.0/me', {
            method: 'GET',
            headers: {
                'Authorization': `Bearer ${token}`,
                'Content-Type': 'application/json',
            },
        });

        if (response.ok) {
            const userData = await response.json();
            return userData.id;
        } else {
            const errorDetails = await response.text();
            console.error("Error to get user Id", response.statusText, errorDetails);
            throw new Error('Error to get user Id');
        }
    }

    async function initiateCall() {
        const hasMicrophonePermission = await getMicrophonePermission();
        if (!hasMicrophonePermission) {
            console.error("We can´t access to mic");
            return;
        }

        try {
           // const token = await signInAndGetToken();
            const token = await getTokenWithClientCredentials();
            console.log(token);
            const userId = 'FIXEDUSERID';// await getUserId(token);
            const phoneNumber = '+1PHONENUMBER'; // document.getElementById("PhoneNumber").value;

          const callRequest = {
    "@@odata.type": "#microsoft.graph.call",
    "callbackUri": "https://callback.example.com",
    "targets": [
        {
            "@@odata.type": "#microsoft.graph.invitationParticipantInfo",
            "identity": {
                "@@odata.type": "#microsoft.graph.identitySet",
                "phone": {
                    "@@odata.type": "#microsoft.graph.phoneNumberIdentity",
                    "id": phoneNumber.replace(/\D/g, ''),
                },
            },
        },
    ],
    "requestedModalities": ["audio"],
    "mediaConfig": {
        "@@odata.type": "#microsoft.graph.serviceHostedMediaConfig",
    },
    "meetingInfo": {
        "@@odata.type": "#microsoft.graph.organizerMeetingInfo",
        "organizer": {
            "@@odata.type": "#microsoft.graph.identitySet",
            "user": {
                "@@odata.type": "#microsoft.graph.identity",
                "id": userId,
            },
        },
    },
};

            const response = await fetch("https://graph.microsoft.com/v1.0/communications/calls", {
                method: "POST",
                headers: {
                    "Authorization": `Bearer ${token}`,
                    "Content-Type": "application/json",
                },
                body: JSON.stringify(callRequest),
            });

            if (response.ok) {
    const call = await response.json();
    console.log("Call Started:", call);
} else {
    const errorDetails = await response.text();
    console.error("Error to Start the call:", response.statusText, errorDetails);
}

        } catch (error) {
            console.error("Error to start the call:", error);
        }
    }
</script>

and in the back I have this

 [HttpPost]
    public async Task<string> GetToken()
    {
        string clientId = "AppCLIENTID";
        string clientSecret = "APPSECRET";
        string tenantId = "TENANTID";
        string scope = "https://graph.microsoft.com/.default";

        using (HttpClient client = new HttpClient())
        {
            var requestBody = new FormUrlEncodedContent(new[]
            {
        new KeyValuePair<string, string>("grant_type", "client_credentials"),
        new KeyValuePair<string, string>("client_id", clientId),
        new KeyValuePair<string, string>("client_secret", clientSecret),
        new KeyValuePair<string, string>("scope", scope),
    });

            HttpResponseMessage response = await client.PostAsync($"https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token", requestBody);

            if (response.IsSuccessStatusCode)
            {
                string jsonResponse = await response.Content.ReadAsStringAsync();
                JObject json = JObject.Parse(jsonResponse);
                return json["access_token"].ToString();
            }
            else
            {
                throw new Exception("Error to get the token");
            }
        }
    }

This is all the privilege than have the app. enter image description here



from Issue creating a call in Microsoft Graph

No comments:

Post a Comment