Turn AuthenticationResult into MVC session - asp.net-mvc

What?
I have an API that takes a username and password and can return an AuthenticationResult. I want to make use of that token in order to start a session within the MVC app. Here's the flow of thins:
User goes to MVC app
User gives password and username
MVC calls API and sends username and password
API returns token to MVC app
After step 4, what can I do to start a session within MVC.
Info on the scenario:
We want to leverage Azure Active Directory to keep all our user info there, but we want users to be able to register using any email, not just our domain. We plan to make an account for every user in AAD using a combination of their email and our domain(since account created in AAD have to belong to a domain). So if the user's email is john#gmail.com, AAD will store johnatgmailcom#ourdomain.onmicrosoft.com
Why?
We can't force a user to create an account using our domain.
We don't want users to see the AAD login screen
We don't want to store any sort of user account info in our DB
We plan to leverage AAD in the future within an iOS/Android apps so having authentication outside the MVC app would allow for this
This is the POC code that we have in our API that authenticates a user login request coming from the MVC app:
AuthenticationContext context = new AuthenticationContext("https://login.microsoftonline.com/cccccccc-cccc-cccc-cccc-ccccccccccccc/oauth2/token");
try
{
UserCredential uc = new UserCredential(userCredendtials.Username, userCredendtials.Password);
AuthenticationResult azureADToken = context.AcquireToken("https://graph.windows.net#" + "ourdomain.onmicrosoft.com", clientID, uc);
return Ok(azureADToken);
}
catch
{
// We didn't authenticate user, send not found - generic error, we can change this
return NotFound();
}
We then check what the result of the API call was within the MVC app to respond accordingly. The $1,000,000 question is how can we start and mange the MVC session as if we were using something like ASP.Net Identity using the token???
We can modify the API and the MVC app as needed but we do have the rewuirement of letting users create their account with any email and to store their info on AAD. If this is totally on the wrong path, any advice is appreciated!

The scenario you described is not supported and not recommended with Azure AD. If you want a white label experience, where uses can pick arbitrary usernames and you can fully customize the cred gathering page, please consider Azure AD B2C: http://blogs.technet.com/b/ad/archive/2015/09/16/azure-ad-b2c-and-b2b-are-now-in-public-preview.aspx

Related

How to use external login provider with a custom user store?

I am developing an ASP.NET MVC 5 Web application that allows user to authenticate using an external provider.
In Internet there are a lot of information teaching how to enable external login providers, but none teaches how to actually authenticate the user in the system when login returns to the MVC site. All information I have found assumes developer is using the default ASP.NET identity user store, so it tells nothing about what to do next.
For example, in Google, when user selects the Google account, the call returns to the site and run the method FindAsync of the custom user store, but, after that, how can I actually log the user in?
This is one my attempts to retrieve the user e-mail:
public Task<T> FindAsync(UserLoginInfo login)
{
var info = AuthenticationManager.GetExternalLoginInfoAsync().Result;
var emailClaim = externalIdentity.Result.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Email);
}
But GetExternalLoginInfoAsync does not belong to AuthenticationManager object.
I have read that Microsoft.AspNet.Identity.Owin should be included, but in my case, that NuGet package is already included, and of course, the corresponding using statement is present in the code.
Any help, please?

How do I test a Azure AD protected Web API in with Visual Studio Test Adapter?

I've created a multi tenant Web API that works just fine. Now I want to build a native client for testing. The Web API app is defined in one tenant. The test app is defined in another tenant that has given admin consent to the Web API.
I want to use the native app to authenticate with username and password in my (non-interactive) integration tests. I cannot use certificate/app-only authentication because I need a real user context.
Getting a token
var userCredential = new UserCredential("admin#clienttenant.onmicrosoft.com", "password");
var context = new AuthenticationContext("https://login.windows.net/common");
return context.AcquireToken("https://webapitenant.onmicrosoft.com/webApiResourceUri", testClientId, userCredential).AccessToken;
The problem with this code is that it doesn't work until I've given admin consent to the native app, even if the user is in the same tenant as the app registration.
Exception thrown:
'Microsoft.IdentityModel.Clients.ActiveDirectory.AdalServiceException' in Microsoft.IdentityModel.Clients.ActiveDirectory.dll
Additional information:
AADSTS65001: The user or administrator has not consented to use the application with ID 'nativeclientid'. Send an interactive authorization request for this user and resource.
Since tests aren't interactive I have to create a console application that uses the above code but with PromptBehaviour.Always. This will prompt me for username and password and show a consent form. After I give consent the tests that is using the same native app registration starts working.
Is there a way to accept the consent form without a interactive GUI?
At the moment there is no other way to write user consent without some sort of user experience. (Which makes sense right?)
If you use the Azure Management Portal, as an administrator of your tenant, all the apps you create should automatically be consented for the resources you selected. This is because the Azure Management Portal specifically will write those consent links as you save your client application.
If you use other portals or APIs to create your application, then you will need to consent to the application at least one time. You do not need to necessarily put prompt behavior on your application to get the consent screen. You can just generate the URL for signing into your application, which will also take you through the consent experience:
https://login.microsoftonline.com/<TenantID>/oauth2/authorize?client_id=<AppID>&response_type=code&redirect_uri=<RedirectURI>&resource=<ResourceURI>&prompt=admin_consent
Note that we added a "prompt=admin_consent" at the end which will consent to the application on-behalf of the whole tenant. With this kind of consent, you will only need to do it once per application to get it working.
I hope this helps!

Login via social (facebook, google etc.) on MVC and native mobile (Web API) on the same project

I'm developing an MVC web application (Asp.net C# - using AngularJS in the front).
Currently, the users can log in to the site using username & password or oauth providers (such as google and Facebook).
We have developed a native mobile application that uses our asp.net web-api.
We want the users will be able to login using the same credentials to the mobile app and the website.
In order to do so, both projects (web-api & web application) are using the same DB with ASP.Net Identity. For example, if the user has created an account using username & password, he can log in on both platforms.
The problem is with external providers (Facebook, google...), when a user create an account on the web view the identity saves the users on the DB with a specific provider key (on the AspNetUserLogins table).
And when the user login (or register) using the mobile app I only have the user token, and I don't know how to log the user in.
Then I've found this post:
WebApi ASP.NET Identity Facebook login
which explains exactly what I've needed, only now I have 2 problems:
Using the user token (from the mobile login) I retrieve his user ID and save it on the AspNetUserLogins table, but when using the web application it saves a different user id, actually it calls that a Provider Key.
(Minor problem) For some reason, using the above link code, I don't get the user email but only his Facebook token and Facebook user id.
Please note,
* I want to use the native approach and can't use a web view on the mobile app because I want the app to use the user Facebook/google native application.
* Also read this: ASP.NET Identity in Microservice Architecture it didn't work.
Thanks in advance!
Shaul
OK, I figured out the answers:
Turns out that the provider key is actually the app specific user id, i.e the user has a specific user id for each Facebook app.
In order to get the Email you should add a specific request for it in the scope in the startup.auth.cs:
var facebookAuthenticationOptions = new FacebookAuthenticationOptions()
{
AppId = DEFINITIONS.FACEBOOK_ID,
AppSecret = DEFINITIONS.FACEBOOK_SECRET,
Provider = new Microsoft.Owin.Security.Facebook.FacebookAuthenticationProvider()
{
// This is for saving the data as user claims
OnAuthenticated = (context) =>
{
context.Identity.AddClaim(new System.Security.Claims.Claim("urn:facebook:access_token", context.AccessToken, null, "Facebook"));
context.Identity.AddClaim(new System.Security.Claims.Claim("urn:facebook:email", context.Email, null, "Facebook"));
return Task.FromResult(0);
}
}
};
// This will help you get the Email
facebookAuthenticationOptions.Scope.Add("email");
app.UseFacebookAuthentication(facebookAuthenticationOptions);

Spring security with login with amazon

We had an application that uses basic auth (user and password), we created the roles in the Database and it worked perfectly.
Now we are migrating to use Login With Amazon and we will also add Login With Facebook.
Is there an easy way to keep our current Spring Security architecture (I do not need the user password anymore though, I would like to keep the roles) and add support to Login With Amazon.
I noticed there is this project for Spring Security with oAuth2 http://projects.spring.io/spring-security-oauth/ It is not clear to me what this does.
The documentation for Spring oAuth is pretty bad, but the examples they provide are workable if you know your way around Spring Security.
I want to to flag here that using oAuth 2 for user authentication isn't recommended and that you should stick to providers that support Open ID Connect. It is not clear to me from a quick glance at Login With Amazon if it's OIDC or just plain oAuth2, but either way you can use their SDK to achieve what you're looking for.
It is definitely possible to use Spring oAuth to set up login over oAuth 2, and probably OIDC as well, but I always end up rolling my own solution because I find it simpler.
I'm going to focus on Login with Amazon here, but login with Facebook should be fairly similar.
There are four things you need to do:
Add Amazon login to your website
Implement support for storing a user's external ID in the user profile
Implement a controller for handling JWT tokens and "logging in" the user
Call the new controller once the user has logged in
The first one should be fairly easy and is detailed in Amazon's documentation
The second one is entirely up to you since it depends on your database solution, but if you're limiting yourself to one or two providers the easiest way is probably to just add the attribute amazonId to the user profile.
The third one will look something like this:
#Controller
public class OidcController {
#Autowired
private ObjectMapper objectMapper;
#RequestMapping("/login/amazon")
public String handleAmazonOidc(#RequestParam String access_token) {
// verify that the access token belongs to us
Content c = Request.Get("https://api.amazon.com/auth/o2/tokeninfo?access_token=" + URLEncoder.encode(access_token, "UTF-8"))
.execute()
.returnContent();
Map<String, Object> m = (Map<String, Object>)objectMapper.readValue(c.toString(), Map.class);
if (!"YOUR-CLIENT-ID".equals(m.get("aud"))) {
// the access token does not belong to us
throw new InvalidTokenException("Invalid token");
}
// Get the user ID from Amazon and get the corresponding user on our end
m = new ObjectMapper().readValue(c.toString(), new TypeReference>(){});
String amazonUserId = m.get("user_id");
User user = getUserFromAmazonId(amazonUserId);
if (user == null) {
// User needs to register first
return "redirect:/register";
}
// Authenticate the user in the current security context
PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(user, token, user.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(token);
// Redirect the user to the front page
return "redirect:/";
}
}
Fourth one is easy, just redirect the user in the callback in the UI:
amazon.Login.authorize(options, 'https://www.example.com/login/amazon');
Now, you're in some trouble since you've already got users in the database that hasn't got an Amazon user ID to look for so you'll probably have to run both authentication schemes in parallel for a while and allow users to connect with Amazon while they're logged in with username and password. Modifying the above controller to insert the Amazon user ID in the current user profile instead of setting it in the security context should be fairly straightforward though.

ASP.NET MVC: Authenticating a user without creating the user

I am creating an MVC 5 application that authenticates a user via Facebook to access a particular resource.
I want to be able to authenticate this user with a cookie as if they are logged into my site as an authenticated user manually.
However, it is a requirement that this user not be actually created as a user in the membership system used by the site, as it is reserved for admins.
Is this possible?
I attempted to do this by creating an identity manually but this was a total hack attempt by piecing together the existing code from the MVC Account controller...
var authUser = new ApplicationUser() { UserName = me.email, Email = me.email };
var manager = HttpContext.GetOwinContext().Authentication;
manager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
manager.SignIn(new AuthenticationProperties() { IsPersistent = true }, await HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>().CreateIdentityAsync(authUser, DefaultAuthenticationTypes.ApplicationCookie));
It almost seems to work, but then it complains that the user id does not exist.
Which I assume means it's attempting to use my user database to authenticate the user, which as I said is not what we want.
So is it possible to manually authenticate a user with the custom default asp.net account implementation and persist an authenticated cookie, but NOT create an actual user?
please let me know what more info I could provide to help find a solution. thank you
shortly after submitting this I found this question: Storing/Retrieving user data without database when using OWIN cookie authentication
which seems to do exactly what I need, indeed this did the trick, thanks and sorry for duplicating the question

Resources