I am writing a small desktop app that hooks into our company phone system and on a incoming call searches the DDI against the Contact entity and then if it finds a match offers to open the record for the user.
For security reasons we do not want to specify username and password in config files but rather use the default credentials for the logged on user. My issue is that my code works on our development server that does not use ADFS STS but it fails on our live server which does use ADFS STS. My code is below. If I use the username and password from the App Settings it works with the STS but commenting them out and using the CredentialCache causes the following error.
IOrganizationService _service;
OrganizationServiceProxy _serviceProxy;
ClientCredentials clientCredentials = new ClientCredentials();
//clientCredentials.UserName.UserName = ConfigurationManager.AppSettings["username"];
//clientCredentials.UserName.Password = ConfigurationManager.AppSettings["password"];
clientCredentials.Windows.ClientCredential = (NetworkCredential)CredentialCache.DefaultCredentials;
Uri organisationUri = new Uri(String.Format(ConfigurationManager.AppSettings["organisationUri"] + "XRMServices/2011/Organization.svc"));
ServicePointManager.ServerCertificateValidationCallback = AcceptAllCertificatePolicy;
IServiceConfiguration<IOrganizationService> serviceConfiguration = ServiceConfigurationFactory.CreateConfiguration<IOrganizationService>(organisationUri);
serviceConfiguration.Authenticate(clientCredentials);
However using default credentials gives the error
{"An error occurred when processing the security tokens in the message."}
Related
I've created a new user account in my test AAD tenant, say testuser1#mytenant.onmicrosoft.com and set a password for it. This new account is a member of a security group that can access a specific Web API. I'm trying to write a test (a console program) that non-interactively obtains an access token using the user credentials and the app id as audience and then calls an endpoint.
How do I do that?
Update:
I'm trying to write a set of integration security tests for my Web API application. The application uses AAD groups it gets as a set of claims and treats them as roles. So I want a set of test user accounts with a known password with different roles to test behavior of an endpoint under different security contexts. The approach worked for me for years with classic AD (where I could impersonate a user using login/password pair and perform a SOAP call to a service with Windows Auth enabled).
Updated2:
I could use a set of app registrations instead of test user accounts and get a token without no problem using client_id/client_secret pair but assigning an enterprise application to a security group requires premium AAD tier which is very expensive :(
This is basically what Resource Owner Password Credentials (ROPC) grant flow is for.
You give Azure AD your app's credentials with a user's credentials and you get an access token.
This flow should not be used for authentication typically, as it exists in the standard mainly as a legacy upgrade path.
And it does not work with federated users, users with MFA or with an expired password.
However, your case of an automated test is one of the scenarios where I think its usage is acceptable.
Here is an example of the call in C#:
string tokenUrl = $"https://login.microsoftonline.com/joonasapps.onmicrosoft.com/oauth2/token";
var req = new HttpRequestMessage(HttpMethod.Post, tokenUrl);
req.Content = new FormUrlEncodedContent(new Dictionary<string, string>
{
["grant_type"] = "password",
["client_id"] = "23d3be1b-a671-4452-a928-78fb842cb969",
["client_secret"] = "REDACTED",
["resource"] = "https://graph.windows.net",
["username"] = "testuser#joonasapps.onmicrosoft.com",
["password"] = "REDACTED"
});
using (var client = new HttpClient())
{
var res = await client.SendAsync(req);
string json = await res.Content.ReadAsStringAsync();
}
ADAL.NET does not expose an overload for doing this AFAIK so you need to do it manually like this.
You'll need to replace the parameters with your app's credentials + your user's credentials of course.
The token URL also needs your tenant id or domain name.
Change the resource parameter to your API's client id/app ID URI.
By "non-interactively" are you referring to the login window? If so given the flow and architecture you've described, this is not possible. How else would you get the users credentials?
You should use this article as a reference when building your solution so you understand the various OAuth 2.0 flows and options, including those for a native application.
https://learn.microsoft.com/en-us/azure/active-directory/develop/authentication-scenarios#native-application-to-web-api
I have a .NET 4.6.2 Windows client application which needs to get an authentication token from our on-premise ADFS server and use it to call an ASP.NET Core REST API. It's client name, id (GUID) and re-direct URI have been registered with ADFS. I am using the latest ADAL (v3.13) library to facilitate the authentication. I am attempting to get a token as demonstrated in the ADAL sample code like this:
AuthenticationContext authenticationContext = new AuthenticationContext("https://<adfs-sts-server>/<rest-api-host>", false);
var result = authenticationContext.AcquireTokenAsync(<rest-api-resource-uri>, clientId, redirectUri, new PlatformParameters(PromptBehavior.Auto));
The AcquireTokenAsync call returns an error, saying: The browser based authentication dialog failed to complete. Reason: The server has not found anything matching the requested URI (Uniform Resource Identifier).
Can anyone tell me:
Is the "requested URI" refered to in the error the https://<adfs-sts-server>/<rest-api-host> or <rest-api-resource-uri>?
Do I need to register <rest-api-host> or <rest-api-resource-uri> with ADFS in some way, and if so how?
Any other information I need to get this to work?
Thanks!
Peter
Using Active Directory Federation Services (ADFS) to provide authentication for on-premise endpoints from a Windows Client
Configuring ADFS
There are 2 parts to configuring ADFS.
Register the client application with ADFS
ADFS needs to be able to identify the application requesting user authentication, whether it be a service, WPF application, Web client or Office Add-in. I have gone generic and added the following client, which we can use for most of our C# requests; we may need to register a new client with different callback for Web clients.
Use one of the many tools out there to generate a GUID for the client ID.
* CLIENT_ID and APP_NAME should be unique.
* For a web client the redirect URI is where the auth service will redirect your call after authenticating the user. It should be an endpoint where you can process the token and continue with your client application. The redirect URI is not really used with rich clients/services/add-ins.
CLIENT_ID = 26E54EC9-7988-4DAE-A527-483A8A78B1C6
APP_NAME = Investplus
DESCRIPTION = Invest+ rich client suite
REDIRECT_URI = https://server/redirect-adfs.html
Instructions for Client registration
(may be possible in a wizard, but this is what I found on the web and it worked fo us)
Log on to the AD FS server as administrator and open a Windows PowerShell command window.
Enter the following command. In Windows PowerShell
Add-AdfsClient -ClientId <CLIENT_ID> -Name <APP_NAME> -RedirectUri <REDIRECT_URI>
Register the resource to be accessed ('Relying Party' in ADFS speak)
I found this link useful, it takes you through the steps of the wizard for setting up a relying party.
Instructions for Relying Party registration
The administrator on the server team will need to use the ADFS Add Relying Party Trust Wizard, and under the "Select Data Source" step select Enter data about the relying party manually.
Values you need to supply for this wizard:
DISPLAY_NAME = "MyInvestApi" (Unique display name for this Relying party)
PROFILE = "AD FS Profile"
ENABLE_SUPPORT_FOR_WS-FEDERATION_PASSIVE_PROTOCOL = true
URL = "https://server/api" (Unique URL for this RP)
ADD_ONE_OR_MORE_IDENTIFIERS = eg. "urn:myInvestApi" and "https://server/api"
ACCEPT_REMAINING_DEFAULTS
when given the opportunity, Add Claim Rules:
SEND_LDAP_ATTRIBUTES_AS_CLAIMS = true
ATTRIBUTE_STORE = Active Directory
SELECT_USEFUL_ATTRIBUTES = User-Principal-Name; Email; Display-Name
Configuring/Coding the Client application
Microsoft provides Active Directory Authentication Libraries (ADAL) for a range of platforms and languages from C# to Javascript, and from iOS to Cordova to Node.
The API exposed has changed significantly in each major version: I am using the latest C# library, currently 3.13.5.
The library makes the coding very simple, just a few lines; where I had problems was:
I couldn't find an explanation of what URL to use for the ADFS
Secure Token Service (STS)
I couldn't find documentation of the whole process as I am doing here (most documentation focussed on Azure FS), I struggled to work out
how the values provided to ADFS for Client and Relying party mapped
to the values used in the code.
What is the ADFS endpoint/URL to use in code?
Microsoft's best practice is to name your ADFS/STS server URL https://sts.domain.com (some people use https://adfs.domain.com, ask your server admins). However, if you try to hit this from a browser you'll get a 404 - Not found and trying to retrieve a token in the code, the ADAL library reports:
The browser based authentication dialog failed to complete. Reason: The server has not found anything matching the requested URI (Uniform Resource Identifier).
This is how I found the endpoint to use:
ADFS pubishes federation metadata at 'https://sts.domain.com/federationmetadata/2007-06/federationmetadata.xml'
Extract this file and open in a text editor.
When configuring the Relying Party, we specified "Enable Support for WS-Federation Passive Protocol" when specifying our resource endpoint, so search the XML for PassiveRequestorEndpoint.
Use the <Address> from this node - in my case https://sts.domain.com/adfs/ls/. I don't know if this will always be the value, or if it is specified when ADFS is setup and therefore potentially different per site.
What other values to use in the code?
We want our client app to retrieve a JSON Web Token (JWT) from ADFS which we can pass to our protected resource for authentication/authorization purposes.
At its most simple, the access token can be retrieved in 3 lines of code + configuration, and this will show how to translate what we have configured in ADFS to the values required by ADAL:
var stsEndpoint = "https://sts.domain.com/adfs/ls/";
var relyingPartyIdentifier = "urn:myInvestApi"; // Tenant in Azure AD speak, but this is an on-premise service
var authority = stsEndpoint + relyingPartyIdentifier;
var restResourceUrl = "https://server/api";
var redirectUri = "https://server/redirect-adfs.html";
const string CLIENT_ID = "26E54EC9-7988-4DAE-A527-483A8A78B1C6";
AuthenticationContext authenticationContext = new AuthenticationContext(authority, false);
var asyncRequest = authenticationContext.AcquireTokenAsync(restResourceUrl, CLIENT_ID, redirectUri, new PlatformParameters(PromptBehavior.Auto));
var accessToken = asyncRequest.Result.AccessToken;
Useful references
ASP.NET Core Token Authentication Guide
ADAL - Native App to REST service - Authentication with ACS via Browser Dialog
Create a line-of-business Azure app with AD FS authentication
OAuth 2 Simplified
To issue the token for the web API, we need to make the ADFS to aware it by creating a relying party trust for the web API. And when we add a replying party we need to specify the identifiers for the replying party like figure below(Windows Server 2012 R2):
Then we can use this identifiers as the resource URI to acquire the token for this replying party. Please ensure that the resource URI is correct as you config like figure above.
And here is an article about developing with ADFS using OAuth:
Developing Modern Applications using OAuth and Active Directory Federation Services
Depending on the version of asdf, you may be able to use 'discovery' to obtain the endpoints to use.
Have a look at this post for more details: http://www.cloudidentity.com/blog/2015/08/21/openid-connect-web-sign-on-with-adfs-in-windows-server-2016-tp3/
When I use:
CloudIdentity identity =
new CloudIdentity()
{
Username = "files.user",
APIKey = "pswd",
};
var _storage = new CloudFilesProvider(identity);
Authentication fails. I figured that the problem is that I have a LON account and the default authentication target is US cloud instance. On openstack.net wiki, I saw the below example.
IIdentityProvider identityProvider = new CloudIdentityProvider();
var userAccess = identityProvider.Authenticate(new RackspaceCloudIdentity{
Username = "MyUserName",
Password = "MyPassword",
CloudInstance =CloudInstance.UK});
In the latest version of the library, RackspaceCloudIdentity has Domain parameter instead of CloudInstance. I guess the example is out dated.
How do I use the Domain parameter? Or is there a better way to authenticate with LON cloud instance?
Rackspace uses global authentication now, so the only difference between the US and UK accounts is the credentials you pass in. If authentication is failing then one of the following is the likely issue:
Your username and/or API key for the CloudIdentity instance are not correct.
The identity service experienced an outage of some sort at the particular moment you tried to authenticate.
Authentication is succeeding, but another error occurred which you have attributed to failed authentication (you didn't provide any exception details so I can't rule this out).
I have been seeing the same error. I believe that it is something to do with child accounts created via the UI. They do not have a DefaultRegion correctly set (it is null).
The only way around this is to use the parent account, or alternatively to manually set the region on every API call.
We're trying to authenticate to our hosted TFS service account in c# using TeamFoundationServer .net control, here is my code :
NetworkCredential tfsCredential = new NetworkCredential(username, password);
TeamFoundationServer tfsServer = new TeamFoundationServer(tfsAddress, tfsCredential);
tfsServer.Authenticate();
Note that this is not an on-premises TFS server, it is the hosted TFS service at tfspreview.com and we try to sign-in with windows live account and with alternate authentication credentials but every time we try to authenticate, internet explorer open in a new windows and ask for credentials.
If we use the IE prompt to connect it works but we want to store the credentials and connect to the server without asking for the credentials every time,
You can either configure basic authentication under your profile or you can use a service credential. It all depends on what sort of permission you need. The basic auth operates under a user account which tends to be bad practice while the service account had elevated permissions.
Configure basic authentication for TF Service
For basic user authentication you should connect to TF Service and open your profile as indicated. There is a "Credentials" tab on your profile which will let you configure those credentials. This is good for per/user access through the API but is not good if you want to run things through a server or service.
Retrieve TFS Service Credentials
I created an application called the TFS Service Credential Viewer that allows you to retrieve the service credentials for your TF Service instance. This is the same thing that the Build & Test servers do when you configure them locally to work against the cloud.
I hope this helps...
You can try with this code based on impersonation of server
var serverUrl = "";
ICredentials credentials = new NetworkCredential(username, password, domain);
ICredentialsProvider TFSProxyCredentials = new NetworkCredentialsProvider(credentials);
TfsTeamProjectCollection currentCollection = new TfsTeamProjectCollection(new Uri(serverUrl), credentials);
// Get the TFS Identity Management Service
IIdentityManagementService identityManagementService = currentCollection.GetService<IIdentityManagementService>();
// Look up the user that we want to impersonate
TeamFoundationIdentity identity = identityManagementService.ReadIdentity(IdentitySearchFactor.AccountName, username, MembershipQuery.None, ReadIdentityOptions.None);
// Open collection impersonated
TfsTeamProjectCollection tfs = new TfsTeamProjectCollection(new Uri(serverUrl), credentials, TFSProxyCredentials, identity.Descriptor);
//For example we can access to service WorkItemStore
var workItemStore = tfs.GetService<WorkItemStore>();
Tfspreview.com now supports basic authentication which would eliminate IE being displayed at all. See here for details on how to set this up for your tfspreview.com and then use the username and password you configured.
If WebApp is configured as Windows Authentication, how to get the user credential in code?
How to create NetworkCredential using this exsiting user credential?
System.Net.CredentialCache.DefaultCredentials;
The DefaultCredentials property
applies only to NTLM, negotiate, and
Kerberos-based authentication.
DefaultCredentials represents the
system credentials for the current
security context in which the
application is running. For a
client-side application, these are
usually the Windows credentials (user
name, password, and domain) of the
user running the application. For
ASP.NET applications, the default
credentials are the user credentials
of the logged-in user, or the user
being impersonated.
Example:
System.Net.WebProxy proxyObject = new System.Net.WebProxy();
proxyObject.Credentials = System.Net.CredentialCache.DefaultCredentials;