Get token from ADFS - wcf-security

I'm trying to obtain a token from ADFS to that I can use it with an on-premise Windows Service Bus installation. I may not have ADFS properly configured because I get the following message:
MSIS3127: The specified request failed.
The code to access the token is as follows:
string adrecaSTS = "trust/13/usernamemixed";
WS2007HttpBinding binding = new WS2007HttpBinding();
binding.Security.Message.EstablishSecurityContext = false;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
binding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
binding.Security.Mode = SecurityMode.TransportWithMessageCredential; //https
string baseSSLUri = #"https://<myadfs>/adfs/services/";
WSTrustChannelFactory trustChannelFactory = new WSTrustChannelFactory(binding, new EndpointAddress(baseSSLUri + adrecaSTS));
trustChannelFactory.TrustVersion = TrustVersion.WSTrust13;
trustChannelFactory.Credentials.UserName.UserName = "username";
trustChannelFactory.Credentials.UserName.Password = "password";
WSTrustChannel tokenClient = (WSTrustChannel)trustChannelFactory.CreateChannel();
//create a token issuance issuance
RequestSecurityToken rst = new RequestSecurityToken(RequestTypes.Issue);
//call ADFS STS
SecurityToken token = tokenClient.Issue(rst);
The endpoint is enabled on ADFS and my client (laptop on separate domain) trusts the certificate from ADFS.
Do I need to set up some kind of trust or something further? This error message is not particularly helpful.

See here:
https://github.com/thinktecture/Thinktecture.IdentityServer.v2/blob/master/src/Libraries/Thinktecture.IdentityServer.Protocols/WSFederation/HrdController.cs
The ValidateToken method has most of the code - but you first need to extract the InnerXml from the generic token and turn that into a SAML security token (again using a token handler).

Found the issue. I was trying to log on as an administrator account. When I used a regular user it worked.
I also had to modify the RequestSecurityToken to have a KeyType of KeyType.Symmetric

I see that you solved your issue, but here is some additional inforamation to potentially help others that might have the same error message but a different cause.
The AD FS error, "MSIS3127...", can have multiple causes. For us, it was caused by one of our relying party claim rules specifying an AD FS attribute store that didn't exist.
In order to debug the error, we checked the Event Viewer on all of the servers running AD FS, and that's where we found the detailed message that called out the attribute store problem. So, if anyone else gets the same error message, then I suggest checking the Event Viewer on AD FS to see if there are additional logs.
Note that AD FS logs to the Event Viewer under the folder/node of Applications and Services Logs => AD FS => Admin

Related

ADFS 2016 On behalf of flow : cannot get any user informations

I'm trying to implement the "on behalf of" flow in an application using ADFS 2016 as STS. As a reference, I look at this Microsoft tutorial (https://learn.microsoft.com/en-ca/windows-server/identity/ad-fs/development/ad-fs-on-behalf-of-authentication-in-windows-server). It's working as it should, I can login into my web application and then use my original access token in UserAssertion to generate a new access token with the proper audience to call my API BUT I found absolutely no way to include any user informations (sub, name, email, upn etc.) into the access token for my API, even if I set claim rules into my ADFS configurations for the API.
I checked the communication between my app and adfs using Fiddler and everything looks like the informations in the tutorial. See the screen shot of the "on behalf of" request below :
Here's the resulting access token :
Finally, here's the code I use to generate my new access token :
private async Task<string> GetAccessToken(ClaimsPrincipal user, string originalAccessToken)
{
var authority = "[authority]";
var context = new AuthenticationContext(authority, false);
string userName = user.FindFirstValue("upn");
var userAssertion = new UserAssertion(originalAccessToken, "urn:ietf:params:oauth:grant-type:jwt-bearer",userName);
var cc = new ClientCredential("https://localhost:44387/", "[client_secret]");
var result = await context.AcquireTokenAsync("https://localhost:44339/", cc, userAssertion);
return result.AccessToken;
}
Have you struggle with that scenario and if yes, did you find a way to fix this ?
Thanks
I've only used the Microsoft On Behalf Of flow with Azure AD and not ADFS, but it looks like you need to send a more detailed scope in your User Info request.
Maybe try sending 'openid profile email', to indicate that you want that type of detail, as in Section 17 of my blog post. Of course this assumes that this type of data has been registered for all users.
TROUBLESHOOTING
Looks like one of these will be the cause:
A suboptimal Microsoft library that does not allow you to send the required scope
Or ADFS 2016 perhaps lacks the scope features that work correctly in Azure AD
I would concentrate on making extra sure you are sending the correct form URL encoded request message, using a tool such as curl, Postman or a plain C# HttpClient. Here is the code I used to send the correct scope - using an open source library rather than a Microsoft one:
Sample NodeJS Code
If you can get the scope sent correctly then you should have a resolution either way:
Either you get the correct data and can update your code
Or the behaviour you want is not supported by ADFS
Good luck ...

"Access Denied" error on Google Auth API for Calendar

I have been trying since yesterdar to authenticate my requests to de Google Calendar V3 API in order to use it, but I am unable to pass through an AggregateException which contains just the text "Access Denied".
My conflictive code is this, being the last line the one who breaks:
UserCredential credential;
using (var stream =
new FileStream(HostingEnvironment.MapPath(credentialsPath), FileMode.Open, FileAccess.Read))
{
string credPath = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
credPath = Path.Combine(credPath, writePath);
var authTask = GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
Scopes,
"user",
CancellationToken.None,
new FileDataStore(credPath, true)
);
credential = authTask.Result;
}
I have already tried recreating the secret key, generating the client_secret.json file again, creating another credentials... also I know that both paths, the JSON and the folder are correct, because I had to solve that before reaching this error.
Any hints on what can be the problem?
Thanks!
"Access Denied" error is usually encountered due to Invalid Credentials.
Here are the suggested actions that you can try:
Get a new access token using the long-lived refresh token.
If this fails, direct the user through the OAuth flow, as described in Authorizing requests with OAuth 2.0.
If you are seeing this for a service account, check that you have successfully completed all the steps in the service account page.
Hope that helps!

How to pass HostingEnvironment.Impersonate credentials to ExchangeService EWS?

Is it possible to pass the credentials of the user browsing my asp.net web application to the EWS FindAppointments call?
I'm only trying to return calendar details for the active browsing user, who will without doubt have permission to read their own calendar, so the issue should not relate to Exchange impersonation with the EWS api discussed here.
The code below works just fine when running localhost, but running from the web server, despite Windows Authentication and Identity Impersonation being configured it throws an access denied error.
using (HostingEnvironment.Impersonate())
{
ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2007_SP1);
service.UseDefaultCredentials = true;
service.AutodiscoverUrl(UserEmailAddress);
Mailbox mb = new Mailbox(UserEmailAddress);
FolderId cfCalendarFolderID = new FolderId(WellKnownFolderName.Calendar, mb);
CalendarView cvCalendarView = new CalendarView(DateTime.Now, DateTime.Now.AddDays(30), 1000);
cvCalendarView.MaxItemsReturned = 3;
Perhaps I'm missing a simple way to pass the HostingEnvironment credentials to my ExchangeService object?
Is there a way to check what the service.UseDefaultCredentials are?
I'm not able to use the following as there isn't a way to get the password from the windows authenticated impersonated user.
service.Credentials = new System.Net.NetworkCredential(username, password, domain);
I've also tried the following, but get the same ServiceResponseException access denied errot.
service.Credentials = System.Net.CredentialCache.DefaultNetworkCredentials;
service.PreAuthenticate = true;
Thanks in advance of your kind assistance.
Additional info which may or may not be relevant:
The Application Pool Identity for the website is NetworkService.
The UserEmailAddress variable is set from an AD lookup based on System.Security.Principal.WindowsIdentity.GetCurrent().Name
EDIT (14th Aug 2012)
To achieve what I'd like to do above, I believe the HostingEnvironment.Impersonate isn't required.
Instead I need to use the ExchangeService's ImpersonatedUserId property.
More details on that here
Only problem though is we're running Exchange 2007 and the power shell command for enabling a service account to impersonate all users (that you would use pass in to the .Credentials parameter) only appears to be compatible with Exchange 2010.
You should try using WebCredentials instead of NetworkCredential - see related SO post. There seems to be an issue with EWS and AutoDiscover + NetworkCredentials

Livelink WCF Webservice - Auth Issues

I'm a Sharepoint/MS Developer and not too familiar with Livelink. Anyways, I see they have a .NET WCF Service. I'm attempting to do Authentication using this web service and as far as I can read from the API docs, It shouldn't be too difficult.
According to the docs, I need to auth initially with a Admin user which I do and this works fine. Then I can impersonate using the currently logged on user.
Everything works fine until I get to the ImpersonateUser part which fails with a very generic "Insufficient permissions to perform this action." error. Is this a issue on the client side? or LL side? Possible Kerberos not setup propely or at all?
Herwith the code:
private string ImpersonateUser(string adminToken)
{
string userToken = string.Empty;
llAuthentication.OTAuthentication fLLAuthentication = new llAuthentication.OTAuthentication();
fLLAuthentication.AuthenticationToken = adminToken;
fAuthServiceUser = new AuthenticationClient();
fAuthServiceUser.Endpoint.Address = new EndpointAddress(this.ServiceRoot + "Authentication.svc");
fAuthServiceUser.ClientCredentials.Windows.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;
userToken = fAuthServiceUser.ImpersonateUser(fLLAuthentication, WindowsIdentity.GetCurrent().Name.ToString());
return userToken;
}
This has nothing to do with Windows authentication. It just means the livelink user you're initially using to login with does not have the right to impersonate other livelink users. Ask your livelink admin to grant this right (I dno't know the exact right off-hand, sorry)

Login error connecting to salesforce.com from Flex

Has anyone suddenly encountered login errors from their users trying to connect to salesforce.com from a Flex app using as3salesforce.swc?
I get the following error... password removed to protect the innocent...
App Domain = null
Api Server name = na3.salesforce.com
_internalServerUrl = https://na3.salesforce.com/services/Soap/u/14.0
loading the policy file: https://na3.salesforce.com/services/Soap/cross-domain.xml
Your application must be running on a https server in order to use https to communicate with salesforce.com!
login with creds
loading the policy file: https://na3.salesforce.com/services/crossdomain.xml
Your application must be running on a https server in order to use https to communicate with salesforce.com!
invoke login
intServerUrl is null
intServerUrl = https://na3.salesforce.com/services/Soap/u/14.0
_invoke login
'5A5D3012-7717-E3C2-9B39-FFBBFF1F1B47' producer set destination to 'DefaultHTTPS'.
Method name is: login
'direct_http_channel' channel endpoint set to http://localhost/pm_server/pm/
'5A5D3012-7717-E3C2-9B39-FFBBFF1F1B47' producer sending message 'E32C7199-72C1-B258-B483-FFBC1641173D'
'direct_http_channel' channel sending message:
(mx.messaging.messages::HTTPRequestMessage)#0
body = "<se:Envelope xmlns:se="http://schemas.xmlsoap.org/soap/envelope/"><se:Header xmlns:sfns="urn:partner.soap.sforce.com"/><se:Body><login xmlns="urn:partner.soap.sforce.com" xmlns:ns1="sobject.partner.soap.sforce.com"><username>simon.palmer#dialectyx.com</username><password>******</password></login></se:Body></se:Envelope>"
clientId = (null)
contentType = "text/xml; charset=UTF-8"
destination = "DefaultHTTPS"
headers = (Object)#1
httpHeaders = (Object)#2
Accept = "text/xml"
SOAPAction = """"
X-Salesforce-No-500-SC = "true"
messageId = "E32C7199-72C1-B258-B483-FFBC1641173D"
method = "POST"
recordHeaders = false
timestamp = 0
timeToLive = 0
url = "https://na3.salesforce.com/services/Soap/u/14.0"
'5A5D3012-7717-E3C2-9B39-FFBBFF1F1B47' producer connected.
Method name is: login
Error: Ignoring policy file at https://na3.salesforce.com/crossdomain.xml due to meta-policy 'by-content-type'.
'5A5D3012-7717-E3C2-9B39-FFBBFF1F1B47' producer acknowledge of 'E32C7199-72C1-B258-B483-FFBC1641173D'.
responseType: Fault
Saleforce Soap Fault: sf:INVALID_LOGIN
INVALID_LOGIN: Invalid username, password, security token; or user locked out.
Comunication Error : sf:INVALID_LOGIN : INVALID_LOGIN: Invalid username, password, security token; or user locked out. : [object Object]
Obviously nobody else out there is building Flex apps on top of salesforce.com..
yippee, I'm first.
Anyhow, I just found out that this is a bug at salesforce.com as at 6th December 2008. The issue is that the scripts which handle login do not cope adequately with the redirect necessary because of load balancing on the salesforce.com servers.
It should be possible to go through the www front door of salesforce.com's api with a URL such as...
"https://www.salesforce.com/services/Soap/u/13.0";
where the 13 represents the version of their API you are targetting. However, all users are actually assigned to a specific server, so the front door should redirect the login request to the approriate place, and it doesn't if you are coming from Flex.
A workround is to specify your server in the URL, such as...
"https://na5.salesforce.com/services/Soap/u/13.0";
...which is what I was doing. That's fine if you are a single user accessing the same resources continually and your account remains attached to that server. However if...
You are distributing your app so anyone who has a salesforce.com enterprise account can log in OR
Your account gets moved because of some internal load balancing (which is what happened to me)
then the approach of providing a fixed server won't work.
The bug (as far as I understand it) is that the www route doesn't adequately redirect to your host server. Last intelligence was that it will be fixed "soon".
I wish I could mark this as the answer...

Resources