I am attempting to write a single sign authentication/authorization MVC web application that authenticates a user from client application(s) using OWIN and Thinktecture.IdentityServer3. I am creating claims in the authentication server in the following class:
using System; using System.Collections.Generic; using System.Linq; using System.Security.Claims; using System.Text; using System.Threading.Tasks; using IdentityServer3.Core; using IdentityServer3.Core.Extensions; using IdentityServer3.Core.Models; using IdentityServer3.Core.Services; using IdentityServer3.Core.Services.Default; using BioAppsIdentityServer.Respositories; using BioAppsIdentityServer.Data_Models; using Avion.Cryptography; namespace BioAppsIdentityServer { public class LocalRegistrationUserService : UserServiceBase { private static IUnitOfWork _uow; public LocalRegistrationUserService(IUnitOfWork uow) { _uow = uow; } public class BioUser { public string Subject { get; set; } public string Username { get; set; } public string Password { get; set; } public List<Claim> Claims { get; set; } } public static List<BioUser> Users { get { return GetLocalUsers(); } } public override Task AuthenticateLocalAsync(LocalAuthenticationContext context) { var user = Users.SingleOrDefault(x => x.Username == context.UserName && PasswordHasher.VerifyPassword(context.Password,x.Password)); if (user != null) { context.AuthenticateResult = new AuthenticateResult(user.Subject, user.Username); } return Task.FromResult(0); } public override Task GetProfileDataAsync(ProfileDataRequestContext context) { // issue the claims for the user // var user = Users.SingleOrDefault(x => x.Subject == context.Subject.GetSubjectId()); // if (user != null) // { // context.IssuedClaims = user.Claims.Where(x => context.RequestedClaimTypes.Contains(x.Type)); // } return Task.FromResult(0); } private static List<BioUser> GetLocalUsers() { List<BioUser> listUsers = new List<BioUser>(); foreach (VUSER usr in _uow.Repository<VUSER>().GetAll()) { List<Claim> userClaims = new List<Claim>(); BioUser memUser = new BioUser(); memUser.Username = usr.EMAILADDRESS; memUser.Password = usr.PASSWORD; memUser.Subject = usr.USERID.ToString(); userClaims.Add(new Claim(Constants.ClaimTypes.GivenName, usr.FIRSTNAME)); userClaims.Add(new Claim(Constants.ClaimTypes.FamilyName, usr.LASTNAME)); userClaims.Add(new Claim(Constants.ClaimTypes.Email, usr.EMAILADDRESS)); if (string.IsNullOrWhiteSpace(usr.MIDDLENAME)) { userClaims.Add(new Claim(Constants.ClaimTypes.MiddleName, string.Empty)); } else { userClaims.Add(new Claim(Constants.ClaimTypes.MiddleName, usr.MIDDLENAME)); } memUser.Claims = userClaims; listUsers.Add(memUser); } return listUsers; } } }
After authentication the claims are processed on the client in the following Startup class
using Microsoft.IdentityModel.Protocols; using Microsoft.Owin; using Microsoft.Owin.Security; using Microsoft.Owin.Security.Cookies; //using Microsoft.Owin.Security.Google; using Microsoft.Owin.Security.OpenIdConnect; using Owin; using System; using System.Collections.Generic; using System.IdentityModel; using System.IdentityModel.Tokens; using System.Security.Cryptography.X509Certificates; using System.Linq; using System.Web.Helpers; using Thinktecture.IdentityModel.Client; using System.Threading.Tasks; using System.Security.Claims; [assembly: OwinStartup(typeof(BioAppsIdentityClient.Startup))] namespace BioAppsIdentityClient { public class Startup { public void Configuration(IAppBuilder app) { app.UseCookieAuthentication(new CookieAuthenticationOptions { AuthenticationType = "Cookies" }); app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions { Authority = "https://localhost:44355/core", ClientId = "bioapp", RedirectUri = "https://localhost:44307/", ResponseType = "id_token", SignInAsAuthenticationType = "Cookies", UseTokenLifetime = false, Notifications = new OpenIdConnectAuthenticationNotifications { SecurityTokenValidated = n => { ClaimsIdentity id = n.AuthenticationTicket.Identity; // we want to keep first name, last name, subject and roles Claim givenName = id.FindFirst(JwtClaimTypes.GivenName); Claim familyName = id.FindFirst(JwtClaimTypes.FamilyName); Claim sub = id.FindFirst(JwtClaimTypes.Subject); IEnumerable<Claim> roles = id.FindAll(JwtClaimTypes.Role); // create new identity and set name and role claim type ClaimsIdentity nid = new ClaimsIdentity( n.AuthenticationTicket.Identity.AuthenticationType, JwtClaimTypes.GivenName, JwtClaimTypes.Role); // keep the id_token for logout nid.AddClaim(new Claim("id_token", n.ProtocolMessage.IdToken)); nid.AddClaim(givenName); nid.AddClaim(familyName); nid.AddClaim(sub); nid.AddClaims(roles); n.AuthenticationTicket = new AuthenticationTicket( nid, n.AuthenticationTicket.Properties); return Task.FromResult(0); } } }); } } }
After the call to the server the ClaimsIdentity object is not null and appears to contain the GivenName claim ("given_name"). However the
Claim givenName = id.FindFirst(JwtClaimTypes.GivenName);
returns a null. All the ClaimsIdentity.FindFirst commands return a null. When I breakpoint and inspect the Clamindentity object I can see the claims "given_name" and the other claims in the ClaimIdentity object.