In my last blog entry, I reviewed the ups and downs (mostly downs) of trying to get a federated identity server (federated login) to work using asp.net core identity with identity server. Both frameworks (asp.net core and Identity Server) are impressive feats and very well orchestrated; But Identity is not simple, and demos and documentation only bring you so far, especially when the technologies all keep evolving individually and each tutorial quickly out dates. After having re-worked the Identity Server tutorials and plenty of other courses (see my epic account here), I was determined to finally deploy a working Identity Server this month and get on with my other projects:
- Start with Quickstart #6: IdentityServer and ASP.NET Identity
- Work through Securing ASP.NET Core with OAuth2 and OpenID Connect by Kevin Dockx (Pluralsight)
- Excellent course, but has several pain points. I’ve re-watched this course three times and worked through most of the coding demos.
- Kevin use asp.net core 1.1 and I used asp.net core 2.0
- Kevin creates his own identity store instead of using asp.net core identity. At first, I wondered why. On re-watching segments, I recognized that building your own Identity persistence model, allows for a better understanding of how any model interacts with Identity Server (including asp.net core identity) and bypasses the black box magic built into asp.net core that might complicate learning.
- Kevin also walks through using a Claims table for storing address, first name, last name… instead of deriving a custom class from ApplicationUser (which is what I did in the past).
- I need to write a review of Kevin’s course – top notch.
- Deploy my identity solution and use this to supply identity to apps on development machine
I came across several problems, I’ll document here:
Creating Self Signed Certificates for Tokens
Kevin Docx describes how to create self-signed certificates with makecert.exe here. Brock Allen describes the makecert command here. You basically create the certificate with makecert and in the mmc utility (after adding the certificate snap-in), you copy the certificate to the “Trusted Root Certification Authorities” certificate folder. You use the token thumbprint code to retrieve this certificate in asp.net core.
At first, I could not get the thumbprint to match a certificate. The thumbprints are easy to incorrectly enter (when copied from the MMC details dialog)
Here is the code to load the certificate:
using (var store = new X509Store(StoreName.My, StoreLocation.LocalMachine))
{
store.Open(OpenFlags.ReadOnly);
var certCollection = store.Certificates.Find(X509FindType.FindByThumbprint, thumbprint_fpnIdSvrSigningCert_3, true);
if (certCollection.Count == 0)
{
throw new Exception("The specified certificate was not found");
}
return certCollection[0];
}
To determine why these were not matching, I wrote out the certificate metadata to the console:
private static void write_certificates_in_store(X509Store store)
{
X509Certificate2Collection collection = (X509Certificate2Collection)store.Certificates;
foreach (X509Certificate2 x509 in collection)
{
try
{
Console.WriteLine("------------------------");
Console.WriteLine("Thumbprint: {0}{1}", x509.Thumbprint, Environment.NewLine);
Console.WriteLine("Friendly Name: {0}{1}", x509.FriendlyName, Environment.NewLine);
Console.WriteLine("Certificate Verified?: {0}{1}", x509.Verify(), Environment.NewLine);
Console.WriteLine("Simple Name: {0}{1}", x509.GetNameInfo(X509NameType.SimpleName, true), Environment.NewLine);
Console.WriteLine("Signature Algorithm: {0}{1}", x509.SignatureAlgorithm.FriendlyName, Environment.NewLine);
// Console.WriteLine("Private Key: {0}{1}", x509.PrivateKey.ToXmlString(false), Environment.NewLine);
// Console.WriteLine("Public Key: {0}{1}", x509.PublicKey.Key.ToXmlString(false), Environment.NewLine);
Console.WriteLine("Certificate Archived?: {0}{1}", x509.Archived, Environment.NewLine);
}
catch (CryptographicException)
{
Console.WriteLine("Information could not be written out for this certificate.");
}
}
}
After getting this to match, the certificate would work on desktop, but not on the server. After some cryptic errors (CryptographicException: keyset does not exist), I finally found this post. In MMC, right click the private certificate, and “Manage Private Keys”. Add “IIS_IUSRS” to permissions.
Adding Claims to the Identity Token
I could not get roles and other claims to be included in the identity token. That is, until I added “AlwaysIncludeUserClaimsInIdToken = true” to the IdentityServer Client configuration. Until I found this, I was manually adding the roles as claims via the services.AddProfileService<myCustomService>().
Registering a User with An External Provider
For a while I wondered about the right way to sync a user authenticated with an external provider (Google, Facebook, Windows) with an email/account on our IdentityServer. This is included in the Asp.Net Core Identity framework, but it is rudimentary. After external authentication, the users identifying info including email are matched against those already in our Identity Server database. If no match is found, the user is redirected to enter their registration information (email is the only field defaulted in the framework). The user enters their email and the account is added with a link to the external provider login.
However, the email the user enters, is by default, an unverified email and when the user logs out and back in, their account with email is found, but if the configuration requires email verification, this email will be invalid (unverified email). So the user is prompted again for registration info, and when they type in their email, they are told that email is already taken. Of course it is. This also was not an easy error to find, and a little tricky to fix (where to add the email verification).
You must be logged in to post a comment.