Использование Identity Server 3 для кэширования внешней аутентификации InMemory - PullRequest
1 голос
/ 29 апреля 2020

Я бы хотел использовать Identity Server 3 в качестве кэширующей аутентификации для внешнего провайдера идентификации. Я пытаюсь объединить 2 примера предоставленных примеров отсюда> https://github.com/IdentityServer/IdentityServer3.Samples

Моя цель - перенаправить пользователя из приложения MVC 5 на внешний адрес для сделать его / ее логин там (локальный логин отключен). Мы можем изменить поведение прослушивающего приложения MVC, поэтому я могу добавить необходимые изменения результата Cookie / HttpPost, чтобы это работало. Я застрял с. NET 4.6.2 tho. При успешном входе в систему я хотел бы запросить внешний источник для утверждений и ролей. Поэтому я копирую части CustomUserService, MVC Authentication примеров.

Когда у меня будет первый логин пользователя на внешнем сайте, я сохраню экземпляр InMemoryUser в измененном UserServiceBase, поэтому второй вход в систему за время существования памяти не должен go к внешнему ресурсу. Остальные обрабатываются с Bearer аутентификацией, идентификация подается из пользовательского UserServiceBase. Всякий раз, когда аннотируется [Authorize(Roles = "SomeRole")], внешний сайт также запрашивается для проверки с помощью пользовательского токена, указывающего c для внешнего сайта (вызов Web api к нему). Я даже не могу добраться до этой части, потому что частичный вход в систему и перенаправление, которые я не могу объединить.

Нет значимых scope требований. Внешний сайт дает все для создания Claims для пользователя. Пока в разработке нет ни сертификата, ни требования https.

Полагаю, это будет PartialLogin. Я не могу заставить его работать, поэтому данная конечная точка externalregistration не дает мне 404, или бесконечное перенаправление, или пропускает частичную информацию для входа.

В данный момент мой код выдает 404, хотя указанные места Мне нужно Map, где мой сервер сопоставлен с .

Startup.cs

public void Configuration(IAppBuilder app)
{
    app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
    {
        Authority = "http://localhost:20000/identity",

        ClientId = "aa",
        ClientSecret = "secret",
        Scope = "openid profile roles sampleApi",
        ResponseType = "id_token token",
        RedirectUri = "http://localhost:20000/",

        SignInAsAuthenticationType = "idsrv",
        UseTokenLifetime = false,

        Notifications = new OpenIdConnectAuthenticationNotifications
        {
            SecurityTokenValidated = async n =>
            {
                // I don't get there, so it doesn't really matter.
                var nid = new ClaimsIdentity(
                    n.AuthenticationTicket.Identity.AuthenticationType,
                    Constants.ClaimTypes.GivenName,
                    Constants.ClaimTypes.Role);

                // get userinfo data
                var userInfoClient = new UserInfoClient(
                    new Uri(n.Options.Authority + "/connect/userinfo"),
                    n.ProtocolMessage.AccessToken);

                var userInfo = await userInfoClient.GetAsync();
                userInfo.Claims.ToList().ForEach(ui => nid.AddClaim(new Claim(ui.Item1, ui.Item2)));

                // keep the id_token for logout
                nid.AddClaim(new Claim("id_token", n.ProtocolMessage.IdToken));

                // add access token for sample API
                nid.AddClaim(new Claim("access_token", n.ProtocolMessage.AccessToken));

                // keep track of access token expiration
                nid.AddClaim(new Claim("expires_at", DateTimeOffset.Now.AddSeconds(int.Parse(n.ProtocolMessage.ExpiresIn)).ToString()));

                // add some other app specific claim
                nid.AddClaim(new Claim("app_specific", "some data"));

                n.AuthenticationTicket = new AuthenticationTicket(
                    nid,
                    n.AuthenticationTicket.Properties);
            },

            RedirectToIdentityProvider = n =>
            {
                if (n.ProtocolMessage.RequestType == OpenIdConnectRequestType.LogoutRequest)
                {
                    var idTokenHint = n.OwinContext.Authentication.User.FindFirst("id_token");

                    if (idTokenHint != null)
                    {
                        n.ProtocolMessage.IdTokenHint = idTokenHint.Value;
                    }
                }

                return Task.FromResult(0);
            }
        }
    });

    app.Map("/identity", idsrvApp =>
    {
        var factory = new IdentityServerServiceFactory()
            .UseInMemoryClients(Clients.Get())
            .UseInMemoryScopes(Scopes.Get());

        factory.UserService = new Registration<IUserService, ExternalRegistrationUserService>();

        var options = new IdentityServerOptions
        {
            RequireSsl = false,

            EnableWelcomePage = false,

            //SigningCertificate = LoadCertificate(),

            Factory = factory,

            AuthenticationOptions = new IdentityServer3.Core.Configuration.AuthenticationOptions
            {
                EnableLocalLogin = false
            },

            LoggingOptions = new LoggingOptions
            {
                EnableWebApiDiagnostics = true,
                WebApiDiagnosticsIsVerbose = true,
                EnableHttpLogging = true,
                EnableKatanaLogging = true
            }
        };
        idsrvApp.UseIdentityServer(options);

    });

    AreaRegistration.RegisterAllAreas();
    FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
    RouteConfig.RegisterRoutes(RouteTable.Routes);
    BundleConfig.RegisterBundles(BundleTable.Bundles);
}

Clients.cs

public static class Clients
{
    public static IEnumerable<Client> Get()
    {
        return new[]
        {
            new Client
            {
                ClientName = "MVC Client",
                ClientId = "aa",
                Flow = Flows.Implicit,
                ClientSecrets = new List<Secret>
                {
                    new Secret("secret".Sha256())
                },
                RedirectUris = new List<string>
                {
                    "http://localhost:20000/"
                },
                PostLogoutRedirectUris = new List<string>
                {
                    "http://localhost:20000/"
                },
                AllowedScopes = new List<string>
                {
                    "openid",
                    "profile",
                    "roles",
                    "sampleApi"
                }
            }
        };
    }
}

ExternalRegistrationUserService.cs

using AuthenticateResult = IdentityServer3.Core.Models.AuthenticateResult;

public class ExternalRegistrationUserService : UserServiceBase
{
    private readonly OwinContext _ctx;

    public ExternalRegistrationUserService(OwinEnvironmentService owinEnv)
    {
        _ctx = new OwinContext(owinEnv.Environment);
    }

    public static List<InMemoryUser> Users = new List<InMemoryUser>();

    public override Task PreAuthenticateAsync(PreAuthenticationContext context)
    {
        var id = _ctx.Request.Query.Get("signin");
        // I get there, and the id is a generated code/token/whatever.
        context.AuthenticateResult = new AuthenticateResult("/identity/externalregistration", id, "Alma",
            identityProvider: "idsrv", authenticationMethod: Constants.PartialSignInAuthenticationType);
        return Task.FromResult(0);
    }

    public override Task AuthenticateExternalAsync(ExternalAuthenticationContext context)
    {
        // I don't get there.
        var id = _ctx.Request.Query.Get("signin");
        context.AuthenticateResult = new AuthenticateResult("/identity/externalregistration", id, "Alma",
            identityProvider: "idsrv", authenticationMethod: Constants.PartialSignInAuthenticationType);
        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);
    }

}

AuthenticationController.cs

public class AuthenticationController : Controller
{
    [Route("identity/externalregistration")]
    [HttpGet]
    public ActionResult Index()
    {
        // I don't get there.
        var ctx = Request.GetOwinContext();
        // If I don't route it below identity/externalregistration, than the variables below are empty.
        var authentication = ctx.Authentication.AuthenticateAsync(Constants.PartialSignInAuthenticationType).GetAwaiter().GetResult();
        var dummy = ctx.Environment.GetIdentityServerPartialLoginAsync().GetAwaiter().GetResult();
        var resumeUrl = ctx.Environment.GetPartialLoginResumeUrlAsync().GetAwaiter().GetResult();
        // external login page
        return Redirect("http://localhost:30000/Account/Login");
    }

    // there will be a Post version, where the 30000 port endpoint Redirects to.
    ...
}

Журналы

...