Is it possible to impersonate Windows user to TFS or TFS web access?
Yes, it is possible. You will want to do something like this:
NetworkCredential serviceUser = new NetworkCredential(username, password, domain);
ICredentialsProvider TfsProxyCredentials = new NetworkCredentialsProvider(serviceUser);
Uri tpcUri = new Uri("http://yourserver:8080/tfs/yourCollectionName");
TfsTeamProjectCollection tpc = new TfsTeamProjectCollection(tpcUri, serviceUser);
// Get the TFS identity management service
IIdentityManagementService ims = tpc.GetService<IIdentityManagementService>();
// Look up the user that we want to impersonate
TeamFoundationIdentity identity = ims.ReadIdentity(IdentitySearchFactor.AccountService, userToImpersonate, MembershipQuery.None, ReadIdentityOptions.None);
TfsTeamProjectCollection impersonatedCollection = new TfsTeamProjectCollection(tpcUri, serviceUser, TfsProxyCredentials, identity.Descriptor);
return impersonatedCollection;
Note that the service user must have the permissions to act on behalf of other users. Your TFSService typically has that privilege.
Related
I'm just creating my first WCF project, so I have a lot of deficiencies in knowledge in this field. My problem is that when I'm calling my WCF url in web browser, I have to enter the credentials but I cannot even use my domain name and password, but I have to choose my personal chip card certificate and enter it's password. After that, everything work like a charm.
My final product should be installed on every user workstation in our domain for IT operations purposes only. So there will be some AD authorization after that.
About certificate... We have our own company root CA certificate, and every workstation have it's own certificate which is it's grandchild:
Example of our certificate tree:
COMPANYROOTCA >> COMPANYSUBCA1 >> WORKSTATIONNAME.DOMAIN (this one is used as WCF service cert)
This is what I have right now for hosting the WCF in my Windows service running under NetworkService Account:
serviceHost.Dispose(); //extension for close() and set to null
Uri httpsUrl = new Uri("baseAdress");
serviceHost = new ServiceHost(typeof(Service.myService), httpsUrl);
WSHttpBinding wsHttpBinding = new WSHttpBinding();
wsHttpBinding.Security.Mode = SecurityMode.Transport;
wsHttpBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
wsHttpBinding.Security.Transport.ProxyCredentialType = HttpProxyCredentialType.None;
WebHttpBinding webHttpBinding = new WebHttpBinding();
webHttpBinding.Security.Mode = WebHttpSecurityMode.Transport;
webHttpBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
webHttpBinding.Security.Transport.ProxyCredentialType = HttpProxyCredentialType.None;
ServiceMetadataBehavior smb = new ServiceMetadataBehavior
{
HttpGetEnabled = false,
HttpsGetEnabled = true,
};
X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
X509Certificate2Collection collection = store.Certificates;
X509Certificate2 cert = collection.OfType<X509Certificate2>().First(c => c.SubjectName.Name == "CN=WorkstationName.Domain");
store.Close();
serviceHost.Credentials.ServiceCertificate.Certificate = cert;
ServiceThrottlingBehavior throttleBehavior = new ServiceThrottlingBehavior
{
MaxConcurrentCalls = 16,
MaxConcurrentInstances = 26,
MaxConcurrentSessions = 10
};
serviceHost.Description.Behaviors.Add(throttleBehavior);
ServiceEndpoint soapEndpoint = serviceHost.AddServiceEndpoint(typeof(Contract.IMyService), wsHttpBinding, "soap");
ServiceEndpoint restEndpoint = serviceHost.AddServiceEndpoint(typeof(Contract.IMyService), webHttpBinding, "rest");
ServiceEndpoint mexEndpoint = serviceHost.AddServiceEndpoint(typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexHttpsBinding(), "mex");
restEndpoint.Behaviors.Add(new WebHttpBehavior());
tempAdminHost.Open();
So my question is: Is there any way, how to, for example, automaticaly get domain account which use the browser and call the url or any alternative how to still use HTTPS but without putting any credentials?
I didn’t see the way you use the credential to authenticate the client. the client credential type of the two endpoints you use to host the service are None. How does the browser ask you to input the credential? Besides, by default, If the server set up the ClientCredentialType to Windows, the client would use the current user as the credential. The current user’s password and account will be default credential when need to provide a credential.
One more thing to note, if you are simply prompted in the browser to select a certificate instead of the credential(user/password), as follows,
We may have configured the following parameter(clientcertnegotiation parameter).
netsh http add sslcert ipport=127.0.0.1:8000 certhash=c20ed305ea705cc4e36b317af6ce35dc03cfb83d appid={c9670020-5288-47ea-70b3-5a13da258012} clientcertnegotiation=enable
Because the way you use to provide a certificate to encrypt the communication is not correct.
serviceHost.Credentials.ServiceCertificate.Certificate = cert;
We need to bind the certificate to Port.
https://learn.microsoft.com/en-us/windows/desktop/http/add-sslcert
when hosting the service in IIS, we accomplish it by the below UI.
And the parameter configuration depends on the below.
So I suspect the process that binds the certificate to the specified port is completed by IIS. and the parameter should be ignored.
Feel free to let me know if there is anything I can help with.
I am implementing authentication for a command line client application that makes a web request to a web API. If I reason correctly, I can apply Azure Active Directory native app authentication scenario.
My concern here is that setting up Azure AD will require significant effort from the client app users on setting up AAD, plus they will have to work with an interactive dialog. This gets even worse in case no human is present, as the service to service scenario is even more complicated.
Is it possible to instead rely on the credentials of the signed-in user of the client computer? Assume Windows-based client machine that is joined to a domain, say FooDomain. The server uses an OWIN-based self-host implementation, Katana.
Related questions:
OWIN Web API Windows Service - Windows Identity Impersonation
#Konrad Jamrozik. IF you are working on .NET and want to use the logged-in user in Windows domain joined (your case), and even AAD joined, my advice would be to use MSAL.NET with the Integrated Windows Authentication (IWA) override. See https://aka.ms/msal-net-iwa. The simplified code looks like this:
string authority = "https://login.microsoftonline.com/contoso.com";
string[] scopes = new string[] { "user.read" };
PublicClientApplication app = new PublicClientApplication(clientId, authority);
var accounts = await app.GetAccountsAsync();
AuthenticationResult result=null;
if (accounts.Any())
{
result = await app.AcquireTokenSilentAsync(scopes, accounts.FirstOrDefault());
}
else
{
result = await app.AcquireTokenByIntegratedWindowsAuthAsync(scopes);
}
This sample explains how to register the app and provides all the details about the code: https://github.com/azure-samples/active-directory-dotnet-iwa-v2
I am trying to search for users in my own and a third party Azure Active Directoriy via the application access flow.
I use the following to get a valid token.
string authority = string.Format(CultureInfo.InvariantCulture, "https://login.microsoftonline.com/{0}", "<AD>.onmicrosoft.com");
AuthenticationContext authContext = new AuthenticationContext(authority);
ClientCredential clientCredential = new ClientCredential(<clientId>, <appKey>);
AuthenticationResult result = await authContext.AcquireTokenAsync("https://graph.windows.net" , clientCredential);
string TokenForApplication = result.AccessToken;
I use this method to search for users with a given name.
public async Task<List<IUser>> UsersSearch(IActiveDirectoryClient client, string searchString)
{
List<IUser> usersList = null;
IPagedCollection<IUser> searchResults = null;
IUserCollection userCollection = client.Users;
searchResults = await userCollection.Where(user =>
user.UserPrincipalName.StartsWith(searchString) ||
user.GivenName.StartsWith(searchString)).Take(10).ExecuteAsync();
usersList = searchResults.CurrentPage.ToList();
return usersList;
}
This all works fine on the Azure AD where I first setup the app.
But when when I try to use the app in another Azure Active directory I get the error: Authorization_RequestDenied: Insufficient privileges to complete the operation."
In my original Azure AD I have set all the permissions I need for the app to access the graph API and search for users:
In the third party Azure AD I have gone through the Admin flow and granted the app all the needed permissions:
As far as I can see I get a valid token to each Azure AD, but I keep getting the same error whenever I try to access the third party Azure AD.
The way I change what AD I am trying to access is by changing <AD> in
string authority = string.Format(CultureInfo.InvariantCulture, "https://login.microsoftonline.com/{0}", "<AD>.onmicrosoft.com");
I keep everything else the same.
From your screenshot , the selected permissions is for Microsoft Graph API(https://graph.microsoft.com) , but according to your code , you are acquiring token for Azure AD Graph api(https://graph.windows.net) .
If you want to use Azure AD Graph api , you should add permissions for Windows Azure Active Directory in Required permissions blade of your multi-tenant app , and do admin consent in other AAD .
if you want to use Microsoft Graph API , you should modify your code , use https://graph.microsoft.com instead of https://graph.windows.net .
I have a web application running on Azure. The web application authenticates the users via OpenID Connect from a Azure Active Directory tenant.
Azure Sample on GitHub.
On the Azure Active Directory tenant I have integrated Google Apps and configured single sing-on to Google Apps and automated user provisioning. Tutorial: How to integrate Google Apps with Azure Active Directory.
In my web application I would like to access user content from Google Apps (e.g. files on Google Drive) of the signed in user via Google API.
Is it possible to do this with the help of the setup single sign-on federation, so that the user only needs to sign in to the web application/Azure AD and for the Web API call there is no need for a further sign in, e.g. by using a token optained by Azure AD for accessing the Google Web API?
Tokens obtained from Azure AD cannot be used directly against Google API. However if you integrated Azure AD and Google Apps you should be able to go through the google token acquisition process without gathering user credentials again. You might want to go through an authorization code flow for getting tokens from google, and inject in the request information that would help to leverage your existing session. Typical examples are passing your user's UPN (via login_hint query parameter) and tenant (domain_hint). However I don't know if the google authorization endpoint will pass those along, you'll need to consult the google api documentation.
I ended up with two solutions:
a) Service Account:
Accessing the users data with a service account on behalf of a user.
For this you have to setup a service account: Using OAuth 2.0 for Server to Server Applications
private static ServiceAccountCredential GetServiceAccountCredential(string user)
{
const string privateKey = "<PRIVATEKEY>";
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer("<SERVICEACOUNTEMAIL>")
{
Scopes = new[] {DriveService.Scope.Drive},
User = user
}.FromPrivateKey(privateKey));
return credential;
}
b) User:
Accessing the users data with the user. For this you have to register your app to get the client ID and secret: Using OAuth 2.0 for Web Server Applications
private static UserCredential GetUserCredential(string user)
{
ClientSecrets secrets = new ClientSecrets
{
ClientId = "<CLIENTID>",
ClientSecret = "<CLIENTSECRET>"
};
IDataStore credentialPersistanceStore = new FileDataStore("Drive.Sample.Credentials");
Task<UserCredential> result = GoogleWebAuthorizationBroker.AuthorizeAsync(
secrets,
new[] {DriveService.Scope.Drive},
user,
CancellationToken.None,
credentialPersistanceStore);
result.Wait();
UserCredential credential = result.Result;
return credential;
}
With the credentials I can request the files from Drive:
Claim emailClaim = ClaimsPrincipal.Current.FindFirst(ClaimTypes.Email);
IConfigurableHttpClientInitializer credential = GetServiceAccountCredential(emailClaim.Value);
//IConfigurableHttpClientInitializer credential = GetUserCredential(emailClaim.Value);
var service = new DriveService(new BaseClientService.Initializer
{
HttpClientInitializer = credential,
ApplicationName = "My App"
});
FileList list = service.Files.List().Execute();
I am not yet sure which option I will use. Maybe you have some advices or suggestions.
I am using a service account to impersonate an admin user successfully with the Admin Directory and Google+ Domain APIs, but I am unable to figure out if Sites can make use of service accounts. Is it even possible? I am referring to the API that allows you to create and delete sites, not the Webmaster Tools API for managing the content and so on.
You can use Service Accounts with Sites API. All you need to do is use SignedJwtAssertionCredentials, impersonate the user from background and access the GData APIs.
Here is the code snippet:
credentials = SignedJwtAssertionCredentials(
SERVICE_ACCOUNT_EMAIL,
file(SERVICE_ACCOUNT_PEM_FILE_PATH, "rb").read(),
scope=["https://sites.google.com/feeds/"],
prn=userEmailYouWantToImpersonate
)
http = httplib2.Http()
http = credentials.authorize(http)
sitesClient = gdata.sites.client.SitesClient(source='mycompany', site='mySite', domain='mydomain')
sitesClient.auth_token = TokenFromOAuth2Creds(credentials)
feed = sitesClient.GetContentFeed()
class TokenFromOAuth2Creds:
def __init__(self, creds):
self.creds = creds
def modify_request(self, req):
if self.creds.access_token_expired or not self.creds.access_token:
self.creds.refresh(httplib2.Http())
self.creds.apply(req.headers)
Yes it is possible. Here's a code example :
SitesService sitesService = new SitesService("MyApplication");
final GoogleCredential credential = new GoogleCredential.Builder()
.setTransport(new NetHttpTransport()).setJsonFactory(new JacksonFactory())
.setServiceAccountId("XXXX.apps.googleusercontent.com")
.setServiceAccountScopes(Collections.singleton(SERVICE_SCOPES))
.setServiceAccountPrivateKeyFromP12File(new File("key.p12"))
.setServiceAccountUser("mysiteowner#domain.com").build();
sitesService.setOAuth2Credentials(credential);
The only thing you have to be cautious with, is that some SitesService methods might not be able to refresh the token properly, in which case you will have to catch the SessionExpiredException and refresh the Credential yourself.
Google Sites API pages says [1] that you can "Create new Sites or copy existing Sites" with it.
[1] https://developers.google.com/google-apps/sites