I have one Dynamics CRM 365(On-Premise) environment with IFD authentication. I am trying to perform operations on CRM with sdk. I am doing test connection first and store that test connection in a cache object. This cache object is of type IDictionary. I am struggling with a strange issue. For different instances it will create different proxy and cache them.
Scenarios:
1) Auth type is Federation - It will take this live id and discovery url and create on Key using MD5 algo and store in IDictionary so next time when I perform any operation it will check for proxy in cache. If cache is true it will take proxy from cache and continue.
This works as expected in dev but in production I have one strange issue.
Suppose for day 1 it establish connection when auth type is AD the it works with only that type of connection for whole day even if I am giving discovery url for Federation.
Next time when I try suppose on day 2 or if I close the app and again open then this time it is not working for same configuration.
I am too confused what is happening exactly.
code for Connection:
private CrmServiceClient CreateServiceClient(AuthenticationProviderType _endpointType, IServiceManagement<IOrganizationService> organizationServiceManagement)
{
AuthCredentials = GetCredentials(_endpointType);
string connectionString = string.Empty;
string organizationUri = OrganizationServiceManagement.CurrentServiceEndpoint.Address.Uri.AbsoluteUri
.Replace(OrganizationServiceManagement.CurrentServiceEndpoint.Address.Uri.AbsolutePath, string.Empty);
switch (_endpointType)
{
case AuthenticationProviderType.None:
break;
case AuthenticationProviderType.ActiveDirectory:
connectionString = $"AuthType=AD;Url={ organizationUri }/{ AuthInfo.OrganizationUniqueName} ; Domain={ AuthInfo.Domain } ; Username={ AuthInfo.UserName }; Password ={ AuthInfo.Password }";
break;
case AuthenticationProviderType.Federation:
connectionString = $"AuthType=IFD;ServiceUri={ organizationUri }/{ AuthInfo.OrganizationUniqueName };Domain={ AuthInfo.Domain };Username={ AuthInfo.Domain }\\{ AuthInfo.UserName };Password={ AuthInfo.Password };";
break;
case AuthenticationProviderType.LiveId:
break;
case AuthenticationProviderType.OnlineFederation:
connectionString = $"AuthType=Office365;Username={AuthInfo.UserName}; Password={AuthInfo.Password}; Url={organizationUri}";
break;
default:
break;
}
var serviceClient = new CrmServiceClient(connectionString);
return serviceClient;
}
Related
I want to write e2e tests that can run in our CI environment which is tightly locked down and has no internet access.
I am using the auth0 react loginWithRedirect function which will attempt to redirect to the auth0 login on their servers and will timeout on CI.
I am able to intercept the call to /authorize in express:
app.get('/auth0/:simulation_id/authorize', middleware, (req, res) => {
const { client_id, redirect_uri, scope, state } = req.query;
Is it now possible for me to generate a mock oauth token that will be accepted by the #auth0/react client code?
Yes. Using the jsrsasign.js library, where accessKey contains a private key:
function createJwt(username, type, realm, scopes, sessionId, expires_in, audience) {
var oHeader = {
alg: 'RS256',
typ: 'JWT'
};
// Payload
var oPayload = {};
var tNow = jwt.KJUR.jws.IntDate.get('now');
var token_expiry = expires_in;
var tEnd = tNow + token_expiry;
oPayload.sub = username;
oPayload.auditTrackingId = uuidv4();
oPayload.iss = "https://mock.org/identities/v1/" + realm + "/token";
oPayload.tokenName = type;
oPayload.token_type = 'Bearer';
oPayload.authGrantId = !sessionId ? uuidv4() : sessionId;
oPayload.aud = (audience ? audience : "https://mock.org/identities/v1/" + realm + "/token");
oPayload.nbf = tNow;
oPayload.scope = scopes;
// oPayload.auth_time = tNow;
oPayload.realm = '/' + realm;
oPayload.exp = tEnd;
oPayload.expires_in = token_expiry * 100000;
oPayload.iat = tNow;
oPayload.jti = uuidv4();
var sHeader = JSON.stringify(oHeader);
var sPayload = JSON.stringify(oPayload);
var prvKey = accessKey;
var sJWT = jwt.KJUR.jws.JWS.sign("RS256", sHeader, sPayload, prvKey);
return sJWT;
}
Set the audience, subject, scp and any other properties in line with the tokens that auth0 is creating.
You also might need to mock out the jwk endpoints, but I doubt the library will care. If it doesn't work it will be due to certain properties not being available on the token.
You might need to create an id token as well, depending on your setup.
Access key is similar to this:
var accessKey = jwt.KEYUTIL.getKey("-----BEGIN PRIVATE KEY-----\n" +
"MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC0g+nePEZ6aFiH\n" +
"ccnle/ryqz1lNvloPGuA5b6GeE8MrT7SATxv54zQ2LnvuRp86cd32elL0YAw3GMc\n" +
... snip ...
"Rno3R82z6SAvy9HgIMzfti5NSVgqC9nmhFEs+ChFWuboGotVV99COVJId9S/567n\n" +
"kz90cLENtD/8JTYAhLea5F/PJBJJHSvQT298ZxR4bw1vQ5Bq2FMnLSYuIOQMkQdr\n" +
"Yqt+8gXW0+3kfyb3cCyI+2HKcQ==\n" +
"-----END PRIVATE KEY-----\n");
And keep going til you've mocked out the entire IdP platform :) (I basically did the same thing with OpenAM a few years ago).
In addition to #stringy05's response; if you've got back-end services (e.g. Java, .NET, etc) that are called by your React app and need to do JWT validation, you might need to statically host your own an OpenID Configuration and JSON Web Key Set endpoints in your own environment.
The reason for this is because JWT validation (by back-end services) would still require communication to the external authorization server. With JWT-based auth this is generally limited to a single (up-front or lazy-loaded) request to get the public key for signature validation, but there could also be additional calls e.g. when the service encounters an unknown kid (key ID) on a JWT, as well as a periodical refresh interval (e.g. daily).
For Auth0 OpenID Configuration and JSON Web Key Set endpoints are hosted at the following locations:
OpenID Configuration: https://[tenant-name].[2-letter-iso-country-code].auth0.com/.well-known/openid-configuration
JSON Web Key Set: https://[tenant-name].[2-letter-iso-country-code].auth0.com/.well-known/jwks.json
These are just static JSON documents. You can probably just copy these from one of your existing tenants, modify accordingly, and then host in your own environment. The modifications you'd need to make include:
OpenID Configuration to reference local jwks_uri endpoint.
Update the key information in the JSON Web Key Set endpoint. Note: you can generate an asymetric key pair using OpenSSL, and see here for information about how to extract modulus and exponent from public key (which are required for the JWKS document). Note: in your React app, you'd need to sign the tokens with the private key that you generate.
Once you've done the above, your back-end services can reference the locally hosted OpenID Configuration and will be able to do JWT validation without communicating to an external authorization server.
I have got 2 sitest hosted on Windows 2012 R2 IIs 8.5.
One is instance of umbraco while other is .Net core based api (Lets call it MyApi).
I want to perform certain search action on umbraco so umbraco makes call to the api which calls back the Umbraco/Api. Call to the MyApi is fine, however the call from MyApi to the Umbraco/Api is problem. The Umbraco api logs:
Search failed System.Net.Http.HttpRequestException: An error occurred while sending the request. ---> System.Net.WebException: The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel. ---> System.Security.Authentication.AuthenticationException: The remote certificate is invalid according to the validation procedure.
Certificates themselves are provided by our customer's inner authority, which is trusted root in server storage. Now originaly we have had problem with certificates, as they missed first DNS record we use for api call (the DNS records are not created yet, we use record in 'hosts') but that shoud be fiex by now.
I have updated the SSL error handler in the code so it logs an error and number in SSL Enum.
System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
System.Net.ServicePointManager.ServerCertificateValidationCallback += (sender, certificate, chain, sslPolicyErrors) =>
{
_logger.LogError("Received TLS errror " + ((int)sslPolicyErrors).ToString());
return false;
};
_logger.LogDebug("Search starting");
if (model.Username != null)
{
model.Favourites = _workplaceContext.GetUserFavourites(_dataContext, model.Username);
}
else if (model.JustFavourites)
{
return BadRequest();
}
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(_configuration.GetValue<string>("UmbracoApiUrl"));
_logger.LogDebug("Searching for addres " + new Uri(_configuration.GetValue<string>("UmbracoApiUrl")));
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
var json = JsonConvert.SerializeObject(model);
using (var stringContent = new StringContent(json, Encoding.UTF8, "application/json"))
{
var response = await client.PostAsync("workplace/search", stringContent);
if (response.IsSuccessStatusCode)
{
_logger.LogDebug("Search API Call success");
var jsonResponse = await response.Content.ReadAsStringAsync();
var responseModel = JsonConvert.DeserializeObject<SearchResponseModel>(jsonResponse);
_logger.LogDebug("Search response TotalCount = " + responseModel.TotalCount);
return responseModel;
}
else
{
_logger.LogDebug("Search API Call status - " + response.StatusCode);
return StatusCode((int)response.StatusCode);
}
}
}
Originaly we received number 2 error. Now we just receive number 0, which should be no problem. Despite that, connections is still not working. I have read various articles regarfding this and enabled troubleshooting through in web config of umbraco, but haven't found any useful information. I need method to troubleshoot the MyApi. Could somone point me a direction how to troubleshoot it to the similar degree of detail as umbraco (i am not much familiar with .NET core apps)?
The interesting part of error log:
2019-07-08 13:35:32.1745||DEBUG|XXXApi.Controllers.WorkplaceController|Search starting
2019-07-08 13:35:32.1901||DEBUG|XXXApi.Controllers.WorkplaceController|Searching for addres https://xxx.yy.zz/umbraco/api/
2019-07-08 13:35:32.2682||ERROR|XXXCApi.Controllers.WorkplaceController|Received TLS errror 0
2019-07-08 13:35:32.2682||ERROR|XXXApi.Controllers.WorkplaceController|Search failed System.Net.Http.HttpRequestException: An error occurred while sending the request. ---> System.Net.WebException: The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel. ---> System.Security.Authentication.AuthenticationException: The remote certificate is invalid according to the validation procedure.
at System.Net.TlsStream.EndWrite(IAsyncResult asyncResult)
at System.Net.PooledStream.EndWrite(IAsyncResult asyncResult)
at System.Net.ConnectStream.WriteHeadersCallback(IAsyncResult ar)
--- End of inner exception stack trace ---
Solution: The original error was in certificate and got fixed, BUT the callback returning false I used for debugging caused problem marking connection unsuccessful. It took like 2 days to figure it out. So since moment i got 0 in enum, it was just bug in my debugging code.
Oh and don't forget to unsubscribe the delgate.
This one has me for a while now, I am trying to build a console app that can call a .net web/wcf service SP, the first leg is to get a token from the idP (ADFS4.0) the pasted code was working fine for a whole day, at some point it stopped working with the following error:
SOAP security negotiation with 'https://adfs.domain.in/adfs/services/trust/13/windowsmixed' for target 'https://adfs.domain.in/adfs/services/trust/13/windowsmixed' failed. See inner exception for more details.
The inner error is:
The Security Support Provider Interface (SSPI) negotiation failed.
NativeErrorCode: 0x80090350 -> SEC_E_DOWNGRADE_DETECTED
I have tried /13/windows and /windowstransport as well as the endpoint.
private static GenericXmlSecurityToken RequestSecurityToken()
{
// set up the ws-trust channel factory
var factory = new Microsoft.IdentityModel.Protocols.WSTrust.WSTrustChannelFactory(new WindowsWSTrustBinding(
SecurityMode.TransportWithMessageCredential), new EndpointAddress(new Uri("https://adfs.domain.in/adfs/services/trust/13/windowsmixed"), EndpointIdentity.CreateSpnIdentity("adfs#domain.in")));
factory.TrustVersion = TrustVersion.WSTrust13;
var rst = new RequestSecurityToken
{
RequestType = RequestTypes.Issue,
KeyType = KeyTypes.Bearer,
AppliesTo = new System.ServiceModel.EndpointAddress(endpoint_address)
};
// request token and return
return factory.CreateChannel().Issue(rst) as GenericXmlSecurityToken;
}
In my case, for some reason, the ADFS was available over VPN but the AD based authentication bits are not happening over VPN. That's why SEC_E_DOWNGRADE_DETECTED is coming. In a regular non VPN environment things are good.
Also, another observation is once SAML token is generated over a regular enterprise network. Subsequent calls to generate the SAML token are going through as expected even on VPN.
So, if you see this error just check if the network you are in is part of the domain (and not public or private network), for SSPI negotiation.
I've been studying Nodejs and Socket.io, the problem is I'm struggling on how to get an url parameter (www.example.com/sample/:sampleid) and use it on my socket connection.
Here's an example:
var roomname;
var username;
app.get('/room/:roomname/user/:username', function(req, res){
var room = {
username: req.params.username,
roomname: req.params.roomname
};
roomname = room.roomname;
username = room.username;
res.render('room', room);
});
var users = {};
io.sockets.on('connection', function (socket) {
socket.username = username;
socket.room = roomname;
users[username] = username;
console.log("joining room: " + roomname)
socket.join(roomname);
console.log("emiting to room: " + roomname)
socket.broadcast.to(roomname).emit('newUser', username);
});
I'm simply storing the name on roomname and using it inside io.sockets.on, but I think that's a hack and should be a better way to achieve this...
What's a good way to solve this kind of situations?
Since you're using res.render and you're already passing room as a data parameter, I think it's best to make use of that and do something like this (I use EJS in my example since I'm used to that):
Client:
<script>socket.emit('join', {room: '<%- room.roomname %>' });</script>
Server:
io.on('connection', (socket) => {
socket.room = '';
socket.on('join', (msg) => {
socket.room = msg.room;
});
});
Otherwise it could be an idea to emit document.location.pathname, but then you'd have to parse the path yourself to get the roomname out of it.
EDIT:
What you're doing at the moment is actually very dangerous, because of the following scenario:
User 1 opens the page, "global" variables username and room get set (let's set them both to foo for now), but user 1 has slow internet, so it takes some time to load and for socket.io to connect.
Now User 2 opens the page, the "global" variables username and room are changed to the new values (let's use bar), before user 1 opens up the socket.io connection.
Now User 1 and User 2 connect with socket.io, but since user 2 has faster internet than user 1, var username and var roomname are now both set to value bar even though User 1 has username and roomname foo.
Just a heads up.
socket can emit any event because socket object is EventEmitter so that it can fire(emit) event in any time and on anther side server can listen to this events by .on function
example
socket.emit('join',{name:"ahmed"})
server
io.sockets.on('join',function(data){
console.log(data.name)
})
I had the same question and arrived at a different solution, where I pass the parameters at the time of connection.
//it's my connection in client side
let socket = socketio(`http://localhost:4444?user=${uuid}&room=${room}`);
// where UUID is my user ID and room id
So on the server side we can get that parameters like below
let userUUID = socket.handshake.query.user;
let roomId = socket.handshake.query.room;
The good thing is that when connection is lost you do not depend on the fact that the client will call your custom connection event again, you can put your logic into the default io.on('connection',);
More info here https://socket.io/docs/server-api/#socket-handshake
I've setup a perfectly functioning application (in VB) that allows user to access his Google Sheets.
The application follows Google's OAuth documentation for displaying a sign-in dialog in a web browser, obtains user's permission and access codes, uses access codes to obtain access token, and then uses the Google Sheet's Query service to get hold of Google Sheets. Very simple. Works fine.
Problem occurs on computers that have internet proxy defined on them. In the rest of my application and most of Google Sheets API, I can define a manual internet proxy. GData's RequestFactory allows manually configuring proxy server. The only line of code that doesn't support (to my current knowledge) is the OAuthUtil library used for obtaining access token. It doesn't allow defining internet proxy server, hence it is unable to resolve host on computers behind proxy environment. Following is my pseudo code:
Dim parameters As New OAuth2Parameters
parameters.ClientId = CLIENT_ID
parameters.ClientSecret = CLIENT_SECRET
parameters.RedirectUri = REDIRECT_URI
parameters.Scope = SCOPE
>>Show browser window and obtain access code
parameters.AccessCode = login.Token
OAuthUtil.GetAccessToken(parameters) '<< Point of failure
Dim requestFactory As GOAuth2RequestFactory = New GOAuth2RequestFactory(Nothing, My.Application.Info.ProductName, parameters)
requestFactory.Proxy = GetProxySettings() '<< my code for defining proxy
myService = New SpreadsheetsService("Application")
myService.RequestFactory = requestFactory
Another important aspect is that my application works on Mac OSX as well using Wine (for web browser I use GeckoFX). If internet proxy is globally defined on the environment then the OAuthUtil works fine, but this doesn't work for Wine. I have tried setting internet proxy in the command-line environment, or in the registry and refreshed system settings, still the applications running in Wine do not understand that proxy is defined. Hence proxy has to be manually defined.
I need help to figure out a solution by any of the following:
* A way to forcefully/manually define proxy for OAuthUtil for obtaining access token
* Any other way to obtain OAuth access token if proxy cannot be defined as above (maybe WebClient can be used?)
* Some way to define global internet proxy in Wine so applications like GData API read and understand the proxy setting. Though I'd rather prefer manually defined proxy at application level.
Any ideas folks?
Regards
F.A.
I've figured it out. Turns out that the 'OAuthUtil.GetAccessToken' only uses system-defined proxy. There is no way to manually define internet proxy, like RequestFactory supports. So there is a work-around using WebClient:
Try
'// Get access token from code
Using WC As New WebClient
' Define proxy
WC.Proxy = GetProxySettings()
' Set parameters
WC.Headers(HttpRequestHeader.ContentType) = "application/x-www-form-urlencoded"
' Get response
Dim postURL = "https://www.googleapis.com/oauth2/v4/token"
Dim postParams = "code=" & parameters.AccessCode &
"&client_id=" & Uri.EscapeDataString(CLIENT_ID) &
"&client_secret=" & Uri.EscapeDataString(CLIENT_SECRET) &
"&redirect_uri=" & Uri.EscapeDataString(REDIRECT_URI) &
"&grant_type=authorization_code"
Dim responsebody As String = WC.UploadString(postURL, postParams)
' Read response
Dim jObj As JObject = JsonConvert.DeserializeObject(responsebody)
' Store token
parameters.AccessToken = jObj("access_token").ToString
parameters.RefreshToken = jObj("refresh_token").ToString
parameters.TokenType = jObj("token_type").ToString
parameters.TokenExpiry = Now().AddSeconds(CDbl(jObj("expires_in").ToString))
End Using
Catch ex As Exception
MsgBox("Error obtaining access token: " & ex.Message, MsgBoxStyle.Critical)
Return Nothing
End Try