This post is part of a series on ASP.NET Core Authentication and I am going to talk about sign-in via social providers and how to maintain a profile store.
You can find the source code outlined in this article on GitHub.
Imagine a scenario where your application successfully uses a third-party provider to sign-in users. That seems pretty standard and straightforward. But what about new requirements your application might have, like provide an email address or have a physical home address? Some providers might not be able to serve you this kind of information, or either the user in question has not filled these up for his/her own reasons.
From the above, seems that you need to take action. In this blog, I will talk about how to setup your application to sign-in a user via a social media network provider, like Facebook or Twitter and how to get this extra information the application requires. I will try to keep it simple, I won’t really work with a data store, I will have my users setup in-memory. Application will require some extra info which will be username, which will be required, user’s email address, which will be optional as well as home address which will be a single field, optional as well.
User is given access via an authenticated cookie. In order to make this scenario happen, I will need to have a different approach, use a temporary authentication cookie, just to sign the user in, and then I will construct the user’s identity with the extra information, destroy the temp cookie and finally sign the user in again with a valid cookie containing all the required claims.
The temporary cookie is used only to store the claims coming from the third-party provider. These claims will be then used to pre-populate the profile form, in which the user will provide some extra information. Upon successful submission of this form, the temporary cookie will be destroyed and a new, valid authentication cookie will be created with claims coming from the profile form.
An example of this can be found at https://careers.microsoft.com/us/en/login, you can try for instance sign-in with your LinkedIn profile. Upon successful login, if you of course visited the site for the first time, you will be redirected to a profile page, in which some information are pre-populated while other needs your attention.
Why a profile store
For the reasons described above, a profile store might be useful in cases you might want to get a hold on the user’s claims or you want to store all the claims in the database per user.
This approach will allow to build complex authentication and authorization scenarios, once you hold yourself some info about the user.
You can also think about this as a way to sign up the user with the less hassle, promoting some better UX, as the user will not have to go through all the bits and pieces of the information you might require, this information can be extracted from the public profile that the user holds on the third-party provider.
Code is based in previous post on talking about social logins, so you might want to check it first, before moving forward, though it’s not required. I will use the same kinda code here, same project layout, same providers and I will just add my stuff here, on setting up this profile store.
First, I will start with the
Startup.cs class, I will add a temporary authentication cookie in services, so I will use this one as the default when user logs in.
I have set the
DefaultSignInScheme to Temporary. Remember, this scheme describes the scheme that will be used when user signs in. The reason behind this was to create a custom sign-in flow, in which I am going to fetch signed-in user claims and then update build required a profile using those or input from the user, if any claim is missing.
Rest configuration remains the same. You might have noticed that I have added another cookie that is actually using the Temporary scheme as the default authentication scheme. This is key for the custom sign-in flow I mentioned earlier.
TemporaryAuthenticationDefaults is just a static class with static string property of value
Next up, I need to setup the
SignOut actions. I will need also a
Profile action, in which the user will edit his/her profile, upon successful sign-in via the third-party provider.
The SignIn action
This is the first view the user visits when tries to sign-in. In this view, I list all the available third-party providers for the user to choose from and login. Before I show the view to the user though, I first check if he/she is already authenticated with the temporary cookie. In case that is true, user is redirected to the profile action, else the standard view is showing.
Then, I have the
SignIn overload action, which takes a provider as a route parameter. This is the one that performs the actual
Challenge (401 Unauthorized) and redirects to the third-party provider login screen.
Also note the
returnUrl, it is appended in the profile URL, which is used in the challenge
AuthenticationProperties as the redirect URI. Reason for this is to have the third-party provider redirect to that URI upon successful login, rather back here, which could cause an endless loop.
The Profile action
This action carries a little bit of work and it is the bread and butter of this very blog.
First, I need to check if user is authenticated with the temporary cookie. If that is not the case, then I have to redirect the user back to
SignIn action, cause something pretty bad has probably happened.
Next, I’ll need to fetch the user from the data store, using the
ProfileService that is injected into the
AuthorizationController. If user is not found, that means this user has never signed-up or signed-in with this application, because upon successful login, user details are persisted in the underlying data store.
So, in the case user is not found, I will display a form with profile details, some of them pre-populated with claims fetched from the third-party provider.
If user is found though, I will call the
SignInUserAsync private method.
The HTTP Post Profile action is checking if user entered the details correctly, by checking the
ModelState and if yes, it creates a new profile and signs-in the user, calling the
The SignInUserAsync method
This is responsible of signing in the user, by performing the following steps:
- Sign-out user from temporary cookie
- Create a new
- Sign-in user with the
CookieAuthenticationDefaultsscheme. If you don’t do this, you won’t be able to store the claims on the cookie, having the user seem likes is not logged in.
- Return an
IActionResultredirect back to the
returnURLor home page, upon successful sign-in.
The SignOut action
This is a pretty straightforward action, a call to the
HttpContext.SignOutAsync, passing now the
CookieAuthenticationDefaults value, which is “cookie”, is enough to sign-out the user and finally, I redirect back to the home page.
In this post you have learned how to provide authentication via third-party providers and how to require extra information from signed-in user, using a temporary cookie. The temporary cookie is to hold the user claims from the third-party provider, while the user edits his/her profile. Upon successful profile edit, temporary cookie is destroyed, a new cookie is created that is used for authenticating the user, populated with claims from the temporary cookie and the profile view model.
If you liked this blog, please like and share! For more, follow me on Twitter.
This post is part of the ASP.NET Core 2.0 Authentication series.
- ASP.NET Core 2.0 Cookie Authentication – Local logins
- ASP.NET Core 2.0 Authentication with local logins – Implementing claims transformation
- ASP.NET Core 2.0 Authentication with local logins – Responding to backend changes
- ASP.NET Core 2.0 Authentication with local logins – Implementing custom authorization policies
- ASP.NET Core 2.1 Authentication with social logins
- ASP.NET Core 2.0 Authentication with social logins – Implementing a profile store
- ASP.NET Core 2.0 Authentication with Azure Active Directory
- ASP.NET Core 2.0 Authentication with Azure Active Directory B2C
- ASP.NET Core API authentication with JWT
- ASP.NET Core 2.0 Authentication, IdentityServer4 and Angular