AAD B2C - Add user to group via post to GrapAPI in Azure function - azure

In a signup custom policy, after the user is created, I want to add him or her to a group. I tried to do it the same way I get the group membership in my signin policy, with a custom Azure function that calls the GraphAPI.
For teststing purpose, I first tried calling GraphAPI with Postman to see if it works. I got it working following the docs and came back with this query :
POST https://graph.microsoft.com/v1.0/groups/{{b2c-beneficiaire-group-id}}/members/$ref
Body:
{
"#odata.id": "https://graph.microsoft.com/v1.0/users/{{b2c-user-id}}"
}
And that work just fine. I get a 204 response and the user is in fact now a member of the group.
Now here's the part where I try to replicate it in my Azure function :
var url = $"https://graph.microsoft.com/v1.0/groups/{groupId}/members/$ref)";
var keyOdataId = "#odata.id";
var valueODataId = $"https://graph.microsoft.com/v1.0/users/{userId}";
var bodyObject = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>(keyOdataId, valueODataId)
};
var jsonData = $#"{{ ""{keyOdataId}"": ""{valueODataId}"" }}";
var groupBody = new StringContent(jsonData, Encoding.UTF8, "application/json");
log.LogInformation($"{url} + body:{await groupBody.ReadAsStringAsync()}");
using (var response = await httpClient.PostAsync(url, groupBody))
{
log.LogInformation("HttpStatusCode=" + response.StatusCode.ToString());
if (!response.IsSuccessStatusCode)
{
throw new InvalidOperationException($"{response.StatusCode} - Reason:{response.ReasonPhrase}. Content:{await response.Content.ReadAsStringAsync()}");
}
}
I've tried a few variations (with HttpRequest and other things) but I always end up with an Odata error :
"BadRequest","message":"The request URI is not valid. Since the segment 'members' refers to a collection,
this must be the last segment in the request URI or it must be followed by an function or action
that can be bound to it otherwise all intermediate segments must refer to a single resource."
From what I see it is related to the OData query (the $ref part). Do you have any idea about what do I have to do to make it work?

It looks like a typo in your url which ends with )
var url = $"https://graph.microsoft.com/v1.0/groups/{groupId}/members/$ref)";

Related

Permissions from Graph API seem to be empty

Another Microsoft Graph API question this time I'm curious about the result.
Why does this return a 200 and with nothing in the value object.
What I've tried:
Add different permissions in the Modify permissions tab
Test different accounts and other SharePoint environments ( I am global admin on those accounts and its no personal account but work account)
I've tested before with the query params such as select, filter and expand. So ive tried things like ?expand=all, expand=items and expand=children and a few more.
Use name or id in the sites/{site name or site id}
Usually I've solved all of my problems with repeating step 1 or 3 but now it seem to give me nothing. Since it's part of the docs im curious what I'm missing here
https://learn.microsoft.com/en-us/graph/api/site-list-permissions?view=graph-rest-1.0&tabs=http
What could be the missing piece here? :)
Edit:
I've tried to solve this issue in a c# mvc 5 app by doing the following code but it still returns the exact same result:
IConfidentialClientApplication app = MsalAppBuilder.BuildConfidentialClientApplication();
var account = await app.GetAccountAsync(ClaimsPrincipal.Current.GetAccountId());
string[] scopes = { "Sites.FullControl.All" };
AuthenticationResult result = null;
HttpClient client = new HttpClient();
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, "https://graph.microsoft.com/v1.0/sites/{site_id_or_name}/permissions");
try
{
//Get acccess token before sending request
result = await app.AcquireTokenSilent(scopes, account).ExecuteAsync().ConfigureAwait(false);
if (result != null)
{
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
//Request to get groups
HttpResponseMessage response = await client.SendAsync(request);
if (response.IsSuccessStatusCode)
{
ViewBag.Permissions = response.Content.ReadAsStringAsync().Result;
}
}
}
catch (Exception ex)
{
//Something went wrong
}
Any idea what is wrong here?
The GitHub project im using: https://github.com/Azure-Samples/ms-identity-aspnet-webapp-openidconnect just add a client id and secret from your app reg and you can copy my method above :)
The reason is very simple, because it does not support delegated permissions, so don't try to have a user login Graph Explorer for testing, because it uses delegated permissions by default.
You need to grant Sites.FullControl.All application permissions to the application in the Azure portal, and then use the client credential flow to obtain an access token. Then you can use postman to call that api.

How to recover Call Record information from MS Teams via Graph API

We have a stand alone process which needs to get call record details of completed calls via the Graph API.
We have obtained record IDs via the Azure Dashboard so that we can use them with the following endpoint
GET https://graph.microsoft.com/v1.0/communications/callRecords/{id}
as shown in
https://learn.microsoft.com/en-us/graph/api/callrecords-callrecord-get?view=graph-rest-1.0
The Azure App has been configured for access and has readAll permissions set.
The following code generates a token for access, but when actually trying to read back a call record specified by id, it always returns 404 Not found.
var scopes = new string[] { "https://graph.microsoft.com/.default" };
IConfidentialClientApplication app;
app = ConfidentialClientApplicationBuilder.Create(clientID)
.WithClientSecret(clientSecret)
.WithAuthority(new Uri(authority))
.Build();
var result = await app.AcquireTokenForClient(scopes).ExecuteAsync();
HttpClient httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
string id = "YYYYYYYY-F571-45D9-ABC6-XXXXXXXXXXXX"; // Real ID, but obfuscated for this example
HttpResponseMessage response = await httpClient.GetAsync(new Uri("https://graph.microsoft.com/v1.0/communications/callRecords/" + id));
Can anyone advise what I am missing.
Thanks

Microsoft Face API 1.0 Error Resource Not Found

I am working on a face recognition project with Microsoft Azure Cognitive services. Not quite sure why I am not able to correct my own JSON Malformed syntax I thought I nail this 6 months ago. I want to create a group name, so I call upon 'Person Group API' and everytime I follow MS example I get errors in my code however in the API testing Console no problems here is my code example borrow from MS site :
{ "error": { "code": "ResourceNotFound", "message": "The requested resource was not found." } }
and the code which is run in Console mode :
static async void CreateGroup()
{
string key1 = "YourKey";
// azure the one should work
var client = new HttpClient();
var queryString = HttpUtility.ParseQueryString(string.Empty);
// Request headers
client.DefaultRequestHeaders.Add
("Ocp-Apim-Subscription-Key", key1);
var uri = "https://westus.api.cognitive.microsoft.com/face/v1.0/
persongroups/{personGroupId}?" + queryString;
HttpResponseMessage response;
// Request body
string groupname = "myfriends";
string body = "{\"name\":\"" + groupname + ","+ "\"}";
// Request body
using (var content = new StringContent
(body, Encoding.UTF8, "application/json"))
{
await client.PostAsync(uri, content)
.ContinueWith(async responseTask =>
{
var responseBody = await responseTask.Result
.Content.ReadAsStringAsync();
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine("Response: {0}", responseBody);
Console.WriteLine("");
Console.WriteLine("Group Created.... ");
Console.WriteLine("Hit ENTER to exit...");
Console.ReadKey();
});
response = await client.PutAsync(uri, content);
Console.WriteLine("what is this {0}", response.ToString());
Console.ReadKey();
}// end of using statement
}// end of CreateGroup
#endregion
I am guess here but I think its my JSON is malformed again and I just don't know what I am doing wrong again this time. According to the site the field name that I require to send over to ms is 'name' : 'userData' is optional.
Faced the similar issue, after adding "/detect" in the uri the issue fixed.
See the below
var uri = "https://westus.api.cognitive.microsoft.com/face/v1.0/detect
Also make sure the subscription key is valid.
Your request url must specify a group ID in place of where you have {personGroupId}. Per the spec the group ID must be:
User-provided personGroupId as a string. The valid characters include
numbers, English letters in lower case, '-' and '_'. The maximum
length of the personGroupId is 64.
Furthermore, the http verb needs to PUT, whereas you've made a client.PostAsync request. So you'll need to change that to client.PutAsync.
Microsoft provides a client library for C# for the Face API where you can find working C# code.
In python, simply this worked for me.
ENDPOINT='https://westcentralus.api.cognitive.microsoft.com'

Create custom extension through Graph API with Client Credentials auth

I have a .NET Web API that I am using to do some interaction with Microsoft Graph and Azure AD. However, when I attempt to create an extension on the user, it comes back with Access Denied.
I know it is possible from the documentation here however, it doesnt seem to work for me.
For the API, I am using client credentials. So my web app authenticates to the API using user credentials, and then from the API to the graph it uses the client.
My app on Azure AD has the Application Permission Read and Write Directory Data set to true as it states it needs to be in the documentation for a user extension.
I know my token is valid as I can retrieve data with it.
Here is my code for retrieving it:
private const string _createApprovalUrl = "https://graph.microsoft.com/beta/users/{0}/extensions";
public static async Task<bool> CreateApprovalSystemSchema(string userId)
{
using(var client = new HttpClient())
{
using(var req = new HttpRequestMessage(HttpMethod.Post, _createApprovalUrl))
{
var token = await GetToken();
req.Headers.Add("Authorization", string.Format("Bearer {0}", token));
req.Headers.TryAddWithoutValidation("Content-Type", "application/json");
var requestContent = JsonConvert.SerializeObject(new { extensionName = "<name>", id = "<id>", approvalLimit = "0" });
req.Content = new StringContent(requestContent, Encoding.UTF8, "application/json");
using(var response = await client.SendAsync(req))
{
var content = await response.Content.ReadAsStringAsync();
ApprovalSystemSchema schema = JsonConvert.DeserializeObject<ApprovalSystemSchema>(content);
if(schema.Id == null)
{
return false;
}
return true;
}
}
}
}
Is there anyone who may have a workaround on this, or information as to when this will be doable?
Thanks,
We took a look and it looks like you have a bug/line of code missing. You appear to be making this exact request:
POST https://graph.microsoft.com/beta/users/{0}/extensions
Looks like you are missing the code to replace the {0} with an actual user id. Please make the fix and let us know if you are now able to create an extension on the user.

USER_AUTHENTICATION_FAILED Creating Envelope

Currently I'm working with a Node.js integration for DocuSign (https://www.npmjs.com/package/docusign-esign), I made all the test with the sandbox account and worked perfectly, right now I'm trying to use a production account, the login process is fine but when I'm going to create the envelope I get a USER_AUTHENTICATION_FAILED error (even if the first login went without errors). I would like to know if someone has experienced same thing or has an idea of how can I fix this.
This is the code that I took from the docusign-esign to create the envelope:
var loginAccount = new docusign.LoginAccount();
loginAccount = loginAccounts[0];
var accountId = loginAccount.accountId;
var envelopesApi = new docusign.EnvelopesApi();
envelopesApi.createEnvelope(accountId, envDef, null, function (error, envelopeSummary, response)
The account Id is the same retrieved after the login process.
One possible cause could be that your DocuSign account is hosted on na2.docusign.net, na3.docusign.net or eu.docusign.net, while your code uses the default www.docusign.com as a base URL.
The login call will pass even if you use www, however all the subsequent API calls will fail if you are not hitting the exact base URL that corresponds to your production account. You should have received this information as part of the DocuSign Go-Live process (formerly known as API Certification). You can always get the base URL from the login call response.
For Node, here how to get the correct base URL from the login call and set it up to the API Client (lines in bold are likely what is missing in your code):
authApi.login(loginOps, function (err, loginInfo, response) {
if (err) {
return next(err);
}
if (loginInfo) {
// list of user account(s)
// note that a given user may be a member of multiple accounts
var loginAccounts = loginInfo.getLoginAccounts();
console.log('LoginInformation: ' + JSON.stringify(loginAccounts));
var loginAccount = loginAccounts[0];
var accountId = loginAccount.accountId;
var baseUrl = loginAccount.baseUrl;
var accountDomain = baseUrl.split("/v2");
apiClient.setBasePath(accountDomain[0]);
docusign.Configuration.default.setDefaultApiClient(apiClient);
next(null, loginAccount);
}
});

Resources