How use AD FS token in console application - c#

i have AD FS server, console app and simple web site wher i can sign in with my domain account.
In my console application i get token from STS. Now, I want log with him to web site (i want check token).
How should I use this token in WebClient, WebApiClient and WCF?
I tried Opened channel in WCF with this token and download page using WebClient but i get log page.
this token is inportant 2 weeks, how cann I cancel this token earlier?
this is my code to get token (works):
string userName = "login";
string password = "pass";
string adfsAdr = #"https://domain/adfs/services/trust/13/usernamemixed";
string webPage = #"https://MyAspNetAppAdrs.com";
var factory = new Microsoft.IdentityModel.Protocols.WSTrust.WSTrustChannelFactory(
new UserNameWSTrustBinding(SecurityMode.TransportWithMessageCredential),
new EndpointAddress(adfsAdr));
factory.TrustVersion = TrustVersion.WSTrust13;
if (_factory.Credentials != null)
{
factory.Credentials.UserName.UserName = userName;
factory.Credentials.UserName.Password = password;
}
var rst = new RequestSecurityToken
{
RequestType = WSTrust13Constants.RequestTypes.Issue,
AppliesTo = new EndpointAddress(webPage),
KeyType = WSTrust13Constants.KeyTypes.Bearer,
};
var channel = factory.CreateChannel();
SecurityToken token = channel.Issue(rst);

Related

Retrieving SAML token for a relying party trust using windows pass-through authentication

I'm attempting to determine a way for an application to retrieve a SAML token to access a relying party trust on behalf of the current context of the application (as a user).
The common recommendation is to retrieve a SAML token using WSTrustChannelFactory but with supplying a username and password. I would like to not do this so it is seamless to the user. I feel like ADFS should support SSO authentication (using passthrough windows auth) for this request. Is this possible?
Here is the typical recommended code that requires credentials:
public SecurityToken GetSamlToken()
{
using (var factory = new WSTrustChannelFactory(
new UserNameWSTrustBinding(SecurityMode.TransportWithMessageCredential),
new EndpointAddress(new Uri("https://serv/adfs/services/trust/13/usernamemixed"))))
{
factory.Credentials.UserName.UserName = "username";
factory.Credentials.UserName.Password = "password";
factory.Credentials.ServiceCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.None;
factory.TrustVersion = TrustVersion.WSTrust13;
WSTrustChannel channel = null;
try
{
string KeyType;
var rst = new RequestSecurityToken
{
RequestType = WSTrust13Constants.RequestTypes.Issue,
AppliesTo = new EndpointAddress("net.tcp://localhost:xxxx/Service1/mex"),
KeyType = Microsoft.IdentityModel.Protocols.WSTrust.WSTrust13Constants.KeyTypes.Bearer,
};
channel = (WSTrustChannel)factory.CreateChannel();
return channel.Issue(rst);
}
finally
{
if (channel != null)
{
channel.Abort();
}
factory.Abort();
}
}
}
Thank you for your help!

How to add SAML token to SOAP Request in C#

I try to call a SOAP service by authenticating myself with a SAML token.
First I get a SAML token for the target by calling the ADFS:
var stsEndpoint = "https://ADFS.EXAMPLE/adfs/services/trust/13/kerberosmixed";
var reliantPartyUri = "http://reliant-party.com";
var binding = new CustomBinding();
var ssbe = SecurityBindingElement.CreateKerberosOverTransportBindingElement();
ssbe.DefaultAlgorithmSuite = SecurityAlgorithmSuite.Basic128;
ssbe.MessageSecurityVersion = MessageSecurityVersion.WSSecurity10WSTrust13WSSecureConversation13WSSecurityPolicy12BasicSecurityProfile10;
binding.Elements.Add(ssbe);
binding.Elements.Add(new TextMessageEncodingBindingElement());
binding.Elements.Add(new HttpsTransportBindingElement());
var factory = new WSTrustChannelFactory(binding, new EndpointAddress(stsEndpoint));
factory.TrustVersion = TrustVersion.WSTrust13;
var rst = new RequestSecurityToken
{
RequestType = RequestTypes.Issue,
AppliesTo = new EndpointReference(reliantPartyUri)
};
var channel = factory.CreateChannel();
var token = channel.Issue(rst);
Now I want to use the SAML token to call a secured SOAP webservice. How is it possible to add the token? I've tried the following without success (the soap request does not contain any token):
//Service was created by an imported WSDL File - Methods and Types renamed for StackOverflow
var request = new Service.WsdlCreatedRequest();
[...]
var wsdlClient = new Service.WsdlCreatedService("HTTPS_Port");
var wsdlChannel = wsdlClient.ChannelFactory.CreateChannelWithIssuedToken(token);
wsdlChannel.WsdlCreatedMethod(request);
Any idea how to use the token in the request?

PowerBI and Azure AD Headless Login

I am trying to embed PowerBI dashboards into my customer MVC portal. My customers don't have AAD accounts, so they can't login to Live when they come to the website, they log into my MVC website with individual authority.
I have registered my App on PowerBI/AAD and have the ClientID and Secret. I make the call to AAD and get an Authorization Code which I then use to get an Athentication Token which the is returned successfully.
When ever I use the access token to get a dashboard it is continually rejected with a 403 Forbidden.
I have gone through all the samples from Microsoft, but they require a user login prompt. I have reviewed the ADAL2.0 code which refers to the AcquireToken Method, but this was deprecated in ADAL3 and replaced with AcquireTokenAsync which has different parameters and I am using this in my example below.
Here is the function to get the token:
protected AuthenticationResult GetAccessToken()
{
string pBiUser = Properties.Settings.Default.PowerBIUser;
string pBiPwd = Properties.Settings.Default.PowerBIPwd;
string pBiClientId = Properties.Settings.Default.PowerBIClientId;
string pBiSecret = Properties.Settings.Default.PowerBIClientSecret;
TokenCache TC = new TokenCache();
ClientCredential CC = new ClientCredential(pBiClientId,pBiSecret);
string AU = Properties.Settings.Default.PowerBIAuthority;
Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext authenticationContext
= new Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext(AU, TC);
AuthenticationResult result = authenticationContext.AcquireTokenAsync("https://analysis.windows.net/powerbi/api"
,CC).Result;
if (result == null)
{
throw new InvalidOperationException("Failed to obtain the PowerBI token");
}
return result;
}
I then take the result token and call. The response receives the 403:
protected PBIDashboards GetDashboards(AuthenticationResult authResult)
{
PBIDashboards pbiDashboards = new PBIDashboards();
var baseAddress = new Uri("https://api.powerbi.com");
using (var httpClient = new System.Net.Http.HttpClient {BaseAddress = baseAddress})
{
httpClient.DefaultRequestHeaders.TryAddWithoutValidation("authorization",
"Bearer " + authResult.AccessToken);
using (**var response** = httpClient.GetAsync("v1.0/myorg/dashboards").Result)
{
string responseData = response.Content.ReadAsStringAsync().Result;
//Deserialize JSON string
pbiDashboards = JsonConvert.DeserializeObject<PBIDashboards>(responseData);
if (pbiDashboards != null)
{
var gridViewDashboards = pbiDashboards.value.Select(dashboard => new
{
Id = dashboard.id,
DisplayName = dashboard.displayName,
EmbedUrl = dashboard.embedUrl
});
}
}
}
return pbiDashboards;
}
Based on the error message(403), the issue is relative to the permission.
And AFAIK the is no such permission we can use when we acquire the access token using the client credentials flow for the Power BI REST. You can refer the permission for the figure below:
To get the token for the Power BI REST without user interaction, we can use the Resource owner password credentials flow. And you can use the 3rd party library PowerBI.Api.Client which already implement this.
After a lot of research, you can make a direct AJAX call to get the token:
private async Task<string> GetAccessToken()
{
string pBiUser = Properties.Settings.Default.PowerBIUser;
string pBiPwd = Properties.Settings.Default.PowerBIPwd;
string pBiClientId = Properties.Settings.Default.PowerBIClientId;
string pBiSecret = Properties.Settings.Default.PowerBIClientSecret;
string pBITenant = Properties.Settings.Default.PowerBITenantId;
string tokenEndpointUri = "https://login.microsoftonline.com/"+pBITenant+"/oauth2/token";
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("grant_type", "password"),
new KeyValuePair<string, string>("username", pBiUser),
new KeyValuePair<string, string>("password", pBiPwd),
new KeyValuePair<string, string>("client_id", pBiClientId),
new KeyValuePair<string, string>("client_secret", pBiSecret),
new KeyValuePair<string, string>("resource", "https://analysis.windows.net/powerbi/api")
});
using (var client = new HttpClient())
{
HttpResponseMessage res = client.PostAsync(tokenEndpointUri, content).Result;
string json = await res.Content.ReadAsStringAsync();
AzureAdTokenResponse tokenRes = JsonConvert.DeserializeObject<AzureAdTokenResponse>(json);
return tokenRes.AccessToken;
}
}
Once you have the string AccessToken, you can then call the Dashboards request.
protected PBIDashboards GetDashboards(string token)
{
PBIDashboards pbiDashboards = new PBIDashboards();
var baseAddress = new Uri("https://api.powerbi.com");
using (var httpClient = new System.Net.Http.HttpClient {BaseAddress = baseAddress})
{
httpClient.DefaultRequestHeaders.TryAddWithoutValidation("authorization",
"Bearer " + token);
using (var response = httpClient.GetAsync("v1.0/myorg/dashboards").Result)
{
string responseData = response.Content.ReadAsStringAsync().Result;
//Deserialize JSON string
pbiDashboards = JsonConvert.DeserializeObject<PBIDashboards>(responseData);
if (pbiDashboards != null)
{
var gridViewDashboards = pbiDashboards.value.Select(dashboard => new
{
Id = dashboard.id,
DisplayName = dashboard.displayName,
EmbedUrl = dashboard.embedUrl
});
}
}
}
return pbiDashboards;
}
This will provide you the list of dashboards and the dashboard Id to call the PowerBI API to build the embeded page in Javascript. I used hidden input fields to store the access token and embed URL to pass over to the Javascript call.
// check if the embed url was selected
var embedUrl = document.getElementById('embed').value;
if (embedUrl === "")
return;
// get the access token.
accessToken = document.getElementById('token').value;
// Embed configuration used to describe the what and how to embed.
// This object is used when calling powerbi.embed.
// You can find more information at https://github.com/Microsoft/PowerBI-JavaScript/wiki/Embed-Configuration-Details.
var config = {
type: 'dashboard',
accessToken: accessToken,
embedUrl: embedUrl
};
// Grab the reference to the div HTML element that will host the dashboard.
var dashboardContainer = document.getElementById('dashboard');
// Embed the dashboard and display it within the div container.
var dashboard = powerbi.embed(dashboardContainer, config);

How to obtain a JWT from ADFS (Windows server 2012R2) using a ClientAssertionCertificate in C#

I'm having trouble obtaining a JWT from ADFS (Windows server 2012R2) using a ClientAssertionCertificate.
It all works fine when I let the user authenticate with his username and password directly into the adfs - login window, but I don't want my application to show the ADFS - login window, because the users of my application won't know the credentials. My application actually IS the user (of an api) so I want to authenticate using a certificate. When i try doing that, I get the message "The authorization server does not support the requested grant_type. The authorization server only supports authorization_code or refresh_token as the grant type.
Does anyone know any workaround or other method to obtain a JWT from ADFS using a certificate? Thx a lot!
My application is a .Net 4.6.1 console application.
This is my code now:
var certPath = Path.Combine(GetCurrentDirectoryFromExecutingAssembly(), "mycertificate.pfx");
var certfile = File.OpenRead(certPath);
var certificateBytes = new byte[certfile.Length];
certfile.Read(certificateBytes, 0, (int)certfile.Length);
var cert = new X509Certificate2(
certificateBytes,
"mypassword",
X509KeyStorageFlags.Exportable |
X509KeyStorageFlags.MachineKeySet |
X509KeyStorageFlags.PersistKeySet);
var certificate = new ClientAssertionCertificate("myclientid", cert);
AuthenticationContext context = new AuthenticationContext("https://sts.example.com/adfs",false);
AuthenticationResult authenticationResult = await context.AcquireTokenAsync("http://example.com/api", certificate);
var token = authenticationResult.AccessToken;
Have you tried using a WSTrustChannelFactory with CertificateWSTrustBinding?
In the RequestSecurityToken you can specify in the TokenType that you want a JWT. The JWT you receive will be a base64 string that you need to decode.
public static async Task<string> GetAccessToken(string authority, string resource, string clientId)
{
var certPath = Path.Combine(GetCurrentDirectoryFromExecutingAssembly(), "mycertificate.pfx");
var certfile = File.OpenRead(certPath);
var certificateBytes = new byte[certfile.Length];
certfile.Read(certificateBytes, 0, (int)certfile.Length);
var cert = new X509Certificate2(
certificateBytes,
"PASSWORD",
X509KeyStorageFlags.Exportable |
X509KeyStorageFlags.MachineKeySet |
X509KeyStorageFlags.PersistKeySet);
var factory = new WSTrustChannelFactory(
new CertificateWSTrustBinding(
SecurityMode.TransportWithMessageCredential),
"https://example.com/adfs/services/trust/13/certificatemixed") {TrustVersion = TrustVersion.WSTrust13};
if (factory.Credentials != null)
factory.Credentials.ClientCertificate.Certificate = cert;
// create token request
var rst = new RequestSecurityToken
{
RequestType = RequestTypes.Issue,
KeyType = KeyTypes.Bearer,
AppliesTo = new EndpointReference("http://example.com/api"),
KeySizeInBits = 0,
TokenType = "urn:ietf:params:oauth:token-type:jwt"
};
// request token and return
var genericXmlSecurityToken = factory.CreateChannel().Issue(rst) as GenericXmlSecurityToken;
return genericXmlSecurityToken != null
? Encoding.UTF8.GetString(Convert.FromBase64String(genericXmlSecurityToken.TokenXml.InnerXml))
: string.Empty;
}

C# ADFS SAML token with Windows Auth / Current logged in user

How do I generate a SAML token without using credentials again?
Scenario: I am trying to send a SAML token to SAP web service. Since multiple users will be using this application I do not want to ask them for credentials but instead get them from current machine windows credentials and generate a SAML token.
This is what is currently being used in my code.
factory.Credentials.UserName.UserName = "bob";
factory.Credentials.UserName.Password = "abc!123";
// create token request
var rst = new RequestSecurityToken
{
RequestType = RequestTypes.Issue,
KeyType = KeyTypes.Symmetric,
AppliesTo = new EndpointReference(_serviceAddress.AbsoluteUri)
};
I use this method in my code to pass through the credentials of the logged in user to our ADFS server for single-sign in to O365 from the applications I write; you might be able to adapt the code to suit your purposes:
private GenericXmlSecurityToken NewGetAdfsSamlTokenWinAuth()
{
try
{
WS2007HttpBinding binding = new WS2007HttpBinding(SecurityMode.Transport);
binding.Security.Message.EstablishSecurityContext = false;
binding.Security.Message.ClientCredentialType = MessageCredentialType.Windows;
WSTrustChannelFactory factory = new WSTrustChannelFactory((binding), new EndpointAddress(this.adfsIntegratedAuthUrl));
factory.TrustVersion = TrustVersion.WSTrustFeb2005;
factory.Credentials.SupportInteractive = false;
var rst = new RequestSecurityToken
{
RequestType = RequestTypes.Issue,
AppliesTo = new EndpointReference("urn:federation:MicrosoftOnline"),
KeyType = KeyTypes.Bearer
};
IWSTrustChannelContract channel = factory.CreateChannel();
return channel.Issue(rst) as GenericXmlSecurityToken;
}
catch (Exception ex)
{
// Do something with the exception
}
return null;
}
This will return a GenericXmlSecurityToken which has a TokenXml.OuterXml property that contains the SAML assertion.

Categories

Resources