Testing a Web App Protected by Passive Federated Auth - asp.net

My team has an ASP.NET MVC-based website and WebAPI that is protected by passive federated authentication. It all works properly. The problem we're having is that we need to test the website and the web API after an automated deployment. How can we authenticate and get the FEDAUTH cookie to the website from automated test code, assuming that the test code is run by a user authorized to access the website?

You can have your Web API support active authentication. It requires some work to change the configuration and authentication handler, but it will make your web API easily accessible from a program client as well. If you just want to get a FEDAUTH cookie in your automated test code, the following code sample can work. It mimics a browser to post the user token to the website and get a cookie back.
// The code needs the STS server and the website url
var stsUrl = "https://your_STS";
var serviceUrl = "https://your_Service";
// Use Windows Credential to get the token
var binding = new WSHttpBinding(SecurityMode.Transport);
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Windows;
var factory = new WSTrustChannelFactory(binding, stsUrl) { TrustVersion = TrustVersion.WSTrust13 };
// Override current login user credential if needed:
// factory.Credentials.Windows.ClientCredential = userCredential;
var rst = new RequestSecurityToken
{
RequestType = RequestTypes.Issue,
KeyType = KeyTypes.Bearer,
AppliesTo = new EndpointReference(serviceUrl)
};
RequestSecurityTokenResponse rstr;
var token = factory.CreateChannel().Issue(rst, out rstr);
var fedSerializer = new System.IdentityModel.Services.WSFederationSerializer();
var rstrContent = fedSerializer.GetResponseAsString(rstr, new WSTrustSerializationContext());
// After this the security token is acquired and saved in rstrContent
var client = new HttpClient();
// Initiate a request to the service, which will be redirected to STS. Read WS fed fields from redirected URL.
var response = client.GetAsync(serviceUrl).Result;
response.EnsureSuccessStatusCode();
var redirectQuery = response.RequestMessage.RequestUri.Query;
var queryParams = System.Web.HttpUtility.ParseQueryString(redirectQuery);
// construct a authentication form
var formData = new Dictionary<string, string>
{
{"wa", queryParams["wa"]},
{"wresult", rstrContent},
{"wctx", queryParams["wctx"] },
};
// post the authentication form to the website.
response = client.PostAsync(serviceUrl, new FormUrlEncodedContent(formData)).Result;
response.EnsureSuccessStatusCode();
// After this, the auth cookie is set in this HttpClient that you can use to access your service

Related

Header token authorization not working for get request in xamarin forms

I'm working on a project where i need to get the list of users from a restful server. However, My code isn't working as I'm not getting the intended result. The rest server uses a JWT token which means i need to be authorized before i can make a request. Please how do i do this in xamarin forms.
below is my code:
{
var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
"Bearer", accessToken);
var json = await client.GetStringAsync(ConstantsValue.BaseAddress + "Users");
var users = JsonConvert.DeserializeObject<List<AddedUsers>>(json);
return users;
}```

WebRequest is forbidden on Sharepoint Online

I need to create web request to specific page in SharePoint Online using Access Token in order to get certain Header information, but I keep getting 403 and I'm not sure why. After toying with access requests some more, I managed to only get 403 using clientContext, and 401 using Postman.
I can get response from grah api in my app and with postman using access token, but the problem is I need to do a web request to get real web page headers like SpRequestDuration and SPIISLatency.
I've followed steps to create my Azure AD and application.
I request user login to get authentication code for my application
loginScopes = [
'User.Read.All',
'Directory.Read.All',
'Group.Read.All',
'Sites.Read.All',
'Reports.Read.All'
'offline_access',
'https://www.sharepoint.com/AllSites.FullControl'
];
const encodedScopes = encodeURIComponent(loginScopes.join(' '));
const encodedRedirectUri = encodeURIComponent(redirectUri);
let url = `https://login.microsoftonline.com/organizations/oauth2/v2.0/authorize?
response_type=code&
client_id=${clientId}
&redirect_uri=${encodedRedirectUri}
&scope=${encodedScopes}
&prompt=select_account`;
After this returns authentication code I create ConfidentialClientApplication with authority : 'https://login.microsoftonline.com/organizations' and with my application clientId, secret, redirect etc. With newly created ConfidentialClientApplication I acquire token silently with scope '{tenantUrl}/AllSites.FullControl'and another token with other scopes.
Now with access token I successfully create ClientContext, which retrieves data from sharepoint site and I can easily make a graph API request and everything will be fine. But the problem is with creating a WebRequest to a specific site (example. {tenantUrl}/SitePages/Forms/ByAuthor.aspx)
var manager = new AuthenticationManager();
var clientContext = manager.GetAzureADAccessTokenAuthenticatedContext(tenantUrl, accessToken);
clientContext.Load(clientContext.Site);
clientContext.Load(oWebsite.Lists);
clientContext.ExecuteQuery(); // Works fine
var request = clientContext.WebRequestExecutorFactory.CreateWebRequestExecutor(clientContext,{tenantUrl}/SitePages/Forms/ByAuthor.aspx).WebRequest;
request.Method = "GET";
//request.Headers.Add("Authorization", $"Bearer {accessToken}");
clientContext.ExecutingWebRequest += delegate (object sender, WebRequestEventArgs e)
{
e.WebRequestExecutor.WebRequest.Headers.Add("Authorization", "Bearer " + accessToken);
};
await request.GetResponseAsync(); // Throws 403
with sharepoint online
string siteUrl = ConfigurationManager.AppSettings["siteURL"];
SecureString passWord = new SecureString();
string pass = ConfigurationManager.AppSettings["userPassword"];
string user = ConfigurationManager.AppSettings["userName"];
foreach (char c in pass.ToCharArray()) passWord.AppendChar(c);
ClientContext clientContext = new ClientContext(siteUrl);
clientContext.Credentials = new SharePointOnlineCredentials(user, passWord);
return clientContext;
without
clientContext.ExecutingWebRequest

IdentityServer Hybrid Flow - Access Token is null after user successful login

I'm having problems in retrieving access token of an authenticated user. below is my configuration
ASP.NET MVC 5 Client:
OpenIdConnect
IdentityServer3 libraries
ResponseType = "code id_token"
ASP.NET Core Identity Server:
IdentityServer4 libraries
Client Config: AllowedGrantTypes =
GrantTypes.HybridAndClientCredentials,
I'm trying to get the access token in my client using this:
AuthorizationCodeReceived = async n =>
{
// use the code to get the access and refresh token
var tokenClient = new TokenClient(TokenEndpoint, "clientid", "secret");
var response = await tokenClient.RequestAuthorizationCodeAsync(n.Code, n.RedirectUri);
},
I used this reference for above implementation - https://github.com/IdentityServer/IdentityServer3/issues/2457
but the properties in the response has null values. I need the access token so that the user logged in the client can access the api. Below is another way that i'm trying to retrieve the access token:
public async Task<ActionResult> CallApiUsingUserAccessToken()
{
var user = User as ClaimsPrincipal;
var accessToken = user.FindFirst("access_token").Value;
var client = new HttpClient();
client.SetBearerToken(accessToken);
var content = await client.GetStringAsync("http://localhost:6001/api/values");
ViewBag.Json = JArray.Parse(content).ToString();
return View("json");
}
however, user.FindFirst("access_token").Value; is null. I'm thinking of migrating my MVC client to Core because I've tried the IdentityServer4 version in an asp.net core but that seems to be a big migration to my part. Thank you.
[updated]
It never occured to me that the endpoints in the IdentityServer3 differs from IDS4. I did have to change var tokenClient = new TokenClient(TokenEndpoint, "client", "secret"); to var tokenClient = new TokenClient("http://localhost:9000/connect/token", "client", "secret") since TokenEndpoint in IDS3 is http://localhost:9000/core/connect/token which the endpoint "core" does not exist in IDS4. I'm able to get the access token in this line var tokenResponse = await tokenClient.RequestAuthorizationCodeAsync(n.Code, n.RedirectUri); but after authorization, i'm still getting nullreference exception to this var accessToken = user.FindFirst("access_token").Value; line of code.
Given the IdentityServer 4 documentation on
Switching to Hybrid Flow and adding API Access back
and an example client from IdentityServer3.Samples
MVC OWIN Client (Hybrid)
you should be able to setup a working environment.
To support debugging you should always do proper response handling as shown in example below and copied from example client. Add any response errors to your question.
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthorizationCodeReceived = async n =>
{
// use the code to get the access and refresh token
var tokenClient = new TokenClient(
Constants.TokenEndpoint,
"mvc.owin.hybrid",
"secret");
var tokenResponse = await tokenClient.RequestAuthorizationCodeAsync(
n.Code, n.RedirectUri);
if (tokenResponse.IsError)
{
throw new Exception(tokenResponse.Error);
}
Finally I recommend to add code for all important parts of an IdentityServer3/4 based setup - because the truth is usually burried in the details.
According to these posts, https://github.com/IdentityServer/IdentityServer3/issues/2457 & https://github.com/IdentityServer/IdentityServer3/issues/2015#issuecomment-172623173, it is a good practice to not include the access token in the claims. Hence, I followed his example, https://github.com/Mich-b/IdentityServerTMLClient/blob/master/IdentityServerTMLClient/Startup.cs, in which the access token is added in the Http Session storage.

Asp.Net Web Api 2 - How to consume JWT access token and user claims using Identity Model

I have implemented an authorization server in a Asp.Net Web Api project as explained in this article.
Now I need to consume the service from a .Net c# client. In the IdentityModel documentation I can see below example:
var client = new TokenClient(
"https://server/token",
"client_id",
"secret");
var response = await client.RequestClientCredentialsAsync("scope");
var token = response.AccessToken;
Questions:
What is the purpose of having client Id and a client secret?
How a user will be authenticated using user credentials?
How can I access the user claims in client side?
What is Scope and what is the use of it?
By using IdentityModel.Client; the token can be consumed in following way.
var client = new TokenClient(authenticationUrl);
client.Timeout = TimeSpan.FromSeconds(60);
var tokenResponse = await client.RequestResourceOwnerPasswordAsync(userName, password);
var handler = new JwtSecurityTokenHandler();
var token = handler.ReadJwtToken(tokenResponse.AccessToken);
in the token itself contains claim properties.

Azure MobileServiceClient fails to authenticate user in ASP.NET

I'm having troubles with OAuth .NET backend authentication for Azure mobile-services in ASP.NET 5.0. I'm trying to implement external login with Facebook,Twitter,Google and Microsoft.
I'm successfully getting access_token from all external sources and then trying to log in into MobileServiceClient.
here is my code
var app = System.Web.HttpContext.Current.Items["AzureClient"] as MobileServiceClient;
app.Logout();
var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();
var accesToken = loginInfo.ExternalIdentity.Claims.FirstOrDefault(c => c.Type == "access_token");
MobileServiceUser user = null;
if (providerName == "Microsoft")
{
user = await app.LoginWithMicrosoftAccountAsync(accessToken);
}
else
{
var token = new JObject();
token.Add("access_token", accessToken);
user = await app.LoginAsync(loginInfo.Login.LoginProvider, token);
}
And I'm getting authenticated but only with facebook token. Microsoft and Google throw 401 unauthorized exception. Twitter throws "Method not allowed". What am I dowing wrong?
I've double-checked that app secret and app keys are populated for all providers in azure management portal.
Please, help
I'm not sure if tokens from social network can be forwarded to MobileServiceClient or not but it works with facebook and doesn't work with all the others. I'm really puzzled about this behaviour;
I finally ended up with creating an ActiveDirectory application and using ADAL AcquireToken method to obtain AD token for my MobileServicesClient. As it is described here
Azure Website Single Sign On accessing Azure Mobile Service from Azure Active Directory as User
here is my Method obtaining token from AD
private string GetAdToken()
{
string clientID = "<clientId>";
string authority = "<AuthorityUrl>";
string resourceURI = "<WebApiUrl>";
var appKey = "<applicationKey>";
var ac = new Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext(authority);
var clientCredential = new ClientCredential(clientID, appKey);
var ar = ac.AcquireToken(resourceURI, clientCredential);
Session["token"] = ar.AccessToken;
return ar.AccessToken;
}
and here is my method which is run before quering Azure datatables through MobileServiceClient.
private async Task<MobileServiceUser> EnsureLogin()
{
var app = System.Web.HttpContext.Current.Items["AzureClient"] as MobileServiceClient;
app.Logout();
JObject token = new JObject();
token["access_token"] = Session["token"].ToString();
return await app.LoginAsync(MobileServiceAuthenticationProvider.WindowsAzureActiveDirectory, token);
}
So now it doesn't metter what provider I use to log in to my web application. MobileServiceClient always works with ad token.
I'm not sure if it is an acceptable practice but it works and maybe this will help somebody like me struggling against azure authentication

Resources