I'm using the Microsoft Office 365 REST API to read calendar items from Office 365 and Outlook.com accounts.
It work's well.
Now, I need to read the same from Office 365 Deutschland accounts. That doesn't work.
I already found out the following:
Use another endpoint for login
International: https://login.microsoftonline.com/common/oauth2/v2.0/authorize
Deutschland: https://login.microsoftonline.de/common/oauth2/authorize
Use another endpoint for the REST API
International: https://outlook.office.com/api/v2.0/me/calendar/events
Deutschland: https://outlook.office.de/api/v2.0/me/calendar/events
Need another ClientID/ClientSecret
International: https://apps.dev.microsoft.com
Deutschland: https://portal.microsoftazure.de
Use different oauth2 scope
International: offline_access https://outlook.office.com/Calendars.Read
Deutschland: offline_access openid https://outlook.office.de/Calendars.Read
With all that, I can get an OAuth2 access token. But when I call
https://outlook.office.de/api/v2.0/me
or
https://outlook.office.de/api/v2.0/me/calendarview?startDateTime=2018-01-01T01:00:00&endDateTime=2018-10-31T23:00:00
with that token, I only get the following error:
Request Headers:
cache-control:"no-cache"
postman-token:"b765d2d1-9ffc-4016-8216-38678af4f245"
authorization:"Bearer AQA*** snip for security***gAA"
user-agent:"PostmanRuntime/7.1.5"
accept:"*/*"
host:"outlook.office.de"
cookie:"ClientId=DFDA316304974E36A43D11CF7BB6D8A3; OIDC=1; OpenIdConnect.nonce.v3.y7kDkk7dHuGjDZ9PZ_xiLj0CjfuLbQt629j5MuTcNp8=636667242602536754.c00ff4a6-e523-4dff-b1ce-24d1d024ce67"
accept-encoding:"gzip, deflate"
Response Headers:
server:"Microsoft-IIS/10.0"
request-id:"e59f5e5e-0980-4914-9087-064270bdd233"
x-calculatedfetarget:"LEJPR01CU002.internal.outlook.com"
x-backendhttpstatus:
0:"401"
1:"401"
x-feproxyinfo:"LEJPR01CA0057.DEUPRD01.PROD.OUTLOOK.DE"
x-calculatedbetarget:"FRXPR01MB0456.DEUPRD01.PROD.OUTLOOK.DE"
x-rum-validated:"1"
x-ms-diagnostics:"2000010;reason="ErrorCode: 'PP_E_RPS_INVALIDCONFIG'. Message: 'Invalid configuration. Check event log for actions.%0d%0a Internal error: Config directory does not exist; config directory must exist and be an absolute path:C:\Program Files\Microsoft Passport RPS\LiveIdConfig.'";error_category="invalid_msa_ticket""
x-besku:"Gen8"
x-diaginfo:"FRXPR01MB0456"
x-beserver:"FRXPR01MB0456"
x-feserver:
0:"LEJPR01CA0057"
1:"FRAPR01CA0084"
x-powered-by:"ASP.NET"
www-authenticate:"Bearer client_id="00000002-0000-0ff1-ce00-000000000000", trusted_issuers="00000001-0003-0000-c000-000000000000#*,00000002-0000-0ff1-ce00-100000000002#84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa", token_types="app_asserted_user_v1 service_asserted_app_v1", authorization_uri="https://login.microsoftonline.de/common/oauth2/authorize", error="invalid_token",Basic Realm="",Basic Realm="",Basic Realm="""
date:"Mon, 09 Jul 2018 09:46:28 GMT"
content-length:"0"
Response Body:
What am I doing wrong?
What does PP_E_RPS_INVALIDCONFIG mean?
Where can I create the directory C:\Program Files\Microsoft Passport RPS\LiveIdConfig?
The problem was the token. It was visually fine, but inside it was not.
With Office 365 Deutschland, you have to use the Azure Active Directory authentication endpoint V1.0 (no version number in the endpoint url means V1.0). For International, you should use the V2.0 endpoint.
The V2 enpoint wants the resource hint in the scope: https://outlook.office.com/Calendars.Read.
The V1 endpoint ignores the scope (and the resource hint in it) and wants an additional OAuth2 uri parameter called resource. The errors did't pointed me in the right direction.
But it was documented (badly) at this point.
resource
recommended
The App ID URI of the target web API (secured resource).
To find the App ID URI, in the Azure Portal, click Azure Active Directory,
click Application registrations, open the application's Settings page, then click Properties.
It may also be an external resource like https://graph.microsoft.com.
This is required in one of either the authorization or token requests.
To ensure fewer authentication prompts place it in the authorization request to ensure
consent is received from the user.
I think it should say required with V1.0.
Here my working authorization endpoint call:
https://login.microsoftonline.de/common/oauth2/authorize?response_type=code&client_id=c9000000-0000-0000-0000-000000000007&redirect_uri=http://localhost:11184/&state=1P3S9RvgQyfd3xLXmbhPcYD12aNHYkBF&scope=offline_access%20Calendars.Read&resource=https://outlook.office.de
By the way: If you use the resource parameter with the V2.0 endpoint, it gives you the error:
https://login.microsoftonline.com/common/oauth2/v2.0/authorize?response_type=code&client_id=6262047e-0000-0000-0000-000000000007&redirect_uri=http://localhost:11184/&state=2pSIj7o7dJJ0bGIv0p1ZfRjK6DV2duwM&scope=offline_access%20https://outlook.office.com/Calendars.Read&resource=https://outlook.office.com
AADSTS90100: The 'resource' request parameter is not supported.
Related
I'm trying to export Office Policies available in 2 place:
In Intune Endpoint manager (https://endpoint.microsoft.com)
In https://config.office.com
I'm generating a token (following intune rest api documentation) an I call:
GET https://config.office.com/api/OfficeSettings/policies HTTP/1.1
The error returned is:
WWW-Authenticate: Bearer error="invalid_token", error_description="S2S17001: SAL was able to validate the protocol, but validation failed as none of the inbound policies were satisfied. Validation failures: '33d566a7-b1b3-4676-b399-b215146b78c4: InvalidAudience."
Apparently the token is invalid and I didn't find any documentation for how to generate the right token.
Microsoft documentation is stating that the intune portal is entirely written on top of graph api but few calls (including this one) are making external calls (but pass the same bearer token).
Programmatically, the token is invalid.
The real question : is how to we read/write the Office Policies? (How do we generate an application token?).
I have created a PAT token for my user account in TFS Server 2017 (on-premises, not VSTS). Using Postman I am attempting to call the following REST API with Basic Auth:
GET http://{server:port}/tfs/DefaultCollection/_apis/projects?api-version=1.0
In response I get the following error:
TF401444: Please sign-in at least once as {Well Known SID for my user account} in a web browser to enable access to the service. - Microsoft Team Foundation Server
I have tried opening the same URL in Chrome and attempting to login with the PAT but with no success (the login is not completed). Any suggestions as to what I am missing here?
TFS seems to be recognising the PAT token as I receive a different error if I revoke it.
The issue here was the use of 'DefaultCollection' in the URI. I didn't have permissions on the default project collection in our TFS instance and needed to replace 'DefaultCollection' with the name of the collection I actually did have access to.
It seems obvious now, but the documentation didn't make it clear that this was a variable part of the URI so I had assumed the API was always hosted as part of the DefaultCollection.
I've set up something called the Data Export Service for Dynamics 365 so that it replicates into an Azure SQL database. This is working as expected.
I'm trying to find a way to be proactively notified if this service encounters any errors. There does not appear to be a native way to do this through the setup in CRM itself, but they do provide an API. The Swagger page outlining all methods can be found here.
I'm trying to call the GetProfilesByOrganizationId method using Postman:
https://discovery.crmreplication.azure.net/crm/exporter/profiles?organizationId=4ef7XXXX-XXXX-XXXX-XXXX-XXXXXX8a98f&status=true
I'm having issues with authentication and always receive the following error:
"Message": "Received unauthenticated requestRequest Url https://discovery.crmreplication.azure.net/crm/exporter/profiles?organizationId=4ef7XXXX-XXXX-XXXX-XXXX-XXXXXX8a98f&status=true"
I have registered an application in Azure that has permission to access Dynamics 365 on behalf of the authenticated user which in this case is me, the administrator.
I have set the Type to OAuth 2.0 on the Authorization tab of Postman. I have requested an Access Token using the Grant Type of Authorization Code against the above application successfully. This has added a header to the request:
Key: Authorization
Value: Bearer BIGLONGACCESSTOKEN
Despite this header being present I still get the error mentioned above.
The API documentation implies the authentication is OAuth2 Implicit Grant Flow (click on any red exclamation mark in the documentation) but I can't get this to work in Postman. When I try to request a token with this method I get the error:
unsupported_response_type
... in the Postman console.
Any ideas how to authenticate (with Implicit Grant?) against this API in Postman?
(I'd accept C# examples if they're more appropriate, but I'd be surprised if Postman can't show me what I need)
It looks like the code sample shown by Microsoft can work if updated with newer methods and with some extra configuration in Azure that's not documented.
Azure configuration
By installing the Data Export service (and assuming it's all working) you'll have a new Enterprise Application listed in Azure AD as Crm Exporter.
To take advantage of this application and authenticate with the Data Export API you must configure an app of your own.
Go to the App registrations tab in Azure AD and add a new application registration.
Give it a name and set the Application type to Native. The redirect URI doesn't typically matter as long as it's valid.
Click the Manifest button to edit the manifest, change the property oauth2AllowImplicitFlow to true and save the changes.
The only other important configuration is Required permissions which should be set as below:
Windows Azure Active Directory
Delegated permissions
Sign in and read user profile
Data Export Service for Microsoft Dynamics 365 (Crm Exporter)
Delegated permissions
Have access to Data Export Service for Microsoft Dynamics 365 API
You will then need to click Grant Permissions.
C# changes
The updated method looks like this:
using Microsoft.IdentityModel.Clients.ActiveDirectory;
string clientId = "11cfXXXX-XXXX-XXXX-XXXX-XXXXXXXXd020";
string user = "my.username#domain.com";
string password = "PASSWORD";
var authParam= await AuthenticationParameters.CreateFromResourceUrlAsync(
new Uri("https://discovery.crmreplication.azure.net/crm/exporter/aad/challenge")
);
var context = new AuthenticationContext(authParam.Authority, false);
var credentials = new UserPasswordCredential(user, password);
var token = await context.AcquireTokenAsync(authParam.Resource, clientId, credentials).AccessToken;
You can now query the Data Export API by providing the token as a header:
Authorization : Bearer eJ0y........Hgzk
curl -X GET --header 'Accept: application/json' 'https://discovery.crmreplication.azure.net/crm/exporter/profiles?organizationId=MyOrgId&status=true'
I am writing an SSO provider for MS Graph APIs Azure AD v2 endpoint leveraging Spring OAuth2.
I am progressing with the implementation and constant testing but I stumbled upon an error returned by AAD which is puzzling me. After all, this should all be plain standard OAuth 2 flow.
I successfully configured my application on MS dev portal, providing a localhost redirect URL (which, for the record, is the only supporting the http scheme. Kudos to MS). So when I invoke http://localhost/myapp/auth/office365 Spring security successfully intercepts the invocation, provides a correct redirect to my browser with client ID to https://login.microsoftonline.com/common/oauth2/v2.0/authorize with expected parameters.
Microsoft shows a consent screen to me, after which I get redirected back to my Spring Security application via HTTP GET with expected authorization code parameter.
The problem is that when the application tries to negotiate the given authorization code for a bearer token headaches start. Spring Security invokes a POST to https://login.microsoftonline.com/common/oauth2/v2.0/token but ends in 401 error.
Here is the stack trace
error="invalid_request", error_description="AADSTS90014: The request body must contain the following parameter: 'client_id'.
Trace ID: 9acd2a10-1cfb-443f-9c57-78d608c00c00
Correlation ID: bf063914-8926-4e8f-b102-7522d0e3b0af
Timestamp: 2017-10-09 15:51:44Z", correlation_id="bf063914-8926-4e8f-b102-7522d0e3b0af", error_codes="[90014]", timestamp="2017-10-09 15:51:44Z", trace_id="9acd2a10-1cfb-443f-9c57-78d608c00c00"
at org.springframework.security.oauth2.common.exceptions.OAuth2ExceptionJackson2Deserializer.deserialize(OAuth2ExceptionJackson2Deserializer.java:100)
at org.springframework.security.oauth2.common.exceptions.OAuth2ExceptionJackson2Deserializer.deserialize(OAuth2ExceptionJackson2Deserializer.java:33)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4001)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3072)
at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:235)
at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readInternal(AbstractJackson2HttpMessageConverter.java:215)
at org.springframework.http.converter.AbstractHttpMessageConverter.read(AbstractHttpMessageConverter.java:193)
at org.springframework.security.oauth2.client.token.OAuth2AccessTokenSupport$AccessTokenErrorHandler.handleError(OAuth2AccessTokenSupport.java:235)
at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:700)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:653)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:621)
at org.springframework.security.oauth2.client.token.OAuth2AccessTokenSupport.retrieveToken(OAuth2AccessTokenSupport.java:137)
at org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeAccessTokenProvider.obtainAccessToken(AuthorizationCodeAccessTokenProvider.java:209)
at org.springframework.security.oauth2.client.token.AccessTokenProviderChain.obtainNewAccessTokenInternal(AccessTokenProviderChain.java:148)
at org.springframework.security.oauth2.client.token.AccessTokenProviderChain.obtainAccessToken(AccessTokenProviderChain.java:121)
at org.springframework.security.oauth2.client.OAuth2RestTemplate.acquireAccessToken(OAuth2RestTemplate.java:221)
at org.springframework.security.oauth2.client.OAuth2RestTemplate.getAccessToken(OAuth2RestTemplate.java:173)
at org.springframework.security.oauth2.client.filter.OAuth2ClientAuthenticationProcessingFilter.attemptAuthentication(OAuth2ClientAuthenticationProcessingFilter.java:105)
at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:212)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:200)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
I have looked into Spring security implementation to find the cause,.
It happens that the error message error="invalid_request", error_description="AADSTS90014: The request body must contain the following parameter: 'client_id'. is self explanatory: MS Graph wants the client ID (which is still supplied by the basic authentication header) in the request body. Stop for a moment. I want to use plain old Spring Security and not third-party specific jars in order not to pollute my classpath.
Looking into Java source code of Spring OAuth 2 the problem is damn clear. Spring uses the client ID only in getParametersForAuthorizeRequest, which is used to generate the redirect URL. When it comes to getParametersForTokenRequest the client ID is not specified in the form.
Question: who is right here? How do I tell Spring that MS wants the client id in the token request after an authorization code has been obtained?
Just to clarify, you're not actually authenticating with or against Microsoft Graph. You're actually authenticating against Azure Active Directory. The Microsoft Graph API accepts the bearer token you'll end up with but it doesn't issue the access token itself.
It isn't clear which endpoint you're using for the Authorization Code flow, AAD has two of them: v1 and v2. The primary difference being that v2 uses a central registration and can authenticate both work/school and personal accounts.
Regardless of the endpoint, you do need to supply the clientid in the request body when you're requesting an access token. There are actually several values you need to provide in the body. Also note that these need to be provided as application/x-www-form-urlencoded.
For the v1 endpoint you provide (line breaks for readability only):
grant_type=authorization_code
&client_id={client-id}
&code={authoization-code}
&redirect_uri={redirect-uri}
&client_secret={client-secret}
&resource={resource-uri}
The v2 endpoint is almost identical but uses scope instead of resource:
grant_type=authorization_code
&client_id={client-id}
&code={authoization-code}
&redirect_uri={redirect-uri}
&client_secret={client-secret}
&scope={scopes}
OP's edit
Now, back to Spring Security. Spring by default uses an HTTP basic authentication scheme against Azure AD. In that scheme, the client ID and secret are encoded into the HTTP Authorization header, then the form only contains the authorization code and state parameter, so here is why I (the OP, ndr) was puzzled about why AAD refused the authorization.
In order to pass client ID and secret into the form, we can tell Spring Security to use a different supported authentication scheme. The form authentication scheme will push the client ID and secret into the form.
The below code works and retrieves the access token.
<oauth2:resource
id="msAdAuthenticationSource"
client-id="${oauth.appId}"
client-secret="${oauth.appSecret}"
type="authorization_code"
authentication-scheme="form"
client-authentication-scheme="form"
use-current-uri="true"
user-authorization-uri="${oauth.authorizationUri}"
access-token-uri="${oauth.accessTokenUri}"
scope="${oauth.scopes}"
pre-established-redirect-uri="${oauth.redirectUri}" />
Please note the two
authentication-scheme="form"
client-authentication-scheme="form"
Problem solved, a lot more to come!
We are trying to connect to a custom Dynamics 365 Finance service operation but are struggling to authorize.
We've set up an app registration
Redirect_uri set to the dynamics url (root)
Enabled implicit grant (both for access tokens and ID tokens)
Single tenant
Assigned the "Dynamics ERP > CustomService.FullAccess" API permission
Assigned the "Dynamics ERP > Odata.FullAccess" API permission
Assigned "Dynamicd ERP > Connector.FullAccess" API permission + granted admin consent for entire AD
Created a secret
We've added the Application (client ID) within the Dynamics 365 environment with a user which has System Administrator role
We can successfully retrieve access tokens both via Postman and .NET (Microsoft.IdentityModel.Clients.ActiveDirectory)
we tried to reach both https://xxxxxxdevaossoap.cloudax.dynamics.com &https://xxxxxxdevaos.cloudax.dynamics.com
Yet when we call custom services, we get a 401 UnAuthorized
A call to Odata also fails for the same reason.
Verify resource in your request for OAuth token (and verify token at https://jwt.io for aud (Audience) field).
It should be same as your primary url without / at the end (like https://d365fo-10-12345678baef10230aos.cloudax.dynamics.com).
Also verify that Azure Active Directory applications (mi=SysAADClientTable) Client Id is equal to appid field in the token (and without any special characters).
You can always inspect Windows Event Log Microsoft-Dynamics-AX-WebApi/Operational
There is good information like this:
Source: Microsoft-Dynamics-AX-WebApi
Category: WebApiOwinConfigurationMissingError
Level: Error
Description: Web API Owin Authentication Configuration Missing Error
infoMessage: Can read the token but failed validating token with exception
'IDX10214: Audience validation failed.
Audiences: 'https://d365fo-10-12345678baef10230aos.cloudax.dynamics.com/'.
Did not match: validationParameters.
ValidAudience: 'null' or validationParameters.ValidAudiences:
'https://d365fo-10-12345678baef10230aos.cloudax.dynamics.com,
00000015-0000-0000-c000-000000000000,
Microsoft.ERP''