IdentityServer / IdentityServer3

OpenID Connect Provider and OAuth 2.0 Authorization Server Framework for ASP.NET 4.x/Katana
https://identityserver.github.io/Documentation/
Apache License 2.0
2.01k stars 764 forks source link

setup federation gateway with two instances of identity server #1004

Closed truthbeliever closed 9 years ago

truthbeliever commented 9 years ago

Hello,

This is related to closed issue https://github.com/IdentityServer/Thinktecture.IdentityServer3/issues/997

I was setting up the external identityprovider by creating an instance of SelfHost (InMem with WS-Fed). Another instance of SelfHost (Minimal) is posing as my actual full fleged Authorisation server. But my authorisationserver is using SelfHost (InMem with WS-Fed) for Authentification and then adds some more claims. But It does not work as expected! Here are the logs!

So my "external" Fed IdentityServer talks like: SelfHost.vshost.exe Information: 0 : [Thinktecture.IdentityServer.Core.Endpoints .AuthenticationController]: 28.02.2015 14:40:43 +00:00 -- rendering login page SelfHost.vshost.exe Information: 0 : [Thinktecture.IdentityServer.Core.Endpoints.AuthenticationController]: 28.02.2015 14:43:57 +00:00 -- Login page submitted SelfHost.vshost.exe Information: 0 : [Thinktecture.IdentityServer.Core.Endpoints.AuthenticationController]: 28.02.2015 14:43:57 +00:00 -- Login credentials successfully validated by user service SelfHost.vshost.exe Information: 0 : [Thinktecture.IdentityServer.Core.Endpoints.AuthenticationController]: 28.02.2015 14:43:58 +00:00 -- issuing cookie SelfHost.vshost.exe Information: 0 : [Thinktecture.IdentityServer.Core.Endpoints.AuthenticationController]: 28.02.2015 14:43:58 +00:00 -- redirecting to: https://localhost:44332/core/wsfed?wtrealm=urn:testrp&wctx=WsFedOwinState%3dAQAAANCMnd8BFdERjHoAwE_Cl-sBAAAAsoXk-jNtHkqfXY_GBkwUgwAAAAACAAAAAAAQZgAAAAEAACAAAAD8ja4rWnfnbDK1TTdplvAY3kYcB99trlvkX1LtV85_-QAAAAAOgAAAAAIAACAAAACaucG89VE5kA SelfHost.vshost.exe Information: 0 : [Thinktecture.IdentityServer.WsFederation.WsFederationController]: 28.02.2015 14:43:58 +00:00 -- Start WS-Federation request Debug: [Thinktecture.IdentityServer.WsFederation.WsFederationController]: 28.02.2015 14:43:58 +00:00 -- https://localhost:44332/core/wsfed?wtrealm=urn%3atestrp&wctx=WsFedOwinState%3dAQAAANCMnd8BFdERjHoAwE_Cl-sBAAAAsoXk-jNtHkqfXY_GBkwUgwAAAAACAAAAAAAQZgAAAAEAACAAAAD8ja4rWnfnbDK1TTdplvAY3kYcB99trlvkX1LtV85_-QAAAAAOgAAAAAIAACAAAACaucG89VE5kAxwb7pcVNcUc5Cg502qjXatuMVMfwdALnAAAAD5m3lJOsC6yT SelfHost.vshost.exe Information: 0 : [Thinktecture.IdentityServer.WsFederation.WsFederationController]: 28.02.2015 14:43:58 +00:00 -- WsFederation signin request SelfHost.vshost.exe Information: 0 : [Thinktecture.IdentityServer.WsFederation.Validation.SignInValidator]: 28.02.2015 14:43:58 +00:00 -- Start WS-Federation signin request validation SelfHost.vshost.exe Information: 0 : [Thinktecture.IdentityServer.WsFederation.Validation.SignInValidator]: 28.02.2015 14:43:58 +00:00 -- End WS-Federation signin request validation { "Realm": "urn:testrp", "RelyingPartyName": "My full fleged Authorisation Identity Server 3 instance", "ReplyUrl": "https://localhost:44333/core" } SelfHost.vshost.exe Information: 0 : [Thinktecture.IdentityServer.WsFederation.ResponseHandling.SignInResponseGenerator]: 28.02.2015 14:43:58 +00:00 -- Creating WS-Federation signin response Debug: [Thinktecture.IdentityServer.WsFederation.Hosting.CookieMiddlewareTrackingCookieService]: 28.02.2015 14:43:58 +00:00 -- Retrieving values of cookie IdSvr.WsFedTracking Debug: [Thinktecture.IdentityServer.WsFederation.Hosting.CookieMiddlewareTrackingCookieService]: 28.02.2015 14:43:58 +00:00 -- Cookie IdSvr.WsFedTracking does not exist Debug: [Thinktecture.IdentityServer.WsFederation.Hosting.CookieMiddlewareTrackingCookieService]: 28.02.2015 14:43:58 +00:00 -- Adding https://localhost:44333/core to IdSvr.WsFedTracking cookie

And then my actual Authorisation server SelfHost.vshost.exe Information: 0 : [Thinktecture.IdentityServer.Core.Endpoints.AuthenticationController]: 28.02.2015 14:43:58 +00:00 -- Callback invoked from external identity provider SelfHost.vshost.exe Error: 0 : [Thinktecture.IdentityServer.Core.Endpoints.AuthenticationController]: 28.02.2015 14:43:58 +00:00 -- no subject or unique identifier claims from external identity SelfHost.vshost.exe Information: 0 : [Thinktecture.IdentityServer.Core.Endpoints.AuthenticationController]: 28.02.2015 14:43:58 +00:00 -- rendering login page with error message: Invalid Account

why comes 28.02.2015 14:43:58 +00:00 -- Cookie IdSvr.WsFedTracking does not exist??

Did I configure something wrong? Any hint is welcome!

external Fed Service is just like the ones from the sample ecxept new relying parties: new RelyingParty { Realm = "urn:testrp", Name = "My full fleged Authorisation Identity Server 3 instance", Enabled = true, ReplyUrl = "https://localhost:44333/core", TokenType = Thinktecture.IdentityModel.Constants.TokenTypes.Saml2TokenProfile11, TokenLifeTime = 1,

                ClaimMappings = new Dictionary<string,string>
                {
                    { "sub", ClaimTypes.NameIdentifier },
                    { "given_name", ClaimTypes.Name },
                    { "email", ClaimTypes.Email }
                }
            },

the full fleged autorisation server is basically like the one from your samples and configured like

    public void Configuration(IAppBuilder appBuilder)
    {
        var factory = InMemoryFactory.Create(
            users:   Users.Get(), 
            clients: Clients.Get(), 
            scopes:  Scopes.Get());

        var options = new IdentityServerOptions
        {               

            IssuerUri = "https://localhost:44333/core",
            SiteName = "Thinktecture IdentityServer3 (self host)",

            SigningCertificate = Certificate.Get(),
            Factory = factory,
            AuthenticationOptions = new AuthenticationOptions
            {
                EnableLocalLogin = false,
                IdentityProviders = ConfigureIdentityProviders,
            },

        };

        appBuilder.UseIdentityServer(options);

    }

    public static void ConfigureIdentityProviders(IAppBuilder app, string signInAsType)
    {
        var adfs = new WsFederationAuthenticationOptions
        {
            AuthenticationType = "adfs",
            Caption = "Nevis",
            SignInAsAuthenticationType = signInAsType,

            MetadataAddress = "https://localhost:44332/core" + "/wsfed/metadata",
            Wtrealm = "urn:testrp",
            //Configuration = new WsFederationConfiguration() {Issuer = "",new "",}
        };
        app.UseWsFederationAuthentication(adfs);
    }
truthbeliever commented 9 years ago

The Authentification server receives no { "sub", ClaimTypes.NameIdentifier }, and no NameIdentifier = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier"; so internal static ExternalIdentity FromClaims(IEnumerable claims) { if (claims == null) throw new ArgumentNullException("claims");

        var subClaim = claims.FirstOrDefault(x => x.Type == Constants.ClaimTypes.Subject);
        if (subClaim == null)
        {
            subClaim = claims.FirstOrDefault(x => x.Type == ClaimTypes.NameIdentifier);

            if (subClaim == null)
            {
                return null;
            }
        }

So I added the following to the Authorisation server(the second one) var adfs = new WsFederationAuthenticationOptions { AuthenticationType = "adfs", Caption = "Nevis", SignInAsAuthenticationType = signInAsType,

            MetadataAddress = "https://localhost:44332/core" + "/wsfed/metadata",
            Wtrealm = "urn:testrp",
            Notifications = new WsFederationAuthenticationNotifications
            {
                //RedirectToIdentityProvider = n =>
                //{
                //    n.ProtocolMessage.Wauth = "urn:oasis:names:tc:SAML:1.0:am:password"; // to get the login view of ADFS to show
                //    return Task.FromResult(0);
                //},
                SecurityTokenValidated = n =>
                {
                    var incomingClaimsFromAdfs = n.AuthenticationTicket.Identity.Claims.ToList();
                    var incomingClaimsHasNameIdentifier = incomingClaimsFromAdfs.Any(c => c.Type == System.Security.Claims.ClaimTypes.NameIdentifier);

                    if (!incomingClaimsHasNameIdentifier)
                    {
                        var emailClaim = incomingClaimsFromAdfs.First(c => c.Type == System.Security.Claims.ClaimTypes.Name);
                        incomingClaimsFromAdfs.Add(new Claim(Thinktecture.IdentityServer.Core.Constants.ClaimTypes.Subject, emailClaim.Value));
                        //var emailClaim = incomingClaimsFromAdfs.First(c => c.Type == System.Security.Claims.ClaimTypes.Email);
                        //incomingClaimsFromAdfs.Add(new Claim(System.Security.Claims.ClaimTypes.NameIdentifier, emailClaim.Value));
                    }
                    else
                    {
                        throw new ApplicationException("Get ADFS to provide the NameIdentifier claim!");
                    }

                    var normalizedClaims = incomingClaimsFromAdfs.Distinct(new ClaimComparer());
                    var claimsIdentity = new ClaimsIdentity(normalizedClaims, n.AuthenticationTicket.Identity.AuthenticationType);
                    n.AuthenticationTicket = new AuthenticationTicket(claimsIdentity, n.AuthenticationTicket.Properties);
                    return Task.FromResult(0);
                }
            }
        };
        app.UseWsFederationAuthentication(adfs);

But now I got strange claims sub e3682a1fb4f38bbb96c291e305da2bc4 amr external auth_time 1425164358 idp adfs family_name Smith name Bob email BobSmith@email.com access_token fc53c4339a01905c23107b6cec19a182 expires_at 01.03.2015 00:59:22 refresh_token 9f41567583138e527458d5cb836bd104 id_token eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6ImEzck1VZ01Gdjl0UGNsTGE2eUYzekFrZnF1RSIsImtpZCI6ImEzck1VZ01Gdjl0UGNsTGE2eUYzekFrZnF1RSJ9.eyJub25jZSI6IjYzNTYwNzYxMTA5NDI5NzgxOS5NRGhqWlRBNU1HUXRPVE0yTXkwME9EZGtMV0ZqWm1ZdE5tSXdZbVprWVRNeU56aGhOelExTmpJNVpXTXRNVGd3WlMwMFlUa3dMVGhpTTJFdE9XVTNZVFZtT0RaaE1UbGkiLCJpYXQiOjE0MjUxNjQzNTksImF0X2hhc2giOiJDZlFYYl9VTHdiVG05MENIcE1IbGpnIiwiY19oYXNoIjoiTTR2TzNUdWlIY0tWeFFUMGZsUWFpZyIsInN1YiI6ImUzNjgyYTFmYjRmMzhiYmI5NmMyOTFlMzA1ZGEyYmM0IiwiYW1yIjoiZXh0ZXJuYWwiLCJhdXRoX3RpbWUiOjE0MjUxNjQzNTgsImlkcCI6ImFkZnMiLCJpc3MiOiJodHRwczovL2xvY2FsaG9zdDo0NDMzMy9jb3JlIiwiYXVkIjoiY29kZWNsaWVudCIsImV4cCI6MTQyNTE2NDY1OSwibmJmIjoxNDI1MTY0MzU5fQ.ARhCJLVhyMNbg27qaHm7fMuI1VCqgsuKejea2NFzbm7pw7XIByyeYpkefnKkzRa9vw9QifugJOrP_dikbLn_8ntxoAo-Wo3NNQJ7zwVOPjzN9b5gosAMaYnUwlwp8E6O6X_LTC9GWvcvGf0v06Smjmn5x2x9TulUjcWyhmuOWKg43OFwHcafjCySH8B6ioVsBP1QpVy3KtShOOn37IxuAdyRHHQkQbMt3ilMimkV2HMRON6yC7wGWZF2RyMR-FPSWLF1xjgtaHx7HQf48IMuyM_pOI-bYo8PAiyThIWe3nkXJH9G__yWFGTODcEtVFpNioe6269wBA4l7Ze0LK8KWg

what a subject!! I debugeed my notification and I added there an Bob as sub claim.

Can someone please give me a hint what I am missing?

Here a the logs of the authorisation server(the second one) local login disabled only one provider for client redirecting to provider URL: https://localhost:44333/core/external?provider=adfs&signin=da6e7664691122d2de43363358bd8a7c External login requested for provider: adfs Triggering challenge for external identity provider Callback invoked from external identity provider external user provider: adfs, provider ID: Bob External identity successfully validated by user service issuing cookie redirecting to: https://localhost:44333/core/connect/authorize?client_id=codeclient&redirect_uri=https:%2F%2Flocalhost:44312%2Fapp&response_mode=for -- Start authorize request

leastprivilege commented 9 years ago

The external IdP must produce either a NameIdentifier (SAML thing) or a sub (OIDC thing) claim to uniquely identify the external user.

The in-memory user service maps the external user id to a new subject identifier the first time it sees the user. looks normal to me.

truthbeliever commented 9 years ago

I added to the in memory users of the SelfHost (InMem with WS-Fed) project new Claim(ClaimTypes.NameIdentifier, "alice"),

Why is now my sub efcbbe726eed350a044c0c21cd2a485d instead of alice?

leastprivilege commented 9 years ago

Check the source code - Alice is an external identifier, sub an internal. If you want to retain the "alice" string, emit it as an additional name claim in your ext. idp

https://github.com/IdentityServer/Thinktecture.IdentityServer3/blob/master/source%2FCore%2FServices%2FInMemory%2FInMemoryUserService.cs#L91

truthbeliever commented 9 years ago

Thank you very much! If I want now to transform the incoming WS Fed claims in my second Selfhost(minimal) instance- What would be the approbiate place? The notifications hook in? Is my solution following about being ok?

public static void ConfigureIdentityProviders(IAppBuilder app, string signInAsType)
    {
        var adfs = new WsFederationAuthenticationOptions
        {
            AuthenticationType = "adfs",
            Caption = "Nevis",
            SignInAsAuthenticationType = signInAsType,

            MetadataAddress = "https://localhost:44332/core" + "/wsfed/metadata",
            Wtrealm = "urn:testrp",
            Notifications = new WsFederationAuthenticationNotifications
            {
                SecurityTokenValidated = n =>
                {
                    var incomingClaimsFromAdfs = n.AuthenticationTicket.Identity.Claims.ToList();
                    var theExtUser = incomingClaimsFromAdfs.First(c => c.Type == System.Security.Claims.ClaimTypes.NameIdentifier).Value;

                    var theNewUserClaims = Users.Get().First(u => u.Subject == theExtUser).Claims.Union(incomingClaimsFromAdfs).Distinct(new ClaimComparer());

                    var claimsIdentity = new ClaimsIdentity(theNewUserClaims, n.AuthenticationTicket.Identity.AuthenticationType);
                    n.AuthenticationTicket = new AuthenticationTicket(claimsIdentity, n.AuthenticationTicket.Properties);
                    return Task.FromResult(0);
                }
            }
        };
        app.UseWsFederationAuthentication(adfs);
    }
leastprivilege commented 9 years ago

up to you.

I would centralize that logic in the IUserService. The notifications are useful to make sure the minimum requirements of a unique id are met.

truthbeliever commented 9 years ago

IUserService like thinktecture IdentityServer3Samples\CustomUserService\CustomUserService

right?

leastprivilege commented 9 years ago

The user service connects IdentityServer to your user/profile store. So that is something you have to implement (could be also in-memory). The CustomUserService is an example for that.

truthbeliever commented 9 years ago

Thank you very much!!!