How to get access token for Unit Testing in .Net - moq

I require an access token to test methods in a service. I tried to hardcode the access token and it is working well. If I run the application after some time all test cases get failed. I believe it's because the access token gets expired. How can I generate access tokens without hardcoding?

The following is the way I used in my work.
First portion inject configuration and second is for getting access token. Hope so it will help you.
public class TestConfigurationManager
{
private IConfiguration _config;
public TestConfigurationManager()
{
IServiceCollection services = new ServiceCollection();
services.AddSingleton<IConfiguration>(Configuration);
}
public IConfiguration Configuration
{
get
{
if (_config == null)
{
var builder = new ConfigurationBuilder().AddJsonFile($"testsettings.json", optional: false);
_config = builder.Build();
}
return _config;
}
}
}
[Fact(DisplayName = "GetAccessToken")]
public async Task GetAccessToken()
{
var _configuration = new TestConfigurationManager().Configuration;
var _httpMessageHandler = new Mock<HttpMessageHandler>();
var httpClient = new HttpClient(_httpMessageHandler.Object);
TokenRequest tokenRequest = new TokenRequest();
_configuration.GetSection(AppSetting.Accounting.Token).Bind(tokenRequest);
string baseAddress = $"{_configuration[AppSetting.Accounting.TokenURL]}"; ;
var form = new Dictionary<string, string>
{
{"grant_type", tokenRequest.GrantType},
{"username", tokenRequest.UserName},
{"password", tokenRequest.Password},
{"key", tokenRequest.Key}
};
httpClient = new HttpClient
{
BaseAddress = new System.Uri(_configuration[AppSetting.Accounting.BaseUrl])
};
HttpResponseMessage tokenResponse = await httpClient.PostAsync(baseAddress, new FormUrlEncodedContent(form));
var jsonContent = await tokenResponse.Content.ReadAsStringAsync();
var tok = JsonConvert.DeserializeObject<TokenResponse>(jsonContent);
Assert.NotNull(tok?.AccessToken);
}

Related

httpclient call is invoked after await keyword in dotnet core

I would like to do some operation which doesn't depend on API response and at the same time I want API to finish its process.
But in my case, API doesn't receive request when postasync is executed.
Instead, Web api receive request after await weatherForeCastdata.
I noticed strange behavior today
when I executed endpoint for first time(both the apis), webapi received request after postasync method. From second time, api receives request after executing await weatherForeCastdata.
I launched applictaion from vs code
browser : chrome
Can anyone help me ?
public async Task<IEnumerable<WeatherForecast>> Get()
{
var rng = new Random();
var weatherForeCastdata = new HttpClientCall<WeatherForecast>(_configuration).PostRequest(_configuration["Services:Payperiod"],new WeatherForecast());
Console.WriteLine("apiinvoked");
var data = await weatherForeCastdata;
//var data1 = await data.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<IEnumerable<WeatherForecast>>(data);
}
public class HttpClientCall<T> where T : class
{
HttpClientHandler httpClientHandler = new HttpClientHandler();
private readonly IConfiguration _configuration;
internal HttpClientCall(IConfiguration configuration)
{
httpClientHandler = new HttpClientHandler();
httpClientHandler.ServerCertificateCustomValidationCallback = (message, cert, chain, sslPolicyErrors) =>
{
if (sslPolicyErrors == SslPolicyErrors.None)
{
return true; //Is valid
}
return true;
};
_configuration = configuration;
}
public async Task<string> PostRequest(string apiUrl, T postObject)
{
using (var client = new HttpClient(httpClientHandler))
{
client.DefaultRequestHeaders.Add("ClientId", _configuration["header"]);
Console.WriteLine(apiUrl);
var response = client.PostAsync(apiUrl, postObject, new JsonMediaTypeFormatter());
var response1=await response;
return await response1.Content.ReadAsStringAsync();
}
}
}

Sustainsys.Saml2 multitenant implementation with app.Map()

I have multi tenant application where each tenant can use different IdP to authenticate. Below code correctly redirects to IdP but problem is to get back the response to ACS endpoint.
Key is the Configuration method which configures the paths and their authentication:
[assembly: OwinStartup(typeof(SSOSamlDemoASPNET.App_Start.Startup))]
namespace SSOSamlDemoASPNET.App_Start
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.Map("/client/okta", (appx) =>
{
ConfigureAuthentication(appx, "/client/okta/Saml2", ...);
});
app.Map("/client/azuread", (appx) =>
{
ConfigureAuthentication(appx, "/client/azuread/Saml2", ...);
});
}
private static void ConfigureAuthentication(IAppBuilder app, string modulePath, string audience, string issuer, string metadataUrl)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = CookieAuthenticationDefaults.AuthenticationType,
CookieName = "LoggedUser",
CookiePath = "/",
CookieManager = new SystemWebCookieManager(),
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
ConfigureSaml(app, modulePath, audience, issuer, metadataUrl);
}
private static void ConfigureSaml(IAppBuilder app, string modulePath, string audience, string issuer, string metadataUrl)
{
var saml2options = new Saml2AuthenticationOptions(false);
var spOptions = new SPOptions
{
EntityId = new EntityId(audience),
ModulePath = modulePath,
PublicOrigin = new Uri("https://localhost:44340/"),
};
spOptions.Logger = new ConsoleLoggerAdapter();
saml2options.SPOptions = spOptions;
saml2options.IdentityProviders.Add(new IdentityProvider(new EntityId(issuer), spOptions)
{
AllowUnsolicitedAuthnResponse = true,
MetadataLocation = metadataUrl,
LoadMetadata = true,
Binding = Saml2BindingType.HttpPost,
});
app.UseSaml2Authentication(saml2options);
}
}
}
Authenticating against individual IdP is done like this:
authProperties.Dictionary["idp"] = "https://sts.windows.net/xxx/";
authProperties.RedirectUri = "https://localhost:44340/client/azuread/ExternalLoginCallback";
HttpContext.Current.Request.GetOwinContext().Authentication.Challenge(authProperties, "Saml2");
When inspecting code of the Sustainsys.Saml2 library (especially Saml2AuthenticationHandler). I found the conditions do not take into account OwinRequest.PathBase and therefore the identity is not coming back to the application.
An example can be (Saml2AuthenticationHandler.Invoke method).
Options.SPOptions.ModulePath = /client/azuread/Saml2
Request.Path = /Saml2/Acs
==> therefore the code inside the condition is not executed.
public override async Task<bool> InvokeAsync()
{
var Saml2Path = new PathString(Options.SPOptions.ModulePath);
if (Request.Path.StartsWithSegments(Saml2Path, out PathString remainingPath))
{
if (remainingPath == new PathString("/" + CommandFactory.AcsCommandName))
{
var ticket = (MultipleIdentityAuthenticationTicket)await AuthenticateAsync();
if (ticket.Identities.Any())
{
Context.Authentication.SignIn(ticket.Properties, ticket.Identities.ToArray());
// No need to redirect here. Command result is applied in AuthenticateCoreAsync.
}
else
{
Response.Redirect(ticket.Properties.RedirectUri);
}
return true;
}
Is there any way to change this behavioral? e.g. saml2Options.Notifications to get this working?
That is obviously a bug/lack of feature, but nothing that will be fixed on the Owin module - it's on life support.
The solution for a multi tenancy owin app is to register one Saml2 middleware and add multiple IdentityProviders to that one. The middleware will handle all responses on the same endpoint and use the configuration from the right IdentityProvider based on where the response came from.

.Net Core 2.0 - Get AAD access token to use with Microsoft Graph

When starting up a fresh .Net Core 2.0 project with Azure AD Authentication you get a working sample that can sign in to your tenant, great!
Now I want to get an access token for the signed in user and use that to work with Microsoft Graph API.
I am not finding any documentation on how to achieve this. I just want a simple way to get an access token and access the graph API, using the template created when you start a new .NET Core 2.0 project. From there I should be able to figure out the rest.
Very important that it works with the project that gets created when following the process where you select Work and school accounts for authentication when creating a new 2.0 MVC Core app in Visual Studio.
I wrote a blog article which shows just how to do that: ASP.NET Core 2.0 Azure AD Authentication
The TL;DR is that you should add a handler like this for when you receive an authorization code from AAD:
.AddOpenIdConnect(opts =>
{
Configuration.GetSection("Authentication").Bind(opts);
opts.Events = new OpenIdConnectEvents
{
OnAuthorizationCodeReceived = async ctx =>
{
var request = ctx.HttpContext.Request;
var currentUri = UriHelper.BuildAbsolute(request.Scheme, request.Host, request.PathBase, request.Path);
var credential = new ClientCredential(ctx.Options.ClientId, ctx.Options.ClientSecret);
var distributedCache = ctx.HttpContext.RequestServices.GetRequiredService<IDistributedCache>();
string userId = ctx.Principal.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
var cache = new AdalDistributedTokenCache(distributedCache, userId);
var authContext = new AuthenticationContext(ctx.Options.Authority, cache);
var result = await authContext.AcquireTokenByAuthorizationCodeAsync(
ctx.ProtocolMessage.Code, new Uri(currentUri), credential, ctx.Options.Resource);
ctx.HandleCodeRedemption(result.AccessToken, result.IdToken);
}
};
});
Here my context.Options.Resource is https://graph.microsoft.com (Microsoft Graph), which I'm binding from config along with other settings (client id etc.).
We redeem a token using ADAL, and store the resulting token in a token cache.
The token cache is something you will have to make, here is the example from the example app:
public class AdalDistributedTokenCache : TokenCache
{
private readonly IDistributedCache _cache;
private readonly string _userId;
public AdalDistributedTokenCache(IDistributedCache cache, string userId)
{
_cache = cache;
_userId = userId;
BeforeAccess = BeforeAccessNotification;
AfterAccess = AfterAccessNotification;
}
private string GetCacheKey()
{
return $"{_userId}_TokenCache";
}
private void BeforeAccessNotification(TokenCacheNotificationArgs args)
{
Deserialize(_cache.Get(GetCacheKey()));
}
private void AfterAccessNotification(TokenCacheNotificationArgs args)
{
if (HasStateChanged)
{
_cache.Set(GetCacheKey(), Serialize(), new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = TimeSpan.FromDays(1)
});
HasStateChanged = false;
}
}
}
The token cache here uses a distributed cache to store tokens, so that all instances serving your app have access to the tokens. They are cached per user, so you can retrieve a token for any user later.
Then when you want to get a token and use MS graph, you'd do something like (important stuff in GetAccessTokenAsync()):
[Authorize]
public class HomeController : Controller
{
private static readonly HttpClient Client = new HttpClient();
private readonly IDistributedCache _cache;
private readonly IConfiguration _config;
public HomeController(IDistributedCache cache, IConfiguration config)
{
_cache = cache;
_config = config;
}
[AllowAnonymous]
public IActionResult Index()
{
return View();
}
public async Task<IActionResult> MsGraph()
{
HttpResponseMessage res = await QueryGraphAsync("/me");
ViewBag.GraphResponse = await res.Content.ReadAsStringAsync();
return View();
}
private async Task<HttpResponseMessage> QueryGraphAsync(string relativeUrl)
{
var req = new HttpRequestMessage(HttpMethod.Get, "https://graph.microsoft.com/v1.0" + relativeUrl);
string accessToken = await GetAccessTokenAsync();
req.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
return await Client.SendAsync(req);
}
private async Task<string> GetAccessTokenAsync()
{
string authority = _config["Authentication:Authority"];
string userId = User.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
var cache = new AdalDistributedTokenCache(_cache, userId);
var authContext = new AuthenticationContext(authority, cache);
string clientId = _config["Authentication:ClientId"];
string clientSecret = _config["Authentication:ClientSecret"];
var credential = new ClientCredential(clientId, clientSecret);
var result = await authContext.AcquireTokenSilentAsync("https://graph.microsoft.com", credential, new UserIdentifier(userId, UserIdentifierType.UniqueId));
return result.AccessToken;
}
}
There we acquire a token silently (using the token cache), and attach it to requests to the Graph.

How to write in memory integration test for a WebApi secured by OpenId Connect, cookie authentication and hosted by NancyFx and Owin

I have a WabApi project which uses Owin and NancyFX. The api is secured by OpenId Connect and cookie authentication.
I have to write some in memory integration tests using HttpClient, which is quite easy as long as you don't try to use authentication based on OpenId Connect and cookie.
Does anyone know how to prepare a proper authentication cookie for HttpClient to let it connect with WebApi as authenticated user?
Currently I'm able to do some http calls to get the proper access token, id token etc. from OpenId Connect provider (implemented by IdentityServer v3), but I have no idea how to prepare authentication cookie for HttpClient.
PS: I uses Hybrid flow for OpenId Connect
Below you can find some of my files.
Server project:
AppStartup for WebApi:
The server application hosts WebApi and OpenId Connect provider (IdentityServer v3) at the same time, so its app starup looks like this:
public class ServerAppStartup
{
public static void Configuration(IAppBuilder app)
{
app.Map("/identity", idsrvApp =>
{
var factory = new IdentityServerServiceFactory {...};
idsrvApp.UseIdentityServer(new IdentityServerOptions
{
SiteName = "server app",
SigningCertificate = ...,
RequireSsl = false,
Factory = factory,
AuthenticationOptions = new AuthenticationOptions {
RememberLastUsername = true
},
EnableWelcomePage = false
});
});
app.SetDefaultSignInAsAuthenticationType("ClientCookie");
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
AuthenticationType = "ClientCookie",
CookieName = CookieAuthenticationDefaults.CookiePrefix + "ClientCookie",
ExpireTimeSpan = TimeSpan.FromMinutes(5)
});
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
AuthenticationType = OpenIdConnectAuthenticationDefaults.AuthenticationType,
SignInAsAuthenticationType = app.GetDefaultSignInAsAuthenticationType(),
Authority = options.BaseUrl+ "identity",
ClientId = options.ClientId,
RedirectUri = options.RedirectUri,
PostLogoutRedirectUri = options.PostLogoutRedirectUri,
ResponseType = "code id_token",
Scope = "openid profile offline_access",
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthorizationCodeReceived = async n =>
{
/* stuff to get ACCESS TOKEN from CODE TOKEN */
},
RedirectToIdentityProvider = n =>
{
if (n.ProtocolMessage.RequestType == OpenIdConnectRequestType.LogoutRequest)
{
var idTokenHint = n.OwinContext.Authentication.User.FindFirst("id_token");
if (idTokenHint != null)
{
n.ProtocolMessage.IdTokenHint = idTokenHint.Value;
}
}
return Task.FromResult(0);
}
}
}
JwtSecurityTokenHandler.InboundClaimTypeMap = new Dictionary<string, string>();
app.UseNancy();
app.UseStageMarker(PipelineStage.MapHandler);
}
Sample nancy module (something like controller in MVC or WebApi):
using System;
using Nancy.ModelBinding;
using Nancy.Security;
namespace Server.Modules
{
public class UsersModule : BaseModule
{
public UsersModule() : base("/users")
{
Get["/getall"] = parameters =>
{
this.RequiresMSOwinAuthentication();
...
return ...;
};
}
}
}
Integration test project:
Test server to let me run WebApi in memory:
public class TestServer: IDisposable
{
private Func<IDictionary<string, object>, Task> _appFunc;
public static CookieContainer CookieContainer;
public Uri BaseAddress { get; set; }
// I uses OwinHttpMessageHandler becaouse it can handle http redirections
public OwinHttpMessageHandler Handler { get; private set; }
public HttpClient HttpClient => new HttpClient(Handler) { BaseAddress = BaseAddress };
public static TestServer Create()
{
CookieContainer = new CookieContainer();
var result = new TestServer();
var appBuilder = new AppBuilder();
appBuilder.Properties["host.AppName"] = "WebApi server";
/* Use configuration of server app */
ServerAppStartup.Configuration(appBuilder);
result._appFunc = appBuilder.Build();
result.Handler = new OwinHttpMessageHandler(result._appFunc)
{
AllowAutoRedirect = true,
AutoRedirectLimit = 1000,
CookieContainer = CookieContainer,
UseCookies = true
};
return result;
}
public void Dispose()
{
Handler.Dispose();
GC.SuppressFinalize(this);
}
}
Sample test:
namespace ServerSpec.Specs.Users
{
public class GetAllUsersSpec
{
private TestServer _server;
public GetAllUsersSpec(){
server = TestServer.create();
}
[Fact]
public void should_return_all_users()
{
/* here I will get error because http client or rather its cookie handler has no authentication cookie */
var users = Get("/users/getall");
...
}
public TResponse Get<TResponse>(string urlFragment)
{
var client = server.HttpClient();
var httpResponse = client.GetAsync(urlFragment).Result;
httpResponse.EnsureSuccessStatusCode();
return httpResponse.Content.ReadAsAsync<TResponse>().Result;
}
}
}
Check out the unit and integration tests in this project:
https://github.com/IdentityModel/IdentityModel.Owin.PopAuthentication
It shows doing in-memory integration testing with IdentityServer in one pipeline, and a (fake) web api in another pipeline that accepts tokens.

How to access HttpContext inside a unit test in ASP.NET 5 / MVC 6

Lets say I am setting a value on the http context in my middleware. For example HttpContext.User.
How can test the http context in my unit test. Here is an example of what I am trying to do
Middleware
public class MyAuthMiddleware
{
private readonly RequestDelegate _next;
public MyAuthMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
context.User = SetUser();
await next(context);
}
}
Test
[Fact]
public async Task UserShouldBeAuthenticated()
{
var server = TestServer.Create((app) =>
{
app.UseMiddleware<MyAuthMiddleware>();
});
using(server)
{
var response = await server.CreateClient().GetAsync("/");
// After calling the middleware I want to assert that
// the user in the HttpContext was set correctly
// but how can I access the HttpContext here?
}
}
Following are two approaches you could use:
// Directly test the middleware itself without setting up the pipeline
[Fact]
public async Task Approach1()
{
// Arrange
var httpContext = new DefaultHttpContext();
var authMiddleware = new MyAuthMiddleware(next: (innerHttpContext) => Task.FromResult(0));
// Act
await authMiddleware.Invoke(httpContext);
// Assert
// Note that the User property on DefaultHttpContext is never null and so do
// specific checks for the contents of the principal (ex: claims)
Assert.NotNull(httpContext.User);
var claims = httpContext.User.Claims;
//todo: verify the claims
}
[Fact]
public async Task Approach2()
{
// Arrange
var server = TestServer.Create((app) =>
{
app.UseMiddleware<MyAuthMiddleware>();
app.Run(async (httpContext) =>
{
if(httpContext.User != null)
{
await httpContext.Response.WriteAsync("Claims: "
+ string.Join(
",",
httpContext.User.Claims.Select(claim => string.Format("{0}:{1}", claim.Type, claim.Value))));
}
});
});
using (server)
{
// Act
var response = await server.CreateClient().GetAsync("/");
// Assert
var actual = await response.Content.ReadAsStringAsync();
Assert.Equal("Claims: ClaimType1:ClaimType1-value", actual);
}
}
The RC1 version of asp.net 5/MVC6 makes it possible to set HttpContext manually in Unit Tests, which is awesome!
DemoController demoController = new DemoController();
demoController.ActionContext = new ActionContext();
demoController.ActionContext.HttpContext = new DefaultHttpContext();
demoController.HttpContext.Session = new DummySession();
DefaultHttpContext class is provided by the platform.
DummySession can be just simple class that implements ISession class. This simplifies things a lot, because no more mocking is required.
It would be better if you unit test your middleware class in isolation from the rest of your code.
Since HttpContext class is an abstract class, you can use a mocking framework like Moq (adding "Moq": "4.2.1502.911", as a dependency to your project.json file) to verify that the user property was set.
For example you can write the following test that verifies your middleware Invoke function is setting the User property in the httpContext and calling the next middleware:
[Fact]
public void MyAuthMiddleware_SetsUserAndCallsNextDelegate()
{
//Arrange
var httpContextMock = new Mock<HttpContext>()
.SetupAllProperties();
var delegateMock = new Mock<RequestDelegate>();
var sut = new MyAuthMiddleware(delegateMock.Object);
//Act
sut.Invoke(httpContextMock.Object).Wait();
//Assert
httpContextMock.VerifySet(c => c.User = It.IsAny<ClaimsPrincipal>(), Times.Once);
delegateMock.Verify(next => next(httpContextMock.Object), Times.Once);
}
You could then write additional tests for verifying the user has the expected values, since you will be able to get the setted User object with httpContextMock.Object.User:
Assert.NotNull(httpContextMock.Object.User);
//additional validation, like user claims, id, name, roles
take a look at this post:
Setting HttpContext.Current.Session in a unit test
I think what you need is this.
public static HttpContext FakeHttpContext(string url)
{
var uri = new Uri(url);
var httpRequest = new HttpRequest(string.Empty, uri.ToString(),
uri.Query.TrimStart('?'));
var stringWriter = new StringWriter();
var httpResponse = new HttpResponse(stringWriter);
var httpContext = new HttpContext(httpRequest, httpResponse);
var sessionContainer = new HttpSessionStateContainer("id",
new SessionStateItemCollection(),
new HttpStaticObjectsCollection(),
10, true, HttpCookieMode.AutoDetect,
SessionStateMode.InProc, false);
SessionStateUtility.AddHttpSessionStateToContext(
httpContext, sessionContainer);
return httpContext;
}
Then you can use it like:
request.SetupGet(req => req.Headers).Returns(new NameValueCollection());
HttpContextFactory.Current.Request.Headers.Add(key, value);

Resources