I'm currently working on a project where we use the Service Bus for Windows Server queues and topics for handling messaging between clients and server. Currently I'm looking into how to handle authentication of the clients and believe we need to use SAS. The clients communicating with the queues can be using both rest and the .net api. I have tried to find resources on best practices especially on how to handle token generation and distribution. For example should we create a service for this that the calling client can connect to providing the access key which would then generate a token returned to the client. Ideas and suggestions would be greatly appreciated.
/Thanks
I would recommend using the WindowsTokenProvider and a Windows user account.
First add a valid Windows user to the Bus with Send permissions on your queue/topic. (You can do this in code or via service bus explorer.)
Then for the Rest API client authentication over Http - You need to post over a valid UserName/Pwd to the STS then add the resulting token to the authorization header of the actual post to the queue.
You can see how to do this here...
For the .Net client over TCP -
Run your client as the same valid windows user, and then call the STS with these implicit credentials by using the TokenProvider.CreateWindowsTokenProvider.
3.1 If you are using the NetMessagingBinding for your client (WCF) then do the following:
var uri = new Uri(string.Format("https://{0}:9355/ServiceBusDefaultNamespace", serverName));
var uris = new List<Uri> {uri};
var securityBehavior = new TransportClientEndpointBehavior
{
TokenProvider = TokenProvider.CreateWindowsTokenProvider(uris)
};
var endpoint = new ServiceEndpoint(contract, new NetMessagingBinding(), new EndpointAddress(serviceBusEndpointAddress));
endpoint.Behaviors.Add(securityBehavior);
3.2 NB - The code above will take the implicit credentials of the current principal and pass them across. However, you can explicitly pass in credentials like this:
var securityBehavior = new TransportClientEndpointBehavior
{
TokenProvider = TokenProvider.CreateWindowsTokenProvider(uris, new NetworkCredential("myUser", "myPassword"))
};
3.3 Or if you just use the plain .NetClient you can do the same like this:
var uri = new Uri(string.Format("https://{0}:9355/ServiceBusDefaultNamespace", serverName));
var uris = new List<Uri> {defaultUri};
var messagingFactorySettings = new MessagingFactorySettings();
messagingFactorySettings.TokenProvider =
TokenProvider.CreateWindowsTokenProvider(uris, new
NetworkCredential("myUser", "myPassword"));
.....
var factory = MessagingFactory.Create("endpoint", messagingFactorySettings);
Related
I’m getting the above error when I’m trying to use a JWT token generated for the Azure Signal R Service (ASRS) in the on-behalf-of scenario.
Here is a description of the flow:
The web app is using the react-adal library to get the access token for the user. Then it connects via Signal R to the Web API A (typically via WebSockets), the Web API A extracts the passed token from the HTTP Request context, uses it for obtaining the token for Web API B in the on-behalf-of-scenario and makes an authorized HTTP call to the Web API B. The code for getting the token for Web API B looks like this:
var userAssertion = new UserAssertion(currentTokenForWebApiA, "urn:ietf:params:oauth:grant-type:jwt-bearer");
var clientCredential = new ClientCredential(clientId, clientSecret);
var authenticationContext = new AuthenticationContext("https://login.windows.net/....");
var authenticationResult = await authenticationContext.AcquireTokenAsync(resource, clientCredential, userAssertion);
Problem
It was working fine when there was no Azure Signal R Service (client was opening Signal R connection directly to the Web API A).
But after setting up in default mode the Azure Signal R Service I’m getting the AADSTS5002730: Invalid JWT token exception.
Azure Signal R Service works as a proxy between the client and the Web API A, according to this ASRS documentation during the "negotiate" request client receives a redirect response with a new access token generated by the Azure SignalR Service SDK. Looks like it supports only HMAC-SHA256 and HMAC-SHA512 signing algorithms for access tokens. This new HS256 token is passed later to the Web API A and the AcquireTokenAsync method fails with the error AADSTS5002730. I compared the token used without the ASRS and when the ASRS is used and indeed the singing algorithm is different, the new one which is giving the error is using HS256 and the old one which was working fine with on behalf-of-scenario was using RS256 algorithm.
I can’t figure out how to make this current flow work, do you have any ideas what needs to be adjusted to make this on-behalf-of scenario working with the Azure Signal R Service? The introduction of ASRS is required due to the scale-out of the Web App A on multiple instances.
Please see if any work around is possible from below.
Firstly check for clientId and secret if they are correct.
If you registered the API in Azure ADAL, you need to get the token from V1 endpoint. If you registered it in the new app portal at apps.dev.microsoft.com or with MSAL library, then you need to use the V2 endpoint. (You can check the aud clain to see endpoint
(v1 or v2 ) when you validate the jwt token )
The API for MSAL is a bit different, you use a class ConfidentialClientApplication instead of AuthenticationContext. Here is a snippet from a sample app
(or if thats not the problem )
Then It is recommended to work with asymmetric(RS256) keys but when it is desired to use symmetric (HS256) keys you can validate jwt token and you may have to set it manually in configure services in issuersigninkey.
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = jwtIssuer,
ValidAudience = jwtAudience,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(jwtKey)),
};
References:
JWT: Symmetic and Asymmetic Key Authentication In ASP.NET Core | by
Nicklas Millard | Dev Genius
asp-net-core-signalr-returns-401-unauthorized-using-azure-ad
sp-net-core-azure-ad-v1-0-jwt-authentication-invalid-signature
I am trying to deploy azure vm using REST API/ Fluent API in .net core application. when retrieve the access token using with help of Microsoft.IdentityModel.Clients.ActiveDirectory dll does not support the UserCredential class so that i have used the below http client method to retrieve access token. But the below code returns the unauthorized token for invoking rest api. Any help would be appreciated.
HttpClient client = new HttpClient();
string tokenEndpoint = "https://login.microsoftonline.com/xxxxxxxxxx/oauth2/token";
var body = "resource=https://management.azure.com&client_id=xxxxxxxx&grant_type=password&username=xxxxxx&password=xxxxxxx";
var stringContent = new StringContent(body, Encoding.UTF8, "application/x-www-form-urlencoded");
var result = await client.PostAsync(tokenEndpoint, stringContent).ContinueWith<string>((response) =>
{
return response.Result.Content.ReadAsStringAsync().Result;
});
If you have turn on Azure Multi-Factor Authentication for the user, it will not work.
If you registry the web app/API application,lease have a try to use an AAD native app then it should work. More details you could refer to this document -Constraints & Limitations section.
No web sites/confidential clients
This is not an ADAL limitation, but an AAD setting. You can only use those flows from a native client. A confidential client, such as a web site, cannot use direct user credentials.
Note : You also could use the fiddler to catch the detail unauthorized error info.
If azure SDK is possible, you also could use the Azure flunent SDK to do that.
using Microsoft.Azure.Management.Fluent;
using Microsoft.Azure.Management.ResourceManager.Fluent;
var credentials = SdkContext.AzureCredentialsFactory.FromUser("username","password",clientId, tenantId, AzureEnvironment.AzureGlobalCloud);
var azure = Azure
.Configure()
.Authenticate(credentials)
.WithDefaultSubscription();
var disk = azure.Disks.Define(diskname)
.WithRegion(localtion)
.WithExistingResourceGroup(resourcgroup)
.WithWindowsFromVhd("vhrul")
.Create();
var vm = azure.VirtualMachines.Define(vmname)
.WithRegion("localtion") //eastasia
.WithNewResourceGroup(resourcgroup)
.WithNewPrimaryNetwork("10.0.0.0/28")
.WithPrimaryPrivateIPAddressDynamic()
.WithoutPrimaryPublicIPAddress()
.WithSpecializedOSDisk(disk, OperatingSystemTypes.Windows)
.WithSize(VirtualMachineSizeTypes.StandardA0)
.Create();
I am fairly new to Restlet and wrote small piece of code to make a HTTP call. It is working but I was wondering how can I add HTTP Connection pooling (apache) into it.
I am not able to find any tutorial or reference code for it.
Client client = new Client(Protocol.HTTP);
ChallengeResponse challengeResponse = new ChallengeResponse(
ChallengeScheme.HTTP_AZURE_SHAREDKEY,
acctName,
accKey);
String url = RestHelper.createRequestURI("CCC");
Request request = new Request(Method.GET, url);
request.setChallengeResponse(challengeResponse);
Response response = client.handle(request);
Any references or help will be appreciated.
In fact, Restlet internally manages a pool at the client connector level. Configuration of this pool can be done using the context of your client. The following example describes to configure it:
Client client = new Client(new Context(), Protocol.HTTP);
client.getContext().getParameters().add("maxConnectionsPerHost","5");
client.getContext().getParameters().add("maxTotalConnections","5");
You can notice that these parameters depend on the underlying client connector you use.
Here are some helpful links:
Doc related to connectors: http://restlet.com/technical-resources/restlet-framework/guide/2.3/core/base/connectors
Javadoc containing parameters for the HTTP client connector: http://restlet.com/technical-resources/restlet-framework/javadocs/2.3/jse/ext/org/restlet/ext/httpclient/HttpClientHelper.html
Notice that if you use ClientResource, you need to share the same client to have only one instance of the client connector under the hood. Otherwise a new one is instantiated for each request. See the way to implement this below:
Client client = new Client(new Context(), Protocol.HTTP);
ClientResource cr = new ClientResource("http://myurl");
cr.setNext(client);
Hope it helps,
Thierry
I have a service that is setup to retrieve a secure token from ADFS and use that token to communicate with other services. When I contact my ADFS windowsmixed endpoint from my local development machine hitting the ADFS service I am able to successfuly retrieve the token. However, when I install my service on the same machine that is running ADFS I receive the following error:
Secure channel cannot be opened because security negotiation with the remote endpoint has failed. This may be due to absent or incorrectly specified EndpointIdentity in the EndpointAddress used to create the channel. Please verify the EndpointIdentity specified or implied by the EndpointAddress correctly identifies the remote endpoint.
I am able to reproduce the error with the following code that simply gets the token. Again this code works when I am on my dev machine hitting the remote server, but it fails when on the server directly. I am using the same user credentials on both. I get the same error within the IIS web service using the app pool credentials and with a simple test client using the code below.
private static SecurityToken GetToken()
{
string stsEndpoint = "https://adfsserver.com/adfs/services/trust/13/windowsmixed";
string appliesTo = "http://domain.com/application/myapplication";
var factory = new WSTrustChannelFactory(
new WindowsWSTrustBinding(SecurityMode.TransportWithMessageCredential),
stsEndpoint);
factory.TrustVersion = TrustVersion.WSTrust13;
var rst = new RequestSecurityToken
{
RequestType = RequestTypes.Issue,
AppliesTo = new EndpointAddress(appliesTo),
KeyType = KeyTypes.Symmetric
};
var channel = factory.CreateChannel();
return channel.Issue(rst);
}
I turned on tracing in the Windows Event Log for ADFS 2.0 debug. When hitting that windowsmixed endpoint directly on the server, I do not receive any entries which leads me to belive that it is not actually getting to the endpoint.
I do receive quite a few audit failures in the security log that are related to the services that I am running:
A handle to an object was requested.
Subject:
Security ID: DOMAIN\ODI$ODIController
Account Name: ODI$ODIController
Account Domain: DOMAIN
Logon ID: 0x1a574b5
Object:
Object Server: SC Manager
Object Type: SERVICE OBJECT
Object Name: WinHttpAutoProxySvc
Handle ID: 0x0
Process Information:
Process ID: 0x1f8
Process Name: C:\Windows\System32\services.exe
Access Request Information:
Transaction ID: {00000000-0000-0000-0000-000000000000}
Accesses: Query status of service
Start the service
Query information from service
Access Reasons: -
Access Mask: 0x94
Privileges Used for Access Check: -
I am able to access the usernamemixed endpoint using stored credentials and receive the proper token, so it seems to be something with authenticating the user to even be able to communicate with the ADFS endpoint.
If I set specific credentials in the code above, it is able to connect. Which again leads me to believe that it is not passing the correct credentials for my Windows user when on the same machine.
factory.Credentials.Windows.ClientCredential = new System.Net.NetworkCredential("UserID", "password1", "dev.domain");
Thank you for any assistance you can provide.
Brian
I had a similar issue. I was able to get it working using the example from here: http://blogs.southworks.net/mwoloski/2009/07/17/getting-a-token-from-adfs-ex-geneva-server-using-wcf/
The difference between your code and the working example is that you modify the message security to use the current security credentials in the binding rather than on the client. If you are using WIF 4.0, you need to modify the code to use a WSTrustChannelFactory instead of WSTrustClient. The other code doesn't change much though.
My code for the factory looks like this:
var binding = new WS2007HttpBinding(SecurityMode.TransportWithMessageCredential);
binding.Security.Message.ClientCredentialType = MessageCredentialType.Windows;
binding.Security.Message.EstablishSecurityContext = false;
var factory = new WSTrustChannelFactory(
binding,
new EndpointAddress(new Uri(sts), EndpointIdentity.CreateUpnIdentity(adfsUpn)));
I have a custom WCF web-service confugured with windows authentication and a WPF client application that needs to call the former. The service checks the username and pull some specific data from a database. So I have to call the service using credentials of the user running the application.
The problem is my service is hosted under another site with windows authentication and users can authenticate there with another accounts. Windows (or IE?) caches last accout used and then my client app uses it too!
Example:
I enter the website under "MYDOMAIN\AdminUser"
I run following code (from the client app, it's not web code)
var client = new TestServiceClient();
var currentUser = WindowsIdentity.GetCurrent(); // just informative field nothing more, i don't use it anyhow
// currentUser.Name = "MYDOMAIN\\MyUserName" - it's current value, i'm not trying to set it
client.ClientCredentials.Windows.ClientCredential = CredentialCache.DefaultNetworkCredentials;
var data = client.GetTestData();
Service gets called by "MYDOMAIN\AdminUser"..
I know I can create NetworkCredential with name and password but I then will have to store it somewhere, encript it and so on..
To clarify the problem: client process running under one account calls the service under another account by itself, just becouse windows supplies the call with another credentials under the hood.