I am struggling to access the Dynamics 2016 CRM OData Web APIs from a console application.
We have Dynamics CRM 2016 installed, configured with Claims-based authentication, and using AD FS v3.0.
My understanding is that a console app (or web app) should be able to access the Web APIs using Windows integrated authentication (i.e. NTML or Kerberos) without any special treatment ... or maybe the OAuth flow should work when enabled.
For a regular user accessing Dynamics "pages", the authentication works fine (redirection to AD FS log in page), but accessing the OData APIs does not seem to work (for instance : https://crm.domain.org/api/discovery/v8.0/ ) :
in a browser I get a Windows login prompt and typing valid credentials always results in a HTTP 401 unauthorized error
in a brower, if I navigate to a Web API url after having logged on on the pages , then I can access the Web APIs (i.e. some cookies must be set and I am already implicitly authorized)
from code, using an HttpClient with specific valid credentials (or current credentials) , I also get a 401
Things I have tried :
if I disable Claims-based authentication completely , HttpClient works fine and I can access the OData APIs
if I leave Claims-based authentication enabled, and activate OAuth via PowerShell Add-PSSnapin Microsoft.Crm.PowerShell ; $ClaimsSettings = Get-CrmSetting -SettingType OAuthClaimsSettings; $ClaimsSettings.Enabled = $true ; Set-CrmSetting -Setting $ClaimsSettings ;.
Windows integrated authentication still does not work, but using Bearer authentication is now possible. I can use this snippet to retrieve the OAuth Endpoint for token generation, and use AuthenticationContext.AcquireTokenAsync to issue a token, and then pass it in the Authorization HTTP Header ... but then, no matter what, I get this error :
Bearer error=invalid_token, error_description
=Error during token validation!, authorization_uri=https://our.adfs.domain.org/adfs/oauth2/authorize, resource_id=https://crm.domain.org/
Am I missing something ? is that possibly a configuration issue ?
From this answer from the dynamics community forum, it looks like the api is pretty strict about the parameters and headers it requires. When doing the request, make sure you have the Cache-Control: no-cache and Content-Type: application/x-www-form-urlencoded headers set.
In the subsequent request to access the api with the retrieved token you should set the Authorization header in the form of Bearer: TOKEN (worth noting since a lot of people actually thought they could directly put the token), the OData-Version: 4.0, Cache-Control: no-cache and Accept: application/json ones too.
Looking at the different OAuth endpoints and the previously linked answer, I'm not sure the authorization uri is the right one (eg https://login.windows.net), so do you make sure that's correct. It's also stated that you should use the OAuth endpoint url and use the WWW-Authenticate header that returns the valid one, even if this route will respond with a 401. I'm sure you already saw this example, but it provides a pretty complete overview of an auth flow and how the token is retrieved using AcquireTokenAsync where you pass your resource and clientID. I might also be looking at an updated page and it's not relevant in your case.
You also want to check if the resource id you specified is the correct one, some people reported to have to specify one in the form of https://crm3.domain.org/ or https://crm4.domain.org/ instead of the bare one, so that could be one thing.
It could also be a configuration issue, given what #l said about the fact an IP would work instead of the domain name. It could very well be a certificate problem, where it's not validated correctly or untrusted, thus creating the error you see even if it's not the appropriate message. Also make sure your 443 port is allowed through your firewall(s).
One interesting post where the author explains that the Form Authentication setting of the AD FS Management Console was required for him to proceed (it's CRM 2013, but might still be related).
Related
Question
If you would use a similar setup as the following examples:
Simple WebAPI
Javascript OIDCClient and usermanager
Would it be possible to protect other clientside files from being accessed? Say for example i have a directory with certain files which you need a certain role to be able to access them.
Would it be possible to protect my SPA from being accessed before logging in?
Or is there a better solution which would have you end up with a protected api, folders/files on a server, SPA and a silent renew mechanism like there is in the OIDCClient?
#dmccaffery helped me out by answering my question, here is his answer for those of you who are interested.
To summarize using the OIDCClient for an SPA is certainly the way to go. Exposing stuff which needs authorization should be done by using an API. Protecting parts of your Angular App can be done using a Route guard.
The way it works is as follows:
The access token is either a JWT or a bearer token (usually) and is
added by the oidc client to every HTTP request in an authorization
header — when a web API receives a reques, the bearer token
authorization middleware will parse this HTTP header and will call the
token introspection endpoint (and potentially the user info endpoint)
to have the token validated and the users claims retrieved… if the
token was manipulated by the client, it will not be valid, and an HTTP
error will be returned (usually a 403). If the token was valid, a
claims identity is created and assigned to the http request context.
The API will now have a thread with an identity already assigned to it
that represents that user.
Also he pointed out 2 pluralsight courses which would probably be useful:
https://www.pluralsight.com/courses/building-securing-restful-api-aspdotnet
https://www.pluralsight.com/courses/oauth2-openid-connect-angular-aspdotnet
Using Owin and OAuth, I've noticed that if you modify the last character of a token you can still get authenticated / authorized successfully.
I'm using a C# WebAPI application, but I don't know if this applies to OAuth in general. This might be by design but it seems like strange behaviour to me so I'm curious to know why this happens.
To replicate this behaviour:
In Visual Studio, create a new project and select 'ASP.NET Web Application' as the template.
Choose the 'Web API' template, and choose 'Individual User Accounts' as the authentication type.
Build the project.
Using a REST client, POST to http://localhost:23220/api/account/register * with a Content-Type: application/json header and the following body:
{
"UserName": "test",
"Password": "password",
"ConfirmPassword": "password"
}
Using a REST client, POST to http://localhost:23220/token * with no headers and the following body:
grant_type=password&username=test&password=password
Using a REST client, GET http://localhost:23220/api/values * with a Authorization: Bearer xxx header, where xxx is the access token you generated in the previous request. This request should be authorized and you should get a 200 response.
Make the same GET request again, but this time modify the last character of the Authorization header value by incrementing it by 1. So for example, if the last character is a, change it to b; if it's 1, change it to 2 etc. The request will still be successful.
* Change the port accordingly.
AccessTokenFormat - The data format used to protect the information contained in the
access token. If not provided by the application the default data
protection provider depends on the host server. The SystemWeb host on
IIS will use ASP.NET machine key data protection, and HttpListener and
other self-hosted servers will use DPAPI data protection.
Since setting up the project this way will run under IIS Express, the token is protected with the MachineKey API. I was not able to find a description of the exact data format of the encrypted value, but from previous work in cryptography, I expect perhaps there is padding at the end. This padding would not be protected by the token's signature, but this doesn't matter as it is just stripped off and discarded. Note that the range of changes is limited, so I expect only the last few bits of the last byte can be modified.
I'm writing my own codes for the OpenID Connect protocol. Basically, I intend to write my own provider and related stuff on Google App Engine's platform using Jersey and Google's datastore via Objectify library.
I'm in the middle of implementing the (access/refresh) token endpoint and there's this client authentication that I need to take care of. I'm just wondering if there are preset authentication schemes' keywords that I could send if in case the client did not have client_secret_basic set during the registration process (or whatever's set in the datastore entry.)
For a failed client authentication with the following methods, the scheme is used as response in the WWW-Authenticate header (401):
client_secret_basic: Basic,
client_secret_post: ???,
client_secret_jwt: ???,
private_key_jwt: ???,
none: obviously none.
I've looked at some open source implementations, nimbus' and OAuth-Apis', but they don't seem to handle this minor issue (they only respond with the generic error response defined in OAuth2 rfc6749#section-5.2.)
If there are no predefined keywords, then I suppose I'll have to make up my own. But it would be great if they exist.
The authoritative list for these is at IANA. There, you can find these handfuls:
Basic
Bearer
Digest
HOBA
Mutual
Negotiate
OAuth
SCRAM-SHA1
SCRAM-SHA-256
vapid
Which of these is client_secret_post, client_secret_jwt and private_key_jwt? None. You'll need to map those to one from the registered list above or return your own values. Better yet, you can submit a draft to the OAuth working group or the OpenID Foundation to get the above IANA registry updated with something that makes sense for these missing cases. Then, we can all interop :-)
I have an ASP.NET MVC application which currently allows users to connect via two ThinkTecture IDP servers. The MVC app trusts both of these IDP servers and it authenticates users perfectly.
The current setup uses the < System.IdentityModel.Services.WSFederationAuthenticationModule > and the < System.IdentityModel.Services.SessionAuthenticationModule > in the < modules > section in the web.config to handle these.
We now have a new party who want to authenticate their users by sending us a SAML v2 token, but the MVC app doesn't seem to recognise it.
I've compared the POST response from both the IDP server (SAML1) and the new login server (SAML2) and there are some subtle differences, which maybe causing problems.
The IDP server seems to use < trust:RequestedSecurityToken > attributes to wrap to < saml:Assertion >. Whereas the new client sends a POST request body containing < saml >< samlp:Response >
My questions are:
1)Is this new < samlp:Response > a SAML2P version which is not supported by Microsoft WIF? Or is it just interested in the < saml:Assertion > element?
2) Where will WIF look for the SAML token? POST Body? Authentication header (Bearer)?
3)Currently, when the user is not authenticated, it redirects them to their local IDP servers, they login and it returns the SAML response, with is then picked up. But the new client, will simply pass a request to view a page with the SAML token (true single-sign-on). I wonder if this difference is causing problems. I currently manually handle redirects to the user's local IDP, so have tried to turn this off for the new client.
EDIT
After much digging...
SAML2 Protocol is not supported by Microsoft WIF and ever likely to be.
SAML2 Protocol messages are usually as a form parameter (saml= < saml:Response>< etc... within the body of the HTTP POST. In my case it didn't use the standard parameter format of (saml=), the XML was just directly inline in the HTTP POST body.
Indeed its not officially supported but you can do something like this to make it work.
http://blogs.msdn.com/b/bradleycotier/archive/2012/10/28/saml-2-0-tokens-and-wif-bridging-the-divide.aspx
AFAIK, samlp is a protocol that is not (yet?) supported by WIF. It is a replacement for the querystring parameters used in WS-Federation. You should look for third party extensions (How should I implement SAMLP 2.0 in an ASP.NET MVC 4 service provider?). Since I haven't used any myself I cananot give further advice.
The best solution I've found is the Kentor IT - AuthServices.
It handles digitally signed SAML tokens.
Although it will not work out of the box for me, as my third party seem to be using a non-standard binding technique (rather than the standard HTTP Post or HTTP Redirect), so I will have to build my own custom-binding.
I'm looking at this and this and it would appear 'easy' to send the credentials in the URL. For example:
http://gooduser:secretpassword#www.example.com/webcallback?foo=bar
This is all well and good but it doesnt work. I've turned fiddler on and for Chrome the Authorization header isnt sent. It appears to exhibit the same behaviour for other browsers (i've got a breakpoint on the server and no Authorize header turns up for Firefox,Safari or IE either)
How to make it better?
Stumbled across this while researching various basic auth implementations.
Browsers generally only send basic auth if they receive a 401 challenge response from the server (more on basic auth protocol). If the endpoint in question accepts both authenticated and non-authenticated users, then a browser-based request will likely never be prompted for the auth parameters.
The easiest way to test this type of setup is to send a curl request (which sends the authentication parameters regardless) to your server endpoint and validate receipt of the authorization header:
curl 'http://gooduser:secretpassword#www.example.com/webcallback?foo=bar'
OK so after much searching and experimenting the approach
http://gooduser:secretpassword#www.example.com/webcallback?foo=bar
does work.
However one needs to be careful to ensure that the secret password DOES NOT contain any special characters. Use a password containing only letters and numbers and a hypen(if you must) and it should work.