Как мне получить доступ к данным Microsoft.Owin.Security.xyz OnAuthenticated context AddClaims?

Я пытаюсь получить пользовательские свойства, которые возвращаются в контексте OnAuthenticated и добавляются в качестве претензий в следующем примере: Как получить доступ к личной информации Facebook с помощью идентификатора ASP.NET( Owin)?

Я вижу, что данные, которые я ожидаю, возвращаются при входе в систему и добавляются в качестве претензии в Starup.Auth.cs. Но, когда я нахожусь в Контроллере учетных записей, единственные претензии, которые появляются в UserManager или UserStore, выдается ЛОКАЛЬНЫМ АВТОРИТЕМ. Никакие претензии не могут быть найдены для Facebook (или других внешних поставщиков). Где заканчиваются претензии, добавленные в контекст? (Я использую VS2013 RTM.)

Полный источник и живой сайт на Azure, связанный здесь: https://github.com/johndpalm/IdentityUserPropertiesSample/tree/VS2013rtm

Вот что у меня в Startup.Auth.cs:

var facebookOptions = new Microsoft.Owin.Security.Facebook.FacebookAuthenticationOptions()
{
    AppId = ConfigurationManager.AppSettings.Get("FacebookAppId"),
    AppSecret = ConfigurationManager.AppSettings.Get("FacebookAppSecret"),
    Provider = new Microsoft.Owin.Security.Facebook.FacebookAuthenticationProvider()
    {
        OnAuthenticated = (context) =>
            {
                const string XmlSchemaString = "http://www.w3.org/2001/XMLSchema#string";
                foreach (var x in context.User)
                {
                    var claimType = string.Format("urn:facebook:{0}", x.Key);
                    string claimValue = x.Value.ToString();
                    if (!context.Identity.HasClaim(claimType, claimValue))
                        context.Identity.AddClaim(new System.Security.Claims.Claim(claimType, claimValue, XmlSchemaString, "Facebook"));

                }
                context.Identity.AddClaim(new System.Security.Claims.Claim("urn:facebook:access_token", context.AccessToken, XmlSchemaString, "Facebook"));
                return Task.FromResult(0);
            }
    }

};

facebookOptions.Scope.Add("email");

app.UseFacebookAuthentication(facebookOptions);

Альтернативным способом захвата внешних свойств входа будет добавление отдельной заявки для токена доступа и заполнение ее свойствами:

const string XmlSchemaString = "http://www.w3.org/2001/XMLSchema#string";
var facebookOptions = new Microsoft.Owin.Security.Facebook.FacebookAuthenticationOptions
{
    AppId = ConfigurationManager.AppSettings.Get("FacebookAppId"),
    AppSecret = ConfigurationManager.AppSettings.Get("FacebookAppSecret"),
    Provider = new Microsoft.Owin.Security.Facebook.FacebookAuthenticationProvider()
    {
        OnAuthenticated = (context) =>
        {
            var claim = new System.Security.Claims.Claim("urn:facebook:access_token", context.AccessToken, XmlSchemaString, "Facebook");
            foreach (var x in context.User)
            {
                string key = string.Format("urn:facebook:{0}", x.Key);
                string value = x.Value.ToString();
                claim.Properties.Add(key, value);
            }

            context.Identity.AddClaim(claim);

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

ПРИМЕЧАНИЕ. Этот пример не работает: хотя было бы неплохо передать одно требование со свойствами. Внешний файл cookie, кажется, отмечает честь свойств претензий. Свойства становятся пустыми, когда они извлекаются позже из идентификатора.

Ответ 1

Мне удалось создать рабочий пример с использованием шаблонов RTM MVC 5, бит OWIN и ASP.NET. Вы можете найти полный источник и ссылку на живой рабочий пример здесь: https://github.com/johndpalm/IdentityUserPropertiesSample

Вот что сработало для меня:

Создайте новое (введите имя поставщика здесь) Объект AuthenticationOptions в Startup.ConfigureAuth(StartupAuth.cs), передав ему идентификатор клиента, секрет клиента и новый AuthenticationProvider. Вы будете использовать лямбда-выражение для передачи методу OnAuthenticated некоторого кода для добавления претензий к идентификатору, который содержит значения, которые вы извлекаете из контекста. Идентичность.

StartUp.Auth.cs

// Facebook : Create New App
// https://dev.twitter.com/apps
if (ConfigurationManager.AppSettings.Get("FacebookAppId").Length > 0)
{
    var facebookOptions = new Microsoft.Owin.Security.Facebook.FacebookAuthenticationOptions()
    {
        AppId = ConfigurationManager.AppSettings.Get("FacebookAppId"),
        AppSecret = ConfigurationManager.AppSettings.Get("FacebookAppSecret"),
        Provider = new Microsoft.Owin.Security.Facebook.FacebookAuthenticationProvider()
        {
            OnAuthenticated = (context) =>
                {
                    context.Identity.AddClaim(new System.Security.Claims.Claim("urn:facebook:access_token", context.AccessToken, XmlSchemaString, "Facebook"));
                    foreach (var x in context.User)
                    {
                        var claimType = string.Format("urn:facebook:{0}", x.Key);
                        string claimValue = x.Value.ToString();
                        if (!context.Identity.HasClaim(claimType, claimValue))
                            context.Identity.AddClaim(new System.Security.Claims.Claim(claimType, claimValue, XmlSchemaString, "Facebook"));

                    }
                    return Task.FromResult(0);
                }
        }

    };
    app.UseFacebookAuthentication(facebookOptions);
}

ПРИМЕЧАНИЕ. Поставщик auth Facebook работает с кодом, используемым здесь. Если вы используете этот же код у поставщика учетной записи Microsoft (или поставщик Foursquare, который я создал с использованием кода учетной записи MS в качестве модели), он не может войти в систему. Если вы выберете только параметр access_token, он будет работать нормально. Похоже, что некоторые параметры нарушают процесс входа в систему. (Проблема была обнаружена на katanaproject.codeplex.com, если ваш интерес к этому интересен.) Я обновлю, если найду причину. Я много не делал с Twitter или Google, не проверяя, что я могу получить access_token.

var msaccountOptions = new Microsoft.Owin.Security.MicrosoftAccount.MicrosoftAccountAuthenticationOptions() 
{
    ClientId = ConfigurationManager.AppSettings.Get("MicrosoftClientId"),
    ClientSecret = ConfigurationManager.AppSettings.Get("MicrosoftClientSecret"),
    Provider = new Microsoft.Owin.Security.MicrosoftAccount.MicrosoftAccountAuthenticationProvider()
    {
        OnAuthenticated = (context) =>
            {
                context.Identity.AddClaim(new System.Security.Claims.Claim("urn:microsoftaccount:access_token", context.AccessToken, XmlSchemaString, "Microsoft"));
                return Task.FromResult(0);
            }
    }                   
};

app.UseMicrosoftAccountAuthentication(msaccountOptions);

В AccountController я извлекаю ClaimsIdentity из AuthenticationManager, используя внешний файл cookie. Затем я добавляю его в идентификатор, созданный с помощью cookie приложения. Я проигнорировал любые претензии, которые начинаются с "... schemas.xmlsoap.org/ws/2005/05/identity/claims", поскольку он, казалось, нарушил логин.

AccountController.cs

private async Task SignInAsync(CustomUser user, bool isPersistent)
{
    AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
    var identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);

// Extracted the part that has been changed in SignInAsync for clarity.
    await SetExternalProperties(identity);

    AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity);
}

private async Task SetExternalProperties(ClaimsIdentity identity)
{
    // get external claims captured in Startup.ConfigureAuth
    ClaimsIdentity ext = await AuthenticationManager.GetExternalIdentityAsync(DefaultAuthenticationTypes.ExternalCookie);

    if (ext != null)
    {
        var ignoreClaim = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims";
        // add external claims to identity
        foreach (var c in ext.Claims)
        {
            if (!c.Type.StartsWith(ignoreClaim))
                if (!identity.HasClaim(c.Type, c.Value))
                    identity.AddClaim(c);
        } 
    }
}

И, наконец, я хочу отображать любые значения из LOCAL AUTHORITY. Я создал частичный вид _ExternalUserPropertiesListPartial, который отображается на странице /Account/Manage. Я получаю претензии, которые я ранее хранил в AuthenticationManager.User.Claim, а затем передал их в представление.

AccountController.cs

[ChildActionOnly]
public ActionResult ExternalUserPropertiesList()
{
    var extList = GetExternalProperties();
    return (ActionResult)PartialView("_ExternalUserPropertiesListPartial", extList);
}

private List<ExtPropertyViewModel> GetExternalProperties()
{
    var claimlist = from claims in AuthenticationManager.User.Claims
                    where claims.Issuer != "LOCAL AUTHORITY"
                    select new ExtPropertyViewModel
                    {
                        Issuer = claims.Issuer,
                        Type = claims.Type,
                        Value = claims.Value
                    };

    return claimlist.ToList<ExtPropertyViewModel>();
}

И только чтобы быть тщательным, представление:

_ExternalUserPropertiesListPartial.cshtml

@model IEnumerable<MySample.Models.ExtPropertyViewModel>

@if (Model != null)
{
    <legend>External User Properties</legend>
    <table class="table">
        <tbody>
            @foreach (var claim in Model)
            {
                <tr>
                    <td>@claim.Issuer</td>
                    <td>@claim.Type</td>
                    <td>@claim.Value</td>
                </tr>
            }
        </tbody>
    </table>
}

Снова рабочий пример и полный код находятся в GitHub: https://github.com/johndpalm/IdentityUserPropertiesSample

И любые отзывы, исправления или улучшения будут оценены.

Ответ 2

Итак, в этой статье объясняется, как все это работает очень хорошо: Развязка owin external auth

Но короткий ответ: когда вы получаете аутентификацию из facebook, это дает вам внешнюю идентичность. Затем вам нужно принять эту внешнюю идентификацию и "войти" в локальное имя приложения, в этом шаге, чтобы вам нужно было добавить любые претензии, которые вы хотите от внешней идентичности, к ClaimsIdentity, которая становится User.Identity.

Изменить: Чтобы уточнить, вы можете сделать это внутри ExternalLoginCallback:

    // GET: /Account/ExternalLoginCallback
    [AllowAnonymous]
    public async Task<ActionResult> ExternalLoginCallback(string returnUrl) {
        var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();
        if (loginInfo == null) {
            return RedirectToAction("Login");
        }

        // Sign in this external identity if its already linked
        var user = await UserManager.FindAsync(loginInfo.Login);
        if (user != null) {
            await SignInAsync(user, isPersistent: false);
            return RedirectToLocal(returnUrl);
        }

    private async Task SignInAsync(ApplicationUser user, bool isPersistent) {
        AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
        var identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
        AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity);
    }

Итак, вам нужно будет передать дополнительные данные в SignIn, который будет выглядеть примерно так:

   ClaimsIdentity id = await AuthenticationManager.GetExternalIdentityAsync(DefaultAuthenticationTypes.ExternalCookie);

В этом заявлении ClaimsIdentity будет добавлено ваше требование, и вам нужно будет добавить эту претензию к идентификатору, созданному в методе SignInAsync, чтобы он отображался.

Ответ 3

Короче говоря, строка, которая требуется после использования AddClaim, выглядит следующим образом:

Взятый от johns ответ выше.

ClaimsIdentity ext = await AuthenticationManager.GetExternalIdentityAsync(DefaultAuthenticationTypes.ExternalCookie);