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

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

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

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

ベアラートークン認証には3つの要素が含まれます。

  • Sitecore Identity(SI)サーバーです。SIサーバーはデフォルトでJWT(JSON Web Token)形式でアクセストークンを発行します。現在、関連するすべてのプラットフォームはJWTトークンの検証をサポートしています。 アクセス トークンと ベアラー トークンは同じものと考えることができます。
  • APIアプリケーションです。
  • MVCクライアントアプリケーションです。アプリケーションはSIサーバーからアクセストークンを要求し、それを使ってAPIへのアクセスを得ます。

本トピックで説明する手順は、以下の仮定に基づく例を使用しています。

このトピックでは、以下の方法を説明します:

APIの保護

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

APIを保護するために:

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

  2. プロジェクトへのパッケージ参照を追加:

    netcoreapp2.1
  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クライアントの設定

MVCクライアントに保護されたAPIを使用するように設定するには:

  1. 起動プロファイルでアプリケーションURLを設定してください。以下の例は、http://localhost:54567/ をURLとしてMVCクライアントを設定していると仮定しています。このURLを、SIサーバークライアント設定のAllowedCorsOriginsGroup1ノードで実際に使っているURLに置き換えてください。

  2. プロジェクトへのパッケージ参照を追加:

    netcoreapp2.1
  3. MVCアプリケーションのStartupクラスのConfigureServicesメソッドで、クッキーと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方式は認証サービスを追加します。クッキーはユーザー認証の主要な手段であり、CookiesがDefaultSchemeとして指定されているためです。DefaultChallengeSchemeがoidcとして指定されているため、ユーザーがログインする際にOpenID Connect方式を使用する必要があります。

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

    AddOpenIdConnectメソッドはOpenID Connectプロトコルを実行するハンドラーを設定します。AuthorityプロパティはSIサーバーが信頼されていることを示します。このクライアントはClientIdプロパティで識別できます。SignInSchemeメソッドは、OpenID Connectプロトコルが完了するとクッキーハンドラーを使ってクッキーを発行します。SaveTokensメソッドは、SIサーバーからのトークンをクッキー内に永続化します(後で必要になります)。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 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"}
    " + $"

    Access token: {accessToken}

    " + $"
    Refresh token: {refreshToken}

    " , "text/html"); } }

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

    Current user: sitecore\Admin Access token: eyJhbG......L8A Refresh token: 4cdf3b4d873a65135553afdf420a47dbc898ba0c1c0ece2407bbbf2bde02a68b

  6. Homeコントローラーにこのアクションを加え、ベアラートークンでAPIを呼び出す:

    Route("/callapi") public async Task 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メソッドでaccess_token引数を渡すことで、HttpContextからベアラー(アクセス)トークンを取得します。リクエストヘッダーにアクセストークンを追加する方法は以下の通りです:

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

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

    { "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;_z=z", "Text": "<p style.......e</a></p>\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

`
GMT

Content-Length: 0 }

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

交換、更新およびアクセストークン

SIサーバーには、プログラム的にトークンを要求するためのトークンエンドポイントがあります。サーバーはOpenID ConnectおよびOAuth 2.0のトークンリクエストパラメータの一部をサポートしています。OpenIDのドキュメント には完全なリストがあります

トークンのリクエストについて:

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

    Route("/exchange") public async Task 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(); 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)が成功すると、このリフレッシュトークンは無効になり、クッキー内のリフレッシュとアクセストークンを新しいものに更新しなければなりません。

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

GetTokenAsyncメソッドは更新されたアクセストークンやリフレッシュトークンを返します。

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