I was trying to follow the steps from this this tutorial to authenticate my app by oAuth and use the retrieved token for EWS managed API.
Here is the final code:
static void Run()
{
string authority = ConfigurationSettings.AppSettings["authority"];
string clientID = ConfigurationSettings.AppSettings["clientID"];
Uri clientAppUri = new Uri(ConfigurationSettings.AppSettings["clientAppUri"]);
string serverName = ConfigurationSettings.AppSettings["serverName"];
AuthenticationResult authenticationResult = null;
AuthenticationContext authenticationContext = new AuthenticationContext(authority, false);
string errorMessage = null;
try
{
Console.WriteLine("Trying to acquire token");
authenticationResult = authenticationContext.AcquireToken(serverName, clientID, clientAppUri, PromptBehavior.Auto);
}
catch (AdalException ex)
{
errorMessage = ex.Message;
if (ex.InnerException != null)
{
errorMessage += "\nInnerException : " + ex.InnerException.Message;
}
}
catch (ArgumentException ex)
{
errorMessage = ex.Message;
}
if (!string.IsNullOrEmpty(errorMessage))
{
Console.WriteLine("Failed: {0}" + errorMessage);
return;
}
Console.WriteLine("\nMaking the protocol call\n");
ExchangeService exchangeService = new ExchangeService(ExchangeVersion.Exchange2013);
exchangeService.Url = new Uri(serverName + "ews/exchange.asmx");
exchangeService.TraceEnabled = true;
exchangeService.TraceFlags = TraceFlags.All;
exchangeService.Credentials = new OAuthCredentials(authenticationResult.AccessToken);
//exchangeService.TraceListener = new TraceListener();
var res = exchangeService.FindFolders(WellKnownFolderName.Root, new FolderView(10));
}
and here is the configuration file:
<add key="authority" value="https://login.windows.net/???.onmicrosoft.com" />
<add key="clientId" value="???" />
<add key="clientAppUri" value="https://localhost/8a4abb13c70dab64a18ae81089bc2cff"/>
<add key="serverName" value="https://outlook.office365.com/" />
Here is the error message:
The request failed. The remote server returned an error: (401) Unauthorized.
and here is trace of ExchangeService:
<Trace Tag="EwsRequestHttpHeaders" Tid="12" Time="2015-08-13 13:56:24Z">
POST /ews/exchange.asmx HTTP/1.1
Content-Type: text/xml; charset=utf-8
Accept: text/xml
User-Agent: ExchangeServicesClient/15.00.0847.030
Accept-Encoding: gzip,deflate
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik1uQ19WWmNBVGZNNXBPWWlKSE1iYTlnb0VLWSIsImtpZCI6Ik1uQ19WWmNBVGZNNXBPWWlKSE1iYTlnb0VLWSJ9.eyJhdWQiOiJodHRwczovL291dGxvb2sub2ZmaWNlMzY1LmNvbS8iLCJpc3MiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC9mMTFjYjAzOS05NzFlLTQxOWItYjg5NS01ZjIxZTkxOWFlZmUvIiwiaWF0IjoxNDM5NDczOTI0LCJuYmYiOjE0Mzk0NzM5MjQsImV4cCI6MTQzOTQ3NzgyNCwidmVyIjoiMS4wIiwidGlkIjoiZjExY2IwMzktOTcxZS00MTliLWI4OTUtNWYyMWU5MTlhZWZlIiwiZW1haWwiOiJoYW1pZC5lbG1pQGNvbnNldHRvLmNvbSIsImlkcCI6Imh0dHBzOi8vc3RzLndpbmRvd3MubmV0L2JiZDIwM2I4LTkwMjEtNDZiZi05NzE2LTUyZWE5MzY1MjQ3Zi8iLCJhbHRzZWNpZCI6IjU6OjEwMDNCRkZEOEIxMTQxMTUiLCJzdWIiOiJPc0t2cnY3YU1hX3VKZktYNGltYXduSVp6SVIybDdkTXBkcUV6M0Y2U2U0IiwiZ2l2ZW5fbmFtZSI6IkhhbWlkIiwibmFtZSI6IkhhbWlkIEVsbWkiLCJhbXIiOlsicHdkIl0sInVuaXF1ZV9uYW1lIjoiaGFtaWQuZWxtaUBjb25zZXR0by5jb20iLCJhcHBpZCI6IjFkMGFiN2ZmLTU5NzItNDJlZS1iZGMwLWYzMzQwNTJhZmZjOCIsImFwcGlkYWNyIjoiMCIsInNjcCI6IkNhbGVuZGFycy5SZWFkIENhbGVuZGFycy5SZWFkV3JpdGUgQ29udGFjdHMuUmVhZCBDb250YWN0cy5SZWFkV3JpdGUgRGlyZWN0b3J5LkFjY2Vzc0FzVXNlci5BbGwgRGlyZWN0b3J5LlJlYWQgRGlyZWN0b3J5LldyaXRlIEZpbGVzLlJlYWQgRmlsZXMuUmVhZC5TZWxlY3RlZCBGaWxlcy5SZWFkV3JpdGUgRmlsZXMuUmVhZFdyaXRlLlNlbGVjdGVkIGZ1bGxfYWNjZXNzX2FzX3VzZXIgR3JvdXAuUmVhZC5BbGwgR3JvdXAuUmVhZFdyaXRlLkFsbCBNYWlsLlJlYWQgTWFpbC5SZWFkV3JpdGUgTWFpbC5TZW5kIG9mZmxpbmVfYWNjZXNzIG9wZW5pZCBTaXRlcy5SZWFkLkFsbCBTaXRlcy5SZWFkV3JpdGUuQWxsIFVzZXIuUmVhZCBVc2VyLlJlYWQuQWxsIFVzZXIuUmVhZEJhc2ljLkFsbCBVc2VyLlJlYWRXcml0ZSBVc2VyLlJlYWRXcml0ZS5BbGwiLCJhY3IiOiIxIn0.tZAyNFquVvyg46lsN79bmpdHhVEPwCIbBXfgsQ3kCzXgmf0LmX3s0A6SV7eSfEKef_-U78HBViAIaUexWeKAV0SKzJZUiQJ0dpDossYt6CfBAlFn4J6_5oZ_jygeNH3xeiCgU4tQrlz5t8iMOeSmBjwIsa2K-Sizd_zC8m3wptg6HI2ubdFJd0VXYqb7WFW_Sb-7wmOZqp8Lybpf3W6qRO14FRpm1f0RGec7kx4jd0EobPUaYWnQaet2I8P-5tuq6fmkJx78mQLGCrkZMcSJVakWVgepgO3LrEvKRKLuzJ9p5fiRiEVGNAzHUubrIjCVcXuquYaPdF16dK3gUT3Uiw
</Trace>
<Trace Tag="EwsRequest" Tid="12" Time="2015-08-13 13:56:24Z" Version="15.00.0847.030">
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages" xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header>
<t:RequestServerVersion Version="Exchange2013" />
</soap:Header>
<soap:Body>
<m:FindFolder Traversal="Shallow">
<m:FolderShape>
<t:BaseShape>AllProperties</t:BaseShape>
</m:FolderShape>
<m:IndexedPageFolderView MaxEntriesReturned="10" Offset="0" BasePoint="Beginning" />
<m:ParentFolderIds>
<t:DistinguishedFolderId Id="root" />
</m:ParentFolderIds>
</m:FindFolder>
</soap:Body>
</soap:Envelope>
</Trace>
A first chance exception of type 'System.Net.WebException' occurred in System.dll
<Trace Tag="EwsResponseHttpHeaders" Tid="12" Time="2015-08-13 13:56:26Z">
HTTP/1.1 401 Unauthorized
request-id: ea025c98-9e74-4799-8ac2-77251f641912
X-CalculatedBETarget: HE1PR09MB0330.eurprd09.prod.outlook.com
X-BackEndHttpStatus: 401
Content-Length: 0
Set-Cookie: ClientId=4HGSRI510SWOAIFT0U31G; expires=Fri, 12-Aug-2016 13:57:17 GMT; path=/; secure; HttpOnly,exchangecookie=86f12da3fb89403bb91c5fe8b525f43d; expires=Sat, 13-Aug-2016 13:57:18 GMT; path=/; HttpOnly,ClientId=4HGSRI510SWOAIFT0U31G; expires=Fri, 12-Aug-2016 13:57:17 GMT; path=/; secure; HttpOnly
Server: Microsoft-IIS/8.0
x-ms-diagnostics: 2000001;reason="No applicable user context claims found.";error_category="invalid_token"
X-DiagInfo: HE1PR09MB0330
X-BEServer: HE1PR09MB0330
X-Powered-By: ASP.NET
X-FEServer: DB5PR09CA0061
WWW-Authenticate: Bearer client_id="00000002-0000-0ff1-ce00-000000000000", trusted_issuers="00000001-0000-0000-c000-000000000000#*", token_types="app_asserted_user_v1", authorization_uri="https://login.windows.net/common/oautA first chance exception of type 'Microsoft.Exchange.WebServices.Data.ServiceRequestException' occurred in Microsoft.Exchange.WebServices.dll
h2/authorize", error="invalid_token",Basic Realm="",Basic Realm=""
Date: Thu, 13 Aug 2015 13:57:17 GMT
</Trace>
Any idea would be greatly appreciated.
Your token has too many scopes in it (i.e. the scp claim). That value should only be full_access_as_user. In the Azure Management Portal, the only Office 365 Exchange Online permission you should have is "Access mailboxes as the signed-in user via Exchange Web Services".
Related
We have been succesful in using the odata v8.1 endpoint in 2016 to impersonate a user.
Please note that the intended request flow is: Postman-->LocalHost Microservice-->CRM
Example of a working request from Postman-->CRM (directly, without going through the microservice)
Accept:application/json
Content-Type:application/json; charset=utf-8
OData-MaxVersion:4.0
OData-Version:4.0
MSCRMCallerID:d994d6ff-5531-e711-9422-00155dc0d345
Cache-Control:no-cache
Against the odata endpoint: ..../api/data/v8.1/leads
Note that this has been successful only when issued directly against the odata v8.1 endpoint via postman.
When attempting to do the same, having a service running locally (Postman-->LocalHost Service-->CRM), this fails, and simply ignores??? the MSCRMCallerID header.
Upon examining headers that were passed to the LocalHost Microservice from Postman, the request, as examined by the debugger in VS 2017:
{Method: POST, RequestUri: 'https://.../api/data/v8.1/leads', Version: 1.1, Content: System.Net.Http.StringContent, Headers:
{
OData-Version: 4.0
OData-MaxVersion: 4.0
MSCRMCallerID: D994D6FF-5531-E711-9422-00155DC0D345
Cache-Control: no-cache
Accept: application/json
Content-Type: application/json; charset=utf-8
}}
The record is created succesfully, however on the CreatedBy field is the service username NOT the MSCRMCallerID username (d994d6ff-5531-e711-9422-00155dc0d345), and the CreatedOnBehalf field is empty.
What are we doing wrong?
How do we get this impersonation working from our service?
EDIT + More Info
Please note that I do believe that I've included all the relevant info, but if I have not, please let me know what other input I should provide on this issue.
What have I tried?
changed the order of headers
played with the case of the headers
ensured that the guid is correct of the user for impersonation
ensured that the user has both delegate and sys admin role (although this is irrelevant because this works when executing requesting directly against crm odata endpoint, rather than the endpoint that the our service exposes
have tried to execute the request against both https AND http
fiddler trace as shown below
Please note that this fiddler trace is a trace showing Postman --> Microservice request. It does not show the communication from the localhost microservice to CRM. (I'm not sure why, perhaps because it is encrypted)
POST https://localhost:19081/.....Leads/API/leads HTTP/1.1
Host: localhost:19081
Connection: keep-alive
Content-Length: 84
Cache-Control: no-cache
Origin: chrome-extension://aicmkgpgakddgnaphhhpliifpcfhicfo
MSCRMCallerID: D994D6FF-5531-E711-9422-00155DC0D345
X-Postman-Interceptor-Id: d79b1d2e-2155-f2ec-4ad7-e9b63e7fb90d
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36
Content-Type: application/json; charset=UTF-8
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.8
Cookie: ai_user=Ka2Xn|2017-05-25T17:30:57.941Z
{
"subject": "created by mscrmcaller user2: d994d6ff-5531-e711-9422-00155dc0d345"
}
#Ram has suggested that we use the organization service to authenticate, is this an option, considering we are executing against Web API? Will the requested token still be valid. (Please note that this may be a silly question, and the reason is because I am not understanding how authentication works).
The following is a code snippet from how we are authenticating currently on every call:
//check headers to see if we got a redirect to the new location
var shouldAuthenticate = redirectUri.AbsoluteUri.Contains("adfs/ls");
if (!shouldAuthenticate)
{
return;
}
var adfsServerName = redirectUri.Authority;
var queryParams = HttpUtility.ParseQueryString(redirectUri.Query);
ServicePointManager.ServerCertificateValidationCallback +=
(sender, cert, chain, sslPolicyErrors) => true;
WSTrustChannelFactory factory = null;
try
{
// use a UserName Trust Binding for username authentication
factory = new WSTrustChannelFactory(
new UserNameWSTrustBinding(SecurityMode.TransportWithMessageCredential),
$"https://{adfsServerName}/adfs/services/trust/13/usernamemixed")
{
Credentials =
{
UserName =
{
UserName = $"{credential.Domain}\\{credential.UserName}",
Password = credential.Password
}
},
TrustVersion = TrustVersion.WSTrust13
};
var rst = new RequestSecurityToken
{
RequestType = RequestTypes.Issue,
AppliesTo = new EndpointReference(_client.BaseAddress.AbsoluteUri),
TokenType = "urn:oasis:names:tc:SAML:1.0:assertion",
KeyType = KeyTypes.Bearer
};
var channel = factory.CreateChannel();
channel.Issue(rst, out RequestSecurityTokenResponse rstr);
var fedSerializer = new WSFederationSerializer();
var rstrContent = fedSerializer.GetResponseAsString(rstr, new WSTrustSerializationContext());
// construct a authentication form
var crmauthenticaionPostDictionary = new Dictionary<string, string>
{
{"wa", queryParams["wa"]},
{"wresult", rstrContent},
{"wctx", queryParams["wctx"]}
};
// post the authentication form to the website.
var crmAuthorizationPostResponse = _client.PostAsync(_client.BaseAddress.AbsoluteUri, new FormUrlEncodedContent(crmauthenticaionPostDictionary)).Result;
var crmAuthorizationPostResponseString = crmAuthorizationPostResponse.Content.ReadAsStringAsync().Result;
//we should be authenticated here
if (
!(
// we are correctly authorized if we got redirected to the correct address that we
// were trying to reach in the first place.
crmAuthorizationPostResponse.StatusCode == HttpStatusCode.Redirect
&& crmAuthorizationPostResponse.Headers.Location == authenticationTestUri
)
)
{
throw new Exception("ADFS Authentication to CRM failed.");
}
When you are doing Postman to CRM request, its direct call & CRM handles it in expected way.
But in Postman -> Microservice -> CRM, the header get lost between Microservice to CRM.
In your Microservice, you have to handle the Header forward manually to CRM SDK call.
HttpWebRequest myHttpWebRequest1= (HttpWebRequest)WebRequest.Create(uri);
myHttpWebRequest1.Headers.Add("MSCRMCallerID", "D994D6FF-5531-E711-9422-00155DC0D345");
Or HTTP Header Forwarding (Sorry I could not find one for Azure / C#)
Update:
Am assuming you are following this MSDN samples to do your CRM web api call in c# microservice. I have included our header in need - MSCRMCallerID. See if it helps you.
public async Task BasicCreateAndUpdatesAsync()
{
Console.WriteLine("--Section 1 started--");
string queryOptions; //select, expand and filter clauses
//First create a new contact instance, then add additional property values and update
// several properties.
//Local representation of CRM Contact instance
contact1.Add("firstname", "Peter");
contact1.Add("lastname", "Cambel");
HttpRequestMessage createRequest1 =
new HttpRequestMessage(HttpMethod.Post, getVersionedWebAPIPath() + "contacts");
createRequest1.Content = new StringContent(contact1.ToString(),
Encoding.UTF8, "application/json");
createRequest1.Headers.Add("MSCRMCallerID", "D994D6FF-5531-E711-9422-00155DC0D345");
HttpResponseMessage createResponse1 =
await httpClient.SendAsync(createRequest1);
if (createResponse1.StatusCode == HttpStatusCode.NoContent) //204
{
Console.WriteLine("Contact '{0} {1}' created.",
contact1.GetValue("firstname"), contact1.GetValue("lastname"));
contact1Uri = createResponse1.Headers.
GetValues("OData-EntityId").FirstOrDefault();
entityUris.Add(contact1Uri);
Console.WriteLine("Contact URI: {0}", contact1Uri);
}
else
{
Console.WriteLine("Failed to create contact for reason: {0}",
createResponse1.ReasonPhrase);
throw new CrmHttpResponseException(createResponse1.Content);
}
}
There are fews things that you have to take care while impersonating
1. To impersonate a user, set the CallerId property on an instance of
OrganizationServiceProxy before calling the service’s Web methods.
2. The user (impersonator) must have the ActOnBehalfOf privilege or be a member of the PrivUserGroup group in Active Directory
Code Example
SystemUser user = null;
user = new SystemUser(systemUser);
OrganizationServiceProxy service = CrmService.Proxy;
service.CallerID = user.Id;
Since your code is not available please ensure all the above fields are set properly
For detailed understanding use the link
https://crmbusiness.wordpress.com/2015/07/21/crm-2015-understanding-impersonation-in-plugins-and-knowing-when-to-use-it/
have followed the steps mentioned in the Push code lab Push Notifications site to integrated Push notification in my dot net ``site
1.register Service worker and also was able to see the Subscription on the page.
Subscription:
Step: have down Loaded the C# Library to send the Push Notifications and have added Test Program to test this C#code. i am able to run the code and no Error were shown but i am not seeing any Push Notification in the Chrome Browser( Kept the same Browser tab opened where i have my Site Running / Also closed the browser and Relaunched to see any Notification).In Either case i am not seeing any Notifications
I am not seeing any Error from the Code an
below is the Response from Web pushClient.
{StatusCode: 201, ReasonPhrase: 'Created', Version: 1.1, Content: System.Net.Http.StreamContent, Headers: { X-Content-Type-Options: nosniff X-Frame-Options: SAMEORIGIN X-XSS-Protection: 1; mode=block Alt-Svc: quic=":443"; ma=2592000; v="39,38,37,36,35" Cache-Control: max-age=0, private Date: Wed, 05 Jul 2017 15:59:43 GMT Location: https://fcm.googleapis.com/fcm/0:1499270383802342%e609af1cf9fd7ecd Server: GSE Content-Length: 0 Content-Type: text/plain Expires: Wed, 05 Jul 2017 15:59:43 GMT }}
var pushEndpoint = #"https://fcm.googleapis.com/fcm/send/dKUDjOvvyjg:APA91bF3jr2gsX--KjonylSL_25TfCGG5mVsFZoYgnESYLep2rlWOI6KK1T9Dghr9E8o1e7a0wtOCH2LqmcCV0pjW7ZuDW7wPptJnnXy3XBu4Eo_CX0fLYGqsqy8voU9pFg6eZyaDFab";
var p256dh = #"BMEI2Zz1LPeFBeBtEZlTV_St0PHb6v_OlldbUoE6wrnnx8ychyz4o7tMt_S_Z0bKQj3vOAS0lMuZRRrgO7-LEGo=";
var auth = #"hwrfOP0UUevO5UdB6WRIQw==";
var subject = #"mailto:megan#gmail.com";
var publicKey = #"BK4ITwwSPZpxelX-oaycWnPuRSnRetbu3QBY4hSm5f1Up24PTrktIrJxRXR9bUIqrGx2YFcDCv48sDwFW50jdmI";
var privateKey = #"PEW_j759M2Q218O9le3GS8OnejmwlWYI1-LtNAP31bg";
var subscription = new PushSubscription(pushEndpoint, p256dh, auth);
var vapidDetails = new VapidDetails(subject, publicKey, privateKey);
//var gcmAPIKey = #"[your key here]";
var webPushClient = new WebPushClient();
try
{
webPushClient.SendNotification(subscription, "payload", vapidDetails);
//webPushClient.SendNotification(subscription, "payload", gcmAPIKey);
}
catch (WebPushException exception)
{
Console.WriteLine("Http STATUS code" + exception.StatusCode);
}
After looking at your service worker source, I think you need to move the showNotification call inside the event.waitUntil call. So something like this:
event.waitUntil(self.registration.showNotification(title, options));
However if you're getting notifications on your home network and not your work network, that leads me to believe something on your work network is blocking it.
I am trying to use the Unified API (Microsoft.Graph 1.0.1) to access my users profil photos, but I only get the following error back when accessing the photo:
Code: ErrorAccessDenied
Message: Access is denied. Check credentials and try again.
Accessing/Listing the other user profile data works fine and my application was added as a "Company Administrator" via PowerShell and has all rights set in the management portal. When I use the GraphExlorer logged in with my admin user it also works fine. Also via the "old" Azure Active Directory Graph API I can read/write to the users thumbnail photo, but thats not the one showing up in Office 365.
How can I get the appropriate access rights to perform actions on users profile photo?
This is the code I use (shortened to the parts in question):
class Program
{
private const string authStringMicrosoft = "https://login.microsoftonline.com/MY_APP_ID/";
private const string clientID = "MY_CLIENT_ID";
private const string clientSecret = "MY_CLIENT_SECRET";
private const string graphResourceId = "https://graph.microsoft.com";
static void Main(string[] args)
{
AsyncContext.Run(RunAsync);
Console.WriteLine("DONE");
Console.ReadLine();
}
private static async Task RunAsync()
{
var token = await GetAppTokenAsync(authStringMicrosoft, graphResourceId);
var authHelper = new AuthenticationHelper() { AccessToken = token }
var graphClient = new GraphServiceClient(authHelper);
await ListUser(graphClient);
}
private static async Task ListUser(GraphServiceClient graphClient)
{
Console.WriteLine("User-List:");
var users = await graphClient.Users.Request().GetAsync();
foreach (var user in users)
{
Console.WriteLine($"{user.UserPrincipalName}:\t\t{user.GivenName} {user.Surname}");
if (user.UserPrincipalName == "USER_WITH_PICTURE")
{
var graphUser = graphClient.Users[user.UserPrincipalName];
var graphPhoto = graphUser.Photo;
var photoInfo = await graphPhoto.Request().GetAsync(); // <= here the exceptions is thrown
Console.WriteLine($"{photoInfo.Id}:\t{photoInfo.Width}x{photoInfo.Height}");
var photoStream = await graphPhoto.Content.Request().GetAsync();
byte[] photoByte = new byte[photoStream.Length];
photoStream.Read(photoByte, 0, (int)photoStream.Length);
File.WriteAllBytes(#"D:\User.jpg", photoByte);
}
}
}
private static async Task<string> GetAppTokenAsync(string authority, string azureGraphAPI)
{
var authenticationContext = new AuthenticationContext(authority);
var clientCred = new ClientCredential(clientID, clientSecret);
var authenticationResult = await authenticationContext.AcquireTokenAsync(azureGraphAPI, clientCred);
return authenticationResult.AccessToken;
}
}
public class AuthenticationHelper : IAuthenticationProvider
{
public string AccessToken { get; set; }
public Task AuthenticateRequestAsync(HttpRequestMessage request)
{
request.Headers.Add("Authorization", "Bearer " + AccessToken);
return Task.FromResult(0);
}
}
I use the following NuGet-packages:
<packages>
<package id="Microsoft.Data.Edm" version="5.7.0" targetFramework="net46" />
<package id="Microsoft.Data.OData" version="5.7.0" targetFramework="net46" />
<package id="Microsoft.Data.Services.Client" version="5.7.0" targetFramework="net46" />
<package id="Microsoft.Graph" version="1.0.1" targetFramework="net46" />
<package id="Microsoft.IdentityModel.Clients.ActiveDirectory" version="2.24.304111323" targetFramework="net46" />
<package id="Newtonsoft.Json" version="8.0.3" targetFramework="net46" />
<package id="Nito.AsyncEx" version="3.0.1" targetFramework="net46" />
<package id="System.Spatial" version="5.7.0" targetFramework="net46" />
</packages>
This is an example request delivering the error (using postman with the token read out from the app above):
GET /v1.0/users/MY_USER_WITH_PHOTO/photo/ HTTP/1.1
Host: graph.microsoft.com
Connection: keep-alive
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik1...
Cache-Control: no-cache
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.112 Safari/537.36
Postman-Token: e756a8a3-22e2-d40c-8e52-15c4d1aa7468
Accept: /
Accept-Encoding: gzip, deflate, sdch
Accept-Language: de,en-US;q=0.8,en;q=0.6
And the response:
HTTP/1.1 403 Forbidden
Cache-Control: private
Transfer-Encoding: chunked
Content-Type: application/json
Server: Microsoft-IIS/8.5
request-id: 96e8dda8-2353-4891-8c42-99cfe7e22887
client-request-id: 96e8dda8-2353-4891-8c42-99cfe7e22887
x-ms-ags-diagnostic: {"ServerInfo":{"DataCenter":"North Europe","Slice":"SliceA","ScaleUnit":"001","Host":"AGSFE_IN_4","ADSiteName":"DUB"}}
Duration: 1367.7691
X-Powered-By: ASP.NET
Date: Sun, 01 May 2016 17:57:02 GMT
Body:
{
"error": {
"code": "ErrorAccessDenied",
"message": "Access is denied. Check credentials and try again.",
"innerError": {
"request-id": "96e8dda8-2353-4891-8c42-99cfe7e22887",
"date": "2016-05-01T17:57:02"
}
}
}
Again, if I remove the /photo from the request I get all common user details without a problem.
Here the permissions of my app (web-app):
Here a decrypted access token:
{
typ: "JWT",
alg: "RS256",
x5t: "MnC_VZcATfM5pOYiJHMba9goEKY",
kid: "MnC_VZcATfM5pOYiJHMba9goEKY"
}.
{
aud: "https://graph.microsoft.com",
iss: "https://sts.windows.net/11205e59-fa81-480f-b497-571579c5389a/",
iat: 1462795409,
nbf: 1462795409,
exp: 1462799309,
appid: "c34a87ef-352a-4af4-a166-eb7e521a0ec9",
appidacr: "1",
idp: "https://sts.windows.net/11205e59-fa81-480f-b497-571579c5389a/",
oid: "1db8c6b5-10ba-40ac-bbff-86ab440c4fd3",
roles: [
"Mail.ReadWrite",
"Device.ReadWrite.All",
"User.ReadWrite.All",
"Calendars.Read",
"Group.Read.All",
"Directory.ReadWrite.All",
"Contacts.ReadWrite",
"Group.ReadWrite.All",
"Directory.Read.All",
"User.Read.All",
"Mail.Read",
"Calendars.ReadWrite",
"Mail.Send",
"MailboxSettings.ReadWrite",
"Contacts.Read"
],
sub: "1db8c6b5-10ba-40ac-bbff-86ab440c4fd3",
tid: "11205e59-fa81-480f-b497-571579c5389a",
ver: "1.0"
}
Just in case anyone else reads this when getting that error. I had this same error when creating my own graphClient and the reason I got it was due to using a non-admin account with...
var users = await graphClient.Users.Request().Select().GetAsync();
With a non-admin account, you only have access to some basic properties like lastname, firstname etc - this worked for me...
var users = await graphClient.Users.Request().Select("mail,givenName,surname").GetAsync();
I'm currently working on a project that has me integrating with Exchange. One of the requirements is to monitor mailboxes for new incoming messages and I thought that leveraging a streaming notifications would be a good idea.
I wrote a sample application to get familiar with how to leverage streaming notifications, however I am encountering the following error: The expected XML node type was Element, but the actual type is Text.
The following is the source of the sample application that I wrote:
using Microsoft.Exchange.WebServices.Data;
using System;
using System.Net;
namespace ExampleProgram
{
class Program
{
public static StreamingSubscriptionConnection streamingConnection;
public static bool RedirectionUrlValidationCallback(string redirectionUrl)
{
bool result = false;
Uri redirectionUri = new Uri(redirectionUrl);
if (redirectionUri.Scheme == "https")
{
result = true;
}
return result;
}
public static void NewMailSubscriptionDisconnect(object sender, SubscriptionErrorEventArgs args)
{
Exception e = args.Exception;
Console.Write("Disconnect: ");
Console.WriteLine(e.Message);
if (streamingConnection != null && !streamingConnection.IsOpen)
{
streamingConnection.Open();
}
}
public static void NewMailSubscriptionError(object sender, SubscriptionErrorEventArgs args)
{
Exception e = args.Exception;
Console.Write("Disconnect: ");
Console.WriteLine(e.Message);
}
public static void NewMailSubscriptionNotification(object sender, NotificationEventArgs args)
{
Console.WriteLine("New message has arrived");
}
static void Main(string[] args)
{
var exchangeService = new ExchangeService(ExchangeVersion.Exchange2013_SP1);
exchangeService.Credentials = new NetworkCredential("username", "password", "domain");
exchangeService.TraceEnabled = true;
exchangeService.TraceFlags = TraceFlags.All;
exchangeService.TraceEnablePrettyPrinting = true;
exchangeService.AutodiscoverUrl("username#example.com", RedirectionUrlValidationCallback);
var newMailSubscription = exchangeService.SubscribeToStreamingNotificationsOnAllFolders(EventType.NewMail);
streamingConnection = new StreamingSubscriptionConnection(exchangeService, 30);
streamingConnection.AddSubscription(newMailSubscription);
streamingConnection.OnNotificationEvent += new StreamingSubscriptionConnection.NotificationEventDelegate(NewMailSubscriptionNotification);
streamingConnection.OnSubscriptionError += new StreamingSubscriptionConnection.SubscriptionErrorDelegate(NewMailSubscriptionError);
streamingConnection.OnDisconnect += new StreamingSubscriptionConnection.SubscriptionErrorDelegate(NewMailSubscriptionDisconnect);
streamingConnection.Open();
do { } while (Console.ReadKey(true).Key != ConsoleKey.Escape);
}
}
}
As you can see from the above source, I have tracing turned on. The following is what is yielded from those traces:
EwsResponseHttpHeader
<Trace Tag="EwsResponseHttpHeaders" Tid="17" Time="2015-10-20 17:42:31Z">
HTTP/1.1 200 OK
Transfer-Encoding: chunked
request-id: <redacted>
X-CalculatedBETarget: EXAMPLE-EXCHANGE-01.example.com
X-NoBuffering: 1
X-DiagInfo: EXAMPLE-EXCHANGE-01
X-BEServer: EXAMPLE-EXCHANGE-01
Cache-Control: private
Set-Cookie: exchangecookie=<redacted>; path=/,X-BackEndCookie=<redacted>; expires=Thu, 19-Nov-2015 17:42:30 GMT; path=/ews; secure; HttpOnly
Server: Microsoft-IIS/8.5
X-AspNet-Version: 4.0.30319
Persistent-Auth: true
X-Powered-By: ASP.NET
X-FEServer: EXAMPLE-EXCHANGE-02
Date: Tue, 20 Oct 2015 17:42:30 GMT
</Trace>
EwsResponse
<Trace Tag="EwsResponse"
Tid="15"
Time="2015-10-20 16:52:07Z"
Version="0.0.0.0">
417 <!-- What is this? -->
<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/">
<soap11:Header xmlns:soap11="http://schemas.xmlsoap.org/soap/envelope/">
<ServerVersionInfo xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
MajorVersion="15"
MinorVersion="0"
MajorBuildNumber="1130"
MinorBuildNumber="6"
Version="V2_23"
xmlns="http://schemas.microsoft.com/exchange/services/2006/types" />
</soap11:Header>
<soap11:Body xmlns:soap11="http://schemas.xmlsoap.org/soap/envelope/">
<m:GetStreamingEventsResponse xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types"
xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages">
<m:ResponseMessages>
<m:GetStreamingEventsResponseMessage ResponseClass="Success">
<m:ResponseCode>NoError</m:ResponseCode>
<m:ConnectionStatus>OK</m:ConnectionStatus>
</m:GetStreamingEventsResponseMessage>
</m:ResponseMessages>
</m:GetStreamingEventsResponse>
</soap11:Body>
</Envelope>
2 <!-- Not sure what this is either... -->
</Trace>
Exception Detail
Microsoft.Exchange.WebServices.Data.ServiceXmlDeserializationException occurred
HResult=-2146233088
Message=The expected XML node type was Element, but the actual type is Text.
Source=Microsoft.Exchange.WebServices
StackTrace:
at Microsoft.Exchange.WebServices.Data.EwsXmlReader.Read(XmlNodeType nodeType) in C:\Projects\ews-managed-api\Core\EwsXmlReader.cs:line 187
InnerException:
The source of EwsXmlReader.cs can be found at: https://github.com/OfficeDev/ews-managed-api/blob/master/Core/EwsXmlReader.cs
It looks like "something" is prepending 417 and appending 2 to the response from the Exchange server. It's quite obvious to me why the exception is being thrown, there is text data where there shouldn't be. What's not obvious to me, is why that text data is there.
Any ideas?
Transfer-Encoding: chunked
That's the key to this puzzle, you are seeing the "chunks". 417 is a value in hexadecimal for the length of the <Envelope> chunk when you remove the pretty-print formatting. 2 is the final chunk, just whitespace. Chunked transfer formatting is explained here.
I reformatted the XML to remove the white space, You can count off exactly 0x417 = 1047 characters:
<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/"><soap11:Header xmlns:soap11="http://schemas.xmlsoap.org/soap/envelope/"><ServerVersionInfo xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" MajorVersion="15" MinorVersion="0" MajorBuildNumber="1130" MinorBuildNumber="6" Version="V2_23" xmlns="http://schemas.microsoft.com/exchange/services/2006/types"/></soap11:Header><soap11:Body xmlns:soap11="http://schemas.xmlsoap.org/soap/envelope/"><m:GetStreamingEventsResponse xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types" xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages"><m:ResponseMessages><m:GetStreamingEventsResponseMessage ResponseClass="Success"><m:ResponseCode>NoError</m:ResponseCode><m:ConnectionStatus>OK</m:ConnectionStatus></m:GetStreamingEventsResponseMessage></m:ResponseMessages></m:GetStreamingEventsResponse></soap11:Body></Envelope>
Obviously the http transfer is supposed to remove them, your question gives no decent lead why this did not happen. But hopefully a pretty good lead to find the underlying cause. Fun puzzle btw :)
I am working with IPCams for the first time and I am trying to connect to an OnVif camera. I have looked on various forums and stack overflow and I have come up with the following code.I know the code doesn't do anything useful but it is just a proof of concept for now. It finds all 4 cameras on my network and then I am manually connecting to one of them to pull back some information such as GetServices.
I get a 400 bad response error at this stage. I have looked at the traffic back and forth with WireShark and it appears that everything is working ok regarding the password being generated in so far as I can tell (security and encryption is not my area at all!).
Can anyone help or advise?
class Program
{
static void Main(string[] args)
{
var endPoint = new UdpDiscoveryEndpoint(DiscoveryVersion.WSDiscoveryApril2005);
var discoveryClient = new DiscoveryClient(endPoint);
discoveryClient.FindProgressChanged += discoveryClient_FindProgressChanged;
discoveryClient.FindCompleted += discoveryClient_FindCompleted;
FindCriteria findCriteria = new FindCriteria();
findCriteria.Duration = new TimeSpan(0, 0, 2);//TimeSpan.MaxValue;
findCriteria.MaxResults = int.MaxValue;
discoveryClient.FindAsync(findCriteria);
Console.ReadKey();
}
private static void discoveryClient_FindCompleted(object sender, FindCompletedEventArgs e)
{
Console.WriteLine("Discovery complete");
}
static void discoveryClient_FindProgressChanged(object sender, FindProgressChangedEventArgs e)
{
foreach (var u in e.EndpointDiscoveryMetadata.ListenUris)
{
string uri = u.OriginalString;
if (uri.Contains("http://192.168.1.162/onvif/device_service"))
{
Console.WriteLine(uri);
EndpointAddress serviceAddress = new EndpointAddress(uri);
HttpTransportBindingElement httpBinding = new HttpTransportBindingElement();
httpBinding.AuthenticationScheme = AuthenticationSchemes.Digest;
var messegeElement = new TextMessageEncodingBindingElement();
messegeElement.MessageVersion = MessageVersion.CreateVersion(EnvelopeVersion.Soap12, AddressingVersion.None);
CustomBinding bind = new CustomBinding(messegeElement, httpBinding);
DeviceClient client = new DeviceClient(bind, serviceAddress);
// Add our custom behavior - this require the Microsoft WSE 3.0 SDK
PasswordDigestBehavior behavior = new PasswordDigestBehavior("test", "test");
client.Endpoint.Behaviors.Add(behavior);
foreach (Service s in client.GetServices(false))
Console.WriteLine(s.ToString());
client.Open();
Console.WriteLine("WSDL = " + client.GetWsdlUrl());
Console.WriteLine("DateTime = " + client.GetSystemDateAndTime());
string a1, b1, c1, d1;
Console.Write(client.GetDeviceInformation(out a1, out b1, out c1, out d1));
}
}
}
}
Wireshark (username and passwor are both test)
POST /onvif/device_service HTTP/1.1
Content-Type: application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver10/device/wsdl/GetServices"
Host: 192.168.1.162
Content-Length: 1232
Expect: 100-continue
Accept-Encoding: gzip, deflate
Connection: Keep-Alive
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">
<s:Header>
<VsDebuggerCausalityData xmlns="http://schemas.microsoft.com/vstudio/diagnostics/servicemodelsink">uIDPo7uVma6HRQNDh2l6T2ZDNzIAAAAA2/ITWE91IUaNFF3UObayz0mz6QvnZppBlYrNJBd1QGsACQAA</VsDebuggerCausalityData>
<Security xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<wsse:UsernameToken wsu:Id="SecurityToken-56f9081e-e9b4-4660-9158-7419af1efde0" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<wsse:Username>test</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">WSKWE5XjP5aPiIiA9JicCOYoDkU=</wsse:Password>
<wsse:Nonce>6sYgS41VHsWKj7n8TNKFjA==</wsse:Nonce>
<wsu:Created>2013-08-09T14:52:45Z</wsu:Created>
</wsse:UsernameToken>
</Security>
</s:Header>
<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<GetServices xmlns="http://www.onvif.org/ver10/device/wsdl">
<IncludeCapability>false</IncludeCapability>
</GetServices>
</s:Body>
</s:Envelope>HTTP/1.1 400 Bad Request
Server: gSOAP/2.7
Content-Type: application/soap+xml; charset=utf-8
Content-Length: 2751
Connection: close
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://www.w3.org/2003/05/soap-envelope" xmlns:SOAP-ENC="http://www.w3.org/2003/05/soap-encoding" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:c14n="http://www.w3.org/2001/10/xml-exc-c14n#" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsa5="http://www.w3.org/2005/08/addressing" xmlns:xmime="http://tempuri.org/xmime.xsd" xmlns:xop="http://www.w3.org/2004/08/xop/include" xmlns:wsrfbf="http://docs.oasis-open.org/wsrf/bf-2" xmlns:wstop="http://docs.oasis-open.org/wsn/t-1" xmlns:tt="http://www.onvif.org/ver10/schema" xmlns:wsrfr="http://docs.oasis-open.org/wsrf/r-2" xmlns:aa="http://www.axis.com/vapix/ws/action1" xmlns:aev="http://www.axis.com/vapix/ws/event1" xmlns:tan1="http://www.onvif.org/ver20/analytics/wsdl/RuleEngineBinding" xmlns:tan2="http://www.onvif.org/ver20/analytics/wsdl/AnalyticsEngineBinding" xmlns:tan="http://www.onvif.org/ver20/analytics/wsdl" xmlns:tds="http://www.onvif.org/ver10/device/wsdl" xmlns:tev1="http://www.onvif.org/ver10/events/wsdl/NotificationProducerBinding" xmlns:tev2="http://www.onvif.org/ver10/events/wsdl/EventBinding" xmlns:tev3="http://www.onvif.org/ver10/events/wsdl/SubscriptionManagerBinding" xmlns:wsnt="http://docs.oasis-open.org/wsn/b-2" xmlns:tev4="http://www.onvif.org/ver10/events/wsdl/PullPointSubscriptionBinding" xmlns:tev="http://www.onvif.org/ver10/events/wsdl" xmlns:timg="http://www.onvif.org/ver20/imaging/wsdl" xmlns:tptz="http://www.onvif.org/ver20/ptz/wsdl" xmlns:trt="http://www.onvif.org/ver10/media/wsdl" xmlns:ter="http://www.onvif.org/ver10/error" xmlns:tns1="http://www.onvif.org/ver10/topics" xmlns:tnsaxis="http://www.axis.com/2009/event/topics">
<SOAP-ENV:Header>
<VsDebuggerCausalityData xmlns="http://schemas.microsoft.com/vstudio/diagnostics/servicemodelsink">uIDPo7uVma6HRQNDh2l6T2ZDNzIAAAAA2/ITWE91IUaNFF3UObayz0mz6QvnZppBlYrNJBd1QGsACQAA</VsDebuggerCausalityData>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<SOAP-ENV:Fault SOAP-ENV:encodingStyle="http://www.w3.org/2003/05/soap-encoding">
<SOAP-ENV:Code>
<SOAP-ENV:Value>SOAP-ENV:Sender</SOAP-ENV:Value>
<SOAP-ENV:Subcode>
<SOAP-ENV:Value>ter:NotAuthorized</SOAP-ENV:Value>
</SOAP-ENV:Subcode>
</SOAP-ENV:Code>
<SOAP-ENV:Reason>
<SOAP-ENV:Text xml:lang="en">Sender not authorized</SOAP-ENV:Text>
</SOAP-ENV:Reason>
<SOAP-ENV:Detail>
<SOAP-ENV:Text>The action requested requires authorization and the sender is not authorized</SOAP-ENV:Text>
</SOAP-ENV:Detail>
</SOAP-ENV:Fault>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
I have onvif enabled on the camera (by creating an administrator user called "test" with a password "test". The camera password is the default "root" and "pass" as it is an Axis camera.
Configure the connection to the camera this way:
ServicePointManager.Expect100Continue = false;
var endPointAddress = new EndpointAddress("http://" + cameraAddress + "/onvif/device_service");
var httpTransportBinding = new HttpTransportBindingElement { AuthenticationScheme = AuthenticationSchemes.Digest };
var textMessageEncodingBinding = new TextMessageEncodingBindingElement { MessageVersion = MessageVersion.CreateVersion(EnvelopeVersion.Soap12, AddressingVersion.None) };
var customBinding = new CustomBinding(textMessageEncodingBinding, httpTransportBinding);
var passwordDigestBehavior = new PasswordDigestBehavior(adminName, adminPassword);
var deviceClient = new DeviceClient(customBinding, endPointAddress);
deviceClient.Endpoint.Behaviors.Add(passwordDigestBehavior);
I know it's almost what you have done but it is important to make ServicePointManager.Expect100Continue false.
Check if the camera have the replay attack protection enabled. If so check the time difference between your computer's time and the camera's. According to the ONVIF's specs the allowed time difference must be +- 5 seconds. Otherwise you get your error.
If this is your case you have several options:
1- Disable the replay attack protection feature. This is not recommended because you would need to disable this feature in all the cameras you need to work with.
2- You can sync the camera's time with your computer time. Again not recommended for the same issue of the first option.
3- If you can change the WSE 3.0 for other option. In WSE3.0 once you have created the UsernameToken you can't change the Created property, which is used to create the encryption. This problem is described in here