Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Thinktecture Identity Server v3 How to keep Claims from external providers?

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.

like image 941
Daniele Avatar asked Jan 26 '15 12:01

Daniele


People also ask

Is Identity Server an identity provider?

IdentityServer. IdentityServer is an OpenID Connect provider - it implements the OpenID Connect and OAuth 2.0 protocols. Different literature uses different terms for the same role - you probably also find security token service, identity provider, authorization server, IP-STS and more.

What is Identity Server authentication?

IdentityServer is an authentication server that implements OpenID Connect (OIDC) and OAuth 2.0 standards for ASP.NET Core. It's designed to provide a common way to authenticate requests to all of your applications, whether they're web, native, mobile, or API endpoints.

Is Identity Server 4 open source?

About IdentityServer4IdentityServer is a free, open source OpenID Connect and OAuth 2.0 framework for ASP.NET Core.


1 Answers

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.

like image 109
Daniele Avatar answered Sep 25 '22 22:09

Daniele