Identity Server 3 - Posted data for token endpoint not read - iis

I have a strange problem for an application I put in production.
The app is a Asp.net 4.7.2 Webapi protected by an embedded Identity Server 3 instance plus an instance of swagger for documentation.
I need only client authentication so I choose client credential as flow.
These are the main configuration data for the application.
new Client {
ClientName = "GDPR Logger Client",
Enabled = true,
ClientId = "gdpr_logger",
Flow = Flows.ClientCredentials,
AccessTokenType = AccessTokenType.Reference,
ClientSecrets = new List<Secret> {
new Secret("secret".Sha256())
},
AllowedScopes = new List<string> {
"write"
},
AccessTokenLifetime = 30
}
app.Map("/auth", auth => {
var options = new IdentityServerOptions {
SiteName = "GDPR LOGGER Authentication Server",
SigningCertificate = LoadCertificate(),
RequireSsl = true,
Factory = new IdentityServerServiceFactory()
.UseInMemoryUsers(new List<InMemoryUser>())
.UseInMemoryClients(Clients.Get())
.UseInMemoryScopes(Scopes.Get());
};
auth.UseIdentityServer(options);
});
private static X509Certificate2 LoadCertificate() {
certificateFilePath = HttpContext.Current.Server.MapPath(ConfigurationManager.AppSettings["RelativeCertPath"]);
X509Certificate2 cert = new X509Certificate2();
cert.Import(certificateFilePath, "GDPRLoggerCert", X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.MachineKeySet);
return cert;}
In my local machine all work perfect, but as soon I put it on my server the Identity Server stop working.
When I try to get the access token for the client (with client_credentials flow) Identity Server respond me with invalid_client when I POST to https://{my-server}/auth/connect/token all data as application/x-www-form-urlencoded content in the request grant_type=client_credentials&client_id=gdpr_logger&client_secret=secret&scope=write.
2018-11-20 09:14:20,035 [244] INFO IdentityServer3.Core.Endpoints.TokenEndpointController - Start token request
2018-11-20 09:14:20,066 [244] DEBUG IdentityServer3.Core.Validation.ClientSecretValidator - Start client validation
2018-11-20 09:14:20,066 [244] DEBUG IdentityServer3.Core.Validation.BasicAuthenticationSecretParser - Start parsing Basic Authentication secret
2018-11-20 09:14:20,082 [244] DEBUG IdentityServer3.Core.Validation.PostBodySecretParser - Start parsing for secret in post body
2018-11-20 09:14:20,082 [244] DEBUG IdentityServer3.Core.Validation.PostBodySecretParser - No secret in post body found
2018-11-20 09:14:20,082 [244] DEBUG IdentityServer3.Core.Validation.X509CertificateSecretParser - Start parsing for X.509 certificate
2018-11-20 09:14:20,082 [244] DEBUG IdentityServer3.Core.Validation.X509CertificateSecretParser - client_id is not found in post body
2018-11-20 09:14:20,082 [244] INFO IdentityServer3.Core.Validation.SecretParser - Parser found no secret
2018-11-20 09:14:20,082 [244] INFO IdentityServer3.Core.Validation.ClientSecretValidator - No client secret found
2018-11-20 09:14:20,082 [244] INFO IdentityServer3.Core.Endpoints.TokenEndpointController - End token request
2018-11-20 09:14:20,097 [244] INFO IdentityServer3.Core.Results.TokenErrorResult - Returning error: invalid_client
If I specify client_id and client_secret as Basic Authentication Header Identity server respond me with unsupported_grant_type.
2018-11-20 09:08:36,113 [323] INFO IdentityServer3.Core.Endpoints.TokenEndpointController - Start token request
2018-11-20 09:08:36,144 [323] DEBUG IdentityServer3.Core.Validation.ClientSecretValidator - Start client validation
2018-11-20 09:08:36,144 [323] DEBUG IdentityServer3.Core.Validation.BasicAuthenticationSecretParser - Start parsing Basic Authentication secret
2018-11-20 09:08:36,144 [323] DEBUG IdentityServer3.Core.Validation.SecretParser - Parser found secret: BasicAuthenticationSecretParser
2018-11-20 09:08:36,144 [323] INFO IdentityServer3.Core.Validation.SecretParser - Secret id found: gdpr_logger
2018-11-20 09:08:36,160 [323] DEBUG IdentityServer3.Core.Validation.SecretValidator - Secret validator success: HashedSharedSecretValidator
2018-11-20 09:08:36,160 [323] INFO IdentityServer3.Core.Validation.ClientSecretValidator - Client validation success
2018-11-20 09:08:36,176 [323] INFO IdentityServer3.Core.Validation.TokenRequestValidator - Start token request validation
2018-11-20 09:08:36,363 [323] ERROR IdentityServer3.Core.Validation.TokenRequestValidator - Grant type is missing.
{
"ClientId": "gdpr_logger",
"ClientName": "GDPR Logger Client",
"Raw": {}
}
2018-11-20 09:08:36,363 [323] INFO IdentityServer3.Core.Endpoints.TokenEndpointController - End token request
2018-11-20 09:08:36,379 [323] INFO IdentityServer3.Core.Results.TokenErrorResult - Returning error: unsupported_grant_type
As you can see in the last log in Raw it seems the data in post are not read/picked up from Identity Server.
I cannot understand what is the problem.

My Server manager solved the problem.
The ModSecurity (in Plesk inside the Web Application Firewall) for a second level domain as in my case blocked the post data because is againt its rules.
Disabling or editing the rules solved the problem.

Related

DocuSign.eSign.Client.ApiException {"error":"consent_required"}" / Response Type Not Supported when attempting to grant consent

I tried to receive a token with the code below. Unfortunatelly I get the error:
DocuSign.eSign.Client.ApiException
HResult=0x80131500
Nachricht = Error while requesting server, received a non successful HTTP code with response Body: {"error":"consent_required"}
I tried with set TLS 12 and without. We run it in dev mode with base path https://demo.docusign.net/restapi
and oAuthBasePath =account-d.docusign.com
I tried also to set the consens manually with the URL below. But I receive the error in (Login Window) invalid Authorization: RequestType is not supported.
https://account-d.docusign.com/oauth/auth?response_type=code&scope=signature%20impersonation&client_id=a5ed47d5-xxxx-xxxx-8a19-756da64391de&redirect_uri=https://www.docusign.com
Is the something wrong with my account setting?
byte[] privateKey=DSHelper.ReadFileContent(DSHelper.PrepareFullPrivateKeyFilePath(privateKeyFilename));
var scopes = new List<string>
{
"signature",
"impersonation",
};
var basePath = ApiClient.Production_REST_BasePath;
var oAuthBasePath = OAuth.Production_OAuth_BasePath;
if (!production)
{
basePath = ApiClient.Demo_REST_BasePath;
oAuthBasePath = OAuth.Demo_OAuth_BasePath;
}
var _apiClient = new ApiClient(basePath);
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
var authToken = _apiClient.RequestJWTUserToken(
clientId,
ImpersonatedUserId,
oAuthBasePath,
privateKey,
1,
scopes);
I found the solution. instead of reponse_type= code I have to use token
What response_type is supported for an integration key depends on how the key is configured. In the Authentication section of the key's configuration panel, Auth Code Grant allows the response type of code, while Implicit Grant allows the response type of token.
DocuSign's authentication docs assume you have "Auth Code Grant" selected, but either is technically acceptable to allow JWT consent.

How to get access token from Azure Active Directory with certificate when service is behind proxy

I need to create service that calls graph api to access company data. In order to authenticate I need JWT token from Azure Active Directory. The authentication will be using application mode with signing certificate. I tried to use MSAL node ConfidentialClientApplication but the service needs to use http proxy to connect to internet. To my knowledge MSAL node does not support this and calls result in library being unable to resolve the address of "https://login.microsoftonline.com". How can I make MSAL node use the proxy or get JWT token without use od MSAL?
In order to get JWT token from azure active directory without MSAL node, one have to generate proper JWT token on its own and then sign it with certificate private key. The header of the token consists of following fields:
{
typ: "JWT",
alg: "RS256",
kid: "156E...",
x5t: "iTYVn..."
}
"kid" is the thumbprint of the certificate used to sign the request - here is a good example how to obtain it for pfx file with powershell https://stackoverflow.com/a/32980899/3588432
"x5t" is base64 encoded and sanitized certificate thumbprint.
Sanitization of base64 encoded string means:
trimming "=" signs at the end
replace "/" with "_"
replace "+" with "-"
Exemplary C# code for the sanitization:
var sanitized = s.Split('=')[0].Replace('+', '-').Replace('/', '_');
and JS code:
var sanitized = s.split('=')[0].replace('+', '-').replace('/', '_');
The payload of the token consists of the following fields:
{
aud: "https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token",
iss: "{clientId}",
nbf: 1617952610,
exp: 1617953210,
sub: "{clientId}",
jti: "e13efcf..."
}
{tenantId} and {clientId} are Azure AD data of application we are authenticating to
"nbf" is the time when the token will began to be valid, normally it is time the token got generated. It has unix epoch format https://en.wikipedia.org/wiki/Unix_time and is an integer.
"exp" - the time the token expires in unix epoch format.
"jti" - a unique token identifier. It may be random generated guid. Should be different for every request.
An example how to get "nbf" value in JavaScript:
var nbf = Math.floor(new Date().getTime() / 1000);
When ready header and payload should be serialized (with sanitization) on concatenated with ".":
var token = JSON.stringify(header) + "." + JSON.stringify(payload);
Then we need to sign it with certificate private key, encode it with base 64 (with sanitization) and prepare a clientAssertion value:
var clientAssertion = token + "." + signedToken;
As a last step can send request to get JWT token:
const body = new URLSearchParams();
const token = await fetch("https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token", {
agent: new HttpsProxyAgent("http://..."),
body: new URLSearchParams({
"client_assertion": clientAssertion,
"client_id": "{clientId}",
"scope": "https://graph.microsoft.com/.default"
"client_assertion_type": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
"grant_type": "client_credentials"
}),
method: "POST",
headers: {
"content-type": "application/x-www-form-urlencoded"
}
})
.then(response => response.json().access_token);

How to use client secret having special characters in groovy

I am trying to run Microsoft graph API to generate token. This is working fine in postman but failed in SoapUI. I think client secret has special characters which are causing the issue.
client secret:
osi5oX-:?0A3YiG4aCpZ.Y[+PW51pZVY
API URL (POST) :
https://login.microsoftonline.com/{tenantID}/oauth2/v2.0/token
Body:
client_id=xxxxx&client_secret=osi5oX-:?0A3YiG4aCpZ.Y[+PW51pZVY&grant_type=client_credentials&scope=https://graph.microsoft.com/.default
Error:
{"error":"invalid_client","error_description":"AADSTS7000215: Invalid client secret is provided.\r\nTrace ID: 32b5bf83-f908-4b4e-9fe6-5b05fd949e00\r\nCorrelation ID: b9b7ce92-f5d1-41d1-8d92-eed8a6a5470b\r\nTimestamp: 2020-05-07 17:27:08Z","error_codes":[7000215],"timestamp":"2020-05-07 17:27:08Z","trace_id":"32b5bf83-f908-4b4e-9fe6-5b05fd949e00","correlation_id":"b9b7ce92-f5d1-41d1-8d92-eed8a6a5470b","error_uri":"https://login.microsoftonline.com/error?code=7000215"}
Use secret after encoding and it works.
import java.net.URLEncoder;
String url = "osi5oX-:?0A3YiG4aCpZ.Y[+PW51pZVY"
String encodedUrl = URLEncoder.encode(url, "UTF-8" );
println(encodedUrl)​

Accessing Function App via App Service Authentication from Dynamics 365 Plugin

I am trying to access an Azure Function from Dynamics 365 Plugin via service to service call: https://learn.microsoft.com/en-us/azure/active-directory/develop/active-directory-protocols-oauth-service-to-service
This function is protected via App Service Authentication.
I created a Function App and enabled App Service Authentication
under Platform Features -> Authentication/Authorisation.
I enabled Azure Active Directory as the Auth Provider and set Management mode to Express
I then got the generated Client ID and Client Secret from the Advanced Mode:
Apparently this is all that is needed to make a token request for the Azure function based, based on article I need 4 required parameters:
Client ID
Client Secret
Grant Type
Resource
I make the following request to generate a token from a Dynamics 365 Plugin but get the following error:
Invalid Plugin Execution Exception: Microsoft.Xrm.Sdk.InvalidPluginExecutionException: {"error":"invalid_client","error_description":"AADSTS70002: Error validating credentials. AADSTS50012: Invalid client secret is provided.\r\nTrace ID: 06ddda7f-2996-4c9b-ab7e-b685ee933700\r\nCorrelation ID: d582e2f2-91eb-4595-b44b-e95f42f2f071\r\nTimestamp: 2018-05-23 06:30:58Z","error_codes":[70002,50012],"timestamp":"2018-05-23 06:30:58Z","trace_id":"06ddda7f-2996-4c9b-ab7e-b685ee933700","correlation_id":"d582e2f2-91eb-4595-b44b-e95f42f2f071"}-The remote server returned an error: (401) Unauthorized.
My code is :
var tokenendpoint = "https://login.microsoftonline.com/de194c13-5ff7-4085-91c3-ac06fb869f28/oauth2/token";
var reqstring = "client_id=" + Uri.EscapeDataString("5f315431-e4da-4f68-be77-4e257b1b9295");
reqstring += "&client_secret=" + Uri.EscapeDataString("/oK7nh8pl+LImBxjm+L7WsQdyILErysOdjpzvA9g9JA=");
reqstring += "&resource=" + Uri.EscapeUriString("https://keyvaultaccess.azurewebsites.net");
reqstring += "&grant_type=client_credentials";
//Token request
WebRequest req = WebRequest.Create(tokenendpoint);
req.ContentType = "application/x-www-form-urlencoded";
req.Method = "POST";
byte[] bytes = System.Text.Encoding.ASCII.GetBytes(reqstring);
req.ContentLength = bytes.Length;
System.IO.Stream os = req.GetRequestStream();
os.Write(bytes, 0, bytes.Length);
os.Close();
//Token response
HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
StreamReader tokenreader = new StreamReader(resp.GetResponseStream());
string responseBody = tokenreader.ReadToEnd();
I have made sure that I have the correct Client Secret and have also encoded it as I have read somewhere that '+' and '/' are no good.
I am getting the same error in Postman
Any ideas??
reqstring += "&resource=" + Uri.EscapeUriString("https://keyvaultaccess.azurewebsites.net");
Since you set resource parameter to https://keyvaultaccess.azurewebsites.net, I assumed that you have set the App ID URI of your AAD app (clientId equals 5f315431-xxxxxxx-4e257b1b9295) to https://keyvaultaccess.azurewebsites.net. I assume that you could retrieve the access_token, but when accessing your azure function endpoint with the access_token, you got the 401 status code.
You need to modify your advanced management mode for Active Directory Authentication, add https://keyvaultaccess.azurewebsites.net to ALLOWED TOKEN AUDIENCES or change the resource parameter to your AAD clientID when sending the token request for acquiring the access_token.
Active Directory Authentication configuration:
TEST:
Docode the JWT token to check the aud property:
Access my Azure Function endpoint:
Note: You need to take care of the Authorization level of your function as follows:
If you also enable function level authentication, your request sent to azure function needs to have the relevant code parameter in the querystring or set the header x-functions-key with the value of your function key, or you may just set the authorization level to Anonymous.

Missing SetToken and SetTokenCookie on JsonServiceClient from TypeScript

We are having trouble assigning the BearerToken when calling ServiceStack using JsonServiceClient from TypeScript. The SetCookie and SetCookieToken methods appear to be missing as described in this question:
ServiceStack Javascript JsonServiceClient missing properties
We tried the solution described there:
this.client.headers.append("Authorization" , "Bearer " + jwtToken);
But when we try this we get an HTTP 400: Bad Request
How can we use the the JsonServiceClient from JavaScript with our BearerToken?
I've just added an explicit setBearerToken() API (in v0.0.28) that adds the Bearer Token HTTP Header:
setBearerToken(token:string): void {
this.headers.set("Authorization", "Bearer " + token);
}
And confirms that it works in this node/browser compatible test:
var client = new JsonServiceClient("http://test.servicestack.net");
client.setBearerToken(jwtToken);
var success = await client.post(new Authenticate());
Your issue is unrelated to JWT. You should check your HTTP Response Headers to find out more about. A 400 Response is a Bad Request Exception, you would get a 401 Unauthorized response for Unauthorized exceptions.
Token Cookie vs Bearer Token
Also note that a setTokenCookie() which passes the JWT Token in the ss=jwt Cookie is unrelated to the Bearer Token which is added to the Authorization HTTP Request Header.
If your JWT is configured correctly when you authenticate via a different auth provider, e.g:
var request = new Authenticate();
request.provider = "credentials";
request.userName = userName;
request.password = password;
var authResponse = await client.post(request);
var jwtToken = authResponse.bearerToken;
The JWT Token will be populated in authResponse.bearerToken which you can use with client.setBearerToken() to add the the HTTP Request Header. After your authenticated you can also go to /auth to see the JWT BearerToken.
If the bearerToken is not populated then JWT Provider isn't populating it for that request. If you're not using https you need to ensure RequireSecureConnection=false to allow it to work over http.
If you prefer you can instead tell ServiceStack you want to use JWT Cookie instead which you can specify when you authenticate with:
var request = new Authenticate();
request.useTokenCookie = true;
In which case instead of returning it in bearerToken the JWT Token is populated in the ss=tok HttpOnly Cookie which JavaScript can't access directly, but will be transparently be sent with each future ServiceClient requests.

Resources