Adding minimal OWIN Identity Authentication to an Existing ASP.NET MVC Application
Note: This article applies to classic .NET Frameworks based ASP.NET MVC applications only. It does not work with .NET Core.
As of ASP.NET 4, ASP.NET provides a fairly useful identity system. If you create a new project and choose an MVC project and choose to add both internal and external authentication, it’s fairly straight forward to get a reasonable identity implementation into your application.
However, if you have an existing application, or if the full Entity Framework based identity structure doesn’t work for you, then the process to hook up a minimal and custom implementation that uses your own domain/business model and classes is not exactly as straightforward. You have to either rip out the pieces you don’t need from an full template install, or add the necessary pieces. In this post I hope I can show you how to do the latter, showing only the pieces that you need.
The process is not necessarily hard – but it’s not very well documented. It’s difficult to find the information necessary to just create the necessary handlers that can deal with linking accounts to external oAuth providers like Google, Facebook, GitHub and so on. So in this post I’ll go over this scenario.
A Real World Use Case
I’ve come down this path myself over the last weekend as one of my old sites – CodePaste.net – required an update to the old OpenID authentication I had running on that site. Codepaste is a pretty old site; in fact it is was the first MVC application I ever built 😃. But it’s been running unencumbered for the last 7 years. That is until Google decided to pull the plug on the old OpenId implementation. Promptly I started getting a ton of emails, and decided I had to re-implement the external providers.
The old implementation used DotNetOpenAuth combined with FormsAuthentication which I blogged about years ago, but DotnetOpenAuth seems to have fallen out of favor over the years, so I decided to bite the bullet and switch over to the newer, built-in OWIN based Identity functionality in ASP.NET 4.
When you first start looking at Identity the amount of information out there is rather overwhelming. Lots of intro articles that talk about how to use the stuff ‘as is’ without customization. But there’s not a lot of information on using the core Identity pieces without the full bore UserManager and Entity Framework data store, in order to use just the Authentication/Authorization on their own and integrate them with my own business objects/domain model.
Just the Core Identity Features for Local and External Logins
The goal of this post is to show the minimal pieces of the OWIN Identity system to handle Local and External account logins and hook them to a custom domain model rather than using the Entity Framework based UserManager. In short focus on the system components that manage authentication and leave the user management to the application.
Just Show me what I need!
The main text of this article is lengthy as it covers the CodePaste.NET login and user management features relevant to the discussion in addition to the core login features. For those of you that want just the nuts and bolts there’s also a summary section at the end that shows just the relevant code in skeleton format that you can just plug into your controllers. I’ve also linked to the full source code of the Account Controller I discuss here if you want to see the full code, rather than bite-sized snippets.
For more detail… read on.
OWIN based Identity in ASP.NET MVC 5
If you haven’t used the new Identity features in ASP.NET MVC 5 at all, I suggest that you check it out first by creating a new MVC Web site and letting it create a default Web site with Individual User Authentication enabled. This will let you see how the default implementation works. If you’re looking at code specific implementation details you can check out the AccountController and ManageController which deal with the user management interfaces. AccountController deals with logging in for both local and external accounts, while ManageController deals with setting up new accounts, email confirmations etc. This is part of the reason why Identity feels pretty overwhelming – when you look at the code there’s a ton of stuff going on and it’s all tightly intermixed with the specific Entity Framework based implementation.
Nevertheless I encourage you to at least take a brief glance at it, maybe stepping through to understand the overall flow of how logins are processed.
Extracting just the necessary Pieces
In order to use the OWIN Idenity pieces in your own application that doesn’t use the EF based UserManager, you’ll have to do a few things. Again, my goal as part of this article is to:
- Support Cookie based User Logins (username/password)
- Support External Logins (Google, Github, Twitter)
- Support ability to create Accounts using both local or external Accounts
- Support logging in with either local or external accounts
In order to accomplish this with an existing application I have to:
- Turn off IIS Authentication for the Application
- Add the appropriate NuGet Packages
- Implement the Account Registration for Local and External Accounts
- Implement Logging in for Local and External Accounts
- Allow for signing in using the OWIN based Login mechanism
Let’s take a look and see what each of these steps look like.
Turn off IIS Authentication for the Application
ASP.NET Identity works using the OWIN platform which is a custom subsystem that doesn’t rely on standard IIS security. Because OWIN can be self hosted there’s no dependency on IIS in this system. On IIS OWIN plugs into the IIS pipeline using a few dynamically inject modules but it essentially completely takes over the Authentication/Authorization process for your application. So, in order to use ASP.NET Identity the first thing you have to do is actually turn off standard IIS authentication in web.config for your application.
<system.web>
<authentication mode="None" />
<system.web>
I did not do this initially and it took me a while to figure why my application kept throwing up browser authentication dialogs instead of navigating to my login page or sending me off to an external provider login. Make sure standard authentication is off!
Adding NuGet Packages
If you have an existing project that doesn’t have any identity features installed you’ll need to get the right assemblies into your project. If you look at a newly created MVC project and the litany of assemblies in there it’s not obvious what’s actually required to get just the basic features.
Luckily, thanks to the power of NuGet, to get just the core identity features you can add just the following packages to get all the references you needed for the core identity features plus a few of the external providers needed.
- Microsoft.Owin.Host.SystemWeb
- Microsoft.Owin.Security.Cookies
The first two packages are enough to light up the OWIN identity framework. If you’re not running on IIS use Microsoft.Owin.Host.SelfHost instead of the SystemWeb host.
You’ll also need to install the the specific external provider packages:
- Microsoft.Owin.Security.Google
- Microsoft.Owin.Security.Twitter
- Owin.Security.Providers
The provider packages add support for specific external providers you can log in with. The Owin.Security.Providers package is a third party library that includes a ton of additional providers you can integrate with and that’s what I used to support GitHub logins, since this is a developer focused site.
Startup Class: Provider Configuration
The next step is to configure OWIN pipeline to actually handle the various login solutions. To do so you have to create a Startup class that configures the various authentication mechanisms. In my case I support Local user authentication (Cookie Auth) as well as external providers for Google, Twitter and GitHub.
Here’s the config code:
namespace CodePasteMvc
{
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
}
// For more information on configuring authentication, please visit http://go.microsoft.com/fwlink/?LinkId=301864
public void ConfigureAuth(IAppBuilder app)
{
// Enable the application to use a cookie to store information for the signed in user
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/LogOn")
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
// App.Secrets is application specific and holds values in CodePasteKeys.json
// Values are NOT included in repro – auto-created on first load
if (!string.IsNullOrEmpty(App.Secrets.GoogleClientId))
{
app.UseGoogleAuthentication(
clientId: App.Secrets.GoogleClientId,
clientSecret: App.Secrets.GoogleClientSecret);
}
if (!string.IsNullOrEmpty(App.Secrets.TwitterConsumerKey))
{
app.UseTwitterAuthentication(
consumerKey: App.Secrets.TwitterConsumerKey,
consumerSecret: App.Secrets.TwitterConsumerSecret);
}
if (!string.IsNullOrEmpty(App.Secrets.GitHubClientId))
{
app.UseGitHubAuthentication(
clientId: App.Secrets.GitHubClientId,
clientSecret: App.Secrets.GitHubClientSecret);
}
AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.NameIdentifier;
}
}
}
This is an implementation of an OWIN startup class. When the OWIN runtime fires up, it looks via Reflection for a class named Startup class with a Configuration(IAppBuilder app) method and when it finds it executes that method.
ConfigureAuth() then configures Cookie Authentication for local logins and the external providers for Google, Twitter and Github. I use a custom ApplicationConfiguration class to hold my secret values in a JSON file using my ApplicationConfiguration class (mainly to keep them out of the the GitHub repo), but you can either hardcode these values or read them from a configuration setting.
Each provider requires what amounts to an application ID and a secret key that you have to configure. In order to use external providers you have to create an application for each at the providers Developer Web site and then pick up the keys these applications generate. Here are links to each of those (assumes you’re logged in to each site):
Note that you can pick and choose your providers. You can use only Cookie authentication, or only externals or both in combination as I’m doing on this site.
Customizing the AccountController
All of the code I’ll be discussing in relation to these logins is located in an MVC controller – specifically the AccountController. There are really 3 main set of features that need to implemented – local logins, external logins and the actual sign in/out operations. Here’s roughly what this looks like in AccountController class:
There are also a few more traditional Controller actions that deal with some of the supporting feature of my implementation – password recovery and account activation specifically. This is my entire implementation, so as you can see it’s considerably simpler – and less featured – than the full (overkill?) Identity implementation you get in a default full features MVC site which is perfect for this post.
Signing in and Out
I’m going to start with signing in and out first, because it’s a core feature that will be used by all the the others operations. A user is signed in whenever either a local or external login succeeds, and this process essentially creates the authentication Cookie that identifies the user and allows the Identity framework to figure out whether the user is already logged in and setup the User Principal object for each request.
If you’ve used FormsAuthentication in ASP.NET you know that there’s a global object that handles management of the user tracking cookie that associates a user with an account. OWIN has its own version of an authentication manager in the IAuthenticationManager interface which is attached to the HttpContext object. To get a reference to it you can use:
HttpContext.GetOwinContext().Authentication;
This object handles creation and deleting of the secure cookie that is used to track the user through the site. The identity cookie is used to track all logged in users, regardless of whether they logged in locally with a username and password or using an external provider like Google. Once a user is authenticated, the SignIn method is called to create the cookie. On subsequent requests, OWIN based Identity subsystem then picks up the Cookie and authorizes the user the appropriate IPrinciple (a ClaimsPrinciple with a ClaimsIdentity) based User whenever the user accesses your site.
Identity sign-ins work with ClaimsIdentity objects, which contains user information stored in claims. The claims provide the user ID and names and any other information you want to store with the authenticated user as cached state.
To simplify sign-in I use a couple of helper functions that look like this:
public void IdentitySignin(AppUserState appUserState, string providerKey = null, bool isPersistent = false)
{
var claims = new List<Claim>();
// create required claims
claims.Add(new Claim(ClaimTypes.NameIdentifier, appUserState.UserId));
claims.Add(new Claim(ClaimTypes.Name, appUserState.Name));
// custom – my serialized AppUserState object
claims.Add(new Claim("userState", appUserState.ToString()));
var identity = new ClaimsIdentity(claims, DefaultAuthenticationTypes.ApplicationCookie);
AuthenticationManager.SignIn(new AuthenticationProperties()
{
AllowRefresh = true,
IsPersistent = isPersistent,
ExpiresUtc = DateTime.UtcNow.AddDays(7)
}, identity);
}
public void IdentitySignout()
{
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie,
DefaultAuthenticationTypes.ExternalCookie);
}
private IAuthenticationManager AuthenticationManager
{
get { return HttpContext.GetOwinContext().Authentication; }
}
SignIn/SignOut
The key methods are SignIn() and SignOut() on the AuthenticationManager, which create or delete the application cookies on the executing request. SignIn() takes an Identity object that includes any claims you have assigned to it. This identity is what you also get back once the user is logged in and you look at Context.User.Identity later to check for authorization.
A Word about AppUserState
Note I’m using an application specific AppUserState class to represent the logged in user’s state which is then added to the Identity’s claims! This is a custom object in my application that basically holds basic User that are the bare minimum needed by the application to display user info like name, admin status, theme etc. This object is persisted and cached inside of the ClaimsIdentity claims and therefore in the cookie, so that the data is available without having to look up a user in the database for each request.
Above I show how the AppUserState is persisted in the identity cookie. For retrieval and storage in a property on the controller, I have a base controller that has an internal AppUserState property that is loaded up from a valid ClaimsPrincipal when a request comes in in BaseController.Initialize():
protected override void Initialize(RequestContext requestContext)
{
base.Initialize(requestContext);
// Grab the user's login information from Identity
AppUserState appUserState = new AppUserState();
if (User is ClaimsPrincipal)
{
var user = User as ClaimsPrincipal;
var claims = user.Claims.ToList();
var userStateString = GetClaim(claims, "userState");
//var name = GetClaim(claims, ClaimTypes.Name);
//var id = GetClaim(claims, ClaimTypes.NameIdentifier);
if (!string.IsNullOrEmpty(userStateString))
appUserState.FromString(userStateString);
}
AppUserState = appUserState;
ViewData["UserState"] = AppUserState;
ViewData["ErrorDisplay"] = ErrorDisplay;
}
The net effect is that anywhere within Controller and most Views this AppUserState object is available for user info display or visual display options.
This approach made great sense when I was using FormsAuthentication because you could effectively only store a single string value which was the serialized AppUserState value. But now ClaimsIdentity can contain multiple values as claims explicitly as a dictionary so it may be much cleaner to simply store any values as claims on the ClaimsIdentity. In the future the AppUserState code could probably be abstracted away, using a custom ClaimsIdentity instead that knows how to persist and retrieve its state from the attached claims instead. I’ll leave that exercise for another day though because AppUserState is used widely in this application.
Either way I want to make it clear that AppUserState is a custom implementation and an application specific implementation detail for my application.
Local Logins using Cookies
There are two steps for each login type: Creating the user and account initially, and then actually logging in the user. So lets start with the registration process for local logins.
Here’s what the User Registration form looks like:
The top of the form holds the local login, while the bottom has the external provider logins. From this form you can choose to sign up with an external login, which fills the local user registration form with any data that might be received from the external provider. Google provides the email address, GitHub both email and name, and Twitter provides only the name for example. In either case a new user is created in the application.
The code that responds to the Register button essentially creates a new account (if there are no validation errors), and signs the user in.
Here’s what the local user login code looks like (keep in mind this application specific):
AcceptVerbs(HttpVerbs.Post)]
[ValidateAntiForgeryToken]
public ActionResult Register(FormCollection formVars)
{
string id = formVars["Id"];
string confirmPassword = formVars["confirmPassword"];
bool isNew = false;
User user = null;
if (string.IsNullOrEmpty(id) || busUser.Load(id) == null)
{
user = busUser.NewEntity();
user.InActive = true;
isNew = true;
}
else
user = busUser.Entity;
UpdateModel<User>(busUser.Entity,
new string[] { "Name", "Email", "Password", "Theme" });
if (ModelState.Count > 0)
ErrorDisplay.AddMessages(ModelState);
if (ErrorDisplay.DisplayErrors.Count > 0)
return View("Register", ViewModel);
if (!busUser.Validate())
{
ErrorDisplay.Message = "Please correct the following:";
ErrorDisplay.AddMessages(busUser.ValidationErrors);
return View("Register", ViewModel);
}
if (!busUser.Save())
{
ErrorDisplay.ShowError("Unable to save User: " + busUser.ErrorMessage);
return View("Register", ViewModel);
}
AppUserState appUserState = new AppUserState();
appUserState.FromUser(user);
IdentitySignin(appUserState, appUserState.UserId);
if (isNew)
{
SetAccountForEmailValidation();
ErrorDisplay.HtmlEncodeMessage = false;
ErrorDisplay.ShowMessage(@"Thank you for creating an account...");
return View("Register", ViewModel);
}
return RedirectToAction("New","Snippet");
}
This code really isn’t very different from how we used to do it previously. You check to see if the saved is a user we already have in the system and if so update her, or else create a new one and update the new user instead.
The key item here that is different is simply that I set the AppUserState as described before and then create call IdentitySignIn() to authenticate that user. On subsequent hits I then get a Context.User with the ClaimsIdentity for that user.
Logging in a Local Account
Once an account is registered you can actually log in. Here’s what the login form looks like:
The code to handle the login looks like this:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult LogOn(string email, string password, bool rememberMe, string returnUrl, bool emailPassword)
{
if (emailPassword)
return EmailPassword(email);
var user = busUser.ValidateUserAndLoad(email, password);
if (user == null)
{
ErrorDisplay.ShowError(busUser.ErrorMessage);
return View(ViewModel);
}
AppUserState appUserState = new AppUserState()
{
Email = user.Email,
Name = user.Name,
UserId = user.Id,
Theme = user.Theme,
IsAdmin = user.IsAdmin
};
IdentitySignin(appUserState, user.OpenId, rememberMe);
if (!string.IsNullOrEmpty(returnUrl))
return Redirect(returnUrl);
return RedirectToAction("New", "Snippet", null);
}
Again as you may guess, the code here simply looks up the username and password and if valid, updates the AppUserState object and then calls IdentitySignin() to log the user in.
Both of these workflows are not different at all from what I used to do with FormsAuthentication. The only real difference is that I’m calling IdentitySignin() instead of FormsAuthentication.Authenticate().
Now on to the fun stuff that you couldn’t easily do before – external logins.
Linking to an External Login
As you can see in the screen shots above both the registration and login forms support using external providers to handle authentication of an account. For the registration form the external login can either be performed at the beginning of registration to pre-fill the registration information from the external provider, or by attaching the external login to an existing local account.
For external logins, when you click any of the provider buttons you are redirected to the provider site (Google, Twitter, GitHub), which checks whether your account is logged in. If it isn’t you’re shuttled to the providers log in page on their server where you can log in and/or specify what kind of rights you want to give to the application that is requesting the login (ie. my site). After you click accept, the server fires a callback request on your server and provides the Claims that the provider makes available. Typically this is a provider key (an identifier for your user’s login) as well as either name or email or both.
External logins are handled via an OAuth2 flow that is managed internally by the OWIN authentication pipeline in ASP.NET. When the requests are fired off they include a callback url which fires first into the OWIN handler. The callback URL is /signin-google or /signin-twitter or /signin-github.
Both creation of an initial account link between a local account and the external account as well as logging has a two endpoint request flow: One to actually start the remote authentication process via a Challenge operation (which is a Redirect really), and one to receive the callback when the authentication is complete.
Here’s the first method that initiates the Linking of an External Account by Challenging and Redirecting to the provider:
[AllowAnonymous]
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> ExternalLinkLogin(string provider)
{
// Request a redirect to the external login provider to link a login for the current user
return new ChallengeResult(provider, Url.Action("ExternalLinkLoginCallback"), AppUserState.UserId);
}
ChallengeResult is a helper class that is part of the stock ASP.NET MVC default controller implementation from which I simply copied it:
private const string XsrfKey = "CodePaste_$31!.2*#";
public class ChallengeResult : HttpUnauthorizedResult
{
public ChallengeResult(string provider, string redirectUri)
: this(provider, redirectUri, null)
{ }
public ChallengeResult(string provider, string redirectUri, string userId)
{
LoginProvider = provider;
RedirectUri = redirectUri;
UserId = userId;
}
public string LoginProvider { get; set; }
public string RedirectUri { get; set; }
public string UserId { get; set; }
public override void ExecuteResult(ControllerContext context)
{
var properties = new AuthenticationProperties { RedirectUri = RedirectUri };
if (UserId != null)
properties.Dictionary[XsrfKey] = UserId;
var owin = context.HttpContext.GetOwinContext();
owin.Authentication.Challenge(properties, LoginProvider);
}
}
The key of this class is the OWIN Authentication.Challenge() method which issues a 302 Redirect to the provider to handle the login with a URL that includes the Redirect URL and some state information. The state in this case is an a user identifier (our user id in this case) that lets us check and make sure the result is the one we’re interested in when it comes back.
When the provider has validated (or failed to validate) the user, it makes callback to your server using a specific URL. The URL that is called back to is ~/signin-google or ~/signin-github or ~/signin-twitter for example. The OWIN pipeline internally handles this callback for you and after it has validated redirects to your actual endpoint so you can handle the authenticated request.
To illustrate check out this Fiddler trace of a Registration Link request, when already logged into my Google account:
Notice all the 302 requests. The first request is initiated by your code using the ChallengeResult which redirects to Google. Google then redirects back to the OWIN internal endpoint to handle the provider OAuth parsing and finally the OWIN pipeline calls into your code with ExternalLinkLoginCallback().
Here’s the Callback method that is called when the Link process is complete:
[AllowAnonymous]
[HttpGet]
public async Task<ActionResult> ExternalLinkLoginCallback()
{
// Handle external Login Callback
var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync(XsrfKey, AppUserState.UserId);
if (loginInfo == null)
{
IdentitySignout(); // to be safe we log out
return RedirectToAction("Register", new {message = "Unable to authenticate with external login."});
}
// Authenticated!
string providerKey = loginInfo.Login.ProviderKey;
string providerName = loginInfo.Login.LoginProvider;
// Now load, create or update our custom user
// normalize email and username if available
if (string.IsNullOrEmpty(AppUserState.Email))
AppUserState.Email = loginInfo.Email;
if (string.IsNullOrEmpty(AppUserState.Name))
AppUserState.Name = loginInfo.DefaultUserName;
var userBus = new busUser();
User user = null;
if (!string.IsNullOrEmpty(AppUserState.UserId))
user = userBus.Load(AppUserState.UserId);
if (user == null && !string.IsNullOrEmpty(providerKey))
user = userBus.LoadUserByProviderKey(providerKey);
if (user == null && !string.IsNullOrEmpty(loginInfo.Email))
user = userBus.LoadUserByEmail(loginInfo.Email);
if (user == null)
{
user = userBus.NewEntity();
userBus.SetUserForEmailValidation(user);
}
if (string.IsNullOrEmpty(user.Email))
user.Email = AppUserState.Email;
if (string.IsNullOrEmpty(user.Name))
user.Name = AppUserState.Name ?? "Unknown (" + providerName + ")";
if (loginInfo.Login != null)
{
user.OpenIdClaim = loginInfo.Login.ProviderKey;
user.OpenId = loginInfo.Login.LoginProvider;
}
else
{
user.OpenId = null;
user.OpenIdClaim = null;
}
// finally save user inf
bool result = userBus.Save(user);
// update the actual identity cookie
AppUserState.FromUser(user);
IdentitySignin(AppUserState, loginInfo.Login.ProviderKey);
return RedirectToAction("Register");
}
There’s a fair bit of code in this snippet, but… most of the code is application specific. The most important piece of the code is the first line that is responsible for returning the LoginInfo object that contains the providerKey and any other claims that the provider makes available. Typically those are a name and/or email. The rest of the code then checks to see if the user already exists and updates it or if not creates a new one and saves it. If all goes where IdenitySignin() is called to effectively log the user in by creating the cookie and authorized ClaimsIdentity on subsequent requests.
When done the user is logged in, but I want to re-display the Registration form to show the external account registration:
Logging in with an External Login
Finally we still have to hook up the logic to log in with an external provider. As with the Linking of the provider, this is a two step process – fire off the initial authentication request. As with the link operation a challenge request handles this:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult ExternalLogin(string provider)
{
string returnUrl = Url.Action("New","Snippet",null);
return new ChallengeResult(provider, Url.Action("ExternalLoginCallback",
"Account", new { ReturnUrl = returnUrl }));
}
Again the same 302 requests are issued to eventually bring back the result into the OWIN pipeline which in turn redirects to the ExternalLoginCallback() method:
[AllowAnonymous]
public async Task<ActionResult> ExternalLoginCallback(string returnUrl)
{
var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();
if (loginInfo == null)
return RedirectToAction("LogOn");
// AUTHENTICATED!
var providerKey = loginInfo.Login.ProviderKey;
// Aplication specific code goes here.
var userBus = new busUser();
var user = userBus.ValidateUserWithExternalLogin(providerKey);
if (user == null)
{
return RedirectToAction("LogOn", new
{
message = "Unable to log in with " + loginInfo.Login.LoginProvider +
". " + userBus.ErrorMessage
});
}
// store on AppUser
AppUserState appUserState = new AppUserState();
appUserState.FromUser(user);
IdentitySignin(appUserState, providerKey, isPersistent: true);
return Redirect(returnUrl);
}
The process is similar to the link operation in the previous example except the login code simply checks to see if the user is valid and if so logs him in using the now familiar IdentySignin() method.
The user is logged in at this point so simply redirect where we want to go.
Unlink an Account
Finally if you want to unlink the account, all I have to do is remove the link in the domain model. In my case I remove the field values for OpenId and OpenIdClaim, so that any subsequent login using an external account will fail as we won’t match the provider key provided by the external provider.
Here’s the code for the unlink operation:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult ExternalUnlinkLogin()
{
var userId = AppUserState.UserId;
var user = busUser.Load(userId);
if (user == null)
{
ErrorDisplay.ShowError("Couldn't find associated User: " + busUser.ErrorMessage);
return RedirectToAction("Register", new { id = userId });
}
user.OpenId = string.Empty;
user.OpenIdClaim = string.Empty;
if (busUser.Save())
return RedirectToAction("Register", new { id = userId });
return RedirectToAction("Register", new { message = "Unable to unlink OpenId. " + busUser.ErrorMessage });
}
After this code executes, the external account link is gone. I show the register page again and this time the external link account is no longer shown replaced again by the three external providers one of which can be hooked up.
And that’s all the base operations!
A quick Review
You may think that that’s a lot of code you have to write for something that should be pretty simple. But keep in mind that this code includes some application specific logic that I provided here in order to provide a somewhat realistic context. While the actual underlying Identity code is pretty small (and I’ve highlighted the core requirements in the snippets in bold), the code shown here is probably the bare minimum you need for a basic self managed user management implementation you probably want to run in a real application.
If you want to see the complete controller code you can check it out on Github.
In summary you basically deal with:
IdentitySignIn, IdentitySignOut for standard LogIn/LogOut functions
Implementing ExternalLinkLogin() and ExternalLinkLoginCallback()
Implementing ExternalLogin() and ExternalLoginCallback()
The various ExternalXXXX methods follow a simple boilerplate of using ChallengeResult for the initial request and calling GetExternalLoginInfoAsync() to pick up the result data. It’s really fairly straight forward as long as you know the pieces you actually need to implement.
Minimal Code Summary
Because the code above is quite lengthy, here’s a summary of just the relevant parts you have to implement with just the basic pieces:
Startup Configuration Class
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
}
// For more information on configuring authentication, please visit http://go.microsoft.com/fwlink/?LinkId=301864
public void ConfigureAuth(IAppBuilder app)
{
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
// Enable the application to use a cookie to store information for the signed in user
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/LogOn")
});
// these values are stored in CodePasteKeys.json
// and are NOT included in repro - autocreated on first load
if (!string.IsNullOrEmpty(App.Secrets.GoogleClientId))
{
app.UseGoogleAuthentication(
clientId: App.Secrets.GoogleClientId,
clientSecret: App.Secrets.GoogleClientSecret);
}
if (!string.IsNullOrEmpty(App.Secrets.TwitterConsumerKey))
{
app.UseTwitterAuthentication(
consumerKey: App.Secrets.TwitterConsumerKey,
consumerSecret: App.Secrets.TwitterConsumerSecret);
}
if (!string.IsNullOrEmpty(App.Secrets.GitHubClientId))
{
app.UseGitHubAuthentication(
clientId: App.Secrets.GitHubClientId,
clientSecret: App.Secrets.GitHubClientSecret);
}
AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.NameIdentifier;
}
}
IdentitySignIn/SignOut
public void IdentitySignin(string userId, string name, string providerKey = null, bool isPersistent = false)
{
var claims = new List<Claim>();
// create *required* claims
claims.Add(new Claim(ClaimTypes.NameIdentifier, userId));
claims.Add(new Claim(ClaimTypes.Name, name));
var identity = new ClaimsIdentity(claims, DefaultAuthenticationTypes.ApplicationCookie);
// add to user here!
AuthenticationManager.SignIn(new AuthenticationProperties()
{
AllowRefresh = true,
IsPersistent = isPersistent,
ExpiresUtc = DateTime.UtcNow.AddDays(7)
}, identity);
}
public void IdentitySignout()
{
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie,
DefaultAuthenticationTypes.ExternalCookie);
}
private IAuthenticationManager AuthenticationManager
{
get
{
return HttpContext.GetOwinContext().Authentication;
}
}
External Link Login
[AllowAnonymous]
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> ExternalLinkLogin(string provider) //Google,Twitter etc.
{
return new ChallengeResult(provider, Url.Action("ExternalLinkLoginCallback"), userId);
}
[AllowAnonymous]
[HttpGet]
public async Task<ActionResult> ExternalLinkLoginCallback()
{
// Handle external Login Callback
var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync(XsrfKey,userId);
if (loginInfo == null)
{
IdentitySignout(); // to be safe we log out
return RedirectToAction("Register", new {message = "Unable to authenticate with external login."});
}
// Authenticated!
string providerKey = loginInfo.Login.ProviderKey;
string providerName = loginInfo.Login.LoginProvider;
// Your code here…
// when all good make sure to sign in user
IdentitySignin(userId, name, providerKey, isPersistent: true);
return RedirectToAction("Register");
}
This code and the External Login also require the ChallengeResult helper class:
// Used for XSRF protection when adding external logins
private const string XsrfKey = "CodePaste_$31!.2*#";
public class ChallengeResult : HttpUnauthorizedResult
{
public ChallengeResult(string provider, string redirectUri)
: this(provider, redirectUri, null)
{ }
public ChallengeResult(string provider, string redirectUri, string userId)
{
LoginProvider = provider;
RedirectUri = redirectUri;
UserId = userId;
}
public string LoginProvider { get; set; }
public string RedirectUri { get; set; }
public string UserId { get; set; }
public override void ExecuteResult(ControllerContext context)
{
var properties = new AuthenticationProperties { RedirectUri = RedirectUri };
if (UserId != null)
properties.Dictionary[XsrfKey] = UserId;
var owin = context.HttpContext.GetOwinContext();
owin.Authentication.Challenge(properties, LoginProvider);
}
}
External Login
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult ExternalLogin(string provider)
{
string returnUrl = Url.Action("New","Snippet",null);
return new ChallengeResult(provider, Url.Action("ExternalLoginCallback",
"Account", new { ReturnUrl = returnUrl }));
}
[AllowAnonymous]
public async Task<ActionResult> ExternalLoginCallback(string returnUrl)
{
if ( string.IsNullOrEmpty(returnUrl) )
returnUrl = "~/";
var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();
if (loginInfo == null)
return RedirectToAction("LogOn");
// AUTHENTICATED!
var providerKey = loginInfo.Login.ProviderKey;
// Your code goes here.
// when all good make sure to sign in user
IdentitySignin(userId, name, providerKey, isPersistent: true);
return Redirect(returnUrl);
}
Local Account Registration
[AcceptVerbs(HttpVerbs.Post)]
[ValidateAntiForgeryToken]
public ActionResult Register(FormCollection formVars)
{
// Capture User Data and Create/Update account
// when all good make sure to sign in user
IdentitySignin(userId, name, appUserState.UserId);
return RedirectToAction("New","Snippet",null);
}cs
Local Account Login
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult LogOn(string email, string password, bool rememberMe, string returnUrl, bool emailPassword)
{
// validate your user
// if all OK sign in
IdentitySignin(userId, name, user.OpenId, rememberMe);
return RedirectToAction("New", "Snippet", null);
}
public ActionResult LogOff()
{
IdentitySignout();
return RedirectToAction("LogOn");
}
Summary
For new projects it’s probably a good idea to think long and hard whether you want to roll your own, or just stick with the stock Identity implementation. Personally I’m not a fan of the EF based implementation that comes out of the box. While it can be customized you still have to massively tweak the UI to make things fit with your application and probably add and remove fields from the Identity models. Worst of all though is the default EF dependency that doesn’t easily integrate into even another EF model. Personally, I prefer to have user management integrated as part of my own domain models of the application rather than linking users to customers for example.
The other advantage of using your own is that you’re not stuck to Microsoft’s whims. We’ve gone through too many authentication frameworks with Microsoft seemingly changing the model with every major release cycle. Granted the new Identity is probably the closest thing that they ever had that is actually usable out of the box, but I’m still wary to rely on anything that Microsoft sticks in the box in this regard for fear of getting it yanked out from under me in the next version. Using my own I don’t have to worry about at least the user management features that can travel with my applications. With my own implementation I might have a little more setup to do, but at least I have a standard way that I can easily carry forward through applications of any version of ASP.NET.
In the end I have to say that although it took me quite a while to get my head around EXACTLY what I needed to implement, was not very difficult. The hard part was just finding the right information on what you need to implement by digging into the generate code and ripping out the relevant pieces. Once you know what’s needed the implementation of the actual code pieces is relatively straight forward.
I hope that this post provides a good summary of what’s required and especially with the Minimal Code Summary it’ll be easier to create the skeleton code required to plug your own domain driven user management into the core identity framework.
Resources
The Voices of Reason
# re: Adding minimal OWIN Identity Authentication to an Existing ASP.NET MVC Application
http://stackoverflow.com/questions/28704700/updating-owin-from-2-1-to-3-0-1-breaks-external-auth
I notice that you're calling GetExternalLoginInfoAsync from the AuthenticationManager. I'm using GetAuthenticationResult. Any idea what the difference is?
# re: Adding minimal OWIN Identity Authentication to an Existing ASP.NET MVC Application
I didn't look closely at the lower level AuthenticationManager methods to see what's needed given that the code does what it's supposed to using ExternalLinkLoginInfoAsync(). Have you tied using that instead? Also notice the use of app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie) instead of your app.SetDefaultSignInAsAuthenticationType().
Bummer to hear that this broke for you though - that really shouldn't be happening with components that are as integral as this.
# re: Adding minimal OWIN Identity Authentication to an Existing ASP.NET MVC Application
# Great Article
# re: Adding minimal OWIN Identity Authentication to an Existing ASP.NET MVC Application
# re: Adding minimal OWIN Identity Authentication to an Existing ASP.NET MVC Application
IAuthenticationManager AuthenticationManager
{
get { return HttpContext.GetOwinContext().Authentication; }
}
Also if you don't have using System.Web which is common in an MVC project you will have an even harder time. Word to your mother.
# re: Adding minimal OWIN Identity Authentication to an Existing ASP.NET MVC Application
# re: Adding minimal OWIN Identity Authentication to an Existing ASP.NET MVC Application
It's very hard to find the good and usefull articles like this. Every forum or question related to MVC5 Authentication are about the default OWIN stuffs. This is the best solution i ever found for the custom Authentication Mechanizm.
I played with the code and works %100 for me for now. You should definitely write more articles about this setup. May be you can add Authorization, Roles, Multiple Roles in a single user, how to show different content in views to different users or roles, or somethink like that.
YOU ARE A HERO!
Cheers.
# re: Adding minimal OWIN Identity Authentication to an Existing ASP.NET MVC Application
This is a truly awesome resource. I was stuck wondering how to integrate the full Identity Framework into my existing application and this fits the bill perfectly.
Must have taken you ages to work out the details - thanks for sharing.
# re: Adding minimal OWIN Identity Authentication to an Existing ASP.NET MVC Application
Regards!
# re: Adding minimal OWIN Identity Authentication to an Existing ASP.NET MVC Application
I removed all the Microsoft.AspNet.Identity packages - did I go too far? If I just register the nuGet packages you suggest, the compiler doesn't like the Startup partial at all.
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
The UseExternalSignInCookie property doesn't exist under IAppBuilder
// Enable the application to use a cookie to store information for the signed in user
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/LogOn")
});
In class Microsoft.Owin.Security.Cookies.CookieAuthenticationOptions, AuthenticateType is a string and not an enum.
And so on..!
Can you clarify the nuGet packages and versions you're using and the namespaces you're 'using' please?
Thanks
# re: Adding minimal OWIN Identity Authentication to an Existing ASP.NET MVC Application
The best thing to figure out the assemblies if you did do this is to create a new project add the nuget references and then copy the package list.
# re: Adding minimal OWIN Identity Authentication to an Existing ASP.NET MVC Application
Having double checked and found nothing missing according to the instructions in the post, I went to your CodePaste.NET GitHub to see what was actually in play there. (Should have done that before bothering you, but... well, next time).
The packages I was missing were these below:
<package id="Microsoft.AspNet.Identity.Core" version="2.2.1" targetFramework="net451" /> <package id="Microsoft.AspNet.Identity.Owin" version="2.2.1" targetFramework="net451" />
From the title and general direction of the post, I'd got the idea that we didn't need the Microsoft.AspNet.Identity packages because we're just using the minimal OWIN classes.
Still, up and moving along now, so much obliged to you Sir.
Regards.
# re: Adding minimal OWIN Identity Authentication to an Existing ASP.NET MVC Application
# re: Adding minimal OWIN Identity Authentication to an Existing ASP.NET MVC Application
# re: Adding minimal OWIN Identity Authentication to an Existing ASP.NET MVC Application
But your code is just as crazy as what the mad scientists at Microsoft are coming up with. They for sure are driving people away from using MS programming languages.
if (!string.IsNullOrEmpty(App.Secrets.GoogleClientId)) { app.UseGoogleAuthentication( clientId: App.Secrets.GoogleClientId, clientSecret: App.Secrets.GoogleClientSecret); }
Nowhere in my project can I get the "App.Secrets.GoogleClientId" from being underlined in red. "The name App does not exist in the current context."
I woke up today thinking I could finally move on to actually working on my CRUD stuff in my application.
# re: Adding minimal OWIN Identity Authentication to an Existing ASP.NET MVC Application
# re: Adding minimal OWIN Identity Authentication to an Existing ASP.NET MVC Application
# re: Adding minimal OWIN Identity Authentication to an Existing ASP.NET MVC Application
I have a MVC4 web site which is currently uses simple membership and sql server for user management. Some of my users are already authenticated through facebook. I want to upgrade to OWIN identity to get the google authentication working. I was wondering if OWIN can consume existing user information in sql server or it has a different schema?
Thanks
# re: Adding minimal OWIN Identity Authentication to an Existing ASP.NET MVC Application
I have to tell you Rick, this article was pure gold. I've been writing our site for awhile and I noticed my security was sending some information clear text. Well, I was going to go down the forms auth route, but saw that wasn't the standard. Thanks to you, this tutorial is a clear cut way to just use the authorization we need without all the bloat. ~Thanks much brother!
# re: Adding minimal OWIN Identity Authentication to an Existing ASP.NET MVC Application
# re: Adding minimal OWIN Identity Authentication to an Existing ASP.NET MVC Application
If want to know in deep about OWIN and how to use it with .NET which is the best tutorial or a reference book.
Can some one please suggest.
# re: Adding minimal OWIN Identity Authentication to an Existing ASP.NET MVC Application
I would like another article about custom role based authentication with new asp.net identity model.
# re: Adding minimal OWIN Identity Authentication to an Existing ASP.NET MVC Application
I was directed here from the Asp.net forum.
I have an existing ASP.Net web application. It is *not* MVC!
Will (should) the steps here enable my ASP.Net web application to permit my members to log in using OWin or would I run into other problems?
If this is possible, after a member logs in using the OWin process, what would I need to do to match up that login identity to a member in my application's SQL database where I store all of my members by their email address?
# re: Adding minimal OWIN Identity Authentication to an Existing ASP.NET MVC Application
The post describes a scenario that doesn't use the default EF identity implementation - it only uses the oWin components but none of the authorization or user management pieces which are deferred for storage in the application.
# re: Adding minimal OWIN Identity Authentication to an Existing ASP.NET MVC Application
AWESOME Article you have my sanity !!!! This is one of the best articles on this topic. I have spent the whole weekend working on the following solution: Using local authentication against LDAP and Windows Azure Active Directory. Just a note to everyone I had to put OpenIdConnectAuthentication in passive mode so that it wouldn't try to authenticate to WAAD immediately.
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Passive,
Soapbox - Frameworks like OWIN are great when it's a common problem but are a nightmare when you need to go off the beaten path!
# re: Adding minimal OWIN Identity Authentication to an Existing ASP.NET MVC Application
Do you have idea on how to integrate MVC website with app mobile.
I mean... now I'm able to do external login on the web mvc, I moved some funtions to web api and I0m triyng to connect also using custom (xamarin) APP. what is the correct approach? I want my APP to be logged in to my mvc backend, using google credentials
Do you have any idea?
Thanks so much
# re: Adding minimal OWIN Identity Authentication to an Existing ASP.NET MVC Application
# re: Adding minimal OWIN Identity Authentication to an Existing ASP.NET MVC Application
# re: Adding minimal OWIN Identity Authentication to an Existing ASP.NET MVC Application
Thanks for this article and helped me a lot to implement OWIN authentication in MVC5 . Can you please guide me how I can invalidate the claimsIndentity after signout from the application?
# re: Adding minimal OWIN Identity Authentication to an Existing ASP.NET MVC Application
Hi, Thanks for the article, very useful indeed. I have a similar asp.net app structure with a global.asax where unity container is configured. Is there a way to use objects injected in Application_Start() in owin startup class?
# re: Adding minimal OWIN Identity Authentication to an Existing ASP.NET MVC Application
Thanks for this article. I had a go at implementing Owin before I realised it was easier and faster to write my own library.
OAuth is not a difficult protocol to implement. But when it takes this much minimal code to implement a library, which has deep reliances on other unneccesary libraries, many entry points and outside code required, you know something is wrong. The application startup config code also doesn't lend itself to multi-tennant apps. This middleware is simply an overengineered mess.
I implemented several providers. Each one had its own slight twist/quirks on OAuth, but I only needed to override 3 properties and a method from a base class for each provider in my library, which were more similar than different. I simply pass in the HttpRequest, the provider, and add one new route to the web app, then look for a ProfileInfo class to eventually come back (or an error). A few lines of readable code.
Owin was useful for monitoring requests and response urls, but I recommend rolling your own code. Otherwise its a sledgehammer to crack a nut.
# re: Adding minimal OWIN Identity Authentication to an Existing ASP.NET MVC Application
# re: Adding minimal OWIN Identity Authentication to an Existing ASP.NET MVC Application
You can also take a look at my StackOverflow post: http://stackoverflow.com/questions/39661593/owin-cookie-authentication-not-working-on-iis-8-5
Thx,
Danny
# re: Adding minimal OWIN Identity Authentication to an Existing ASP.NET MVC Application
Hi, thanks a lot for this post, it was quite difficult to find just this and not the whole Identity + EF implementation. I have a question though, regarding cookie timeout and presenting it to the user.
I have been following this kind of approach to it: http://stackoverflow.com/questions/23090706/how-to-know-when-owin-cookie-will-expire
basically I setup my cookie with a SlidingExpiration = true, then, after the user logs in I do a request by ajax to get the current remaining cookie valid time, but by doing that request (which runs periodically by 1 sec or so) then the cookie itself gets renewed and never expires, do you have any suggestions to present that info to the user and not confuse the ajax request with a user's request?
thanks in advance!!
# re: Adding minimal OWIN Identity Authentication to an Existing ASP.NET MVC Application
This article is excellent and exactly what I needed!! I had spent hours reading through fragmented information across many different sites with no results to show for it at all.
After reading through this article it took less than 15 minutes to get the login working!
So thank you very much for taking the time to write this, it's greatly appreciated
James
# re: Adding minimal OWIN Identity Authentication to an Existing ASP.NET MVC Application
There is a bug in the SignIn() method where it fails to set the Claims Principal and Claims Identity for the current request. You need to manually set it in case the authentication request itself also needs to access authorized actions/controllers. Luckily we can easily set the User principal:
var identity = new ClaimsIdentity(claims, DefaultAuthenticationTypes.ApplicationCookie);
AuthenticationManager.SignIn(new AuthenticationProperties()
{
AllowRefresh = true,
IsPersistent = isPersistent,
ExpiresUtc = DateTime.UtcNow.AddDays(7)
}, identity);
/* This ensures the current request is authroized in case your app needs to access authorized actions/controllers */
AuthenticationManager.User = new ClaimsPrincipal(identity);
# re: Adding minimal OWIN Identity Authentication to an Existing ASP.NET MVC Application
@Narbeh - I suppose that makes sense and is easy enough to do, although I find it doubtful that you'd need the Principal to check if you're in a the middle of a login operation.
What's your use case where that's necessary?
# re: Adding minimal OWIN Identity Authentication to an Existing ASP.NET MVC Application
Rick,
I ran into this issue when implementing some automation/simulation that requires a claims principal for a background process. Let's say you want to programmatically authenticate and call a protected controller/action. The automation invokes the SignIn method and then immediately calls RenderAction() to invoke an action that requires authorization. Since SignIn() appears to just create and return an auth cookie, we have no other subsequent request that would send that auth cookie to be handled and a Principal to be created.
Of course you could argue that if it's a single request, you don't need to even call the SignIn() method and just opt to create a new Claims Principal but if we could use the same authentication method as a regular human user then it prevents me from creating a separate process for the automation. By just creating the ClaimsPrincipal during the sign in makes the method more versatile.
It took me a few hours to figure out why my automation was returning a 401 and redirecting to the login page. I can't imagine them leaving this out intentionally but who knows. There may be a case where the auth cookie to be created is intentionally set to be expired and we can't assume the request is authenticated after SignIn() so it could pose a security risk. Great article by the way, I learned a lot about OWIN authentication.
# re: Adding minimal OWIN Identity Authentication to an Existing ASP.NET MVC Application
Thanks Rick, finally an article which cleared the clutter and highlighted the essential pieces.
My setup: My app is a .net 4.6 framework app and is not using system.web.mvc, instead it is a layered app. It is called by a frontend in angular, which calls api routes which in turn land on the .net app controllers which are based on system.web.http and uses token based authentication. Currently, we use AuthenticationFilterAttribute for implementing authentication.
We now need to integrate this app with external login providers like google, fb etc.
My question is as follows:
Do i need to add system.web.mvc to use owin startup class, ChallengeResult (inhereting from HttpUnauthorizedResult which is only in system.web.mvc and and all related code)?
Can I continue to use token based authentication, or will Cookie based authentication need to be used in order to have the external 3rd parties integrated?
Will I need to remove the AuthenticationFilterAttribute and just have the owin startup be r responsible for authentication?
Thanks for your help.
# re: Adding minimal OWIN Identity Authentication to an Existing ASP.NET MVC Application
Finally found an article that does not direct me to the new project wizard to start a complete project without telling me what does what.
With this article I understand what's going on and how to handle the results without using the EF.
Excellent article, thanks.
# re: Adding minimal OWIN Identity Authentication to an Existing ASP.NET MVC Application
Hi Rick, You must hear this all the time, however this article is making my life easy! This is the first article that shows the parts needed to implement from scratch and why. I searched for hours and most search results were way into the weeds already! Thanks again! and keep on writing articles. This is not the first of yours I have read and wont be the last! Regards.
# re: Adding minimal OWIN Identity Authentication to an Existing ASP.NET MVC Application
Just wanted to say a quick thanks for this blog entry. It covers a lot of the little things that places like StackOverflow and other blogs miss out.
Made my life a lot easier!
Thanks.
Sam.
# re: Adding minimal OWIN Identity Authentication to an Existing ASP.NET MVC Application
Hello, really nice and on the topic article and as you mentioned in start of your article that "Lots of intro articles that talk about how to use the stuff ‘as is’ without customization. But there’s not a lot of information on using the core Identity pieces without the full bore UserManager and Entity Framework data store, in order to use just the Authentication/Authorization on their own and integrate them with my own business objects/domain model." then it really makes me feel then I am not alone among those stuff.
Finally got what I was looking for but now I have a couple of queries
- Expire time is set at two places (in Startup class and another in AuthenticationProperties in Signin Method), what is difference between two and which one takes precedence?
- What will happen to another when another thing expires first than other in above question.
- How AllowRefresh = true works in SignIn method
- You mentioned above that "The identity cookie is used to track all logged in users", can you please explains this bit more. Can we notify other users about user online/offline based on it, if yes then how, also does SlidingExpiration is used for this purpose ?
Many thanks for such nice article and good bye to those massive and full of EF and default template based code explanation on the topic 😃
# re: Adding minimal OWIN Identity Authentication to an Existing ASP.NET MVC Application
Just adding a comment for other people who are new to 3rd party provider login... In the code it states
// these values are stored in CodePasteKeys.json
// and are NOT included in repro - autocreated on first load
if (!string.IsNullOrEmpty(App.Secrets.GoogleClientId))
{
app.UseGoogleAuthentication(
clientId: App.Secrets.GoogleClientId,
clientSecret: App.Secrets.GoogleClientSecret);
}
The App is not resolved. It is probably a class that he dumps reading of his json file into.
Either way, I was not familiar with 3rd party providers. So I searched the web for clientId and Google authentication and what i found was you have to register with those providers you website applications so you can interact with them. And in the case of google they will give you a clientid and a clientsecret that you will use when contacting them to verify a user.
Just thought a put a note here in case anyone else who is unfamiliar with working with third party logins did not understand. I would say if you are not using third party login you can probably comment out that code. That is what i am doing for now.
# re: Adding minimal OWIN Identity Authentication to an Existing ASP.NET MVC Application
Hi Rick, great article but I have an issue and wondered if you're still monitoring this article. I'd likt know why even after signing in:
HttpContext.GetOwinContext().Authentication.SignIn(new AuthenticationProperties() , identity);
any subsequent requests (even redirects) are not authenticated/authorized.
Have I misunderstood something. Any help would be very much appreciated.
# re: Adding minimal OWIN Identity Authentication to an Existing ASP.NET MVC Application
I'm keen to integrate .NET apps with an Identity Server, which in turn contains several Services and Identity Providers but there's not much material out there explaining how to do it in .NET.
Nice post by the way!
Thanks.