Web API In Memory Integration Testing - Authentication between two services - entity-framework-6

I have searched lots of posts here regarding my problem but have not found anything related to what I am trying to achieve.
I have created a Web API micro service for handling user authentication. The service uses identity framework and issues bearer tokens. I have another Web API which is secured using the [Authroize] attribute on the controllers. Both services talk to the same database via Entity Framework.
One hurdle I had to overcome whilst setting up this infrastructure was both services require the same machine key in their web.configs. When everything is deployed it works great.
However, I am trying to write some integration tests for my main API using the Microsoft Owin TestServer. This allows me to write integration tests against an in memory copy of the API which also uses an in memory copy of the database using the entity framework migrations.
I have successfully created integration tests for my user service. However, I am struggling to get integration tests working for my main service. I have created a solution with a new test project in it. I have also linked in my user service and main service. In the bootstrap for the integration tests, I spin up an in memory copy of each service by using each services Startup.cs.
Everything works ok until I try to hit a controller that is secured with the [Authorize] attribute. I have successfully hit the user service to register a user, activate that user and obtain the bearer token. I am then passing this over in the request to the main service in the auth header. However, I always an Unauthorized response from the service.
I suspect this is related to machine keys again and have tried putting the same machine key into an App.config within the integration test project. This did not make any difference.
If anyone has managed to get this scenario working I would very much appreciate any advice on what could be causing the problem. I haven't pasted any code as there is quite a lot involved but would be happy to paste any specifics.
Testing in this way is super fast and really powerful. However, there seems to be a lack of info out there on getting this working.
EDIT
Here is some code as requested. The two in memory APIs are created in a OneTimeSetUp as follows:
_userApi = TestServer.Create<UserApi.Startup>();
_webApi = TestServer.Create<WebApi.Startup>();
The Authorized HttpClient is then taken from the API as follows:
var client = _webApi.HttpClient;
var authorizationValue = new AuthenticationHeaderValue("Bearer", token);
client.DefaultRequestHeaders.Authorization = authorizationValue;
"token" is obtained from the user service.
The GET is then performed using the client as follows:
var result = await client.GetAsync(uri);
EDIT 2
I should also point out that I have authenticated integration tests working within the User Service. The problem is only when I try to use both services together. E.g. Obtain token from User Service and use that token to authenticate against my main service. The in memory entity framework database is created as follows:
AppDomain.CurrentDomain.SetData("DataDirectory", Path.Combine(TestContext.CurrentContext.TestDirectory, string.Empty));
using (new ContractManagerDatabase())
{
System.Data.Entity.Database.SetInitializer(new DropCreateDatabaseAlways<ContractManagerDatabase>());
}

So after days of battling with this I have finally found a solution. The first thing I did was to move the integration tests back into the main Web API. Having taken a step back and thinking more about the problem I realised that by creating a separate solution and spinning up my user service and Web API in memory all I was really testing was Identity Framework and not the API itself.
I have integration tests working just fine in my user service for both unauthenticated and authenticated calls. It seemed to me that I should be able to do the same with the Web API and not rely on the user service.
I thought that I must be able to "bypass" the [Authorize] attribute to test the API. After some google searching, I found the following article which was most valuable in getting the solution working:
https://blogs.taiga.nl/martijn/2016/03/10/asp-net-web-api-owin-authenticated-integration-tests-without-authorization-server/
The key things for me were as follows:
The http client needed to be constructed differently for the test.
var client = new HttpClient(_webApp.Handler) {BaseAddress = new Uri("http://localhost")};
The call to GET had to be constructed differently for the test.
token = token == string.Empty ? GenerateToken(userName, userId) : token;
var request = new HttpRequestMessage(HttpMethod.Get, uri);
request.Headers.Add("Authorization", "Bearer " + token);
var client = GetAuthenticatedClient(token);
return await client.SendAsync(request);
The token needs to be generated as follows. I also needed to add a claim for the User Id as this is taken from the request within the controller.
private static string GenerateToken(string userName, int userId)
{
var claims = new[]
{
new Claim(ClaimTypes.Name, userName),
new Claim(ClaimTypes.NameIdentifier, userId.ToString())
};
var identity = new ClaimsIdentity(claims, "Test");
var properties = new AuthenticationProperties { ExpiresUtc = DateTime.UtcNow.AddHours(1) };
var ticket = new AuthenticationTicket(identity, properties);
var format = new TicketDataFormat(_dataProtector);
var token = format.Protect(ticket);
return token;
}
I hope this post helps others with similar issues and thanks to everyone who contributed.

Related

How can I access a mailbox with restricted permissions through EWS without interactive login?

We need to read out distribution lists from a contact folder of a dedicated exchange/outlook mailbox (O365). The process must run as a service with no user interaction.
Unfortunately the Graph API does not support distribution lists (not even the Graph beta version does). Because of this we have to use another API - I tried using EWS.
I succeeded by granting full_access_as_app permission to our service. However this allows to read and modify ANY data in ANY mailbox which is a security risk. Granting this permission only to read out some distribution lists from one mailbox is not acceptable.
So I tried to use the ROPC flow that should allow authenticating a user and then accessing the mailbox with the permissions of this user. I followed the information here: How to get OAuth2 access token for EWS managed API in service/daemon application
(Btw I found this post linked in the discussion here: https://github.com/microsoftgraph/microsoft-graph-docs/issues/5659 which has some more information about the topic.)
I exactly followed the steps mentioned above but unfortunately this is not working: I always get a “401 Unauthorized” exception when doing the EWS calls (OAuth calls succeed) and no additional information.
According to https://developer.microsoft.com/en-us/graph/blogs/upcoming-changes-to-exchange-web-services-ews-api-for-office-365/ this is no longer working. So how can I read out distribution lists from a specific mailbox without giving full access and without an interactive login?
EDIT
Here as requested the full code:
string[] ewsScopes = { "https://outlook-tdf-2.office.com/EWS.AccessAsUser.All" };
IPublicClientApplication clientApplication = PublicClientApplicationBuilder.Create(appId).WithAuthority(AzureCloudInstance.AzurePublic, tenantId).Build();
NetworkCredential credentials = new NetworkCredential(appUsername, appPassword);
AuthenticationResult authResult = await clientApplication.AcquireTokenByUsernamePassword(ewsScopes, credentials.UserName, credentials.SecurePassword).ExecuteAsync().ConfigureAwait(false);
ExchangeService exchangeService = new ExchangeService
{
Url = new Uri("https://outlook.office365.com/EWS/Exchange.asmx"),
Credentials = new OAuthCredentials(authResult.AccessToken),
};
ItemView view = new ItemView(int.MaxValue)
{
PropertySet = new PropertySet(ItemSchema.Id),
};
SearchFilter.IsEqualTo filter = new SearchFilter.IsEqualTo(ItemSchema.ItemClass, "IPM.Contact");
FindItemsResults<Item> ewsResult = await exchangeService.FindItems(WellKnownFolderName.Contacts, filter, view).ConfigureAwait(false);
I have also tried with other scopes such as "https://outlook.office.com/EWS.AccessAsUser.All" or "https://outlook.office365.com/EWS.AccessAsUser.All" but without success. I feel the problem might be related to the scope? I can see that the Exchange legacy API that was listed in the Azure UI when adding permissions is now gone...?
The Scope in your code is wrong (I'm not sure where you got that from) it should be
string[] ewsScopes = { "https://outlook.office.com/EWS.AccessAsUser.All" };
Using your code with the scope you had gives a 401 error and if you look at the response headers of the EWS response it actually tells you that the scope is the issue eg
2000003;reason="The audience claim value is invalid for current resource. Audience claim is 'https://outlook-tdf-2.office.com/', request url is 'https://outlook.office365.com/EWS/Exchange.asmx' and resource type is 'Exchange'.";error_category="invalid_resource"
Using your code with the correct scope works fine
But then why is the option to add the Exchange Legacy permission (step 6 in the MS docu mentioned above) gone from the Azure UI?
Its hasn't been removed from the AzureUI they have just moved all the Exchange legacy permissions (including the ones used in the Exchange Admin cmdlets which isn't really a legacy API) under the Graph Permissions. Why they did this way and didn't communicate it well (I'll only just saw it today as well) I'm not sure.
Yesterday Nov 19th Microsoft has updated the documentation: https://learn.microsoft.com/en-us/exchange/client-developer/exchange-web-services/how-to-authenticate-an-ews-application-by-using-oauth
Following the new documentation it works (again). The main difference is to use the shortened scope "EWS.AccessAsUser.All" and not any of the full scopes found in many examples and posts such as “https://outlook.office.com/EWS.AccessAsUser.All”, “https://outlook.office365.com/EWS.AccessAsUser.All” etc.
Thank you MS for wasting my time.
Distribution groups are only exposed in Exchange PowerShell today, and are not currently supported via the Microsoft Graph API.
Please vote on this feature request on UserVoice:

OWIN External OAuth providers - get user consent for new scopes

In our application we have following setup:
Web Application (Angular JS) + Web API
We support external logins Google & Facebook.
We have setup necessary infrastructure with default scopes and wired-up during the Startup. This works fine for login.
For additional features, say user wants to import his/her contacts from Google, we need to get consent again with new scopes. Can someone let me know how to do this?
One way is - to include all necessary scopes during login phase but we want to get consents only for required stuff at required time.
I googled but couldn't get any information on this.
In our specific case of importing contacts from Google a/c, I thought of specifically creating a new controller and start fresh authentication mechanism using GoogleAuthorizationCodeFlow (without OWIN middleware) as explained here https://developers.google.com/api-client-library/dotnet/guide/aaa_oauth but it is in the context of MVC but we need support for Web API. So any pointer is highly appreciated.
Finally got the solution.
Below links helped a lot; my solution is a combination of ideas from below links:
http://www.yogihosting.com/implementing-google-contacts-api-version-3-0-oauth-2-0-in-csharp-and-asp-net/
(note: Google OAuth links mentioned in this article, especially for token generation, have changed. For correct URLs, please see https://developers.google.com/identity/protocols/OAuth2WebServer)
https://www.themarketingtechnologist.co/google-oauth-2-enable-your-application-to-access-data-from-a-google-user/
Above links explain how to get access_token and authorization_code. Once we have those values, we can create UserCredentials that can be used for accessing specific Google APIs as mentioned in the below link:
Upload video to youtube with mvc application (all code behind)
Things to remember:
One ends up contacting Google authorization server twice (once for access token and once for authorization code). In both the cases, redirect_uri must be specified and IT IS IMPORTANT for that redirect_uri to be SAME in both the cases; otherwise it won't work. Also, it is important for that redirect_uri to be mentioned in the Google console where the project/application is defined and registered. I faced issues because of mismatched redirect_uri values and below link helped me in solving it:
Google API token endpoint POST returns Bad Request 400
While accessing Google API we need to include filters. In my case for People API, I needed to get email addresses and without required filters, it was always null/empty. So, below is the code for that:
var peopleService = new PeopleService(new BaseClientService.Initializer()
{
HttpClientInitializer = credentials,
ApplicationName = "MyApp",
});
var connList = peopleService.People.Me.Connections.List();
connList.RequestMaskIncludeField = "person.emailAddresses";
connList.PageSize = 500;
ListConnectionsResponse connectionsResponse = connList.Execute();
IList<Person> connections = connectionsResponse.Connections;
Without connList.RequestMaskIncludeField = "person.emailAddresses"; email address is always empty.
Hope this helps someone.

How to manage API side authorization for Google?

I'm responsible for the API side of our product. We have several different clients, from browsers to iPads to Chromebooks. Right now, all our authentication is done directly from the client to our API, with username & password.
I've inherited some code that does authentication using OAuth, with the usual username/password setup. So inside my OwinAuthConfig class, I have:
var oAuthAuthorizationOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Authenticate"),
Provider = new MyAuthorizationProvider(),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
AllowInsecureHttp = true
};
app.UseOAuthAuthorizationServer(oAuthAuthorizationOptions);
Then, through some dark magic, this connects up with my MyAuthorizationProvider class (which inherits OAuthAuthorizationServerProvider), and on login, this invokes the method:
public override Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{ ... }
where context contains the important stuff (Username and Password) which I can then use to authenticate the user, build his claims, create an AuthenticationTicket and this information then magically gets returned to the client with the access token etc.
All well and good.
Now I have a new requirement - to allow 3rd party authentication from Google. In this case, the client app (iOS/Android/whatever) does the authentication with Google, and they should just pass the token (and any other required info) to me on the API side. On my side I then need to re-authenticate the Google token, and get all the user info from Google (email, name, etc.), from which I should then again link that to our User table, build up the claims etc. and return a new token to the client, which will be used in all subsequent calls.
Being kinda new to the whole OWIN pipeline thing, I'm not sure exactly how to go about this. I could write a new GoogleAuthController, that just acts like any other controller, and have an API that accepts the Google token, and returns the new token and other info in the same format that the username/password authentication API does it. But 2 things are nagging at me:
I have this awkward feeling like this is the noobie way of doing things, reinventing the wheel, and really there's a super-cool magical way of hooking things together that I should rather be using; and
In MyAuthorizationProvider.GrantResourceOwnerCredentials(), I've got access to an OAuthGrantResourceOwnerCredentialsContext object, which allows me to validate my new AuthenticationTicket. If I'm doing this inside a plain vanilla controller, I have no idea how I would mark that ticket as validated.
Any clues, please?
EDIT I've seen the Google auth flow as described here. I'm still confused by how best to manage the process from the API side. The client will be obtaining the authorization code, and then calling the API with that auth code. I get that then I've got to take that auth code and convert it to a token by calling the Google API. (Or maybe that should be the client's responsibility?) Either way, I then need to use that token to go back to the Google API and get the user's name, email and avatar image, then I need to match up that email with my own database to identify the user and build up their claims. Then I need to return a new token that the client can use to connect to me going forward.
Let me be more specific about my questions, before my question is closed as "too broad":
When the client has completed authentication with the Google API, it gets back a "code". That code still needs to be converted into a token. Whose responsibility should that be - the client or the API? (I'm leaning towards making it the client's responsibility, if just for the reason of distributing the workload better.)
Whether the client is passing through a code or a token, I need to be able to receive it in the API. Should I just use a plain vanilla Controller to receive it, with an endpoint returning an object of type AuthenticationProperties, or is there some special OWIN way of doing this?
If I'm using a plain vanilla Controller, how do I validate my token? In other words, how do I get access to the OWIN context so that I can mark the AuthenticationTicket as validated?
How do I write an automated test that simulates the client side of the process? AFAICT, the authentication wants to have a user physically click on the "Allow" button to grant my app access to their identity stuff, before it will generate the auth code. In an automated test, I would want to pass username/password etc. all from code. How do you do that?
So I found a solution of my own. It's only slightly kludgy, doesn't require referencing any Google OWIN libraries, and best of all, reuses the code from my username/password authentication.
Firstly, I get the app to call the same Authenticate endpoint as I do for username/password, only with dummy credentials, and add in a "GoogleToken" header, containing the token.
In my authentication code, I check for the GoogleToken header, and if it exists, follow that code path to validate on the Google servers, get an email address, and link to my own User table. Then the rest of the process for building claims and returning a new API token follows the original path.
start here : https://developers.google.com/identity/protocols/OAuth2#basicsteps
This explains how oAuth2 works. So you receive a Google token, now you call Google and request the user's details. you will receive their email which is enough to authenticate them. You could store the token as they are valid for a while and you can keep reusing it for whatever you need until it expires or it is invalidated.
Check this discussion on the same subject :
How can I verify a Google authentication API access token?
if you need more info on how OAuth2 works I can point you to one of my own articles : https://eidand.com/2015/03/28/authorization-system-with-owin-web-api-json-web-tokens/
There's a lot to take in, but it sounds like you need to understand how these things work together. Hope this helps.
Update:
I don't have full access to your setup, but I hope that the following code might help you with using Google as ID provider. Please add the following code to your startup.auth.cs file.
var googleAuthOptions = new GoogleOAuth2AuthenticationOptions
{
ClientId = "ef4ob24ttbgmt2o8eikgg.apps.googleusercontent.com",
ClientSecret = "DAK0qzDasdfasasdfsadwerhNjb-",
Scope = { "openid", "profile", "email" },
Provider = new GoogleOAuth2AuthenticationProvider
{
OnAuthenticated = async ctx =>
{
//You can get the claims like this and add them to authentication
var tokenClaim = new Claim("GoogleAccessToken", ctx.AccessToken);
var emailClaim = new Claim("email", ctx.Email);
var claimsIdentity = new ClaimsIdentity();
claimsIdentity.AddClaim(tokenClaim);
claimsIdentity.AddClaim(emailClaim);
HttpContext.Current
.GetOwinContext()
.Authentication
.SignIn(claimsIdentity);
await Task.CompletedTask;
}
},
AuthenticationType = "Google"
};
app.UseGoogleAuthentication(googleAuthOptions);
This allows the Google to act as ID Provider and the OnAuthenticated gets called when the authentication is successful. You can get the claims out of it and use them to signin. Please let me know if this worked, if not give me more details about your setup (what kind of framework, client setup and may be more details about your setup in startup file).
Thank you.
Please see this link for details on how we can use Google as ID Provider. I am sure you might have looked at this link, but in case you missed it. If none of these links work for you please include specific details on where you are deviating from what is mentioned in the links.
I assume you have a different requirement than what is specified in those links. Hence, I will try to answer your questions individually. Please let me know if you have any further questions.
When the client has completed authentication with the Google API, it gets back a "code". That code still needs to be converted into a token. Whose responsibility should that be - the client or the API? (I'm leaning towards making it the client's responsibility, if just for the reason of distributing the workload better.)
Exchanging the code for access token is definitely the responsibility of the API as the token exchange involves sending the ClientId and Client Secret along with the code. Client secret is supposed to be saved on the server side (API) but not on the client
Whether the client is passing through a code or a token, I need to be able to receive it in the API. Should I just use a plain vanilla Controller to receive it, with an endpoint returning an object of type AuthenticationProperties, or is there some special OWIN way of doing this?
This should work seamlessly if you are using the Google provider as mentioned in the above links. If not, the endpoint should be an anonymous endpoint accepting the code and making a request to Google (may be by using HttpClient) to get the access token along with the profile object for user related information.
If I'm using a plain vanilla Controller, how do I validate my token? In other words, how do I get access to the OWIN context so that I can mark the AuthenticationTicket as validated?
You have to implement OnGrantAuthorizationCode as part of your MyAuthorizationProvider class. This gives access to the context to set validated to true.
How do I write an automated test that simulates the client side of the process? AFAICT, the authentication wants to have a user physically click on the "Allow" button to grant my app access to their identity stuff, before it will generate the auth code. In an automated test, I would want to pass username/password etc. all from code. How do you do that?
This can be achieved partially, but, with that partial test you can be sure of good test coverage against your code. So, you have to mock the call to the Google API and assume that you have retrieved a valid response (hard code the response you received from a valid manual test). Now test your code on how it behaves with the valid response. Mock the Google API cal for an invalid response and do the same. This is how we are testing our API now. This assumes that Google API is working fine and tests my code for both valid/ in-valid responses.
Thank you,
Soma.
Having gone through something like this recently, I'll try to answer at least some of your questions:
The client should be getting a token from Google, which you can pass unaltered through to the API:
function onSignIn(googleUser) {
var profile = googleUser.getBasicProfile();
var idToken = googleUser.getAuthResponse().id_token;
}
A plain vanilla Controller should do it. The client can subsequently post an object in there, containing at least that token plus the client id (might be useful to know where the request comes from) and even the providerUserId;
Unfortunately I'm not that familiar with the Owin stack
Fully end-to-end integration testing might be tricky, although you might achieve something through tools like Selenium, or some mocking tool. The API however should be testable just by posting some fake data to that vanilla controller, although you might have to rely on some sort of mock implementation when you get to validating that token through Google (although you could also validate it manually on the server, provided you get the Google public api key).

Authentication with OWIN and WsFederation for MVC, web api and signalR apps

For my company, I have to make a POC to check if we can use wsFederation authentication for our project, which has a MVC app, some webapi controllers, and some signalR hubs, all in differents projects.
We'd also like to use the OWIN authentication middleware in both the client apps and the Identity provider app.
I use Thinktecture Identity Server v2 as the Identity provider for a start (but we'll have to develop ou own at some point).
For the MVC app, it's pretty straight forward and it works fine, using a SAML2 token.
But now things get a bit more complicated as I'd like an authenticated user on the web app to be able to call a controller method from the web api app (which is different of the MVC one, remember), using ajax calls.
I've read many things about delegating and actAs tokens, but I'm a bit lost and don't where or how to start this part. Also, i can't find anything about delegation using OWIN authentication.
So my first question is : is it possible to achieve this ?
And then : could someone point me in the right direction?
I followed Vittorio Bertocci's instructions when I was working on it.
http://www.cloudidentity.com/blog/2013/01/09/using-the-jwt-handler-for-implementing-poor-man-s-delegation-actas/
A couple of notes about it, where it says JWTSecurityTokenHandler, it is now JwtSecurityTokenHandler. It is a small typo, but it is a good way to loose 15 minutes if you are not aware of it.
I was also not able to use the X509 FindByThumbprint section either. I think that I did not have my local certificate registered properly. Once I am at work tomorrow I will post what I had to change in order to get it to work.
Dominick Baier (http://leastprivilege.com/) also does a course on pluralsight called WebApi v2 Security that does a great job talking about how to inject into the security pipeline and setting up the web api project to handle this.
As another option, you could replace the TokenValidationHandler class that Vittorio uses with the Microsoft.Owin.Security.Jwt package and implement the following code in the Startup.cs file.
app.UseJwtBearerAuthentication(
new JwtBearerAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
AllowedAudiences = new[] { ConfigurationSettings.AppSettings["ida:Realm"] },
IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
{
new SymmetricKeyIssuerSecurityTokenProvider(
ConfigurationSettings.AppSettings["ida:ValidIssuer"],
ConfigurationSettings.AppSettings["ida:SymmetricKey"])
},
Provider = new OAuthBearerAuthenticationProvider
{
OnValidateIdentity = context =>
{
var identity = context.Ticket.Identity;
return System.Threading.Tasks.Task.FromResult<object>(null);
}
}
});

Creating an API based non-interactive process for creating users

Has anyone successfully created an non-interactive workflow. Basically, what I am trying to accomplish is this. From my CRM, I am trying to do a Valance API callout to my LMS that will create an LMS user when the user is created in the CRM.
This needs to occur without any "human interaction".
I am using the Valance Java SDK (https://github.com/Desire2Learn-Valence/valence-sdk-java) as a framework for my code, at at its core, am executing this Flow:
D2LAppContext DAC = new D2LAppContext(aid,key);
URL targetURL = new URL(protocol+endpt + actiontype);
URL authEP = DAC.createWebUrlForAuthentication(endpt , hport,
targetURL);
httprequest (Get) authEP (which returns a 200 status code - OK)
ID2LUserContext DUC = DAC.createUserContext(authEP, endpt, hport,
true); ( I have also tried this with passing the userid and userkey
generated from the API test application, based on logging in as an
interactive user.
URL userURL = DUC.createAuthenticatedUri(actiontype, 'GET');
httprequest (Get) userURL (which returns a 403 status code -
Forbidden)
Does anyone have a suggestion on how to accomplish this? One question I have is which userid and userkey is the API expecting? The one I use to login to the LMS with via the UI? Or something returned from the Authorization call?
Thanks!
Jim
This question is similar to a question that I asked recently:
Consuming the Valence API as an application
This is how we solved this problem over at Learning Objects.
Basically, what you need to do is create a service account (a user that your application will masquerade as) with the correct permissions needed to create a user on your D2L server. Then you must requisition a userId and userKey for that user by using one of the sample applications available on D2L's github repo, or their API test tool (kind of hack-ish, but it's the only way).
Once you have the userId and userKey it is only a matter of using them to create signed requests to your D2L server.

Resources