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

Federated IdentityProvider with Passive Login #604

Closed ghilios closed 9 years ago

ghilios commented 9 years ago

Hey, I'm having one hell of a time trying to this scenario to work. I am trying to set up IdentityServer v3 to use Azure AD as the only IdentityProvider, transform and enrich claims (ie, by calling GraphAPI) and then using that token instead.

My setup: 1) Relying Party app hosting a Web Api. https://localhost/SampleApp 2) Identity Server instance hosted on https://localhost/StsWithAdfs/auth (ignore the Adfs naming) 3) An Azure AD instance. I added an Application with App URI https://msedge.corp.microsoft.com/SDK that redirects to https://localhost/StsWithAdfs/auth/.

When I issue a request to /SampleApp/api/Values, it redirects to /StsWithAdfs/auto/wsfed?wtrealm... and then to /StsWithAdfs/auth/login?signin=... At no point does it seem to redirect to Azure AD. I always get stuck at an IdentityServer login screen asking for user name and password (ie, not my Azure AD creds). Is it also possible to get this to passively authenticate with Azure AD without any interaction from the user? In case it helps, below is the code I've been cobbling together. Perhaps I'm mixing my "Client" and "RelyingParty" concepts also, but I've tried various combinations to no avail.

Thanks for any help you can provide. I've been bashing my head against the desk for 3 days now!

Code for /StsWithAdfs

        public void Configuration(IAppBuilder app)
        {
            app.Map(
                "/auth",
                idSrv =>
                    {
                        var factory = InMemoryFactory.Create(
                            users: Enumerable.Empty<InMemoryUser>(),
                            scopes: StandardScopes.All,
                            clients: new Client[]
                                         {
                                             new Client()
                                                 {
                                                     ClientName = "client",
                                                     Enabled = true,
                                                     ClientId = "clientid",
                                                     Flow = Flows.AuthorizationCode,
                                                     ClientUri = "https://msedge.corp.microsoft.com/SDK",
                                                     RedirectUris = new List<Uri>()
                                                                        {
                                                                            new Uri("https://hiliosdev.corp.microsoft.com/SampleApp")
                                                                        },
                                                    IdentityTokenSigningKeyType = SigningKeyTypes.Default
                                                 }
                                         });
                        var cert = GetCertificate("CN=HILIOSDEV.redmond.corp.microsoft.com");

                        var options = new IdentityServerOptions
                                          {
                                              IssuerUri = "https://hiliosdev.corp.microsoft.com/StsWithAdfs",
                                              SiteName = "Edge SDK",
                                              SigningCertificate = cert,
                                              Factory = factory,
                                              PluginConfiguration = ConfigurePlugins,
                                              AuthenticationOptions = new AuthenticationOptions()
                                                                          {
                                                                              IdentityProviders = (appSrv, str) =>
                                                                                  {
                                                                                      appSrv.SetDefaultSignInAsAuthenticationType(WsFederationAuthenticationDefaults.AuthenticationType);
                                                                                      var ws = new WsFederationAuthenticationOptions()
                                                                                                   {
                                                                                                       MetadataAddress = "https://login.windows.net/689264f3-0b7f-4b2d-ab44-a65c27be2ac8/FederationMetadata/2007-06/FederationMetadata.xml",
                                                                                                       Wtrealm = "https://msedge.corp.microsoft.com/SDKPrototype",
                                                                                                       SignInAsAuthenticationType = str,
                                                                                                       AuthenticationType = WsFederationAuthenticationDefaults.AuthenticationType,
                                                                                                   };
                                                                                      appSrv.UseWsFederationAuthentication(ws);
                                                                                  },
                                                                                  EnableLocalLogin = false
                                                                          }
                                          };

                        idSrv.UseIdentityServer(options);
                    });
        }

        private void ConfigurePlugins(IAppBuilder pluginApp, IdentityServerOptions options)
        {
            var relyingParties = new List<RelyingParty>();
            /* var relyingParties = new List<RelyingParty>
            {   
                new RelyingParty
                {
                    Realm = "https://msedge.corp.microsoft.com/SDK",
                    Name = "Test App",
                    Enabled = true,
                    ReplyUrl = "https://hiliosdev.corp.microsoft.com/StsWithAdfs/auth/",
                    TokenType = Thinktecture.IdentityModel.Constants.TokenTypes.JsonWebToken,
                    TokenLifeTime = 1,

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

            var wsFedOptions = new WsFederationPluginOptions
            {
                IdentityServerOptions = options,
                Factory = new WsFederationServiceFactory
                {
                    UserService = options.Factory.UserService,
                    RelyingPartyService = Registration.RegisterFactory<IRelyingPartyService>(() => new InMemoryRelyingPartyService(relyingParties))
                },
                MetadataEndpoint = new EndpointSettings() { IsEnabled = true }
            };

            pluginApp.UseWsFederationPlugin(wsFedOptions);
        }

Code for /SampleApp

    {
        public void ConfigurationAuth(IAppBuilder app)
        {
            app.SetDefaultSignInAsAuthenticationType(WsFederationAuthenticationDefaults.AuthenticationType);

            app.UseCookieAuthentication(new CookieAuthenticationOptions
            {
                AuthenticationType = WsFederationAuthenticationDefaults.AuthenticationType
            });

            app.UseWsFederationAuthentication(new WsFederationAuthenticationOptions
            {
                MetadataAddress = "https://hiliosdev.corp.microsoft.com/StsWithAdfs/auth/wsfed/metadata",
                Wtrealm = "https://msedge.corp.microsoft.com/SDK"
            });

            ServicePointManager.ServerCertificateValidationCallback = (sender, certificate, chain, errors) => true;
        }
ghilios commented 9 years ago

I'm close I think. If I wipe my cookies and not use Fiddler for the initial request, it goes through login.windows.net each time. It doesn't ultimately redirect back to the originating Web Api endpoint though (/SampleApp/api/Values). Rather, it goes back to /StsWithAdfs/auth and redirects to /StsWithAdfs/auth/callback where it ends. Below is a summary

  1. GET https://hiliosdev.corp.microsoft.com/SampleApp/api/Values 302 Redirect to https://hiliosdev.corp.microsoft.com/StsWithAdfs/auth/wsfed?wtrealm=https%3a%2f%2fmsedge.corp.microsoft.com%2fSDK&wctx=WsFedOwinState%3dF9QFmkPNOxaoRsMdv7Ag0Z2rcDGo226uAIHMcK0PpK3dNaxDVK55NwFgfcYtU6gFCjTOdtHBI11mEpkRTeGeqyGyMjsG4gYCVhEULSjydBIoiZST_s3bGnq9AwnTQPaS3rq0jY8_07u3RenZscWPpGK4d_vCvTA6ORNtHqqOvkA&wa=wsignin1.0
  2. GET https://hiliosdev.corp.microsoft.com/StsWithAdfs/auth/wsfed?wtrealm=https%3a%2f%2fmsedge.corp.microsoft.com%2fSDK&wctx=WsFedOwinState%3dF9QFmkPNOxaoRsMdv7Ag0Z2rcDGo226uAIHMcK0PpK3dNaxDVK55NwFgfcYtU6gFCjTOdtHBI11mEpkRTeGeqyGyMjsG4gYCVhEULSjydBIoiZST_s3bGnq9AwnTQPaS3rq0jY8_07u3RenZscWPpGK4d_vCvTA6ORNtHqqOvkA&wa=wsignin1.0 302 Redirect to https://hiliosdev.corp.microsoft.com/StsWithAdfs/auth/login?signin=9802c0d7bfec44c9a6f9fa980f0daf16
  3. GET https://hiliosdev.corp.microsoft.com/StsWithAdfs/auth/login?signin=9802c0d7bfec44c9a6f9fa980f0daf16 302 Redirect to https://hiliosdev.corp.microsoft.com/StsWithAdfs/auth/external?provider=Federation&signin=9802c0d7bfec44c9a6f9fa980f0daf16
  4. GET https://hiliosdev.corp.microsoft.com/StsWithAdfs/auth/external?provider=Federation&signin=9802c0d7bfec44c9a6f9fa980f0daf16 302 Redirect to https://login.windows.net/689264f3-0b7f-4b2d-ab44-a65c27be2ac8/wsfed?wtrealm=https%3a%2f%2fmsedge.corp.microsoft.com%2fSDKPrototype&wctx=WsFedOwinState%3d8vFvcHOM3fNy95-pbmbKvH-zEjRamb1H695yfhPoYMSm9VKj_tACsVLoCEzCW0yW8R15DW7nQ4NWxTFW7gw8jvta4ce6oz5GbRarHceFfUxMXZ4-1GSOxGRS_z0IdycOaZ4cWrlY-I6ZYu2B61Fp1nsojkACuNBxFBmoCOC_uDlit12C27ubHL2r9GKICsQuWSnATDJzC0OstPbestb4kBH2MvPR9mkgjFESzzrulqo&wa=wsignin1.0
  5. GET https://login.windows.net/689264f3-0b7f-4b2d-ab44-a65c27be2ac8/wsfed?wtrealm=https%3a%2f%2fmsedge.corp.microsoft.com%2fSDKPrototype&wctx=WsFedOwinState%3d8vFvcHOM3fNy95-pbmbKvH-zEjRamb1H695yfhPoYMSm9VKj_tACsVLoCEzCW0yW8R15DW7nQ4NWxTFW7gw8jvta4ce6oz5GbRarHceFfUxMXZ4-1GSOxGRS_z0IdycOaZ4cWrlY-I6ZYu2B61Fp1nsojkACuNBxFBmoCOC_uDlit12C27ubHL2r9GKICsQuWSnATDJzC0OstPbestb4kBH2MvPR9mkgjFESzzrulqo&wa=wsignin1.0 200 OK (text/html)
  6. POST https://hiliosdev.corp.microsoft.com/StsWithAdfs/auth/ 302 Redirect to /StsWithAdfs/auth/callback
  7. GET https://hiliosdev.corp.microsoft.com/StsWithAdfs/auth/callback 200 OK (text/html)
brockallen commented 9 years ago

Ok, I see you have disabled local login.... so now you are getting back to IdentityServer from AAD, yes? So perhaps your katana middleware in the RP simply isn't monitoring the callback URL being submitted to.

ghilios commented 9 years ago

Ahh I finally figured this out. The issue is in the InMemoryUserService. When authorizing the external user, it looks for a "name" claim. Azure AD only returns givenname and surname claims, so the authorization fails. I got around this by providing my own IUserService implementation that uses the oid claim instead, which I'll then use in GetProfileAsync to query Graph API for the email address and other claims.

Thanks for looking!