ASP.NET Web API и OpenID Connect: как получить токен доступа из кода авторизации

Я пытаюсь запустить OpenID Connect... Пользователю моего веб-API удалось получить код авторизации для поставщика OpenID Connect. Как я должен передать этот код в свой веб-API ASP.NET? Как мне настроить OWIN Middleware, чтобы я мог получить токен доступа с использованием кода авторизации?

UPDATE: SPA использует AJAX для связи с моей веб-службой (ASP.NET Web API). В моей веб-службе используется промежуточное ПО OWIN. Я установил OpenIDConnect в качестве механизма аутентификации. Когда веб-служба вызывается в первый раз, она успешно перенаправляет пользователя на страницу входа в систему поставщика OpenID Connect. Пользователь смог войти в систему и получить в результате код авторизации. AFAIK этот код теперь можно использовать (через мой веб-сервис) для токена доступа. Однако я не знаю, как вернуть этот код в мой веб-сервис (это делается с помощью заголовка?), А затем, что настроить, чтобы получить токен доступа. Я предполагаю, что я мог бы вызывать конечную точку токена вручную, но я хотел бы использовать компонент OWIN.

Ответ 1

Похоже, что рекомендуется использовать событие AuthorizationCodeReceived для обмена кодом Auth для токена доступа. У Vittorio есть запись в блоге, которая описывает общий поток.

Вот пример из этого примера приложения на GitHub кода Startup.Auth.cs, чтобы установить это:

app.UseOpenIdConnectAuthentication(
    new OpenIdConnectAuthenticationOptions
    {
        ClientId = clientId,
        Authority = Authority,
        Notifications = new OpenIdConnectAuthenticationNotifications()
        {
            AuthorizationCodeReceived = (context) =>
           {
               var code = context.Code;
               ClientCredential credential = new ClientCredential(clientId, appKey);
               string tenantID = context.AuthenticationTicket.Identity.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid").Value;
               string signedInUserID = context.AuthenticationTicket.Identity.FindFirst(ClaimTypes.NameIdentifier).Value;
               AuthenticationContext authContext = new AuthenticationContext(string.Format("https://login.windows.net/{0}", tenantID), new EFADALTokenCache(signedInUserID));
               AuthenticationResult result = authContext.AcquireTokenByAuthorizationCode(
                           code, new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path)), credential, graphResourceID);

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

Примечание. Событие AuthorizationCodeReceived вызывается только один раз, когда авторизация действительно имеет место. Если код аутентификации уже создан и сохранен, это событие не вызывается. Вы должны выйти или удалить файлы cookie, чтобы это событие произошло.

Ответ 2

BenV уже ответила на вопрос, но там больше, чтобы рассмотреть.

class partial Startup
{
    public void ConfigureAuth(IAppBuilder app)
    {
        // ...

        app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
          {
            ClientId = clientId,
            Authority = authority,
            Notifications = new OpenIdConnectAuthenticationNotifications() {
                AuthorizationCodeReceived = (context) => {
                   string authorizationCode = context.Code;
                   // (tricky) the authorizationCode is available here to use, but...
                   return Task.FromResult(0);
                }
            }
          }
    }
}

Две проблемы:

  • Прежде всего, authorizationCode быстро истечет. Нет смысла хранить его.
  • Вторая проблема заключается в том, что событие AuthorizationCodeReceived не будет запущено ни для одной из перезагрузок страниц, если срок действия authorizationCode не истек и сохранен внутри сеанса.

Что вам нужно сделать - вызывать AcquireTokenByAuthorizationCodeAsync, который будет кэшировать его и правильно обрабатывать внутри TokenCache.DefaultShare:

AuthorizationCodeReceived = (context) => {
    string authorizationCode = context.Code;
    AuthenticationResult tokenResult = await context.AcquireTokenByAuthorizationCodeAsync(authorizationCode, new Uri(redirectUri), credential);
    return Task.FromResult(0);
}

Теперь, прежде чем каждый вызовет ресурс, вызовите AcquireTokenSilentAsync, чтобы получить accessToken (он будет использовать TokenCache или молча использовать refreshToken). Если токен истек, он будет вызывать исключение AdalSilentTokenAcquisitionException (вызвать процедуру продления кода доступа).

// currentUser for ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier")
AuthenticationResult authResult = await context.AcquireTokenSilentAsync(resourceUri, credential, currentUser);

Вызов AcquireTokenSilentAsync очень быстрый, если маркер кэширован.

Ответ 3

Чтобы выполнить пользовательскую авторизацию, вам необходимо обойти проверку подлинности owin по умолчанию:

           new OpenIdConnectAuthenticationOptions
            {
                ...,
                TokenValidationParameters = new System.IdentityModel.Tokens.TokenValidationParameters
                {
                    ValidateIssuer = false
                },