I'm trying to follow the simple guide mvcGettingStarted.
Now, I've implemented both GoogleAuthentication
and FacebookAuthentication
providers, and everything is working as expected, I actually can log-in, and if I sign in with my identity server I also got the Role claims per user.
I was wondering, what if I want to keep all the claims given from the external providers?
Simple example.
This is how my Facebook provider setup looks like:
var facebookOptions = new FacebookAuthenticationOptions() {
AuthenticationType = "Facebook",
Caption = "Sign in with Facebook",
AppId = "*****",
AppSecret = "****",
SignInAsAuthenticationType = signInAsType,
Provider = new FacebookAuthenticationProvider() {
OnAuthenticated = (context) => {
foreach (var x in context.User) {
context.Identity.AddClaim(new Claim(x.Key, x.Value.ToString()));
}
return Task.FromResult(context);
}
},
};
facebookOptions.Scope.Add("email");
facebookOptions.Scope.Add("public_profile");
facebookOptions.Scope.Add("user_friends");
app.UseFacebookAuthentication(facebookOptions);
In the for each loop I'm trying to store all the Facebook claims in the Identity, but when I get back in the SecurityTokenValidated
callback, my Identity hasn't them.
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions() {
Authority = "https://localhost:44302/identity/",
ClientId = "my_client",
Scope = "openid profile roles email",
RedirectUri = "https://localhost:44302/",
ResponseType = "id_token token",
SignInAsAuthenticationType = "Cookies",
UseTokenLifetime = false,
Notifications = new OpenIdConnectAuthenticationNotifications() {
SecurityTokenValidated = async context => {
//let's clean up this identity
//context.AuthenticationTicket.Identity doesn't have the claims added in the facebook callback
var nid = new ClaimsIdentity(
context.AuthenticationTicket.Identity.AuthenticationType,
Constants.ClaimTypes.GivenName,
Constants.ClaimTypes.Role);
........
Is it because I'm manipulating two different Identities? Is there a right way to achieve what I am trying to do? Thank you.
As @brock-allen said, the user service is the right path to follow. So I went on and implemented a simple UserService
public class UserService {
private static InMemoryUserService _service = null;
public static InMemoryUserService Get() {
if(_service == null)
_service = new InMemoryUserService(Users.Get());
return _service;
}
}
registered my userservice in my factory like this
public void Configuration(IAppBuilder app) {
AntiForgeryConfig.UniqueClaimTypeIdentifier = Constants.ClaimTypes.Subject;
JwtSecurityTokenHandler.InboundClaimTypeMap = new Dictionary<string, string>();
var factory = InMemoryFactory.Create(
users: Users.Get(),
clients: Clients.Get(),
scopes: Scopes.Get());
factory.UserService = new Registration<IUserService>(resolver => UserService.Get());
.....
(Of course that's the Configuration method inside my Startup class)
So now I can authenticate the external user inside the authentication callback of the external provider (in this case facebook), specifing all the claims that I need:
var facebookOptions = new FacebookAuthenticationOptions() {
AuthenticationType = "Facebook",
Caption = "Sign in with Facebook",
AppId = "******",
AppSecret = "*******",
SignInAsAuthenticationType = signInAsType,
Provider = new FacebookAuthenticationProvider() {
OnAuthenticated = (context) => {
foreach (var x in context.User) {
context.Identity.AddClaim(new Claim(x.Key, x.Value.ToString()));
}
ExternalIdentity identity = new ExternalIdentity() {
Claims = context.Identity.Claims,
Provider = "Facebook",
ProviderId = "Facebook"
};
SignInMessage signInMessage = new SignInMessage();
UserService.Get().AuthenticateExternalAsync(identity, signInMessage);
return Task.FromResult(context);
}
},
}
Now, I can do
List<Claim> claims = await UserService.Get().GetProfileDataAsync(User as ClaimsPrincipal) as List<Claim>;
And see that my User has all the claims facebook provided during authentication. Of course this code is just for testing purposes, it can be improved a lot.