クライアントアプリケーションでベアラートークンを使用する
このページの翻訳はAIによって自動的に行われました。可能な限り正確な翻訳を心掛けていますが、原文と異なる表現や解釈が含まれる場合があります。正確で公式な情報については、必ず英語の原文をご参照ください。
このトピックでは、ベアラートークン認証とSitecore Identityサーバーを使ってMVCクライアントからAPIに安全にアクセスする方法について説明します。
ベアラートークン認証には3つの要素が含まれます。
- Sitecore Identity(SI)サーバーです。SIサーバーはデフォルトでJWT(JSON Web Token)形式でアクセストークンを発行します。現在、関連するすべてのプラットフォームはJWTトークンの検証をサポートしています。 アクセス トークンと ベアラー トークンは同じものと考えることができます。
- APIアプリケーションです。
- MVCクライアントアプリケーションです。アプリケーションはSIサーバーからアクセストークンを要求し、それを使ってAPIへのアクセスを得ます。
本トピックで説明する手順は、以下の仮定に基づく例を使用しています。
-
SIサーバーはhttps://localhost:44356/上で動作します。例を使う場合は実際のURLに変更してください。
-
MVCクライアントはIDとしてMvcClientされており、SIサーバー上で次のように設定されています。
MvcClient Sample MVC client 0 true true 120 120 true false false client\_credentials hybrid {AllowedCorsOrigin}/signin-oidc {AllowedCorsOrigin}/signout-callback-oidc http://localhost:54567 openid sitecore.profile sitecore.profile.api true
このトピックでは、以下の方法を説明します:
APIの保護
この節では、SIサーバーとベアラートークン認証を用いてAPIを保護する基本的なシナリオを概説します。ASP.NETコアベースのAPIを保護するには、DIでJWTベアラー認証ハンドラを設定し、認証ミドルウェアをパイプラインに追加するだけです。
APIを保護するために:
-
ASP.NET Core Web APIテンプレートを使ってVisual Studio新しいプロジェクトを作成し、ローンチプロファイルでアプリケーションURLを設定してください。ここで使っている例は、APIをhttp://localhost:55600/として設定することを前提としています。このURLを、自分のソリューションで実際に使っているURLに置き換えてください。
-
プロジェクトへのパッケージ参照を追加:
netcoreapp2.1 -
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メソッドは認証ミドルウェアをパイプラインに追加し、ホストへのすべての呼び出しで自動的に認証が実行されます。
-
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 }); } }
MVCクライアントの設定
MVCクライアントに保護されたAPIを使用するように設定するには:
-
起動プロファイルでアプリケーションURLを設定してください。以下の例は、http://localhost:54567/ をURLとしてMVCクライアントを設定していると仮定しています。このURLを、SIサーバークライアント設定のAllowedCorsOriginsGroup1ノードで実際に使っているURLに置き換えてください。
-
プロジェクトへのパッケージ参照を追加:
netcoreapp2.1 -
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として指定してください(これは実質的にハイブリッドフローを使うことを意味します)。
-
ConfigureメソッドでUseAuthenticationメソッドを呼び出して、各リクエストに対して認証サービスが実行されることを確認してください。パイプラインにMVCする前に認証ミドルウェアを追加する必要があります。
-
認証ハンドシェイクを発動するには、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
-
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()}"); }
-
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&_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
` GMTContent-Length: 0 }
つまり、アクセストークンが期限切れになり、新しいトークンを取得する必要があるということです。次のセクションでは、その方法について説明します。
交換、更新およびアクセストークン
SIサーバーには、プログラム的にトークンを要求するためのトークンエンドポイントがあります。サーバーはOpenID ConnectおよびOAuth 2.0のトークンリクエストパラメータの一部をサポートしています。OpenIDのドキュメント には完全なリストがあります。
トークンのリクエストについて:
-
ホームコントローラーに以下のアクションを追加します:
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)が成功すると、このリフレッシュトークンは無効になり、クッキー内のリフレッシュとアクセストークンを新しいものに更新しなければなりません。
-
認証プロパティを取得するためにAuthenticateAsyncメソッドを呼び出します。 UpdateTokenValueメソッドはトークンとプロパティ内の有効期限タイムスタンプを更新し、最後にSignInAsyncメソッドが認証クッキーを保存します。
GetTokenAsyncメソッドは更新されたアクセストークンやリフレッシュトークンを返します。