I'm trying find out if an email address is already taken in my Azure AD B2C directory.
var token = await this.GetTokenAsync();
var client = new HttpClient();
var id = HttpUtility.UrlEncode("adrian_mydomain.com#EXT##xxxxxxxxx.onmicrosoft.com");
////var id = HttpUtility.UrlEncode("adrian#mydomain.com"); // This also fails.
////var id = HttpUtility.UrlEncode("adrian_mydomain.com#EXT#"); // This also fails.
////var id = "xxxx-xxxx-xxxxxxxx-xxxxxxxxxx"; // This also fails (user object id).
var resource = $"{this.graphConfig.GraphUri}/{this.graphConfig.Tenant}/users/{id}?api-version=1.6";
//// This line below works, it returns all the users, so I do know the token is good and the resource URI is valid, etc.
////var resource = $"{this.graphConfig.GraphUri}/{this.graphConfig.Tenant}/users?api-version=1.6";
var request = new HttpRequestMessage(HttpMethod.Get, resource);
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
var response = await client.SendAsync(request);
var content = await response.Content.ReadAsStringAsync();
I'm encoding my email address in the same way that I see my email address encoded when I get all users. I have a feeling I'm close, if it is even possible to query by email address.
Currently all the things I've tried either return a 400 or a 404. Does anyone know if there is a way to query by email address (sign in name)?
EDIT
On a similar theme, I'm also trying a query to change a user's password to no avail. I figure if I can get the query working for one, I can get it working on the other.
Since it is a odata, you can query using odata syntax. Odata syntax here
var queryString = HttpUtility.ParseQueryString(string.Empty);
queryString["api-version"] = "1.6";
queryString["$filter"] = "signInNames/any(x:x/value eq 'paule#test.in')";
string url = "https://graph.windows.net/" + tenant + "/users"+ "?" + queryString;
$filter did the trick
queryString["$filter"] = "signInNames/any(x:x/value eq 'paule#test.co.uk')";
Take a look at the B2C.exe implementation, first get that working:
https://azure.microsoft.com/nl-nl/documentation/articles/active-directory-b2c-devquickstarts-graph-dotnet/
You will notice that the user is referenced by GUID or by UPN, not by email!
Emails are in the collection signInNames
To query on email address, you will need to specify a filter:
https://msdn.microsoft.com/en-us/library/azure/ad/graph/api/users-operations#GetUsers
Start with the GetUsers(to get all users), then update password and last the filter.
signInNames isn't the only place that emails are stored. It could also be userPrincipalName or otherMails. You'll want to use the following query to search all possible fields for an email.
/users?api-version=1.6&$filter=otherMails/any(x:x eq '{email}') or userPrincipalName eq '{email}' or signInNames/any(x:x/value eq '{email}')
I'm also trying to find a user by their login/email address.
Here's my (obfuscated XXXX) query string:
"https://graph.windows.net/XXXX.onmicrosoft.com/users?api-version=1.6&$filter=signInNames/any(x: x/value eq 'dd.rrr#XXXX.com')"
It doesn't error, but doesn't find the user (whom I know to exist, because GetAllUsers finds it).
However, looking at the user details, I can see:
"showInAddressList": null,
"signInNames": [],
"sipProxyAddress": null,
Could this be a clue as to why search doesn't work?
How can a user NOT have a signInName?
Related
I am trying to use the MS Graph SDK (beta) for C# to get all the groups a user belongs to transitively with a filter to only get "security groups".
My code looks like this:
var upn = "some.body#org.com";
var request = await _graphClient.Users[upn].TransitiveMemberOf
.Request()
.Filter("securityEnabled eq true")
.GetAsync();
When I run this code, I get an error that The specified filter to the reference property query is currently not supported. I know that the same API endpoint can be called successfully using Postman with the same filter so I assume I'm missing something in my C# code?
Please use the below code for filtering the securityEnabled in c# using graph sdk
try
{
List<Option> requestOptions = new List<Option>();
requestOptions.Add(new QueryOption("$count", "true"));
var request = await graphClient.Users["sruthi#xx.live"].TransitiveMemberOf
.Request(requestOptions).Header("ConsistencyLevel", "eventual")
.Filter("securityEnabled eq true")
.GetAsync();
Console.WriteLine(JsonConvert.SerializeObject(request));
}
catch(Exception e)
{
Console.WriteLine(e.Message);
}
Thanks to Sruthi's answer, I was able to get user transitive memberships. Obviously, this can often result in a huge list and the Graph API will only provide you a paginated response so you have to cycle through every page to get everything.
This is what the code looks like now:
public async Task<List<Group>> ListTransitiveGroupMembershipsOfUserAsync(string upn)
{
var requestOptions = new List<Option>
{
new QueryOption("$count", "true")
};
var directoryObjects = new List<DirectoryObject>();
var request = await _graphClient.Users[upn].TransitiveMemberOf
.Request(requestOptions).Header("ConsistencyLevel", "eventual")
.Filter("securityEnabled eq true")
.GetAsync();
directoryObjects.AddRange(request.CurrentPage);
while (request.NextPageRequest != null)
{
var currentHeaders = request.NextPageRequest.Headers;
IUserTransitiveMemberOfCollectionWithReferencesPage newPage;
if (!currentHeaders.Any())
request = await request.NextPageRequest.Header("ConsistencyLevel", "eventual").GetAsync();
else
request = await request.NextPageRequest.GetAsync();
directoryObjects.AddRange(request.CurrentPage);
}
return directoryObjects.Cast<Group>().ToList();
}
Initially, my code failed when it tried to get the results for page 2 onwards because it kept appending eventual to the ConsistencyLevel header. So when sending the HTTP request for page 2, the header was like: ConsistencyLevel: eventual, eventual or ConsistencyLevel: eventual, eventual, eventual for page 3 and so on so forth.
To get around this, I added the little if (!currentHeaders.Any()) block to only add the ConsistencyLevel header if it doesn't already exist.
Hopefully that'll help anyone else who gets caught out by cycling through paginated responses!.
I'm trying to make a search field to allow users of my application to search for mailboxes but i want my filter to return only SharedMailBoxes.
So i tried to make my get request as usual and hope that one of the parameters from the returned Microsoft.Graph.User object can allow me to filter my users. e.g userType = sharedmailbox. but none of these parameters seems to indicate the user's type
public static async Task<Users> searchUsers(string token, string search)
{
string query = String.Format("https://graph.microsoft.com/v1.0/users?$filter=startswith(displayname,'{0}') or startswith(surname,'{1}')", search, search);
string users = null;
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
HttpResponseMessage response = httpClient.GetAsync(query).Result;
users = response.Content.ReadAsStringAsync().Result;
Users usersJson = JsonConvert.DeserializeObject<Users>(users);
return usersJson;
}
I'm using Microsoft graph v1.0.
Is there a way to find out the type of my user ?
if not. Is there an other API that i'm not aware of that can allow me to manage my SharedMailBoxes and, let's be crazy, also my resource mailboxes ?
I would like to have some kind of people picker functionality with auto complete features in my asp.net mvc 5 app to search for a user in a specific Azure AD group. It's a demo "todo app" that allows to assign a todo to a user that is member of a the group.
I tried with both the Graph API directly and the Azure Graph Client library but I don't seem to find a way to achieve what I want. The graph api allows to get the members of a group but adding filter "startswith" fails as when adding the filter the api returns only directory object which don't include for example DisplayName property... the client library doesn't help much either except for the batch functionality which offers a way but with a lot of overhead... I then would have to get a filtered resultset of user regardless of group membership (using User List stuff in the api), all members of the group and then fish out using Linq the correct result set.... would work fine for dev/testing but in production with a couple of hundred users this would be insane...
Any ideas or suggestions would be much appreciated. Thanks!
EDIT
Below my code that is called from client side Javascript to search for user;
AccessGroupId is the Azure AD group used to authorize users. Only
members of this group can access the web app which I handle in custom
OWin Middleware
The method is intented to be used to find a user in that group
Code works fine as below only there is no filtering applied which is the intentaion with the input parameter pre (which comes from a textbox in the ui). I get all the members of the access group.
public async Task<JsonResult> FindUser(string pre)
{
string AccessGroupId = ConfigurationManager.AppSettings["AccessGroupId"];
AuthenticationContext authCtx = new AuthenticationContext(String.Format(CultureInfo.InvariantCulture, "{0}/{1}", SecurityConfiguration.LoginUrl, SecurityConfiguration.Tenant));
ClientCredential credential = new ClientCredential(SecurityConfiguration.ClientId, SecurityConfiguration.AppKey);
AuthenticationResult assertionCredential = await authCtx.AcquireTokenAsync(SecurityConfiguration.GraphUrl, credential);
var accessToken = assertionCredential.AccessToken;
var graphUrl = string.Format("https://graph.windows.net/mytenant.onmicrosoft.com/groups/{0}/members?api-version=2013-11-08, AccessGroupId );
HttpClient client = new HttpClient();
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, graphUrl);
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
HttpResponseMessage response = await client.SendAsync(request);
String responseString = await response.Content.ReadAsStringAsync();
JObject jsonReponse = JObject.Parse(responseString);
var l = from r in jsonReponse["value"].Children()
select new
{
UserObjectId = r["objectId"].ToString(),
UserPrincipalName = r["userPrincipalName"].ToString(),
DisplayName = r["displayName"].ToString()
};
//users = Newtonsoft.Json.JsonConvert.DeserializeObject<List<User>>(responseString);
return Json(l, JsonRequestBehavior.AllowGet);
}
When I add a filter to the same api call instead of returning the members (users, groups and/or contacts), it returns directory objects (that doesn't have displayName) which are not really usefull in the above code, unless I would query the api again (in batch) to retrieve the users displayname but that looks like a lot of overhead to me.
var graphUrl = string.Format("https://graph.windows.net/mytenant.onmicrosoft.com/groups/{0}/members?api-version=2013-11-08&$filter=startswith(displayName,'{1}')", AccessGroupId, pre);
I'd highlight two possible approaches:
Execute requests to Graph API using a custom JS library.
You'd need still need to care for accesstokens and have a look at ADAL.js
A sample app (not finalized as of this writing) available at:
AzureADSamples WebApp-GroupClaims-DotNet
Have a look at AadPickerLibrary.js
Try using ActiveDirectoryClient
It would look something like:
public async Task<JsonResult> FindUser(string pre) {
ActiveDirectoryClient client = AADHelper.GetActiveDirectoryClient();
IPagedCollection<IUser> pagedCollection = await client.Users.Where(u => u.UserPrincipalName.StartsWith(pre, StringComparison.CurrentCultureIgnoreCase)).ExecuteAsync();
if (pagedCollection != null)
{
do
{
List<IUser> usersList = pagedCollection.CurrentPage.ToList();
foreach (IUser user in usersList)
{
userList.Add((User)user);
}
pagedCollection = await pagedCollection.GetNextPageAsync();
} while (pagedCollection != null);
}
return Json(userList, JsonRequestBehavior.AllowGet);
}
More detailed sample is available at:
AzureADSamples WebApp-GraphAPI-DotNet
I am using the Microsoft.AspNet.Mvc.Facebook NuGet package. I would like to exchange a regular token for the extended access token (the one that replaced the offline_access permission).
From Googling around I found the URL should be in this format:
https://graph.facebook.com/oauth/access_token?
client_id=[APP_ID]&
client_secret=[APP_SECRET]&
grant_type=fb_exchange_token&
fb_exchange_token=[EXISTING_ACCESS_TOKEN]
So I use the following code:
var longToken = await context.Client.PostTaskAsync("/oauth/access_token",
new
{
client_id = fbApp.AppId,
client_secret = fbApp.AppSecret,
grant_type = "fb_exchange_token",
fb_exchange_token = context.AccessToken
});
This returns a null. No error or anything. Just a null value.
Edit: Also tried the following, which also did not work. But a GET seems more logical than a POST anyway.
dynamic result = context.Client.Get("oauth/access_token",
new
{
client_id = fbApp.AppId,
client_secret = fbApp.AppSecret,
grant_type = "fb_exchange_token",
fb_exchange_token = context.AccessToken
});
var longToken = result.access_token as string;
I have successfully done this as a GET request, not a POST :) Just put the necessary parameters into the URL and request it as a GET request and the response returns the long term access token.
EDIT
When you get the result from this, you should parse the query string first (am not sure in C# but maybe you could use this link: http://msdn.microsoft.com/en-us/library/ms150046(v=vs.110).aspx).
After that try to get the access_token property and I got it right on my end. I was doing it in node.js though, but essentially the same flow.
I am developing a Windows Store App which requires user to authenticate using Yammer credentials. I have written the following code to achieve the functionality.
var client_id = <<My Client ID >>;
var client_secret = <<My Client Secret>>;
string redirectURI = WebAuthenticationBroker.GetCurrentApplicationCallbackUri().ToString();
string loginURI = "https://www.yammer.com/dialog/oauth?client_id=" + client_id + "&redirect_uri=" + redirectURI;
var result = await WebAuthenticationBroker.AuthenticateAsync(WebAuthenticationOptions.None, new Uri(loginURI), new Uri(redirectURI));
if (result.ResponseStatus == WebAuthenticationStatus.Success)
{
var response = result.ResponseData;
string[] keyValPairs = response.Split('=');
HttpClient client = new HttpClient();
string url = "https://www.yammer.com/oauth2/access_token.json?client_id=" + client_id + "&client_secret=" + client_secret + "&code=" + keyValPairs[1];
HttpResponseMessage JSONresponse = await client.GetAsync(url);
string content = await JSONresponse.Content.ReadAsStringAsync();
}
When I ran the app for the first time it asked me for my credentials and I was able to login.
Now when I run my app , it takes my previous entered credentials and logs in automatically. I am sure that I did not check "Keep Me signed In" option.
I want the app to ask for credentials every time I run it.
Please help and thanks in advance.
I had a similar problem with an app using the Pocket OAUTH 2.0 API. I am using the WebAuthenticationBroker http://msdn.microsoft.com/en-us/library/windows/apps/windows.security.authentication.web.webauthenticationbroker.aspx and its AuthenticateAsync method but I think the problem is the same.
I had used the AuthenticateAsync(options, url) method which ended up -once credentials are saved - I was never able to use a different login so basically could not change accounts.
Tried AuthenticateAsync(options, url, redirectURI) method and same problem.
But then I realized that redirectURI is set to GetCurrentApplicationCallbackUri as in your example this seems to trigger SSO.
Changing the redirectURI to a custom one (e.g. your own domain) seems to disable SSO and credentials have to be added every time as wanted.
This is easy with the AuthenticateAsync method as it uses a WebAuthenticationBroker, not sure how to adapt this with the HttpClient you use above but I maybe you could use
WebAuthenticationBroker?
But my code example (Javascript but same API):
var authweb = Windows.Security.Authentication.Web
var OAUTH_URI = 'https://$WHATEVERSERVICE'
var OAUTH_CODE = '$GOT BEFORE'
var endURI = 'http://$YOURDOMAIN.com' // this is the important part
var authorizeUri = new Windows.Foundation.Uri(OAUTH_URI
+ '?request_token=' + encodeURIComponent(OAUTH_CODE) // POCKET SPECIFIC PARAM
+ '&redirect_uri=' + encodeURIComponent(endURI)
);
authweb.WebAuthenticationBroker.authenticateAsync(
authweb.WebAuthenticationOptions.none,
authorizeUri,
Windows.Foundation.Uri(endURI)
).done(...
This way I get a clean (no prefilled or even submitted) login form every time.
Hope this helps?