1. Sitecoreのアイデンティティ

クライアントアプリケーションでのベアラートークンの使用

Version:
日本語翻訳に関する免責事項

このページの翻訳はAIによって自動的に行われました。可能な限り正確な翻訳を心掛けていますが、原文と異なる表現や解釈が含まれる場合があります。正確で公式な情報については、必ず英語の原文をご参照ください。

このトピックでは、ベアラー トークン認証とSitecore Identityサーバーを使用して、MVCクライアントからAPIに安全にアクセスする方法について説明します。

ベアラートークン認証には、次の3つのことが含まれます。

  • Sitecore Identity (SI) サーバー。SIサーバは、デフォルトでJWT(JSON Web Token)形式でアクセストークンを発行します。今日、関連するすべてのプラットフォームは、JWTトークンの検証をサポートしています。 accessトークンとbearerトークンは同じものと考えることができます。

  • APIアプリケーション。

  • MVCクライアント アプリケーション。アプリケーションはSIサーバーにアクセス トークンをリクエストし、それを使用してAPIにアクセスします。

このトピックで説明する手順では、次の前提に基づく例を使用します。

  • SIサーバーはhttps://localhost:44356/で実行されます。例を使用する場合は、これを実際のURLに変更してください。

  • MVCクライアントはIDとしてMvcClientを持ち、SIサーバー上で次のように構成されます。

    <?xml version="1.0" encoding="utf-8"?>
    <Settings>
      <Sitecore>
        <IdentityServer>
          <Clients>
            <SampleMvcClient>
              <ClientId>MvcClient</ClientId>
              <ClientName>Sample MVC client</ClientName>
              <AccessTokenType>0</AccessTokenType>
              <AllowOfflineAccess>true</AllowOfflineAccess>
              <AlwaysIncludeUserClaimsInIdToken>true</AlwaysIncludeUserClaimsInIdToken>
              <AccessTokenLifetimeInSeconds>120</AccessTokenLifetimeInSeconds>
              <IdentityTokenLifetimeInSeconds>120</IdentityTokenLifetimeInSeconds>
              <AllowAccessTokensViaBrowser>true</AllowAccessTokensViaBrowser>
              <RequireConsent>false</RequireConsent>
              <RequireClientSecret>false</RequireClientSecret>
              <AllowedGrantTypes>
                <AllowedGrantType1>client_credentials</AllowedGrantType1>
                <AllowedGrantType2>hybrid</AllowedGrantType2>
              </AllowedGrantTypes>
              <RedirectUris>
                <RedirectUri1>{AllowedCorsOrigin}/signin-oidc</RedirectUri1>
              </RedirectUris>
              <PostLogoutRedirectUris>
                <PostLogoutRedirectUri1>{AllowedCorsOrigin}/signout-callback-oidc</PostLogoutRedirectUri1>
              </PostLogoutRedirectUris>
              <AllowedCorsOrigins>
                <AllowedCorsOriginsGroup1>http://localhost:54567</AllowedCorsOriginsGroup1>
              </AllowedCorsOrigins>
              <AllowedScopes>
                <AllowedScope1>openid</AllowedScope1>
                <AllowedScope2>sitecore.profile</AllowedScope2>
                <AllowedScope3>sitecore.profile.api</AllowedScope3>
              </AllowedScopes>
              <UpdateAccessTokenClaimsOnRefresh>true</UpdateAccessTokenClaimsOnRefresh>
            </SampleMvcClient>
          </Clients>
        </IdentityServer>
      </Sitecore>
    </Settings>

このトピックでは、次の方法について説明します。

APIの保護

このセクションでは、SIサーバーとベアラー トークン認証を使用してAPIを保護する基本的なシナリオの概要を説明します。ASP.NET CoreベースのAPIを保護するには、DIでJWTベアラー認証ハンドラーを構成し、認証ミドルウェアをパイプラインに追加するだけです。

APIを保護するには、次のようにします。

  1. ASP.NET Core Web APIテンプレートを使用してVisual Studioで新しいプロジェクトを作成し、起動プロファイルでアプリケーションのURLを構成します。ここで使用する例では、URLとしてhttp://localhost:55600/ を使用してAPIを構成することを前提としています。このURLは、独自のソリューションで実際に使用するURLに置き換えます。

  2. パッケージ参照をプロジェクトに追加します。

    <Project Sdk="Microsoft.NET.Sdk.Web">
      <PropertyGroup>
        <TargetFramework>netcoreapp2.1</TargetFramework>
      </PropertyGroup>
      <ItemGroup>
        <PackageReference Include="Microsoft.AspNetCore.App" />
      </ItemGroup>
    </Project>
  3. Startupクラスを設定します。

    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvcCore()
                .AddAuthorization()
                .AddJsonFormatters();
    
            services.AddAuthentication("Bearer")
                .AddJwtBearer("Bearer", options =>
                {
                    options.Authority = "https://localhost:44356";
                    options.RequireHttpsMetadata = false;
    
                    options.Audience = "sitecore.profile.api";
                });
        }
    
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            app.UseAuthentication();
            app.UseMvc();
        }
    }

    AddAuthentication方式では、認証サービスが追加され、Bearerがデフォルトのスキームとして設定されます。AddJwtBearerメソッドは、SIサーバーアクセストークン検証ハンドラを追加して、認証サービスがそれを使用できるようにします。UseAuthenticationメソッドは、認証ミドルウェアをパイプラインに追加して、ホストへの呼び出しごとに認証が自動的に実行されるようにします。

  4. 新しいコントローラーをAPIプロジェクトに追加します。

    [Route("[controller]")]
    [Authorize]
    public class IdentityController : ControllerBase
    {
        [HttpGet]
        public IActionResult Get()
        {
            return new JsonResult(from c in User.Claims
                select new
                {
                    c.Type,
                    c.Value
                });
        }
    }

コントローラー(http://localhost:55600/identity)に移動すると、401ステータスコードが返されます。これは、APIに資格情報が必要であり、SIサーバーによって保護されていることを示しています。

MVCクライアントを構成する

保護されたAPIを使用するようにMVCクライアントを設定するには、次のようにします。

  1. 起動プロファイルでアプリケーションのURLを設定します。次の例では、URLとしてhttp://localhost:54567/ を使用してMVCクライアントを構成したことを前提としています。このURLは、SIサーバー クライアント構成のAllowedCorsOriginsGroup1ノードにある独自のソリューションで実際に使用するURLに置き換えます。

  2. パッケージ参照をプロジェクトに追加します。

    <Project Sdk="Microsoft.NET.Sdk.Web">
      <PropertyGroup>
        <TargetFramework>netcoreapp2.1</TargetFramework>
      </PropertyGroup>
    
      <ItemGroup>
        <PackageReference Include="Microsoft.AspNetCore.All" Version="[2.1.3]" />
        <PackageReference Include="Microsoft.AspNetCore.Authentication.Abstractions" Version="[2.1.1.0]" />
        <PackageReference Include="IdentityModel" Version="[3.8.0]" />
      </ItemGroup>
    </Project>
  3. MVCアプリケーションのStartupクラスのConfigureServicesメソッドで、CookieとOpenID Connect認証のサポートを追加します。

    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();
    
            services.AddAuthentication(options =>
                {
                    options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                    options.DefaultChallengeScheme = "oidc";
                })
                .AddCookie(options =>
                {
                    options.ExpireTimeSpan = TimeSpan.FromMinutes(60);
                    options.Cookie.Name = "mvcimplicit";
                })
                .AddOpenIdConnect("oidc", options =>
                {
                    options.ClientId = "MvcClient";
                    options.Authority = "https://localhost:44356";
                    options.RequireHttpsMetadata = false;
                    options.GetClaimsFromUserInfoEndpoint = true;
                    options.ResponseType = "code token";
    
                    options.Scope.Clear();
                    options.Scope.Add("openid");
                    options.Scope.Add("sitecore.profile");
                    options.Scope.Add("offline_access");
                    options.Scope.Add("sitecore.profile.api");
    
                    options.SaveTokens = true;
    
                    options.TokenValidationParameters = new TokenValidationParameters
                    {
                        NameClaimType = JwtClaimTypes.Name,
                        RoleClaimType = JwtClaimTypes.Role,
                    };
                });
        }
    
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            app.UseDeveloperExceptionPage();
            app.UseStaticFiles();
            app.UseAuthentication();
            app.UseMvcWithDefaultRoute();
        }
    }

    AddAuthentication方式では、認証サービスが追加されます。Cookieは、CookiesがCookieとして指定されるため、ユーザーを認証する主要な方法ですDefaultSchemeDefaultChallengeSchemeoidcとして指定されているため、ユーザーがログインするときには、OpenID Connectスキームを使用する必要があります。

    AddCookieメソッドは、Cookieを処理できるハンドラーを追加します。

    AddOpenIdConnectメソッドは、OpenID Connectプロトコルを実行するハンドラーを構成します。Authorityプロパティは、SIサーバーが信頼されていることを指定します。このクライアントは、ClientIdプロパティで識別できます。SignInSchemeメソッドは、OpenID Connectプロトコルが完了すると、Cookieハンドラを使用してCookieを発行します。SaveTokensメソッドは、SIサーバーからのトークンをCookieに保持します(後で必要になります)。ResponseTypeプロパティをcode tokenとして指定します (これは実質的にハイブリッド フローを使用することを意味します)。

  4. ConfigureメソッドでUseAuthenticationメソッドを呼び出して、各リクエストに対して認証サービスが実行されることを確認します。パイプラインでMVCの前に認証ミドルウェアを追加する必要があります。

  5. 認証ハンドシェイクをトリガーするには、HomeコントローラーにAuthorize属性を追加します。

    [Authorize]
    public class Home : Controller
    {
        private static readonly HttpClient HttpClient = new HttpClient();
     
        public async Task<IActionResult> Index()
        {
            string accessToken = await HttpContext.GetTokenAsync("access_token");
            string refreshToken = await HttpContext.GetTokenAsync("refresh_token");
    
            return Content($"Current user: <span id=\"UserIdentityName\">{User.Identity.Name ?? "anonymous"}</span><br/>" +
                $"<div>Access token: {accessToken}</div><br/>" +
                $"<div>Refresh token: {refreshToken}</div><br/>"
                , "text/html");
        }
    }
    

    ブラウザでこのコントローラに移動すると、SIサーバへのリダイレクトが試行され、認証が成功すると、次のページが表示されます。

    Current user: sitecore\Admin
    Access token: eyJhbG......L8A
    Refresh token: 4cdf3b4d873a65135553afdf420a47dbc898ba0c1c0ece2407bbbf2bde02a68b
  6. 次のアクションをHomeコントローラーに追加して、ベアラー トークンを使用してAPIを呼び出します。

    [Route("/callapi")]
        public async Task<IActionResult> CallApi()
        {
            string accessToken = await HttpContext.GetTokenAsync("access_token");
    
            var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost:55600/identity");
            request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
            HttpResponseMessage response = await HttpClient.SendAsync(request);
    
            if (response.StatusCode != HttpStatusCode.OK)
            {
                return Content(response.ToString());
            }
    
            return Content($"{await response.Content.ReadAsStringAsync()}");
        }
  7. ベアラー (アクセス) トークンは、GetTokenAsyncメソッドを使用してHttpContextから取得するには、access_token引数を渡します。これは、リクエストヘッダーにアクセストークンを追加する方法です。

    request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);

    http://localhost:54567/callapi URLに移動します。答えは次のようになります。

    [
    {
    "type": "nbf",
    "value": "1543572239"
    },
    {
    "type": "exp",
    "value": "1543572359"
    },
    {
    "type": "iss",
    "value": "https://localhost:44356"
    },
    {
    "type": "aud",
    "value": "https://localhost:44356/resources"
    },
    {
    "type": "aud",
    "value": "sitecore.profile.api"
    },
    {
    "type": "client_id",
    "value": "MvcClient"
    },
    {
    "type": "name",
    "value": "sitecore\\Admin"
    },
    ........

呼び出し元のURL (http://localhost:55600/identity) を変更して、Sitecoreアイテム サービスなどの別のAPIを呼び出すことができます。

{
"ItemID": "110d559f-dea5-42ea-9c1c-8a5df7e70ef9",
"ItemName": "Home",
"ItemPath": "/sitecore/content/Home",
"ParentID": "0de95ae4-41ab-4d01-9eb0-67441b7c2450",
"TemplateID": "76036f5e-cbce-46d1-af0a-4143f9b557aa",
"TemplateName": "Sample Item",
"CloneSource": null,
"ItemLanguage": "en",
"ItemVersion": "1",
"DisplayName": "Home",
"HasChildren": "False",
"ItemIcon": "/temp/iconcache/network/16x16/home.png",
"ItemMedialUrl": "/-/icon/Network/48x48/home.png.aspx",
"ItemUrl": "~/link.aspx?_id=110D559FDEA542EA9C1C8A5DF7E70EF9&amp;amp;_z=z",
"Text": "&lt;p style.......e&lt;/a&gt;&lt;/p&gt;\r",
"Title": "Sitecore Experience Platform"
}

しばらくすると (SIサーバー上のクライアント構成でAccessTokenLifetimeInSecondsパラメーターとして指定)、次のような結果が得られます。

StatusCode: 401, ReasonPhrase:
'Unauthorized', Version: 1.1, Content:
System.Net.Http.HttpConnection+HttpConnectionResponseContent, Headers:
{
  Server:
Kestrel
 
WWW-Authenticate: Bearer error="invalid_token",
error_description="The token is expired"
  X-SourceFiles:
=?UTF-8?B?Qzpcclxfd1xpc1xzYW1wbGVzXEFwaVxpZGVudGl0eQ==?=
  X-Powered-By:
ASP.NET
  Date: Fri, 30
Nov 2018 07:42:20 GMT
 
Content-Length: 0
}

これは、アクセストークンの有効期限が切れているため、新しいトークンを取得する必要があることを意味します。次のセクションでは、これを行う方法について説明します。

Exchangeの更新トークンとアクセス トークン

SIサーバーには、プログラムでトークンを要求するために使用するトークン エンドポイントがあります。サーバーは、OpenID ConnectおよびOAuth 2.0トークン要求パラメーターのサブセットをサポートします。OpenIDのドキュメンテーション には、完全なリストがあります

トークンを要求するには:

  1. ホームコントローラーに次のアクションを追加します。

    [Route("/exchange")]
    public async Task<IActionResult> Exchange()
    {
        var disco = await DiscoveryClient.GetAsync("https://localhost:44356");
        if (disco.IsError) throw new Exception(disco.Error);
    
        var tokenClient = new TokenClient(disco.TokenEndpoint, "MvcClient", "secret");
        var rt = await HttpContext.GetTokenAsync("refresh_token");
        var tokenResult = await tokenClient.RequestRefreshTokenAsync(rt);
    
        if (!tokenResult.IsError)
        {
            var expiresAt = (DateTime.UtcNow + TimeSpan.FromSeconds(tokenResult.ExpiresIn)).ToString("o", CultureInfo.InvariantCulture);
    
            var authService = HttpContext.RequestServices.GetRequiredService<IAuthenticationService>();
            AuthenticateResult authenticateResult = await authService.AuthenticateAsync(HttpContext, null);
            AuthenticationProperties properties = authenticateResult.Properties;
    
            properties.UpdateTokenValue(OpenIdConnectParameterNames.RefreshToken, tokenResult.RefreshToken);
            properties.UpdateTokenValue(OpenIdConnectParameterNames.AccessToken, tokenResult.AccessToken);
            properties.UpdateTokenValue(OpenIdConnectParameterNames.ExpiresIn, expiresAt);
    
            await authService.SignInAsync(HttpContext, null, authenticateResult.Principal, authenticateResult.Properties);
    
            return Redirect("/");
        }
    
        return BadRequest();
    }

    TokenClientクラスのインスタンスを使用して、SIサーバーから新しいトークンを要求します。この要求の前に、有効な更新トークンが必要です。リクエストが成功すると(RequestRefreshTokenAsync)、この更新トークンは無効になり、Cookie内の更新トークンとアクセストークンを新しいトークンで更新する必要があります。

  2. AuthenticateAsyncメソッドを呼び出して、認証プロパティを取得します。UpdateTokenValueメソッドは、トークンとプロパティの有効期限のタイムスタンプを更新し、最後にSignInAsyncメソッドが認証Cookieを保存します。

これで、GetTokenAsyncメソッドは更新されたアクセストークンまたは更新トークンを返します。

この記事を改善するための提案がある場合は、 お知らせください!